From f7bc8a2a73b07d69751e96b062a75e8eec2271a3 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Mon, 14 Oct 2024 14:46:06 +0900 Subject: [PATCH 01/68] =?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=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/floor-plan/CanvasMenu.jsx | 7 +- .../wallLineOffset/WallLineOffsetSetting.jsx | 19 +- .../modal/wallLineOffset/type/WallLine.jsx | 91 ++----- .../roofcover/useWallLineOffsetSetting.js | 246 ++++++++++++++++-- 4 files changed, 263 insertions(+), 100 deletions(-) diff --git a/src/components/floor-plan/CanvasMenu.jsx b/src/components/floor-plan/CanvasMenu.jsx index d8b7510c..320277fb 100644 --- a/src/components/floor-plan/CanvasMenu.jsx +++ b/src/components/floor-plan/CanvasMenu.jsx @@ -191,7 +191,12 @@ export default function CanvasMenu(props) {
- + {canvasZoom}%
diff --git a/src/components/floor-plan/modal/wallLineOffset/WallLineOffsetSetting.jsx b/src/components/floor-plan/modal/wallLineOffset/WallLineOffsetSetting.jsx index fcd48af1..a013934c 100644 --- a/src/components/floor-plan/modal/wallLineOffset/WallLineOffsetSetting.jsx +++ b/src/components/floor-plan/modal/wallLineOffset/WallLineOffsetSetting.jsx @@ -7,9 +7,20 @@ import { useWallLineOffsetSetting } from '@/hooks/roofcover/useWallLineOffsetSet export default function WallLineOffsetSetting({ setShowWallLineOffsetSettingModal }) { const { getMessage } = useMessage() - const { type, setType, buttonMenu, currentWallLineRef, TYPES, radioTypeRef, arrow1Ref, arrow2Ref, length1Ref, length2Ref, handleSave } = - useWallLineOffsetSetting() - const [buttonAct, setButtonAct] = useState(1) + const { + type, + setType, + buttonMenu, + currentWallLineRef, + TYPES, + radioTypeRef, + arrow1Ref, + arrow2Ref, + length1Ref, + length2Ref, + handleSave, + wallLineEditRef, + } = useWallLineOffsetSetting() const wallLineProps = { length1Ref, @@ -45,7 +56,7 @@ export default function WallLineOffsetSetting({ setShowWallLineOffsetSettingModa
{getMessage('setting')}
- {type === TYPES.WALL_LINE_EDIT && } + {type === TYPES.WALL_LINE_EDIT && } {type === TYPES.OFFSET && }
diff --git a/src/components/floor-plan/modal/wallLineOffset/type/WallLine.jsx b/src/components/floor-plan/modal/wallLineOffset/type/WallLine.jsx index 4f5c6ea7..15ccefa3 100644 --- a/src/components/floor-plan/modal/wallLineOffset/type/WallLine.jsx +++ b/src/components/floor-plan/modal/wallLineOffset/type/WallLine.jsx @@ -1,8 +1,8 @@ import { useMessage } from '@/hooks/useMessage' -import { useEffect, useState } from 'react' +import { forwardRef, useEffect, useImperativeHandle, useState } from 'react' import { useEvent } from '@/hooks/useEvent' -export default function WallLine({ length1Ref, length2Ref, arrow1Ref, arrow2Ref, radioTypeRef, currentWallLineRef }) { +export default forwardRef(function WallLine({ length1Ref, length2Ref, arrow1Ref, arrow2Ref, radioTypeRef, currentWallLineRef }, ref) { const { getMessage } = useMessage() const { addDocumentEventListener, initEvent } = useEvent() const [type, setType] = useState(1) @@ -10,76 +10,25 @@ export default function WallLine({ length1Ref, length2Ref, arrow1Ref, arrow2Ref, const [arrow2, setArrow2] = useState('up') useEffect(() => { - addDocumentEventListener('keydown', document, keyDown) - return () => { initEvent() } }, []) + useImperativeHandle(ref, () => ({ + setArrow, + })) + + const setArrow = () => { + setArrow1(arrow1Ref.current) + setArrow2(arrow2Ref.current) + } + const onChange = (e) => { setType(Number(e.target.value)) radioTypeRef.current = e.target.value } - const handleBtnClick = (direction) => { - document.dispatchEvent(new KeyboardEvent('keydown', { key: direction })) - } - - const keyDown = (e) => { - if (currentWallLineRef.current === null) { - alert('보조선을 먼저 선택하세요') - return - } - - const key = e.key - - switch (key) { - case 'Down': // IE/Edge에서 사용되는 값 - case 'ArrowDown': { - if (radioTypeRef.current === '1') { - setArrow1('down') - arrow1Ref.current = 'down' - } else { - setArrow2('down') - arrow2Ref.current = 'down' - } - - break - } - case 'Up': // IE/Edge에서 사용되는 값 - case 'ArrowUp': - if (radioTypeRef.current === '1') { - setArrow1('up') - arrow1Ref.current = 'up' - } else { - setArrow2('up') - arrow2Ref.current = 'up' - } - break - case 'Left': // IE/Edge에서 사용되는 값 - case 'ArrowLeft': - if (radioTypeRef.current === '1') { - setArrow1('left') - arrow1Ref.current = 'left' - } else { - setArrow2('left') - arrow2Ref.current = 'left' - } - break - case 'Right': // IE/Edge에서 사용되는 값 - case 'ArrowRight': - if (radioTypeRef.current === '1') { - setArrow1('right') - arrow1Ref.current = 'right' - } else { - setArrow2('right') - arrow2Ref.current = 'right' - } - break - } - } - return ( <>
@@ -107,10 +56,10 @@ export default function WallLine({ length1Ref, length2Ref, arrow1Ref, arrow2Ref,
- - - - + + + +
@@ -141,10 +90,10 @@ export default function WallLine({ length1Ref, length2Ref, arrow1Ref, arrow2Ref,
- - - - + + + +
@@ -155,4 +104,4 @@ export default function WallLine({ length1Ref, length2Ref, arrow1Ref, arrow2Ref,
) -} +}) diff --git a/src/hooks/roofcover/useWallLineOffsetSetting.js b/src/hooks/roofcover/useWallLineOffsetSetting.js index 6536bc80..dce62b9b 100644 --- a/src/hooks/roofcover/useWallLineOffsetSetting.js +++ b/src/hooks/roofcover/useWallLineOffsetSetting.js @@ -10,6 +10,7 @@ export function useWallLineOffsetSetting() { const { showLine, addLine } = useLine() const { getMessage } = useMessage() const { addCanvasMouseEventListener, initEvent } = useEvent() + const wallLineEditRef = useRef(null) const length1Ref = useRef(null) const length2Ref = useRef(null) const radioTypeRef = useRef('1') @@ -17,6 +18,22 @@ export function useWallLineOffsetSetting() { const arrow1Ref = useRef(null) const arrow2Ref = useRef(null) + const drawLine = (point1, point2, idx) => { + const line = addLine([point1.x, point1.y, point2.x, point2.y], { + stroke: 'black', + strokeWidth: 4, + idx: idx, + selectable: true, + name: 'outerLine', + x1: point1.x, + y1: point1.y, + x2: point2.x, + y2: point2.y, + }) + + line.attributes = { ...currentWallLineRef.current.attributes } + } + const TYPES = { WALL_LINE_EDIT: 'wallLineEdit', OFFSET: 'offset', @@ -36,37 +53,114 @@ export function useWallLineOffsetSetting() { showLine(outerLine) }) - const roofs = canvas.getObjects().filter((obj) => obj.name === 'roofBase') - - roofs.forEach((roof) => { - roof.innerLines.forEach((innerLine) => { - canvas.remove(innerLine) - }) - canvas.remove(roof) + //outerLine과 그 text만 남겨둔다. + const exceptObjs = canvas.getObjects().filter((obj) => obj.name !== 'outerLine' && obj.parent?.name !== 'outerLine') + exceptObjs.forEach((obj) => { + canvas.remove(obj) }) - const wallLines = canvas.getObjects().filter((obj) => obj.name === 'wallLine') - wallLines.forEach((wallLine) => { - canvas.remove(wallLine) - }) + canvas.setActiveObject(outerLines[0]) + currentWallLineRef.current = outerLines[0] + addCircleByLine(currentWallLineRef.current) addCanvasMouseEventListener('mouse:down', mouseDown) return () => { + removeOuterLineEditCircle() canvas.discardActiveObject() initEvent() } }, []) + useEffect(() => { + removeOuterLineEditCircle() + addCanvasMouseEventListener('mouse:down', mouseDown) + if (type === TYPES.WALL_LINE_EDIT) { + addCircleByLine(currentWallLineRef.current) + } + }, [type]) + + const removeOuterLineEditCircle = () => { + canvas.remove(...canvas.getObjects().filter((obj) => obj.name === 'outerLineEditCircleStart' || obj.name === 'outerLineEditCircleEnd')) + } + const mouseDown = (e) => { + removeOuterLineEditCircle() if (!e.target || (e.target && e.target.name !== 'outerLine')) { + currentWallLineRef.current = null return } currentWallLineRef.current = e.target + if (type === TYPES.WALL_LINE_EDIT) { + addCircleByLine(currentWallLineRef.current) + } + } + + const addCircleByLine = (line) => { + const direction = currentWallLineRef.current?.direction + let startPoint + let endPoint + let circle1, circle2 + + if (direction === 'top' || direction === 'bottom') { + // y1, y2중에 더 작은 값이 startPoint, 더 큰 값이 endPoint + if (line.y1 > line.y2) { + startPoint = { x: line.x2, y: line.y2 } + endPoint = { x: line.x1, y: line.y1 } + } else { + startPoint = { x: line.x1, y: line.y1 } + endPoint = { x: line.x2, y: line.y2 } + } + arrow1Ref.current = 'down' + arrow2Ref.current = 'up' + } else { + // x1, x2중에 더 작은 값이 startPoint, 더 큰 값이 endPoint + if (line.x1 > line.x2) { + startPoint = { x: line.x2, y: line.y2 } + endPoint = { x: line.x1, y: line.y1 } + } else { + startPoint = { x: line.x1, y: line.y1 } + endPoint = { x: line.x2, y: line.y2 } + } + arrow1Ref.current = 'right' + arrow2Ref.current = 'left' + } + + circle1 = new fabric.Circle({ + radius: 10, + fill: 'red', + top: startPoint.y - 10, + left: startPoint.x - 10, + name: 'outerLineEditCircleStart', + hasControls: false, + hasBorders: false, + lockMovementX: true, + lockMovementY: true, + }) + + circle2 = new fabric.Circle({ + radius: 10, + fill: 'blue', + top: endPoint.y - 10, + left: endPoint.x - 10, + name: 'outerLineEditCircleEnd', + hasControls: false, + hasBorders: false, + lockMovementX: true, + lockMovementY: true, + }) + + wallLineEditRef.current.setArrow() + canvas.add(circle1) + canvas.add(circle2) } const handleSave = () => { + if (currentWallLineRef.current === null) { + alert('보조선을 먼저 선택하세요') + return + } switch (type) { case TYPES.WALL_LINE_EDIT: handleWallLineEditSave() @@ -78,19 +172,49 @@ export function useWallLineOffsetSetting() { } const handleWallLineEditSave = () => { - const direction = currentWallLineRef.current.direction - let canDirections = direction === 'left' || direction === 'right' ? ['up', 'down'] : ['left', 'right'] - if (radioTypeRef === 1) { - if (!canDirections.includes(arrow1Ref.current)) { - alert('방향을 다시 선택하세요') - return + const startPoint = canvas + .getObjects() + .filter((obj) => obj.name === 'outerLineEditCircleStart') + .map((obj) => { + return { + x: obj.left + obj.radius, + y: obj.top + obj.radius, + } + })[0] + const endPoint = canvas + .getObjects() + .filter((obj) => obj.name === 'outerLineEditCircleEnd') + .map((obj) => { + return { + x: obj.left + obj.radius, + y: obj.top + obj.radius, + } + })[0] + const length = Number(length1Ref.current.value) / 10 + let line1, line2 + if (radioTypeRef.current === '1') { + // 1지점 선택시 방향은 down, right만 가능 + if (arrow1Ref.current === 'down') { + drawLine({ x: startPoint.x, y: startPoint.y }, { x: startPoint.x, y: startPoint.y + length }, currentWallLineRef.current.idx) + drawLine({ x: startPoint.x, y: startPoint.y + length }, { x: endPoint.x, y: endPoint.y }, currentWallLineRef.current.idx) + } else { + drawLine({ x: startPoint.x, y: startPoint.y }, { x: startPoint.x + length, y: startPoint.y }, currentWallLineRef.current.idx) + drawLine({ x: startPoint.x + length, y: startPoint.y }, { x: endPoint.x, y: endPoint.y }, currentWallLineRef.current.idx) } } else { - if (!canDirections.includes(arrow2Ref.current)) { - alert('방향을 다시 선택하세요') - return + // 2지점일 경우 방향은 up, left만 가능 + if (arrow2Ref.current === 'up') { + drawLine({ x: endPoint.x, y: endPoint.y }, { x: endPoint.x, y: endPoint.y - length }, currentWallLineRef.current.idx) + drawLine({ x: endPoint.x, y: endPoint.y - length }, { x: startPoint.x, y: startPoint.y }, currentWallLineRef.current.idx) + } else { + drawLine({ x: endPoint.x, y: endPoint.y }, { x: endPoint.x - length, y: endPoint.y }, currentWallLineRef.current.idx) + drawLine({ x: endPoint.x - length, y: endPoint.y }, { x: startPoint.x, y: startPoint.y }, currentWallLineRef.current.idx) } } + removeOuterLineEditCircle() + canvas.remove(currentWallLineRef.current) + currentWallLineRef.current = null + canvas.renderAll() } const handleOffsetSave = () => { @@ -103,26 +227,100 @@ export function useWallLineOffsetSetting() { } const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') + const idx = currentWallLineRef.current.idx - const prevLine = outerLines.find((line) => (line.idx === idx - 1 < 0 ? outerLines.length - 1 : idx - 1)) + const prevIdx = idx - 1 <= 0 ? outerLines.length : idx - 1 + const nextIdx = idx + 1 > outerLines.length ? 1 : idx + 1 + const currentLine = currentWallLineRef.current - const nextLine = outerLines.find((line) => (line.idx === idx + 1 > outerLines.length - 1 ? 0 : idx + 1)) + const prevLine = outerLines.find((line) => line.idx === prevIdx) + const nextLine = outerLines.find((line) => line.idx === nextIdx) + + const length = length1Ref.current.value / 10 + switch (arrow1Ref.current) { case 'up': { - console.log(prevLine, currentLine, nextLine) + const currentLineY = Math.floor(Math.max(currentLine.y1, currentLine.y2)) + currentLine.set({ y1: currentLineY - length, y2: currentLineY - length }) + + if (Math.abs(currentLineY - prevLine.y1) < 2) { + prevLine.set({ y1: currentLineY - length }) + } else { + prevLine.set({ y2: currentLineY - length }) + } + if (Math.abs(currentLineY - nextLine.y1) < 2) { + nextLine.set({ y1: currentLineY - length }) + } else { + nextLine.set({ y2: currentLineY - length }) + } + break } case 'down': { + const currentLineY = Math.floor(Math.max(currentLine.y1, currentLine.y2)) + currentLine.set({ y1: currentLineY + length, y2: currentLineY + length }) + + if (Math.abs(currentLineY - prevLine.y1) < 2) { + prevLine.set({ y1: currentLineY + length }) + } else { + prevLine.set({ y2: currentLineY + length }) + } + if (Math.abs(currentLineY - nextLine.y1) < 2) { + nextLine.set({ y1: currentLineY + length }) + } else { + nextLine.set({ y2: currentLineY + length }) + } break } case 'left': { + const currentLineX = Math.floor(Math.max(currentLine.x1, currentLine.x2)) + currentLine.set({ x1: currentLineX - length, x2: currentLineX - length }) + + if (Math.abs(currentLineX - prevLine.x1) < 2) { + prevLine.set({ x1: currentLineX - length }) + } else { + prevLine.set({ x2: currentLineX - length }) + } + if (Math.abs(currentLineX - nextLine.x1) < 2) { + nextLine.set({ x1: currentLineX - length }) + } else { + nextLine.set({ x2: currentLineX - length }) + } break } case 'right': { + const currentLineX = Math.floor(Math.max(currentLine.x1, currentLine.x2)) + currentLine.set({ x1: currentLineX + length, x2: currentLineX + length }) + + if (Math.abs(currentLineX - prevLine.x1) < 2) { + prevLine.set({ x1: currentLineX + length }) + } else { + prevLine.set({ x2: currentLineX + length }) + } + if (Math.abs(currentLineX - nextLine.x1) < 2) { + nextLine.set({ x1: currentLineX + length }) + } else { + nextLine.set({ x2: currentLineX + length }) + } + break } } + canvas.renderAll() } - return { type, setType, buttonMenu, TYPES, length1Ref, length2Ref, radioTypeRef, currentWallLineRef, arrow1Ref, arrow2Ref, handleSave } + return { + type, + setType, + buttonMenu, + TYPES, + length1Ref, + length2Ref, + radioTypeRef, + currentWallLineRef, + arrow1Ref, + arrow2Ref, + handleSave, + wallLineEditRef, + } } From d6f46b419a2582578fd686812fca65e8d4aae939 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Mon, 14 Oct 2024 16:16:28 +0900 Subject: [PATCH 02/68] =?UTF-8?q?=EC=A7=80=EB=B6=95=ED=98=95=EC=83=81=20?= =?UTF-8?q?=EC=88=98=EB=8F=99=20=EC=84=A4=EC=A0=95=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../roofShape/RoofShapePassivitySetting.jsx | 43 +++-- .../modal/roofShape/passivity/Eaves.jsx | 6 +- .../modal/roofShape/passivity/Gable.jsx | 6 +- .../modal/roofShape/passivity/Shed.js | 4 +- src/hooks/roofcover/useAuxiliaryDrawing.js | 1 + src/hooks/roofcover/useEavesGableEdit.js | 1 + src/hooks/roofcover/useOuterLineWall.js | 1 + src/hooks/roofcover/usePropertiesSetting.js | 24 +-- .../roofcover/useRoofShapePassivitySetting.js | 175 ++++++++++++++++++ src/hooks/roofcover/useRoofShapeSetting.js | 1 + .../roofcover/useWallLineOffsetSetting.js | 36 +++- 11 files changed, 257 insertions(+), 41 deletions(-) create mode 100644 src/hooks/roofcover/useRoofShapePassivitySetting.js diff --git a/src/components/floor-plan/modal/roofShape/RoofShapePassivitySetting.jsx b/src/components/floor-plan/modal/roofShape/RoofShapePassivitySetting.jsx index ebdc6bb9..828b2ad3 100644 --- a/src/components/floor-plan/modal/roofShape/RoofShapePassivitySetting.jsx +++ b/src/components/floor-plan/modal/roofShape/RoofShapePassivitySetting.jsx @@ -4,15 +4,26 @@ import Eaves from '@/components/floor-plan/modal/roofShape/passivity/Eaves' import Gable from '@/components/floor-plan/modal/roofShape/passivity/Gable' import Shed from '@/components/floor-plan/modal/roofShape/passivity/Shed' import { useMessage } from '@/hooks/useMessage' +import { useRoofShapePassivitySetting } from '@/hooks/roofcover/useRoofShapePassivitySetting' export default function RoofShapePassivitySetting({ setShowRoofShapePassivitySettingModal }) { + const { handleSave, handleConfirm, handleRollback, buttons, type, setType, TYPES, offsetRef, pitchRef } = useRoofShapePassivitySetting() const { getMessage } = useMessage() - const [buttonAct, setButtonAct] = useState(1) - const buttons = [ - { id: 1, name: getMessage('eaves') }, - { id: 2, name: getMessage('gable') }, - { id: 3, name: getMessage('windage') }, - ] + + const eavesProps = { + offsetRef, + pitchRef, + } + + const gableProps = { + offsetRef, + pitchRef, + } + + const shedProps = { + offsetRef, + } + return (
@@ -25,7 +36,7 @@ export default function RoofShapePassivitySetting({ setShowRoofShapePassivitySet
{buttons.map((button) => ( - ))} @@ -33,17 +44,23 @@ export default function RoofShapePassivitySetting({ setShowRoofShapePassivitySet
{getMessage('setting')}
- {buttonAct === 1 && } - {buttonAct === 2 && } - {buttonAct === 3 && } + {type === TYPES.EAVES && } + {type === TYPES.GABLE && } + {type === TYPES.SHED && }
- - + +
- +
diff --git a/src/components/floor-plan/modal/roofShape/passivity/Eaves.jsx b/src/components/floor-plan/modal/roofShape/passivity/Eaves.jsx index 22e15a34..3a16b5c0 100644 --- a/src/components/floor-plan/modal/roofShape/passivity/Eaves.jsx +++ b/src/components/floor-plan/modal/roofShape/passivity/Eaves.jsx @@ -1,6 +1,6 @@ import { useMessage } from '@/hooks/useMessage' -export default function Eaves() { +export default function Eaves({ offsetRef, pitchRef }) { const { getMessage } = useMessage() return ( <> @@ -9,7 +9,7 @@ export default function Eaves() { {getMessage('slope')}
- +
@@ -18,7 +18,7 @@ export default function Eaves() { {getMessage('eaves.offset')}
- +
mm
diff --git a/src/components/floor-plan/modal/roofShape/passivity/Gable.jsx b/src/components/floor-plan/modal/roofShape/passivity/Gable.jsx index e2eb6342..69b2cf9d 100644 --- a/src/components/floor-plan/modal/roofShape/passivity/Gable.jsx +++ b/src/components/floor-plan/modal/roofShape/passivity/Gable.jsx @@ -1,6 +1,6 @@ import { useMessage } from '@/hooks/useMessage' -export default function Gable() { +export default function Gable({ offsetRef, pitchRef }) { const { getMessage } = useMessage() return ( <> @@ -9,7 +9,7 @@ export default function Gable() { {getMessage('slope')}
- +
@@ -18,7 +18,7 @@ export default function Gable() { {getMessage('gable.offset')}
- +
mm diff --git a/src/components/floor-plan/modal/roofShape/passivity/Shed.js b/src/components/floor-plan/modal/roofShape/passivity/Shed.js index dfdee92c..474c2f60 100644 --- a/src/components/floor-plan/modal/roofShape/passivity/Shed.js +++ b/src/components/floor-plan/modal/roofShape/passivity/Shed.js @@ -1,6 +1,6 @@ import { useMessage } from '@/hooks/useMessage' -export default function Shed() { +export default function Shed({ offsetRef }) { const { getMessage } = useMessage() return ( <> @@ -9,7 +9,7 @@ export default function Shed() { {getMessage('shed.width')}
- +
mm diff --git a/src/hooks/roofcover/useAuxiliaryDrawing.js b/src/hooks/roofcover/useAuxiliaryDrawing.js index de213685..62a3c687 100644 --- a/src/hooks/roofcover/useAuxiliaryDrawing.js +++ b/src/hooks/roofcover/useAuxiliaryDrawing.js @@ -27,6 +27,7 @@ import { calculateDistance, calculateIntersection, distanceBetweenPoints, findCl import { fabric } from 'fabric' import { useAdsorptionPoint } from '@/hooks/useAdsorptionPoint' +// 보조선 작성 export function useAuxiliaryDrawing() { const canvas = useRecoilValue(canvasState) const { diff --git a/src/hooks/roofcover/useEavesGableEdit.js b/src/hooks/roofcover/useEavesGableEdit.js index d2c69303..96ec864f 100644 --- a/src/hooks/roofcover/useEavesGableEdit.js +++ b/src/hooks/roofcover/useEavesGableEdit.js @@ -7,6 +7,7 @@ import { LINE_TYPE } from '@/common/common' import { useLine } from '@/hooks/useLine' import { useMode } from '@/hooks/useMode' +// 처마.케라바 변경 export function useEavesGableEdit() { const canvas = useRecoilValue(canvasState) const { getMessage } = useMessage() diff --git a/src/hooks/roofcover/useOuterLineWall.js b/src/hooks/roofcover/useOuterLineWall.js index e37495d9..d3b54c1c 100644 --- a/src/hooks/roofcover/useOuterLineWall.js +++ b/src/hooks/roofcover/useOuterLineWall.js @@ -30,6 +30,7 @@ import { import { calculateAngle } from '@/util/qpolygon-utils' import { fabric } from 'fabric' +//외벽선 그리기 export function useOuterLineWall() { const canvas = useRecoilValue(canvasState) const { addCanvasMouseEventListener, addDocumentEventListener, removeAllMouseEventListeners, removeAllDocumentEventListeners, removeMouseEvent } = diff --git a/src/hooks/roofcover/usePropertiesSetting.js b/src/hooks/roofcover/usePropertiesSetting.js index ebd5182e..a2905f43 100644 --- a/src/hooks/roofcover/usePropertiesSetting.js +++ b/src/hooks/roofcover/usePropertiesSetting.js @@ -7,6 +7,7 @@ import { usePolygon } from '@/hooks/usePolygon' import { useLine } from '@/hooks/useLine' import { outerLinePointsState } from '@/store/outerLineAtom' +// 외벽선 속성 설정 export function usePropertiesSetting() { const canvas = useRecoilValue(canvasState) @@ -19,17 +20,7 @@ export function usePropertiesSetting() { const { removeLine, hideLine } = useLine() useEffect(() => { - if (!currentObject) { - return - } - if (currentObject.name !== 'outerLine') { - return - } - - const type = currentObject.attributes?.type - const lines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') - lines.forEach((line) => { const lineType = line.attributes?.type if (!lineType) { @@ -39,6 +30,14 @@ export function usePropertiesSetting() { }) } }) + if (!currentObject) { + return + } + if (currentObject.name !== 'outerLine') { + return + } + + const type = currentObject.attributes?.type if (!type) { currentObject.set({ @@ -46,6 +45,8 @@ export function usePropertiesSetting() { strokeWidth: 4, }) } + + canvas.renderAll() }, [currentObject]) const history = useRef([]) @@ -106,6 +107,7 @@ export function usePropertiesSetting() { } const lastLine = history.current.pop() + delete lastLine.attributes lastLine.set({ stroke: '#000000', strokeWidth: 4, @@ -131,7 +133,7 @@ export function usePropertiesSetting() { hideLine(line) }) - const wall = addPolygonByLines(lines, { name: 'WallLine', fill: 'transparent', stroke: 'black' }) + const wall = addPolygonByLines(lines, { name: 'wallLine', fill: 'transparent', stroke: 'black' }) wall.lines = [...lines] diff --git a/src/hooks/roofcover/useRoofShapePassivitySetting.js b/src/hooks/roofcover/useRoofShapePassivitySetting.js new file mode 100644 index 00000000..245ee0d9 --- /dev/null +++ b/src/hooks/roofcover/useRoofShapePassivitySetting.js @@ -0,0 +1,175 @@ +import { canvasState, currentObjectState } from '@/store/canvasAtom' +import { useRecoilValue } from 'recoil' +import { useEffect, useRef, useState } from 'react' +import { useLine } from '@/hooks/useLine' +import { useMessage } from '@/hooks/useMessage' +import { useEvent } from '@/hooks/useEvent' +import { LINE_TYPE } from '@/common/common' +import { useMode } from '@/hooks/useMode' +import { usePolygon } from '@/hooks/usePolygon' + +//지붕형상 수동 설정 +export function useRoofShapePassivitySetting() { + const TYPES = { + EAVES: 'eaves', + GABLE: 'gable', + SHED: 'shed', + } + const canvas = useRecoilValue(canvasState) + const { getMessage } = useMessage() + const { showLine, hideLine } = useLine() + const { addCanvasMouseEventListener, initEvent } = useEvent() + const { drawRoofPolygon } = useMode() + const { addPolygonByLines } = usePolygon() + const currentObject = useRecoilValue(currentObjectState) + const offsetRef = useRef(null) + const pitchRef = useRef(null) + const currentLineRef = useRef(null) + + const history = useRef([]) + + const [type, setType] = useState(TYPES.EAVES) + + const buttons = [ + { id: 1, name: getMessage('eaves'), type: TYPES.EAVES }, + { id: 2, name: getMessage('gable'), type: TYPES.GABLE }, + { id: 3, name: getMessage('windage'), type: TYPES.SHED }, + ] + + useEffect(() => { + addCanvasMouseEventListener('mouse:down', mouseDown) + const wallLines = canvas.getObjects().filter((obj) => obj.name === 'wallLine') + canvas.remove(wallLines) + + const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') + outerLines.forEach((outerLine, idx) => { + if (idx === 0) { + currentLineRef.current = outerLine + } + outerLine.set({ selectable: true }) + showLine(outerLine) + outerLine.bringToFront() + }) + canvas?.renderAll() + + return () => { + canvas?.discardActiveObject() + initEvent() + } + }, []) + + useEffect(() => { + const lines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') + lines.forEach((line) => { + line.set({ + stroke: '#000000', + strokeWidth: 4, + }) + }) + if (!currentObject) { + return + } + if (currentObject.name !== 'outerLine') { + return + } + + currentObject.set({ + stroke: '#EA10AC', + strokeWidth: 4, + }) + + currentLineRef.current = currentObject + + canvas.renderAll() + }, [currentObject]) + + const mouseDown = (e) => { + if (!e.target) { + currentLineRef.current = null + return + } + + if (e.target && e.target.name === 'outerLine') { + currentLineRef.current = e.target + } + } + + const nextLineFocus = (selectedLine) => { + const lines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') + const index = lines.findIndex((line) => line === selectedLine) + + const nextLine = lines[index + 1] || lines[0] + canvas.setActiveObject(nextLine) + } + + const handleConfirm = () => { + if (!currentLineRef.current) { + alert('선택된 외곽선이 없습니다.') + return + } + let attributes + const offset = Number(offsetRef.current.value) / 10 + if (type === TYPES.EAVES) { + attributes = { + type: LINE_TYPE.WALLLINE.EAVES, + offset, + pitch: pitchRef.current.value, + } + } else if (type === TYPES.GABLE) { + attributes = { + type: LINE_TYPE.WALLLINE.GABLE, + pitch: pitchRef.current.value, + offset, + } + } else if (type === TYPES.SHED) { + attributes = { + type: LINE_TYPE.WALLLINE.SHED, + offset, + } + } + + currentLineRef.current.set({ + attributes, + }) + + history.current.push(currentLineRef.current) + nextLineFocus(currentLineRef.current) + } + + const handleRollback = () => { + if (history.current.length === 0) { + return + } + const lastLine = history.current.pop() + + delete lastLine.attributes + lastLine.set({ + stroke: '#000000', + strokeWidth: 4, + }) + + canvas.setActiveObject(lastLine) + canvas.renderAll() + } + + const handleSave = (fn) => { + const exceptObjs = canvas.getObjects().filter((obj) => obj.name !== 'outerLine' && obj.parent?.name !== 'outerLine') + const lines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') + exceptObjs.forEach((obj) => { + canvas.remove(obj) + }) + + lines.forEach((line) => { + hideLine(line) + }) + + const wall = addPolygonByLines(lines, { name: 'wallLine', fill: 'transparent', stroke: 'black' }) + + wall.lines = [...lines] + + const roof = drawRoofPolygon(wall) + canvas.renderAll() + fn(false) + } + return { handleSave, handleConfirm, buttons, type, setType, TYPES, offsetRef, pitchRef, handleRollback } +} diff --git a/src/hooks/roofcover/useRoofShapeSetting.js b/src/hooks/roofcover/useRoofShapeSetting.js index 2c51b2bd..c0d65a21 100644 --- a/src/hooks/roofcover/useRoofShapeSetting.js +++ b/src/hooks/roofcover/useRoofShapeSetting.js @@ -7,6 +7,7 @@ import { usePolygon } from '@/hooks/usePolygon' import { useMode } from '@/hooks/useMode' import { useLine } from '@/hooks/useLine' +// 지붕형상 설정 export function useRoofShapeSetting() { const [shapeNum, setShapeNum] = useState(1) const [buttonAct, setButtonAct] = useState(1) diff --git a/src/hooks/roofcover/useWallLineOffsetSetting.js b/src/hooks/roofcover/useWallLineOffsetSetting.js index dce62b9b..882635e0 100644 --- a/src/hooks/roofcover/useWallLineOffsetSetting.js +++ b/src/hooks/roofcover/useWallLineOffsetSetting.js @@ -5,6 +5,7 @@ import { useMessage } from '@/hooks/useMessage' import { useEvent } from '@/hooks/useEvent' import { useLine } from '@/hooks/useLine' +// 외벽선 편집 및 오프셋 export function useWallLineOffsetSetting() { const canvas = useRecoilValue(canvasState) const { showLine, addLine } = useLine() @@ -29,6 +30,7 @@ export function useWallLineOffsetSetting() { y1: point1.y, x2: point2.x, y2: point2.y, + direction: currentWallLineRef.current.direction, }) line.attributes = { ...currentWallLineRef.current.attributes } @@ -191,26 +193,42 @@ export function useWallLineOffsetSetting() { } })[0] const length = Number(length1Ref.current.value) / 10 - let line1, line2 + const currentIdx = currentWallLineRef.current.idx + let point1, point2, point3 if (radioTypeRef.current === '1') { // 1지점 선택시 방향은 down, right만 가능 if (arrow1Ref.current === 'down') { - drawLine({ x: startPoint.x, y: startPoint.y }, { x: startPoint.x, y: startPoint.y + length }, currentWallLineRef.current.idx) - drawLine({ x: startPoint.x, y: startPoint.y + length }, { x: endPoint.x, y: endPoint.y }, currentWallLineRef.current.idx) + point1 = { x: startPoint.x, y: startPoint.y } + point2 = { x: startPoint.x, y: startPoint.y + length } + point3 = { x: endPoint.x, y: endPoint.y } } else { - drawLine({ x: startPoint.x, y: startPoint.y }, { x: startPoint.x + length, y: startPoint.y }, currentWallLineRef.current.idx) - drawLine({ x: startPoint.x + length, y: startPoint.y }, { x: endPoint.x, y: endPoint.y }, currentWallLineRef.current.idx) + point1 = { x: startPoint.x, y: startPoint.y } + point2 = { x: startPoint.x + length, y: startPoint.y } + point3 = { x: endPoint.x, y: endPoint.y } } } else { // 2지점일 경우 방향은 up, left만 가능 if (arrow2Ref.current === 'up') { - drawLine({ x: endPoint.x, y: endPoint.y }, { x: endPoint.x, y: endPoint.y - length }, currentWallLineRef.current.idx) - drawLine({ x: endPoint.x, y: endPoint.y - length }, { x: startPoint.x, y: startPoint.y }, currentWallLineRef.current.idx) + point1 = { x: startPoint.x, y: startPoint.y } + point2 = { x: startPoint.x, y: startPoint.y - length } + point3 = { x: endPoint.x, y: endPoint.y } } else { - drawLine({ x: endPoint.x, y: endPoint.y }, { x: endPoint.x - length, y: endPoint.y }, currentWallLineRef.current.idx) - drawLine({ x: endPoint.x - length, y: endPoint.y }, { x: startPoint.x, y: startPoint.y }, currentWallLineRef.current.idx) + point1 = { x: startPoint.x, y: startPoint.y } + point2 = { x: startPoint.x - length, y: startPoint.y } + point3 = { x: endPoint.x, y: endPoint.y } } } + + const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') + outerLines.forEach((outerLine) => { + if (outerLine.idx >= currentIdx + 1) { + outerLine.idx = outerLine.idx + 1 + } + }) + + drawLine(point1, point2, currentIdx) + drawLine(point2, point3, currentIdx + 1) + removeOuterLineEditCircle() canvas.remove(currentWallLineRef.current) currentWallLineRef.current = null From 4a1cc31d4b4f65aa5d025874f7f0c8f092dfe40c Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Mon, 14 Oct 2024 17:00:51 +0900 Subject: [PATCH 03/68] =?UTF-8?q?=EC=95=88=EC=93=B0=EB=8A=94=20=EA=B2=83?= =?UTF-8?q?=EB=93=A4=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/roofcover/useAuxiliaryDrawing.js | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/src/hooks/roofcover/useAuxiliaryDrawing.js b/src/hooks/roofcover/useAuxiliaryDrawing.js index 62a3c687..a2810163 100644 --- a/src/hooks/roofcover/useAuxiliaryDrawing.js +++ b/src/hooks/roofcover/useAuxiliaryDrawing.js @@ -30,25 +30,13 @@ import { useAdsorptionPoint } from '@/hooks/useAdsorptionPoint' // 보조선 작성 export function useAuxiliaryDrawing() { const canvas = useRecoilValue(canvasState) - const { - addCanvasMouseEventListener, - addDocumentEventListener, - removeAllMouseEventListeners, - removeAllDocumentEventListeners, - removeMouseEvent, - removeMouseLine, - initEvent, - } = useEvent() + const { addCanvasMouseEventListener, addDocumentEventListener, removeMouseLine, initEvent } = useEvent() const { getIntersectMousePoint } = useMouse() const { addLine, removeLine } = useLine() const { tempGridMode } = useTempGrid() const { getAdsorptionPoints } = useAdsorptionPoint() - const verticalHorizontalMode = useRecoilValue(verticalHorizontalModeState) - const adsorptionPointAddMode = useRecoilValue(adsorptionPointAddModeState) - const adsorptionPointMode = useRecoilValue(adsorptionPointModeState) const adsorptionRange = useRecoilValue(adsorptionRangeState) - const interval = useRecoilValue(dotLineIntervalSelector) // 가로 세로 간격 const [buttonAct, setButtonAct] = useState(1) From e9c4be5939f85538f7090626f29d47e9bf20ce4d Mon Sep 17 00:00:00 2001 From: yjnoh Date: Mon, 14 Oct 2024 17:31:50 +0900 Subject: [PATCH 04/68] =?UTF-8?q?=EB=A9=B4=ED=98=95=EC=83=81=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/layout.js | 2 +- src/common/common.js | 2 + .../floor-plan/modal/object/ObjectSetting.jsx | 173 +++++++++++++++++- .../modal/object/type/OpenSpace.jsx | 21 ++- .../floor-plan/modal/object/type/Shadow.jsx | 16 +- src/hooks/object/useObjectBatch.js | 6 + src/hooks/surface/useSurfaceShapeBatch.js | 75 +------- src/util/canvas-util.js | 95 +++++++++- 8 files changed, 303 insertions(+), 87 deletions(-) create mode 100644 src/hooks/object/useObjectBatch.js diff --git a/src/app/layout.js b/src/app/layout.js index f7aafcd0..6c04bbec 100644 --- a/src/app/layout.js +++ b/src/app/layout.js @@ -75,7 +75,7 @@ export default async function RootLayout({ children }) { {children} )} - {/* */} + diff --git a/src/common/common.js b/src/common/common.js index ae96db06..9aa7f9f0 100644 --- a/src/common/common.js +++ b/src/common/common.js @@ -93,7 +93,9 @@ export const LineType = { // 오브젝트 배치 > 개구배치, 그림자배치 export const BATCH_TYPE = { OPENING: 'opening', + OPENING_TEMP: 'openingTemp', SHADOW: 'shadow', + SHADOW_TEMP: 'shadowTemp', } // 오브젝트 배치 > 프리입력, 치수입력 export const INPUT_TYPE = { diff --git a/src/components/floor-plan/modal/object/ObjectSetting.jsx b/src/components/floor-plan/modal/object/ObjectSetting.jsx index c1405e1d..de7e36c3 100644 --- a/src/components/floor-plan/modal/object/ObjectSetting.jsx +++ b/src/components/floor-plan/modal/object/ObjectSetting.jsx @@ -1,6 +1,16 @@ +'use client' + +import { useState, useRef } from 'react' +import { useRecoilValue } from 'recoil' import { useMessage } from '@/hooks/useMessage' +import { INPUT_TYPE, BATCH_TYPE } from '@/common/common' +import { useEvent } from '@/hooks/useEvent' +import { canvasState } from '@/store/canvasAtom' +import { useSwal } from '@/hooks/useSwal' +import { polygonToTurfPolygon, rectToPolygon, pointsToTurfPolygon } from '@/util/canvas-util' +import * as turf from '@turf/turf' + import WithDraggable from '@/components/common/draggable/WithDraggable' -import { useState } from 'react' import OpenSpace from '@/components/floor-plan/modal/object/type/OpenSpace' import Shadow from '@/components/floor-plan/modal/object/type/Shadow' import TriangleDormer from '@/components/floor-plan/modal/object/type/TriangleDormer' @@ -8,7 +18,155 @@ import PentagonDormer from '@/components/floor-plan/modal/object/type/PentagonDo export default function ObjectSetting({ setShowObjectSettingModal }) { const { getMessage } = useMessage() + const canvas = useRecoilValue(canvasState) const [buttonAct, setButtonAct] = useState(1) + const [preObjects, setPreObjects] = useState([]) + const { addCanvasMouseEventListener, initEvent } = useEvent() + const { swalFire } = useSwal() + + /** + * 개구배치, 그림자배치 + */ + const objectPlacement = { + typeRef: useRef([]), //프리입력, 치수입력 + widthRef: useRef(null), + heightRef: useRef(null), + isCrossRef: useRef(null), + } + + const applyObject = () => { + setShowObjectSettingModal(false) + + let preObjectsArray = preObjects + const surfaceShapePolygons = canvas?.getObjects().filter((obj) => obj.name === 'surfaceShapeBatch') + + console.log(preObjectsArray) + + if (surfaceShapePolygons.length === 0) { + swalFire({ text: '지붕이 없어요 지붕부터 그리세요', icon: 'error' }) + return + } + + //개구배치, 그림자배치 + if (buttonAct === 1 || buttonAct === 2) { + const type = objectPlacement.typeRef.current.find((radio) => radio.checked).value + const isCrossChecked = objectPlacement.isCrossRef.current.checked + + let rect, isDown, origX, origY + let selectedSurface + + //프리입력 + if (type === INPUT_TYPE.FREE) { + addCanvasMouseEventListener('mouse:down', (e) => { + isDown = true + const pointer = canvas.getPointer(e.e) + + surfaceShapePolygons.forEach((surface) => { + if (surface.inPolygon({ x: pointer.x, y: pointer.y })) { + selectedSurface = surface + } + }) + + if (!selectedSurface) { + swalFire({ text: '지붕안에 그려야해요', icon: 'error' }) + setShowObjectSettingModal(true) //메뉴보이고 + initEvent() //이벤트 초기화 + return + } + + origX = pointer.x + origY = pointer.y + + rect = new fabric.Rect({ + left: origX, + top: origY, + originX: 'left', + originY: 'top', + width: 0, + height: 0, + angle: 0, + stroke: 'black', + }) + + //개구냐 그림자냐에 따라 변경 + rect.set({ + fill: buttonAct === 1 ? 'white' : 'rgba(0, 0, 0, 0.3)', + name: buttonAct === 1 ? BATCH_TYPE.OPENING_TEMP : BATCH_TYPE.SHADOW_TEMP, + }) + + canvas?.add(rect) + }) + + addCanvasMouseEventListener('mouse:move', (e) => { + if (!isDown) return + + if (selectedSurface) { + const pointer = canvas.getPointer(e.e) + const width = pointer.x - origX + const height = pointer.y - origY + + rect.set({ width: Math.abs(width), height: Math.abs(height) }) + + if (width < 0) { + rect.set({ left: Math.abs(pointer.x) }) + } + if (height < 0) { + rect.set({ top: Math.abs(pointer.y) }) + } + + canvas?.renderAll() + } + }) + + addCanvasMouseEventListener('mouse:up', (e) => { + if (rect) { + const rectPolygon = pointsToTurfPolygon(rectToPolygon(rect)) + const selectedSurfacePolygon = polygonToTurfPolygon(selectedSurface) + + //지붕 밖으로 그렸을때 + if (!turf.booleanWithin(rectPolygon, selectedSurfacePolygon)) { + swalFire({ text: '지붕안에 그리라고요...', icon: 'error' }) + //일단 지워 + const deleteTarget = canvas?.getObjects().filter((obj) => obj.name === BATCH_TYPE.OPENING_TEMP || obj.name === BATCH_TYPE.SHADOW_TEMP) + canvas?.remove(...deleteTarget) + setShowObjectSettingModal(true) //메뉴보이고 + initEvent() //이벤트 초기화 + return + } + + // if (!isCrossChecked) { + // const isCross = preObjectsArray.some((object) => turf.booleanOverlap(pointsToTurfPolygon(rectToPolygon(object), rectPolygon))) + + // console.log(isCross) + + // if (isCross) { + // swalFire({ text: '겹치기 불가요...', icon: 'error' }) + // const deleteTarget = canvas?.getObjects().filter((obj) => obj.name === BATCH_TYPE.OPENING_TEMP || obj.name === BATCH_TYPE.SHADOW_TEMP) + // canvas?.remove(...deleteTarget) + // setShowObjectSettingModal(true) //메뉴보이고 + // initEvent() //이벤트 초기화 + // return + // } + // } + + isDown = false + const complateName = buttonAct === 1 ? BATCH_TYPE.OPENING : BATCH_TYPE.SHADOW + rect.set({ name: complateName }) + rect.setCoords() + + preObjectsArray.push(rect) + + console.log('preObjectsArray', preObjectsArray) + setPreObjects(preObjectsArray) + + setShowObjectSettingModal(true) //메뉴보이고 + initEvent() + } + }) + } + } + } + const buttonMenu = [ { id: 1, name: getMessage('modal.object.setting.type.open.space.placement') }, { id: 2, name: getMessage('modal.object.setting.type.shadow.placement') }, @@ -34,13 +192,20 @@ export default function ObjectSetting({ setShowObjectSettingModal }) {
{getMessage('setting')}
- {buttonAct === 1 && } - {buttonAct === 2 && } + {buttonAct === 1 && } + {buttonAct === 2 && } {buttonAct === 3 && } {buttonAct === 4 && }
- +
diff --git a/src/components/floor-plan/modal/object/type/OpenSpace.jsx b/src/components/floor-plan/modal/object/type/OpenSpace.jsx index 1c79dc09..2ffe9b26 100644 --- a/src/components/floor-plan/modal/object/type/OpenSpace.jsx +++ b/src/components/floor-plan/modal/object/type/OpenSpace.jsx @@ -1,18 +1,23 @@ +import { forwardRef } from 'react' import { useMessage } from '@/hooks/useMessage' +import { INPUT_TYPE } from '@/common/common' -export default function OpenSpace() { +const OpenSpace = forwardRef((props, refs) => { const { getMessage } = useMessage() + + //체크하면 값바꿈 + return (
- + (refs.typeRef.current[0] = el)} />
- + (refs.typeRef.current[1] = el)} />
@@ -24,7 +29,7 @@ export default function OpenSpace() {
- +
mm
@@ -35,7 +40,7 @@ export default function OpenSpace() {
- +
mm
@@ -45,9 +50,11 @@ export default function OpenSpace() {
- +
) -} +}) + +export default OpenSpace diff --git a/src/components/floor-plan/modal/object/type/Shadow.jsx b/src/components/floor-plan/modal/object/type/Shadow.jsx index bef52631..71bfb6ed 100644 --- a/src/components/floor-plan/modal/object/type/Shadow.jsx +++ b/src/components/floor-plan/modal/object/type/Shadow.jsx @@ -1,18 +1,20 @@ +import { forwardRef } from 'react' import { useMessage } from '@/hooks/useMessage' +import { INPUT_TYPE } from '@/common/common' -export default function Shadow() { +const Shadow = forwardRef((props, refs) => { const { getMessage } = useMessage() return (
- + (refs.typeRef.current[0] = el)} />
- + (refs.typeRef.current[1] = el)} />
@@ -24,7 +26,7 @@ export default function Shadow() {
- +
mm
@@ -35,7 +37,7 @@ export default function Shadow() {
- +
mm
@@ -46,4 +48,6 @@ export default function Shadow() {
) -} +}) + +export default Shadow diff --git a/src/hooks/object/useObjectBatch.js b/src/hooks/object/useObjectBatch.js new file mode 100644 index 00000000..1d395d27 --- /dev/null +++ b/src/hooks/object/useObjectBatch.js @@ -0,0 +1,6 @@ +export function useObjectBatch() { + const { getMessage } = useMessage() + const canvas = useRecoilValue(canvasState) + + const openObjectBatch = () => {} +} diff --git a/src/hooks/surface/useSurfaceShapeBatch.js b/src/hooks/surface/useSurfaceShapeBatch.js index b52781b6..c9e898e9 100644 --- a/src/hooks/surface/useSurfaceShapeBatch.js +++ b/src/hooks/surface/useSurfaceShapeBatch.js @@ -1,21 +1,22 @@ 'use client' -import { useEffect, useRef, useState } from 'react' -import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil' +import { useRecoilValue } from 'recoil' import { canvasState } from '@/store/canvasAtom' import { MENU } from '@/common/common' -import { getIntersectionPoint } from '@/util/canvas-util' +import { getIntersectionPoint, setSurfaceShapePattern } from '@/util/canvas-util' import { degreesToRadians } from '@turf/turf' import { QPolygon } from '@/components/fabric/QPolygon' import { fabric } from 'fabric' import { useSwal } from '@/hooks/useSwal' import { useMessage } from '@/hooks/useMessage' +import { useEvent } from '@/hooks/useEvent' export function useSurfaceShapeBatch() { const { getMessage } = useMessage() const canvas = useRecoilValue(canvasState) const { swalFire } = useSwal() + const { addCanvasMouseEventListener, initEvent } = useEvent() const applySurfaceShape = (surfaceRefs, selectedType, setShowPlacementSurfaceSettingModal) => { let length1, length2, length3, length4, length5 @@ -57,7 +58,7 @@ export function useSurfaceShapeBatch() { let points = [] if (checkSurfaceShape(surfaceId, { length1, length2, length3, length4, length5 })) { setShowPlacementSurfaceSettingModal(false) - canvas?.on('mouse:move', (e) => { + addCanvasMouseEventListener('mouse:move', (e) => { if (!isDrawing) { return } @@ -110,15 +111,13 @@ export function useSurfaceShapeBatch() { obj.set({ direction: direction }) obj.set({ originAngle: originAngle }) - // setCurrentPattern(obj) canvas?.renderAll() }) - canvas?.on('mouse:down', (e) => { + addCanvasMouseEventListener('mouse:down', (e) => { isDrawing = false obj.set('name', MENU.BATCH_CANVAS.SURFACE_SHAPE_BATCH) - canvas?.off('mouse:down') - canvas.off('mouse:move') + initEvent() setSurfaceShapePattern(obj) setShowPlacementSurfaceSettingModal(true) }) @@ -565,66 +564,6 @@ export function useSurfaceShapeBatch() { return points } - - //면형상 선택 클릭시 지붕 패턴 입히기 - const setSurfaceShapePattern = (polygon) => { - const ratio = window.devicePixelRatio || 1 - - let width = 265 / 10 - let height = 150 / 10 - let roofStyle = 2 - const inputPatternSize = { width: width, height: height } //임시 사이즈 - const patternSize = { ...inputPatternSize } // 입력된 값을 뒤집기 위해 - - if (polygon.direction === 'east' || polygon.direction === 'west') { - //세로형이면 width height를 바꿈 - ;[patternSize.width, patternSize.height] = [inputPatternSize.height, patternSize.width] - } - - // 패턴 소스를 위한 임시 캔버스 생성 - const patternSourceCanvas = document.createElement('canvas') - patternSourceCanvas.width = polygon.width * ratio - patternSourceCanvas.height = polygon.height * ratio - const ctx = patternSourceCanvas.getContext('2d') - const offset = roofStyle === 1 ? 0 : patternSize.width / 2 - - const rows = Math.floor(patternSourceCanvas.height / patternSize.height) - const cols = Math.floor(patternSourceCanvas.width / patternSize.width) - - ctx.strokeStyle = 'green' - ctx.lineWidth = 0.4 - - for (let row = 0; row <= rows; row++) { - const y = row * patternSize.height - - ctx.beginPath() - ctx.moveTo(0, y) // 선 시작점 - ctx.lineTo(patternSourceCanvas.width, y) // 선 끝점 - ctx.stroke() - - for (let col = 0; col <= cols; col++) { - const x = col * patternSize.width + (row % 2 === 0 ? 0 : offset) - const yStart = row * patternSize.height - const yEnd = yStart + patternSize.height - - ctx.beginPath() - ctx.moveTo(x, yStart) // 선 시작점 - ctx.lineTo(x, yEnd) // 선 끝점 - ctx.stroke() - } - } - - // 패턴 생성 - const pattern = new fabric.Pattern({ - source: patternSourceCanvas, - repeat: 'repeat', - }) - - polygon.set('fill', null) - polygon.set('fill', pattern) - canvas?.renderAll() - } - return { applySurfaceShape, } diff --git a/src/util/canvas-util.js b/src/util/canvas-util.js index 4efd9138..6b232b95 100644 --- a/src/util/canvas-util.js +++ b/src/util/canvas-util.js @@ -1,5 +1,5 @@ import { intersect } from 'mathjs' - +import * as turf from '@turf/turf' /** * Collection of function to use on canvas */ @@ -725,3 +725,96 @@ export const getIntersectionPoint = (p1, p2, y) => { const x = (y - intercept) / slope return { x, y } } + +export const pointsToTurfPolygon = (points) => { + const coordinates = points.map((point) => [point.x, point.y]) + coordinates.push(coordinates[0]) + return turf.polygon([coordinates]) +} + +export const polygonToTurfPolygon = (polygon) => { + const coordinates = polygon.points.map((point) => [point.x, point.y]) + coordinates.push(coordinates[0]) + return turf.polygon( + [coordinates], + {}, + { + parentId: polygon.parentId, + }, + ) +} + +export const rectToPolygon = (rect) => { + const points = [] + const left = rect.left + const top = rect.top + const width = rect.width * rect.scaleX // 스케일 적용 + const height = rect.height * rect.scaleY // 스케일 적용 + + // 네 개의 꼭짓점 좌표 계산 + points.push({ x: left, y: top }) // 좌상단 + points.push({ x: left + width, y: top }) // 우상단 + points.push({ x: left + width, y: top + height }) // 우하단 + points.push({ x: left, y: top + height }) // 좌하단 + + return points +} + +//면형상 선택 클릭시 지붕 패턴 입히기 +export function setSurfaceShapePattern(polygon) { + const ratio = window.devicePixelRatio || 1 + + let width = 265 / 10 + let height = 150 / 10 + let roofStyle = 2 + const inputPatternSize = { width: width, height: height } //임시 사이즈 + const patternSize = { ...inputPatternSize } // 입력된 값을 뒤집기 위해 + + if (polygon.direction === 'east' || polygon.direction === 'west') { + //세로형이면 width height를 바꿈 + ;[patternSize.width, patternSize.height] = [inputPatternSize.height, patternSize.width] + } + + // 패턴 소스를 위한 임시 캔버스 생성 + const patternSourceCanvas = document.createElement('canvas') + patternSourceCanvas.width = polygon.width * ratio + patternSourceCanvas.height = polygon.height * ratio + const ctx = patternSourceCanvas.getContext('2d') + const offset = roofStyle === 1 ? 0 : patternSize.width / 2 + + const rows = Math.floor(patternSourceCanvas.height / patternSize.height) + const cols = Math.floor(patternSourceCanvas.width / patternSize.width) + + ctx.strokeStyle = 'green' + ctx.lineWidth = 0.4 + + for (let row = 0; row <= rows; row++) { + const y = row * patternSize.height + + ctx.beginPath() + ctx.moveTo(0, y) // 선 시작점 + ctx.lineTo(patternSourceCanvas.width, y) // 선 끝점 + ctx.stroke() + + for (let col = 0; col <= cols; col++) { + const x = col * patternSize.width + (row % 2 === 0 ? 0 : offset) + const yStart = row * patternSize.height + const yEnd = yStart + patternSize.height + + ctx.beginPath() + ctx.moveTo(x, yStart) // 선 시작점 + ctx.lineTo(x, yEnd) // 선 끝점 + ctx.stroke() + } + } + + // 패턴 생성 + const pattern = new fabric.Pattern({ + source: patternSourceCanvas, + repeat: 'repeat', + }) + + polygon.set('fill', null) + polygon.set('fill', pattern) + polygon.canvas?.renderAll() +} From 8e530e440855ab13c98ac02a0b0f1704bd5172ed Mon Sep 17 00:00:00 2001 From: minsik Date: Mon, 14 Oct 2024 17:37:47 +0900 Subject: [PATCH 05/68] =?UTF-8?q?-=20=EC=9C=A1=EC=A7=80=EB=B6=95=20?= =?UTF-8?q?=EA=B8=B0=EB=B3=B8=20=EC=84=A4=EC=A0=95=20=EB=AA=A8=EB=8B=AC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/floor-plan/CanvasMenu.jsx | 2 + src/components/floor-plan/FloorPlan.jsx | 1 + src/components/floor-plan/MenuDepth01.jsx | 5 +- .../floor-plan/modal/basic/BasicSetting.jsx | 15 +- .../floor-plan/modal/basic/step/Module.jsx | 15 +- .../modal/basic/step/pitch/PitchModule.jsx | 91 ++++++++++ .../modal/basic/step/pitch/PitchPlacement.jsx | 159 ++++++++++++++++++ src/locales/ja.json | 9 + src/locales/ko.json | 9 + 9 files changed, 296 insertions(+), 10 deletions(-) create mode 100644 src/components/floor-plan/modal/basic/step/pitch/PitchModule.jsx create mode 100644 src/components/floor-plan/modal/basic/step/pitch/PitchPlacement.jsx diff --git a/src/components/floor-plan/CanvasMenu.jsx b/src/components/floor-plan/CanvasMenu.jsx index eae0ea4e..ab445ca9 100644 --- a/src/components/floor-plan/CanvasMenu.jsx +++ b/src/components/floor-plan/CanvasMenu.jsx @@ -48,6 +48,7 @@ export default function CanvasMenu(props) { setShowWallLineOffsetSettingModal, setShowRoofAllocationSettingModal, setShowBasicSettingModal, + setShowPropertiesSettingModal, } = props const [menuNumber, setMenuNumber] = useState(null) @@ -104,6 +105,7 @@ export default function CanvasMenu(props) { setShowRoofAllocationSettingModal, setShowObjectSettingModal, setShowBasicSettingModal, + setShowPropertiesSettingModal, type, } diff --git a/src/components/floor-plan/FloorPlan.jsx b/src/components/floor-plan/FloorPlan.jsx index d614a809..1de51962 100644 --- a/src/components/floor-plan/FloorPlan.jsx +++ b/src/components/floor-plan/FloorPlan.jsx @@ -84,6 +84,7 @@ export default function FloorPlan() { setShowWallLineOffsetSettingModal, setShowRoofAllocationSettingModal, setShowBasicSettingModal, + setShowPropertiesSettingModal, } useEffect(() => { diff --git a/src/components/floor-plan/MenuDepth01.jsx b/src/components/floor-plan/MenuDepth01.jsx index c7642894..21631434 100644 --- a/src/components/floor-plan/MenuDepth01.jsx +++ b/src/components/floor-plan/MenuDepth01.jsx @@ -23,6 +23,7 @@ export default function MenuDepth01(props) { setShowRoofAllocationSettingModal, setShowObjectSettingModal, setShowBasicSettingModal, + setShowPropertiesSettingModal, } = props const { getMessage } = useMessage() const [activeMenu, setActiveMenu] = useState() @@ -42,6 +43,7 @@ export default function MenuDepth01(props) { setShowWallLineOffsetSettingModal(id === 6) setShowRoofAllocationSettingModal(id === 7) setShowPlaceShapeDrawingModal(false) + setShowPropertiesSettingModal(false) } if (type === 'surface') { @@ -53,7 +55,7 @@ export default function MenuDepth01(props) { setShowMovementModal(false) setShowWallLineOffsetSettingModal(false) setShowRoofAllocationSettingModal(false) - + setShowPropertiesSettingModal(false) setShowSlopeSettingModal(id === 0) setShowPlaceShapeDrawingModal(id === 1) setShowPlacementSurfaceSettingModal(id === 2) @@ -69,6 +71,7 @@ export default function MenuDepth01(props) { setShowMovementModal(false) setShowWallLineOffsetSettingModal(false) setShowRoofAllocationSettingModal(false) + setShowPropertiesSettingModal(false) setShowBasicSettingModal(id === 0) } } diff --git a/src/components/floor-plan/modal/basic/BasicSetting.jsx b/src/components/floor-plan/modal/basic/BasicSetting.jsx index d28e3ef5..c6663cba 100644 --- a/src/components/floor-plan/modal/basic/BasicSetting.jsx +++ b/src/components/floor-plan/modal/basic/BasicSetting.jsx @@ -3,12 +3,16 @@ import WithDraggable from '@/components/common/draggable/withDraggable' import { useState } from 'react' import Orientation from '@/components/floor-plan/modal/basic/step/Orientation' import Module from '@/components/floor-plan/modal/basic/step/Module' +import PitchModule from '@/components/floor-plan/modal/basic/step/pitch/PitchModule' +import PitchPlacement from '@/components/floor-plan/modal/basic/step/pitch/PitchPlacement' import Placement from '@/components/floor-plan/modal/basic/step/Placement' +import { useRecoilState } from 'recoil' +import { canvasSettingState } from '@/store/canvasAtom' export default function BasicSetting({ setShowBasicSettingModal }) { const { getMessage } = useMessage() const [tabNum, setTabNum] = useState(1) - + const [canvasSetting, setCanvasSetting] = useRecoilState(canvasSettingState) return (
@@ -27,8 +31,13 @@ export default function BasicSetting({ setShowBasicSettingModal }) {
{getMessage('modal.module.basic.setting.module.placement')}
{tabNum === 1 && } - {tabNum === 2 && } - {tabNum === 3 && } + {/*배치면 초기설정 - 입력방법: 복시도 입력 || 실측값 입력*/} + {canvasSetting.roofSizeSet && canvasSetting.roofSizeSet != 3 && tabNum === 2 && } + {canvasSetting.roofSizeSet && canvasSetting.roofSizeSet != 3 && tabNum === 3 && } + + {/*배치면 초기설정 - 입력방법: 육지붕*/} + {canvasSetting.roofSizeSet && canvasSetting.roofSizeSet == 3 && tabNum === 2 && } + {canvasSetting.roofSizeSet && canvasSetting.roofSizeSet == 3 && tabNum === 3 && }
{tabNum !== 1 && ( diff --git a/src/components/floor-plan/modal/basic/step/Module.jsx b/src/components/floor-plan/modal/basic/step/Module.jsx index 8109da08..f50bb408 100644 --- a/src/components/floor-plan/modal/basic/step/Module.jsx +++ b/src/components/floor-plan/modal/basic/step/Module.jsx @@ -74,12 +74,15 @@ export default function Module({}) { ))} - - - - - - + + {Array.from({ length: 3 - moduleData.rows.length }).map((_, i) => ( + + + + + + + ))}
diff --git a/src/components/floor-plan/modal/basic/step/pitch/PitchModule.jsx b/src/components/floor-plan/modal/basic/step/pitch/PitchModule.jsx new file mode 100644 index 00000000..255833a8 --- /dev/null +++ b/src/components/floor-plan/modal/basic/step/pitch/PitchModule.jsx @@ -0,0 +1,91 @@ +import { useMessage } from '@/hooks/useMessage' +import QSelectBox from '@/components/common/select/QSelectBox' + +export default function PitchModule({}) { + const { getMessage } = useMessage() + const SelectOption01 = [{ name: '0' }, { name: '0' }, { name: '0' }, { name: '0' }] + const moduleData = { + header: [ + { name: getMessage('module'), width: 150, prop: 'module', type: 'color-box' }, + { + name: `${getMessage('높이')} (mm)`, + prop: 'height', + }, + { name: `${getMessage('width')} (mm)`, prop: 'width' }, + { name: `${getMessage('output')} (W)`, prop: 'output' }, + ], + rows: [ + { + module: { name: 'Re.RISE-G3 440', color: '#AA6768' }, + height: { name: '1134' }, + width: { name: '1722' }, + output: { name: '440' }, + }, + { + module: { + name: 'Re.RISE MS-G3 290', + color: '#67A2AA', + }, + height: { name: '1134' }, + width: { name: '1722' }, + output: { name: '240' }, + }, + ], + } + return ( + <> +
+
+
+ {getMessage('modal.module.basic.setting.module.setting')} +
+ +
+
+
+ + + + {moduleData.header.map((data) => ( + + ))} + + + + {moduleData.rows.map((row) => ( + <> + + {moduleData.header.map((header) => ( + <> + {header.type === 'color-box' && ( + + )} + {!header.type && header.type !== 'color-box' && } + + ))} + + + ))} + {Array.from({ length: 3 - moduleData.rows.length }).map((_, i) => ( + + + + + + + ))} + +
+ {data.name} +
+
+ + {row[header.prop].name} +
+
{row[header.prop].name}
+
+
+
+ + ) +} diff --git a/src/components/floor-plan/modal/basic/step/pitch/PitchPlacement.jsx b/src/components/floor-plan/modal/basic/step/pitch/PitchPlacement.jsx new file mode 100644 index 00000000..4c5aef36 --- /dev/null +++ b/src/components/floor-plan/modal/basic/step/pitch/PitchPlacement.jsx @@ -0,0 +1,159 @@ +import { useMessage } from '@/hooks/useMessage' + +export default function PitchPlacement() { + const { getMessage } = useMessage() + const moduleData = { + header: [ + { type: 'check', name: '', prop: 'check', width: 70 }, + { type: 'color-box', name: getMessage('module'), prop: 'module' }, + { type: 'text', name: `${getMessage('output')} (W)`, prop: 'output', width: 70 }, + ], + rows: [ + { + check: false, + module: { name: 'Re.RISE-G3 440', color: '#AA6768' }, + output: { name: '440' }, + }, + { + check: false, + module: { + name: 'Re.RISE MS-G3 290', + color: '#67A2AA', + }, + output: { name: '240' }, + }, + ], + } + return ( + <> +
+
+
+ + + + {moduleData.header.map((data) => ( + + ))} + + + + {moduleData.rows.map((row) => ( + <> + + {moduleData.header.map((header) => ( + <> + {header.type === 'color-box' && ( + + )} + {header.type === 'check' && ( + + )} + {header.type && header.type !== 'color-box' && header.type !== 'check' && } + + ))} + + + ))} + +
+ {data.type === 'check' ? ( +
+ + +
+ ) : ( + data.name + )} +
+
+ + {row[header.prop].name} +
+
+
+ + +
+
{row[header.prop].name}
+
+
+
+
+
+
+
+
{getMessage('modal.module.basic.setting.pitch.module.placement.standard.setting')}
+
+
+
+
+ + +
+
+ + +
+
+
+
+
+
+
+
+
+
+
割り当て設定
+
+
+
+
+ + {getMessage('modal.module.basic.setting.pitch.module.row.amount')} + +
+ +
+ mm +
+
+ + {getMessage('modal.module.basic.setting.pitch.module.row.margin')} + +
+ +
+ mm +
+
+ + {getMessage('modal.module.basic.setting.pitch.module.column.amount')} + +
+ +
+ mm +
+
+ + {getMessage('modal.module.basic.setting.pitch.module.column.margin')} + +
+ +
+ mm +
+
+
+
+
+
{getMessage('modal.module.basic.setting.pitch.module.allocation.setting.info')}
+
+
+
+
+
+ + ) +} diff --git a/src/locales/ja.json b/src/locales/ja.json index f4f1e299..f8f15c2e 100644 --- a/src/locales/ja.json +++ b/src/locales/ja.json @@ -109,6 +109,15 @@ "modal.module.basic.setting.module.placement.arrangement.standard.eaves": "軒側", "modal.module.basic.setting.module.placement.arrangement.standard.ridge": "龍丸側", "modal.module.basic.setting.module.placement.maximum": "最大配置する。", + "modal.module.basic.setting.pitch.module.placement.standard.setting": "配置基準の設定", + "modal.module.basic.setting.pitch.module.placement.standard.setting.south": "南向きに設置す", + "modal.module.basic.setting.pitch.module.placement.standard.setting.select": "指定した辺を基準に設置する", + "modal.module.basic.setting.pitch.module.allocation.setting": "割り当て設定", + "modal.module.basic.setting.pitch.module.allocation.setting.info": "※バッチパネルの種類が1種類の場合にのみ使用できます。", + "modal.module.basic.setting.pitch.module.row.amount": "単数", + "modal.module.basic.setting.pitch.module.row.margin": "上下間隔", + "modal.module.basic.setting.pitch.module.column.amount": "熱数", + "modal.module.basic.setting.pitch.module.column.margin": "左右間隔", "modal.module.basic.setting.prev": "以前", "modal.module.basic.setting.passivity.placement": "手動配置", "modal.module.basic.setting.auto.placement": "設定値に自動配置", diff --git a/src/locales/ko.json b/src/locales/ko.json index 9d518a5e..ee31c6c3 100644 --- a/src/locales/ko.json +++ b/src/locales/ko.json @@ -113,6 +113,15 @@ "modal.module.basic.setting.module.placement.arrangement.standard.eaves": "처마쪽", "modal.module.basic.setting.module.placement.arrangement.standard.ridge": "용마루쪽", "modal.module.basic.setting.module.placement.maximum": "최대배치 실시한다.", + "modal.module.basic.setting.pitch.module.placement.standard.setting": "배치 기준 설정", + "modal.module.basic.setting.pitch.module.placement.standard.setting.south": "남향으로 설치한다", + "modal.module.basic.setting.pitch.module.placement.standard.setting.select": "지정한 변을 기준으로 설치한다", + "modal.module.basic.setting.pitch.module.allocation.setting": "할당 설정", + "modal.module.basic.setting.pitch.module.allocation.setting.info": "※배치 패널 종류가 1종류일 경우에만 사용할 수 있습니다.", + "modal.module.basic.setting.pitch.module.row.amount": "단수", + "modal.module.basic.setting.pitch.module.row.margin": "상하간격", + "modal.module.basic.setting.pitch.module.column.amount": "열수", + "modal.module.basic.setting.pitch.module.column.margin": "좌우간격", "modal.module.basic.setting.prev": "이전", "modal.module.basic.setting.passivity.placement": "수동 배치", "modal.module.basic.setting.auto.placement": "설정값으로 자동 배치", From d2ec45437d4f439660aca757a4ad6d026df5a7b6 Mon Sep 17 00:00:00 2001 From: basssy Date: Mon, 14 Oct 2024 17:58:38 +0900 Subject: [PATCH 06/68] =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=ED=99=94?= =?UTF-8?q?=EB=A9=B4,=20=EB=AC=BC=EA=B1=B4=ED=98=84=ED=99=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/main/ChangePasswordPop.jsx | 8 +- src/components/management/Stuff.jsx | 11 +- src/components/management/StuffDetail.jsx | 244 ++++++++++-------- src/components/management/StuffQGrid.jsx | 17 +- .../management/StuffSearchCondition.jsx | 3 +- .../management/popup/DesignRequestPop.jsx | 5 + .../management/popup/FindAddressPop.jsx | 111 ++++++++ .../management/popup/FindAddressPopQGrid.jsx | 6 + src/styles/_grid-detail.scss | 123 +++++---- 9 files changed, 331 insertions(+), 197 deletions(-) create mode 100644 src/components/management/popup/DesignRequestPop.jsx create mode 100644 src/components/management/popup/FindAddressPop.jsx create mode 100644 src/components/management/popup/FindAddressPopQGrid.jsx diff --git a/src/components/main/ChangePasswordPop.jsx b/src/components/main/ChangePasswordPop.jsx index 45685c26..e9355d24 100644 --- a/src/components/main/ChangePasswordPop.jsx +++ b/src/components/main/ChangePasswordPop.jsx @@ -1,10 +1,11 @@ -import React from 'react' +import React, { useState } from 'react' import { useMessage } from '@/hooks/useMessage' import { useForm } from 'react-hook-form' import { sessionStore } from '@/store/commonAtom' import { useRecoilValue, useRecoilState } from 'recoil' import { useAxios } from '@/hooks/useAxios' import { globalLocaleStore } from '@/store/localeAtom' +import { useRouter } from 'next/navigation' export default function ChangePasswordPop() { const globalLocaleState = useRecoilValue(globalLocaleStore) @@ -12,7 +13,7 @@ export default function ChangePasswordPop() { const { patch } = useAxios(globalLocaleState) const { getMessage } = useMessage() const [sessionState, setSessionState] = useRecoilState(sessionStore) - + const router = useRouter() const formInitValue = { password1: '', password2: '', @@ -74,6 +75,8 @@ export default function ChangePasswordPop() { if (res.result.resultCode === 'S') { alert(getMessage('main.popup.login.success')) setSessionState({ ...sessionState, pwdInitYn: 'Y' }) + //메인으로 이동 + router.push('/') } else { alert(res.result.resultMsg) } @@ -154,7 +157,6 @@ export default function ChangePasswordPop() { className="btn-origin grey" onClick={() => { router.push('/login') - setOpen(false) }} > {getMessage('main.popup.login.btn2')} diff --git a/src/components/management/Stuff.jsx b/src/components/management/Stuff.jsx index 02f44561..bc0e6779 100644 --- a/src/components/management/Stuff.jsx +++ b/src/components/management/Stuff.jsx @@ -76,16 +76,7 @@ export default function Stuff() { headerCheckboxSelectionCurrentPageOnly: true, //페이징시 현재 페이지만 체크되도록 checkboxSelection: true, showDisabledCheckboxes: true, - // .centered { - // .ag-header-cell-label { - // justify-content: center !important; - // } - // } cellStyle: { textAlign: 'center' }, - //suppressMovable: true, //헤더 못움직이게 - // width : 100 - // minWidth : 100 - // maxWidth : 100 valueFormatter: function (params) { if (params.value) { return dayjs(params?.value).format('YYYY.MM.DD HH:mm:ss') @@ -96,8 +87,8 @@ export default function Stuff() { }, { field: 'objectNo', + width: '180px', headerName: getMessage('stuff.gridHeader.objectNo'), - // headerClass: 'centered', //_test.scss에 추가 테스트 cellRenderer: function (params) { if (params.data.objectNo) { return ( diff --git a/src/components/management/StuffDetail.jsx b/src/components/management/StuffDetail.jsx index bca2fd17..9729c1af 100644 --- a/src/components/management/StuffDetail.jsx +++ b/src/components/management/StuffDetail.jsx @@ -7,11 +7,16 @@ import Select from 'react-dropdown-select' import Link from 'next/link' import { useAxios } from '@/hooks/useAxios' import { globalLocaleStore } from '@/store/localeAtom' -import { queryStringFormatter, isEmptyArray } from '@/util/common-utils' +import { isEmptyArray } from '@/util/common-utils' import { useMessage } from '@/hooks/useMessage' import { useForm } from 'react-hook-form' import { useRecoilValue } from 'recoil' +import { sessionStore } from '@/store/commonAtom' +import FindAddressPop from './popup/FindAddressPop' +import DesignRequestPop from './popup/DesignRequestPop' export default function StuffDetail() { + const sessionState = useRecoilValue(sessionStore) + const router = useRouter() const searchParams = useSearchParams() const { getMessage } = useMessage() @@ -56,14 +61,18 @@ export default function StuffDetail() { const [prefValue, setPrefValue] = useState('') const [saleStoreList, setSaleStoreList] = useState([]) // 판매점 리스트 const [otherSaleStoreList, setOtherSaleStoreList] = useState([]) + const [originOtherSaleStoreList, setOriginOtherSaleStoreList] = useState([]) const [areaIdList, setAreaIdList] = useState([]) //발전시뮬레이션 리스트 const [windSpeedList, setWindSpeedList] = useState([]) //기준풍속 리스트 const [isFormValid, setIsFormValid] = useState(false) //임시저장, 진짜저장 버튼 컨트롤 - const [buttonValid, setButtonValid] = useState(false) //주소검색 활성화 컨트롤 + const [showButtonValid, setShowButtonValid] = useState(false) //주소검색 활성화 컨트롤 const objectNo = searchParams.get('objectNo') //url에서 물건번호 꺼내서 바로 set + // const [] //1차판매점 자동완성 선택값 + // const [] //2차판매점 자동완성 선택값 + const [editMode, setEditMode] = useState('NEW') const [detailData, setDetailData] = useState({}) @@ -85,15 +94,15 @@ export default function StuffDetail() { // 도도부현API get({ url: '/api/object/prefecture/list' }).then((res) => { if (!isEmptyArray(res)) { - // console.log('도도부현API 결과:::', res) + console.log('도도부현API 결과:::', res) setPrefCodeList(res) } }) - // 판매점목록 API /api/object/saleStore/판매점코드/list - 판매점 목록 조회 // 임시 1차점 판매점코드 saleStoreId=201TES01 // T01 //1차점 : X167 - get({ url: `/api/object/saleStore/X167/list` }).then((res) => { + // get({ url: `/api/object/saleStore/X167/list` }).then((res) => { + get({ url: `/api/object/saleStore/${sessionState?.storeId}/list` }).then((res) => { if (!isEmptyArray(res)) { // console.log('판매점 결과:::::', res) setSaleStoreList(res) @@ -111,32 +120,31 @@ export default function StuffDetail() { // 도도부현API get({ url: '/api/object/prefecture/list' }).then((res) => { if (!isEmptyArray(res)) { - // console.log('신규화면 도도부현API 결과:::', res) + console.log('신규화면 도도부현API 결과:::', res) setPrefCodeList(res) } }) - // 판매점목록 API /api/object/saleStore/판매점코드/list - 판매점 목록 조회 // 임시 1차점 판매점코드 saleStoreId=201TES01 // T01 //1차점 : X167 - get({ url: `/api/object/saleStore/X167/list` }).then((res) => { + get({ url: `/api/object/saleStore/T01/list` }).then((res) => { + // get({ url: `/api/object/saleStore/${sessionState?.storeId}/list` }).then((res) => { if (!isEmptyArray(res)) { - // console.log('판매점 결과:::::', res) const firstList = res.filter((row) => row.saleStoreLevel === '1') const otherList = res.filter((row) => row.saleStoreLevel !== '1') - // console.log('first:::::', firstList) - // console.log('otherList:::::', otherList) + //1차점 셀렉트박스 setSaleStoreList(firstList) //1차 판매점 자동완성 값 셋팅 - form.setValue('saleStoreId', firstList[0].saleStoreId) + //form.setValue('saleStoreId', firstList[0].saleStoreId) //1차 판매점 번호 셋팅 - form.setValue('saleStoreName', firstList[0].saleStoreId) + form.setValue('saleStoreName', firstList[0]?.saleStoreId) //1차점 아닌 판매점 셀렉트박스 + setOriginOtherSaleStoreList(otherList) setOtherSaleStoreList(otherList) - form.setValue('otherSaleStoreId', otherList[0].saleStoreId) - form.setValue('otherSaleStoreName', otherList[0].saleStoreId) + // form.setValue('otherSaleStoreId', otherList[0].saleStoreId) + form.setValue('otherSaleStoreName', otherList[0]?.saleStoreId) } }) } @@ -144,32 +152,83 @@ export default function StuffDetail() { //1차점 변경 이벤트 const onSelectionChange = (key) => { - if (key == null) { + if (!isEmptyArray(key)) { + setOtherSaleStoreList(otherSaleStoreList) + form.setValue('saleStoreId', key[0].saleStoreId) + form.setValue('saleStoreName', key[0].saleStoreId) + + //선택한 1차점 정보로 2차점 list 추리기 + //長府工産株式会社 大阪支社 + let newOtherSaleStoreList = originOtherSaleStoreList.filter((row) => row.firstAgentId === key[0].saleStoreId) + setOtherSaleStoreList(newOtherSaleStoreList) + } else { + //X누름 form.setValue('saleStoreId', '') form.setValue('saleStoreName', '') - } else { - const name = saleStoreList.find((obj) => obj.saleStoreId === key.target.value).saleStoreName - form.setValue('saleStoreId', key.target.value) - form.setValue('saleStoreName', name) + setOtherSaleStoreList(originOtherSaleStoreList) } } //2차점 변경 이벤트 const onSelectionChange2 = (key) => { - const name = otherSaleStoreList.find((obj) => obj.saleStoreId === key.target.value).saleStoreName - form.setValue('otherSaleStoreId', key.target.value) - form.setValue('otherSaleStoreNm', name) + if (!isEmptyArray(key)) { + form.setValue('otherSaleStoreId', key[0].saleStoreId) + form.setValue('otherSaleStoreId', key[0].saleStoreId) + } else { + form.setValue('otherSaleStoreId', '') + form.setValue('otherSaleStoreId', '') + } } // 우편번호 숫자만 체크 const _zipNo = watch('zipNo') useEffect(() => { if (_zipNo !== '' && _zipNo.length === 7 && !_zipNo.match(/\D/g)) { - setButtonValid(true) + //setButtonValid(true) } else { - setButtonValid(false) + //setButtonValid(false) } }, [_zipNo]) + //팝업에서 넘어온 우편정보 + const setZipInfo = (info) => { + console.log('팝업에서 넘어온 데이타::::::::', info) + + // const params = { + // zipcode: _zipNo, + // } + + // get({ url: `https://zipcloud.ibsnet.co.jp/api/search?${queryStringFormatter(params)}` }).then((res) => { + // //7830060 + // //9302226 + // //0790177 3개짜리 + // if (res.status === 200) { + // if (res.results != null) { + // console.log('주소검색::', res.results) + // // console.log('prefcode::', res.results[0].prefcode) + // // console.log('address::', res.results[0].address2 + res.results[0].address3) + // setPrefValue(res.results[0].prefcode) + // form.setValue('prefId', res.results[0].prefcode) + // form.setValue('prefName', res.results[0].address1) + // form.setValue('address', res.results[0].address2 + res.results[0].address3) + // } else { + // alert('등록된 우편번호에서 주소를 찾을 수 없습니다. 다시 입력해주세요.') + // form.setValue('prefId', '') + // form.setValue('prefName', '') + // form.setValue('address', '') + // form.setValue('zipNo', '') + // setPrefValue('') + // setAreaIdList([]) + // form.setValue('areaId', '') + // // form.setValue('areaName', '') + // setWindSpeedList([]) + // form.setValue('windSpeed', '') + // } + // } else { + // alert(res.message) + // } + // }) + } + //임시저장 저장 버튼 컨트롤 // dispCompanyName: '', //담당자 // objectName: '', //물건명 @@ -201,10 +260,10 @@ export default function StuffDetail() { const _installHeight = watch('installHeight') useEffect(() => { - // console.log('mode:::::', editMode) + console.log('mode:::::', editMode) if (editMode === 'NEW') { const formData = form.getValues() - // console.log('폼::::::::::::', formData) + console.log('폼::::::::::::', formData) let errors = {} if (!_dispCompanyName || _dispCompanyName.trim().length === 0) { errors.dispCompanyName = true @@ -251,7 +310,7 @@ export default function StuffDetail() { errors.installHeight = true } - // console.log('errors::', errors) + console.log('errors::', errors) setIsFormValid(Object.keys(errors).length === 0) } else { // console.log('상세일때 폼체크') @@ -273,49 +332,21 @@ export default function StuffDetail() { // 주소검색 API const onSearchPostNumber = () => { - const params = { - zipcode: _zipNo, - } + console.log('주소검색클릭!!') - get({ url: `https://zipcloud.ibsnet.co.jp/api/search?${queryStringFormatter(params)}` }).then((res) => { - //7830060 - //9302226 - //0790177 3개짜리 - if (res.status === 200) { - if (res.results != null) { - console.log('주소검색::', res.results) - // console.log('prefcode::', res.results[0].prefcode) - // console.log('address::', res.results[0].address2 + res.results[0].address3) - setPrefValue(res.results[0].prefcode) - form.setValue('prefId', res.results[0].prefcode) - form.setValue('prefName', res.results[0].address1) - form.setValue('address', res.results[0].address2 + res.results[0].address3) - } else { - alert('등록된 우편번호에서 주소를 찾을 수 없습니다. 다시 입력해주세요.') - form.setValue('prefId', '') - form.setValue('prefName', '') - form.setValue('address', '') - form.setValue('zipNo', '') - setPrefValue('') - setAreaIdList([]) - form.setValue('areaId', '') - // form.setValue('areaName', '') - setWindSpeedList([]) - form.setValue('windSpeed', '') - } - } else { - alert(res.message) - } - }) + setShowButtonValid(true) } useEffect(() => { + console.log('우편번호검색해서 값이왔어:::::::::::', prefValue) if (prefValue !== '') { // 발전량시뮬레이션 지역 목록 // /api/object/prefecture/도도부현코드/list get({ url: `/api/object/prefecture/${prefValue}/list` }).then((res) => { if (!isEmptyArray(res)) { - // console.log('발전량 시뮬레이션::::::::', res) + console.log('발전량 시뮬레이션::::::::', res) + //res에 areaId추가됨 + // form.setValue('areaId', res[0].areaId) form.setValue('areaId', res[0].prefId) form.setValue('areaName', res[0].prefName) setAreaIdList(res) @@ -404,7 +435,7 @@ export default function StuffDetail() { workName: null, } console.log('임시저장params::', params) - return + // return await post({ url: '/api/object/save-object', data: params }).then((res) => { console.log('res::::::', res) }) @@ -498,18 +529,16 @@ export default function StuffDetail() {
-
- {saleStoreList?.length > 0 && ( - - )} +
+
@@ -526,18 +555,17 @@ export default function StuffDetail() {
-
- {otherSaleStoreList?.length > 0 && ( - - )} +
+
- +
- -
*우편번호 7자리를 입력한 후, 주소검색 버튼을 클릭해 주십시오
+
*주소검색 버튼을 클릭한 후, 도도부현 정보를 선택해주십시오.
@@ -581,20 +601,16 @@ export default function StuffDetail() {
-
- - {/* {prefCodeList?.length > 0 && ( - - )} */} - {/* {prefCodeList?.length > 0 && ( - + {prefCodeList.map((row) => ( + + ))} - )} */} + )}
@@ -644,8 +660,8 @@ export default function StuffDetail() { })}
- m/s이하 - + m/s이하 +
@@ -784,6 +800,8 @@ export default function StuffDetail() { )} )} + {showButtonValid && } + ) } diff --git a/src/components/management/StuffQGrid.jsx b/src/components/management/StuffQGrid.jsx index 4fd6c42d..f9a65052 100644 --- a/src/components/management/StuffQGrid.jsx +++ b/src/components/management/StuffQGrid.jsx @@ -40,7 +40,6 @@ export default function StuffQGrid(props) { suppressMovable: true, resizable: false, suppressSizeToFit: false, - headerClass: 'centered', //_test.scss에 추가 테스트 } }, []) @@ -67,11 +66,7 @@ export default function StuffQGrid(props) { //더블클릭 const onCellDoubleClicked = useCallback((event) => { - // if (event.column.colId === 'company') { - // return - // } else { props.getCellDoubleClicked(event) - // } }, []) //컨텐츠에 따라 컬럼넓이 자동조절 @@ -82,7 +77,6 @@ export default function StuffQGrid(props) { }, []) const onGridReady = useCallback((event) => { - // console.log('event:::', event) // 헤더 사이즈 조정 컬럼에 width값으로 계산 event.api.sizeColumnsToFit() }, []) @@ -92,6 +86,13 @@ export default function StuffQGrid(props) { gridData ? setRowData(gridData) : '' }, [gridData]) + // 임시는 row색깔 구분 + const getRowClass = (row) => { + if (row.data.objectNo.substring(0, 1) === 'T') { + return 'important_row' + } + } + return (
물건 목록이 없습니다.'} + // getRowStyle={getRowStyle} + getRowClass={getRowClass} />
) diff --git a/src/components/management/StuffSearchCondition.jsx b/src/components/management/StuffSearchCondition.jsx index 21b84dd8..19981fc5 100644 --- a/src/components/management/StuffSearchCondition.jsx +++ b/src/components/management/StuffSearchCondition.jsx @@ -260,7 +260,8 @@ export default function StuffSearchCondition() { options={schSelSaleStoreList} value={stuffSearch?.schSelSaleStoreId ? stuffSearch.schSelSaleStoreId : schSelSaleStoreId} labelField="saleStoreName" - valueField="saleStoreName" + valueField="saleStoreId" + searchBy="saleStoreName" onChange={onSelectionChange} clearable={true} onClearAll={handleClear} diff --git a/src/components/management/popup/DesignRequestPop.jsx b/src/components/management/popup/DesignRequestPop.jsx new file mode 100644 index 00000000..ae7fd858 --- /dev/null +++ b/src/components/management/popup/DesignRequestPop.jsx @@ -0,0 +1,5 @@ +import React from 'react' + +export default function DesignRequestPop() { + return
+} diff --git a/src/components/management/popup/FindAddressPop.jsx b/src/components/management/popup/FindAddressPop.jsx new file mode 100644 index 00000000..32ffcfcd --- /dev/null +++ b/src/components/management/popup/FindAddressPop.jsx @@ -0,0 +1,111 @@ +import React, { useEffect, useState } from 'react' +import { useForm } from 'react-hook-form' +import { queryStringFormatter } from '@/util/common-utils' +import { useAxios } from '@/hooks/useAxios' +import { globalLocaleStore } from '@/store/localeAtom' +import { useRecoilValue } from 'recoil' +import FindAddressPopQGrid from './FindAddressPopQGrid' + +export default function FindAddressPop(props) { + const globalLocaleState = useRecoilValue(globalLocaleStore) + + const { get } = useAxios(globalLocaleState) + const [prefId, setPrefId] = useState('') + const [address1, setAddress1] = useState('') + const [address2, setAddress2] = useState('') + const [address3, setAddress3] = useState('') + const gridRef = useRef() + + const [gridProps, setGridProps] = useState({ + gridData: [], + isPageable: false, + gridColumns: [], + }) + const formInitValue = { + zipNo: '', + } + const { register, setValue, getValues, handleSubmit, resetField, control, watch } = useForm({ + defaultValues: formInitValue, + }) + + const form = { register, setValue, getValues, handleSubmit, resetField, control, watch } + //우편번호 검색 + const searchPostNum = () => { + // //7830060 + // //9302226 + // //0790177 3개짜리 + console.log(watch('zipNo')) + const params = { + zipcode: watch('zipNo'), + } + + get({ url: `https://zipcloud.ibsnet.co.jp/api/search?${queryStringFormatter(params)}` }).then((res) => { + console.log('우편조회 API결과:::::', res) + if (res.status === 200) { + } else { + alert('등록된 우편번호에서 주소를 찾을 수 없습니다. 다시 입력해주세요.') + } + }) + } + // 주소적용 클릭 + const zipInfo = () => { + console.log('주소적용 클릭:::::::::') + // setAddress3('3333') + // setAddress3('4444') + // setAddress3('5555') + props.zipInfo({ + zipNo: '3434343', + address1: '3333', + address2: '4444', + address3: '5555', + }) + + props.setShowButtonValid(false) + } + + //숫자만 + const handleKeyUp = (e) => { + let input = e.target + input.value = input.value.replace(/[^0-9]/g, '') + } + return ( +
+
+
+
+

우편번호 郵便番号

+ +
+
+
+
+ + +
+
+ +
+
+
+ + +
+
+
+
+
+ ) +} diff --git a/src/components/management/popup/FindAddressPopQGrid.jsx b/src/components/management/popup/FindAddressPopQGrid.jsx new file mode 100644 index 00000000..f5b7c2fa --- /dev/null +++ b/src/components/management/popup/FindAddressPopQGrid.jsx @@ -0,0 +1,6 @@ +import React from 'react' + +export default function FindAddressPopGrid(props) { + const { gridData, gridColumns, isPageable = true, gridRef } = props + return
+} diff --git a/src/styles/_grid-detail.scss b/src/styles/_grid-detail.scss index 4cf9d3b2..79a8cb9a 100644 --- a/src/styles/_grid-detail.scss +++ b/src/styles/_grid-detail.scss @@ -1,64 +1,63 @@ -.q-grid{ - height: fit-content; - .ag-theme-quartz { - outline: none; - border: none; - --ag-border-radius: 0px; - --ag-wrapper-border-radius: 0px; - --ag-header-height: 40px; - --ag-header-foreground-color: white; - --ag-header-background-color: #5D6A76; - // --ag-header-cell-hover-background-color: rgb(80, 40, 140); - --ag-header-cell-moving-background-color: #5D6A76; - .ag-root-wrapper{ - outline: none; - border: none; - - } - .ag-header{ - border-bottom: none; - border-radius: 4px; - } - .ag-header-cell{ - font-size: 13px; - color: #fff; - } - .ag-header-cell-label{ - justify-content: center; - } - .ag-header-cell-resize{ - &:after{ - display: none; - } - } - .ag-row{ - border-bottom: 1px solid #ECF0F4; - &:nth-child(2n){ - background-color: #F7F9FA; - } - &.important_row{ - background-color: #e7e7e7; - } - } - .ag-cell{ - font-size: 13px; - color: #45576F; - } - .ag-icon-desc::before, - .ag-icon-asc::before, - .ag-icon-filter::before{ - color: #fff; - } +.q-grid { + height: fit-content; + .ag-theme-quartz { + outline: none; + border: none; + --ag-border-radius: 0px; + --ag-wrapper-border-radius: 0px; + --ag-header-height: 40px; + --ag-header-foreground-color: white; + --ag-header-background-color: #5d6a76; + // --ag-header-cell-hover-background-color: rgb(80, 40, 140); + --ag-header-cell-moving-background-color: #5d6a76; + .ag-root-wrapper { + outline: none; + border: none; } - .copy-ico-wrap{ - display: flex; - align-items: center; - .copy_ico{ - width: 18px; - height: 18px; - background: url(../../public/static/images/sub/copy_grid_ico.svg)no-repeat center; - background-size: cover; - margin-left: 12px; - } + .ag-header { + border-bottom: none; + border-radius: 4px; } -} \ No newline at end of file + .ag-header-cell { + font-size: 13px; + color: #fff; + } + .ag-header-cell-label { + justify-content: center; + } + .ag-header-cell-resize { + &:after { + display: none; + } + } + .ag-row { + border-bottom: 1px solid #ecf0f4; + &:nth-child(2n) { + background-color: #f7f9fa; + } + &.important_row { + background-color: #e7e7e7; + } + } + .ag-cell { + font-size: 13px; + color: #45576f; + } + .ag-icon-desc::before, + .ag-icon-asc::before, + .ag-icon-filter::before { + color: #fff; + } + } + .copy-ico-wrap { + display: flex; + align-items: center; + .copy_ico { + width: 18px; + height: 18px; + background: url(../../public/static/images/sub/copy_grid_ico.svg) no-repeat center; + background-size: cover; + margin-left: 12px; + } + } +} From df335960e7b1aaed9ba3b2a786372bb261f10dc1 Mon Sep 17 00:00:00 2001 From: basssy Date: Tue, 15 Oct 2024 08:22:52 +0900 Subject: [PATCH 07/68] =?UTF-8?q?=EB=AC=BC=EA=B1=B4=ED=98=84=ED=99=A9=20?= =?UTF-8?q?=EC=9A=B0=ED=8E=B8=EB=B2=88=ED=98=B8=20=EA=B2=80=EC=83=89?= =?UTF-8?q?=ED=8C=9D=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/management/StuffDetail.jsx | 6 +++--- src/components/management/popup/FindAddressPop.jsx | 14 +++++++++----- src/locales/ja.json | 4 ++++ src/locales/ko.json | 4 ++++ 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/components/management/StuffDetail.jsx b/src/components/management/StuffDetail.jsx index 9729c1af..96d2bb11 100644 --- a/src/components/management/StuffDetail.jsx +++ b/src/components/management/StuffDetail.jsx @@ -94,7 +94,7 @@ export default function StuffDetail() { // 도도부현API get({ url: '/api/object/prefecture/list' }).then((res) => { if (!isEmptyArray(res)) { - console.log('도도부현API 결과:::', res) + // console.log('도도부현API 결과:::', res) setPrefCodeList(res) } }) @@ -120,7 +120,7 @@ export default function StuffDetail() { // 도도부현API get({ url: '/api/object/prefecture/list' }).then((res) => { if (!isEmptyArray(res)) { - console.log('신규화면 도도부현API 결과:::', res) + // console.log('신규화면 도도부현API 결과:::', res) setPrefCodeList(res) } }) @@ -338,8 +338,8 @@ export default function StuffDetail() { } useEffect(() => { - console.log('우편번호검색해서 값이왔어:::::::::::', prefValue) if (prefValue !== '') { + console.log('우편번호검색해서 값이왔어:::::::::::', prefValue) // 발전량시뮬레이션 지역 목록 // /api/object/prefecture/도도부현코드/list get({ url: `/api/object/prefecture/${prefValue}/list` }).then((res) => { diff --git a/src/components/management/popup/FindAddressPop.jsx b/src/components/management/popup/FindAddressPop.jsx index 32ffcfcd..bf276218 100644 --- a/src/components/management/popup/FindAddressPop.jsx +++ b/src/components/management/popup/FindAddressPop.jsx @@ -1,15 +1,19 @@ -import React, { useEffect, useState } from 'react' +import React, { useEffect, useState, useRef } from 'react' import { useForm } from 'react-hook-form' import { queryStringFormatter } from '@/util/common-utils' import { useAxios } from '@/hooks/useAxios' import { globalLocaleStore } from '@/store/localeAtom' import { useRecoilValue } from 'recoil' import FindAddressPopQGrid from './FindAddressPopQGrid' +import { useMessage } from '@/hooks/useMessage' export default function FindAddressPop(props) { const globalLocaleState = useRecoilValue(globalLocaleStore) const { get } = useAxios(globalLocaleState) + + const { getMessage } = useMessage() + const [prefId, setPrefId] = useState('') const [address1, setAddress1] = useState('') const [address2, setAddress2] = useState('') @@ -73,7 +77,7 @@ export default function FindAddressPop(props) {
-

우편번호 郵便番号

+

{getMessage('stuff.addressPopup.title')}

@@ -87,7 +91,7 @@ export default function FindAddressPop(props) { maxLength={7} onKeyUp={handleKeyUp} {...form.register('zipNo')} - placeholder="우편번호의 7자리를 입력하세요. 郵便番号の7桁を入力してください。" + placeholder={getMessage('stuff.addressPopup.placeholder')} />
@@ -97,10 +101,10 @@ export default function FindAddressPop(props) {
diff --git a/src/locales/ja.json b/src/locales/ja.json index f8f15c2e..eddf5a2a 100644 --- a/src/locales/ja.json +++ b/src/locales/ja.json @@ -381,6 +381,10 @@ "stuff.gridHeader.specDate": "仕様確認日", "stuff.gridHeader.createDatetime": "登録日", "stuff.message.periodError": "最大1年間閲覧可能.", + "stuff.addressPopup.title": "郵便番号", + "stuff.addressPopup.placeholder": "郵便番号の7桁を入力してください。", + "stuff.addressPopup.btn1": "閉じる", + "stuff.addressPopup.btn2": "住所適用", "length": "長さ", "height": "高さ", "output": "出力", diff --git a/src/locales/ko.json b/src/locales/ko.json index ee31c6c3..ef41a4a8 100644 --- a/src/locales/ko.json +++ b/src/locales/ko.json @@ -385,6 +385,10 @@ "stuff.gridHeader.specDate": "사양확인일", "stuff.gridHeader.createDatetime": "등록일", "stuff.message.periodError": "최대1년 조회 가능합니다.", + "stuff.addressPopup.title": "우편번호", + "stuff.addressPopup.placeholder": "우편번호의 7자리를 입력하세요.", + "stuff.addressPopup.btn1": "닫기", + "stuff.addressPopup.btn2": "주소적용", "length": "길이", "height": "높이", "output": "출력", From fd1bbe46d4b8ea8bf1afef4eb9a01d361374e711 Mon Sep 17 00:00:00 2001 From: Daseul Kim Date: Tue, 15 Oct 2024 11:23:13 +0900 Subject: [PATCH 08/68] =?UTF-8?q?refactor:=20canvas=20plan=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=EC=82=AC=ED=95=AD=20=ED=99=95=EC=9D=B8=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=ED=95=A8=EC=88=98=20=EC=88=98=EC=A0=95,=20canvas?= =?UTF-8?q?=EC=99=80=20db=EA=B0=84=20=EC=A0=80=EC=9E=A5=ED=8F=AC=EB=A7=B7?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/usePlan.js | 44 ++++++++++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/src/hooks/usePlan.js b/src/hooks/usePlan.js index c7f6620e..427fe511 100644 --- a/src/hooks/usePlan.js +++ b/src/hooks/usePlan.js @@ -54,6 +54,7 @@ export function usePlan() { 'y2', 'attributes', 'stickeyPoint', + 'text', ]) const str = JSON.stringify(objs) @@ -76,32 +77,49 @@ export function usePlan() { */ const checkModifiedCanvasPlan = () => { removeMouseLines() - const canvasStr = addCanvas() - const canvasStatus = dbToCanvasFormat(canvasToDbFormat(canvasStr)) + const canvasStatus = addCanvas() const initPlanData = initCanvasPlans.find((plan) => plan.id === currentCanvasPlan.id) - if (JSON.parse(canvasStr).objects.length === 0 && initPlanData === undefined) { - // 저장 안 된 빈 캔버스 - return false - } else if (initPlanData && canvasStatus === initPlanData.canvasStatus) { - // 저장 되어있고 변경사항 없는 캔버스 - return false + + if (!initPlanData) { + // 새로운 캔버스 + return JSON.parse(canvasStatus).objects.length > 0 } else { - return true + // 저장된 캔버스 + if (canvasStatus === initPlanData.canvasStatus) { + return false + } else { + // 각각 object들의 id 목록을 추출하여 비교 + const canvasObjsIds = getObjectIds(JSON.parse(canvasStatus).objects) + const dbObjsIds = getObjectIds(JSON.parse(initPlanData.canvasStatus).objects) + + return canvasObjsIds.length !== dbObjsIds.length || !canvasObjsIds.every((id, index) => id === dbObjsIds[index]) + } } } + const getObjectIds = (objects) => { + return objects + .filter((obj) => obj.hasOwnProperty('id')) + .map((obj) => obj.id) + .sort() + } /** * DB에 저장된 데이터를 canvas에서 사용할 수 있도록 포맷화 */ const dbToCanvasFormat = (cs) => { - return JSON.stringify(cs.replace(/##/g, '"').replace(/\\/g, '').slice(1, -1)) + // return JSON.stringify(cs.replace(/##/g, '"')) //.replace(/\\/g, ''))//.slice(1, -1)) + // JSON.stringify()를 사용하면 "가 \"로 바뀐다. 따라서, JSON.stringify를 제거 + // canvasToDbFormat()에서 \\의 상황을 없앴으므로 replace(/\\/g, '')를 제거 + return cs.replace(/##/g, '"') } /** * canvas의 데이터를 DB에 저장할 수 있도록 포맷화 */ const canvasToDbFormat = (cs) => { - return JSON.stringify(cs).replace(/"/g, '##') + // return JSON.stringify(cs).replace(/"/g, '##') + // addCanvas()에서 JSON.stringify()를 거쳐서 나오는데, 또 감싸버려서 \가 \\로 된다. 따라서, JSON.stringify를 제거 + return cs.replace(/"/g, '##') } /** @@ -130,9 +148,7 @@ export function usePlan() { setInitCanvasPlans((initCanvasPlans) => initCanvasPlans.map((plan) => (plan.id === currentCanvasPlan.id ? { ...plan, canvasStatus: canvasStatus } : plan)), ) - setPlans((plans) => - plans.map((plan) => (plan.id === currentCanvasPlan.id ? { ...plan, canvasStatus: canvasStatus } : plan)), - ) + setPlans((plans) => plans.map((plan) => (plan.id === currentCanvasPlan.id ? { ...plan, canvasStatus: canvasStatus } : plan))) }) .catch((error) => { swalFire({ text: error.message, icon: 'error' }) From f07a32988e9f3788dc8a03021a4d33451c34d695 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Tue, 15 Oct 2024 12:28:39 +0900 Subject: [PATCH 09/68] =?UTF-8?q?=EB=AA=A8=EB=8B=AC=20=EB=8B=AB=EB=8A=94?= =?UTF-8?q?=20=ED=95=A8=EC=88=98=20hook=20=EA=B8=B0=EB=B3=B8=20=ED=8C=8C?= =?UTF-8?q?=EB=9D=BC=EB=AF=B8=ED=84=B0=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../modal/auxiliary/AuxiliaryDrawing.jsx | 2 +- .../modal/eavesGable/EavesGableEdit.jsx | 2 +- .../outerlinesetting/PropertiesSetting.jsx | 2 +- .../outerlinesetting/WallLineSetting.jsx | 4 +- .../roofAllocation/RoofAllocationSetting.jsx | 76 +------ .../roofShape/RoofShapePassivitySetting.jsx | 3 +- .../modal/roofShape/RoofShapeSetting.jsx | 2 +- src/hooks/roofcover/useAuxiliaryDrawing.js | 10 +- src/hooks/roofcover/useOuterLineWall.js | 9 +- src/hooks/roofcover/usePropertiesSetting.js | 4 +- .../roofcover/useRoofAllocationSetting.js | 127 +++++++++++ .../roofcover/useRoofShapePassivitySetting.js | 6 +- src/hooks/roofcover/useRoofShapeSetting.js | 10 +- src/hooks/usePolygon.js | 206 ++++++++++++++++++ src/util/qpolygon-utils.js | 2 +- 15 files changed, 367 insertions(+), 98 deletions(-) create mode 100644 src/hooks/roofcover/useRoofAllocationSetting.js diff --git a/src/components/floor-plan/modal/auxiliary/AuxiliaryDrawing.jsx b/src/components/floor-plan/modal/auxiliary/AuxiliaryDrawing.jsx index 1732f3f7..d0f87114 100644 --- a/src/components/floor-plan/modal/auxiliary/AuxiliaryDrawing.jsx +++ b/src/components/floor-plan/modal/auxiliary/AuxiliaryDrawing.jsx @@ -49,7 +49,7 @@ export default function AuxiliaryDrawing({ setShowAuxiliaryModal }) { handleFix, buttonAct, setButtonAct, - } = useAuxiliaryDrawing() + } = useAuxiliaryDrawing(setShowAuxiliaryModal) const outerLineProps = { length1, diff --git a/src/components/floor-plan/modal/eavesGable/EavesGableEdit.jsx b/src/components/floor-plan/modal/eavesGable/EavesGableEdit.jsx index 1267fe98..bab1e696 100644 --- a/src/components/floor-plan/modal/eavesGable/EavesGableEdit.jsx +++ b/src/components/floor-plan/modal/eavesGable/EavesGableEdit.jsx @@ -10,7 +10,7 @@ import { useEavesGableEdit } from '@/hooks/roofcover/useEavesGableEdit' export default function EavesGableEdit({ setShowEavesGableEditModal }) { const { getMessage } = useMessage() - const { type, setType, buttonMenu, TYPES, pitchRef, offsetRef, widthRef, radioTypeRef } = useEavesGableEdit() + const { type, setType, buttonMenu, TYPES, pitchRef, offsetRef, widthRef, radioTypeRef } = useEavesGableEdit(setShowEavesGableEditModal) const eavesProps = { pitchRef, offsetRef, diff --git a/src/components/floor-plan/modal/outerlinesetting/PropertiesSetting.jsx b/src/components/floor-plan/modal/outerlinesetting/PropertiesSetting.jsx index 248567e3..d26b2ef3 100644 --- a/src/components/floor-plan/modal/outerlinesetting/PropertiesSetting.jsx +++ b/src/components/floor-plan/modal/outerlinesetting/PropertiesSetting.jsx @@ -6,7 +6,7 @@ export default function PropertiesSetting(props) { const { getMessage } = useMessage() const { setShowPropertiesSettingModal } = props - const { handleSetEaves, handleSetGable, handleRollback, handleFix, closeModal } = usePropertiesSetting() + const { handleSetEaves, handleSetGable, handleRollback, handleFix, closeModal } = usePropertiesSetting(setShowPropertiesSettingModal) return ( diff --git a/src/components/floor-plan/modal/outerlinesetting/WallLineSetting.jsx b/src/components/floor-plan/modal/outerlinesetting/WallLineSetting.jsx index 54bd6844..975a582a 100644 --- a/src/components/floor-plan/modal/outerlinesetting/WallLineSetting.jsx +++ b/src/components/floor-plan/modal/outerlinesetting/WallLineSetting.jsx @@ -39,7 +39,7 @@ export default function WallLineSetting(props) { outerLineDiagonalLengthRef, handleRollback, handleFix, - } = useOuterLineWall() + } = useOuterLineWall(setShowOutlineModal) const outerLineProps = { length1, @@ -171,7 +171,7 @@ export default function WallLineSetting(props) { +
diff --git a/src/components/floor-plan/modal/roofShape/RoofShapePassivitySetting.jsx b/src/components/floor-plan/modal/roofShape/RoofShapePassivitySetting.jsx index 828b2ad3..8eaa4a49 100644 --- a/src/components/floor-plan/modal/roofShape/RoofShapePassivitySetting.jsx +++ b/src/components/floor-plan/modal/roofShape/RoofShapePassivitySetting.jsx @@ -7,7 +7,8 @@ import { useMessage } from '@/hooks/useMessage' import { useRoofShapePassivitySetting } from '@/hooks/roofcover/useRoofShapePassivitySetting' export default function RoofShapePassivitySetting({ setShowRoofShapePassivitySettingModal }) { - const { handleSave, handleConfirm, handleRollback, buttons, type, setType, TYPES, offsetRef, pitchRef } = useRoofShapePassivitySetting() + const { handleSave, handleConfirm, handleRollback, buttons, type, setType, TYPES, offsetRef, pitchRef } = + useRoofShapePassivitySetting(setShowRoofShapePassivitySettingModal) const { getMessage } = useMessage() const eavesProps = { diff --git a/src/components/floor-plan/modal/roofShape/RoofShapeSetting.jsx b/src/components/floor-plan/modal/roofShape/RoofShapeSetting.jsx index f906a67e..60355848 100644 --- a/src/components/floor-plan/modal/roofShape/RoofShapeSetting.jsx +++ b/src/components/floor-plan/modal/roofShape/RoofShapeSetting.jsx @@ -37,7 +37,7 @@ export default function RoofShapeSetting({ setShowRoofShapeSettingModal }) { buttonMenu, handleConfirm, handleRollBack, - } = useRoofShapeSetting() + } = useRoofShapeSetting(setShowRoofShapeSettingModal) const ridgeProps = { pitch, setPitch, eavesOffset, setEavesOffset } const patternProps = { pitch, setPitch, eavesOffset, setEavesOffset, gableOffset, setGableOffset } diff --git a/src/hooks/roofcover/useAuxiliaryDrawing.js b/src/hooks/roofcover/useAuxiliaryDrawing.js index a2810163..2e2e0a1b 100644 --- a/src/hooks/roofcover/useAuxiliaryDrawing.js +++ b/src/hooks/roofcover/useAuxiliaryDrawing.js @@ -28,7 +28,7 @@ import { fabric } from 'fabric' import { useAdsorptionPoint } from '@/hooks/useAdsorptionPoint' // 보조선 작성 -export function useAuxiliaryDrawing() { +export function useAuxiliaryDrawing(setShowAuxiliaryModal) { const canvas = useRecoilValue(canvasState) const { addCanvasMouseEventListener, addDocumentEventListener, removeMouseLine, initEvent } = useEvent() const { getIntersectMousePoint } = useMouse() @@ -613,11 +613,15 @@ export function useAuxiliaryDrawing() { addCanvasMouseEventListener('mouse:move', mouseMove) } - const handleFix = (fn) => { + const handleFix = () => { if (!confirm('지붕선 완료하시겠습니까?')) { return } - fn(close) + + const roofBases = canvas.getObjects().find((obj) => obj.name === 'roofBase') + roofBases.innerLines = [...lineHistory.current] + + setShowAuxiliaryModal(close) } return { diff --git a/src/hooks/roofcover/useOuterLineWall.js b/src/hooks/roofcover/useOuterLineWall.js index d3b54c1c..269ef24f 100644 --- a/src/hooks/roofcover/useOuterLineWall.js +++ b/src/hooks/roofcover/useOuterLineWall.js @@ -31,7 +31,7 @@ import { calculateAngle } from '@/util/qpolygon-utils' import { fabric } from 'fabric' //외벽선 그리기 -export function useOuterLineWall() { +export function useOuterLineWall(setShowOutlineModal) { const canvas = useRecoilValue(canvasState) const { addCanvasMouseEventListener, addDocumentEventListener, removeAllMouseEventListeners, removeAllDocumentEventListeners, removeMouseEvent } = useEvent() @@ -68,8 +68,6 @@ export function useOuterLineWall() { const isFix = useRef(false) - const closeModalFn = useRef(null) - useEffect(() => { if (adsorptionPointAddMode || tempGridMode) { return @@ -209,7 +207,7 @@ export function useOuterLineWall() { removeAllMouseEventListeners() removeAllDocumentEventListeners() canvas?.renderAll() - closeModalFn.current(false) + setShowOutlineModal(false) } if (points.length < 3) { @@ -749,7 +747,7 @@ export function useOuterLineWall() { setPoints((prev) => prev.slice(0, prev.length - 1)) } - const handleFix = (fn) => { + const handleFix = () => { if (points.length < 3) { return } @@ -779,7 +777,6 @@ export function useOuterLineWall() { }) isFix.current = true - closeModalFn.current = fn } return { diff --git a/src/hooks/roofcover/usePropertiesSetting.js b/src/hooks/roofcover/usePropertiesSetting.js index a2905f43..ad703356 100644 --- a/src/hooks/roofcover/usePropertiesSetting.js +++ b/src/hooks/roofcover/usePropertiesSetting.js @@ -8,7 +8,7 @@ import { useLine } from '@/hooks/useLine' import { outerLinePointsState } from '@/store/outerLineAtom' // 외벽선 속성 설정 -export function usePropertiesSetting() { +export function usePropertiesSetting(setShowPropertiesSettingModal) { const canvas = useRecoilValue(canvasState) const currentObject = useRecoilValue(currentObjectState) @@ -161,7 +161,7 @@ export function usePropertiesSetting() { canvas.renderAll() setPoints([]) - fn(false) + setShowPropertiesSettingModal(false) } return { handleSetEaves, handleSetGable, handleRollback, handleFix, closeModal } diff --git a/src/hooks/roofcover/useRoofAllocationSetting.js b/src/hooks/roofcover/useRoofAllocationSetting.js new file mode 100644 index 00000000..fe321c1e --- /dev/null +++ b/src/hooks/roofcover/useRoofAllocationSetting.js @@ -0,0 +1,127 @@ +import { useRecoilValue } from 'recoil' +import { canvasState } from '@/store/canvasAtom' +import { useState } from 'react' +import { setSurfaceShapePattern } from '@/util/canvas-util' +import { splitPolygonWithLines } from '@/util/qpolygon-utils' + +// 지붕면 할당 +export function useRoofAllocationSetting(setShowRoofAllocationSettingModal) { + const canvas = useRecoilValue(canvasState) + + const roofMaterials = [ + { + id: 'A', + name: '기와1', + type: 'A', + width: '200', + length: '200', + alignType: 'parallel', + }, + { + id: 'B', + name: '기와2', + type: 'B', + rafter: '200', + alignType: 'parallel', + }, + { + id: 'C', + name: '기와3', + type: 'C', + hajebichi: '200', + alignType: 'stairs', + }, + { + id: 'D', + name: '기와4', + type: 'D', + length: '200', + alignType: 'stairs', + }, + ] + const widths = [ + { name: '200', id: 'q' }, + { name: '250', id: 'q1' }, + { name: '300', id: 'q2' }, + ] + const lengths = [ + { name: '200', id: 'w' }, + { name: '250', id: 'w1' }, + { name: '300', id: 'w2' }, + ] + const rafters = [ + { name: '200', id: 'e' }, + { name: '250', id: 'e1' }, + { name: '300', id: 'e2' }, + ] + + const [values, setValues] = useState([ + { + id: 'A', + type: 'A', + roofMaterial: { name: '기와1' }, + width: { name: '200' }, + length: { name: '250' }, + rafter: { name: '300' }, + alignType: 'stairs', + }, + ]) + + const [radioValue, setRadioValue] = useState('A') + + const [selectedRoofMaterial, setSelectedRoofMaterial] = useState(roofMaterials[0]) + + const onAddRoofMaterial = () => { + setValues([...values, selectedRoofMaterial]) + } + + const onDeleteRoofMaterial = (id) => { + setValues(values.filter((value) => value.id !== id)) + } + + // 선택한 지붕재로 할당 + const handleSave = () => { + const roofBases = canvas.getObjects().filter((obj) => obj.name === 'roofBase') + const wallLines = canvas.getObjects().filter((obj) => obj.name === 'wallLine') + roofBases.forEach((roofBase) => { + splitPolygonWithLines(roofBase) + + roofBase.innerLines.forEach((line) => { + canvas.remove(line) + }) + + canvas.remove(roofBase) + }) + + wallLines.forEach((wallLine) => { + canvas.remove(wallLine) + }) + + const roofs = canvas.getObjects().filter((obj) => obj.name === 'roof') + + roofs.forEach((roof) => { + setSurfaceShapePattern(roof) + }) + setShowRoofAllocationSettingModal(false) + } + + const handleRadioOnChange = (e) => { + setRadioValue(e.target) + } + + return { + handleSave, + onAddRoofMaterial, + onDeleteRoofMaterial, + handleRadioOnChange, + widths, + lengths, + rafters, + values, + roofMaterials, + selectedRoofMaterial, + setSelectedRoofMaterial, + radioValue, + setRadioValue, + } +} diff --git a/src/hooks/roofcover/useRoofShapePassivitySetting.js b/src/hooks/roofcover/useRoofShapePassivitySetting.js index 245ee0d9..8efb53c6 100644 --- a/src/hooks/roofcover/useRoofShapePassivitySetting.js +++ b/src/hooks/roofcover/useRoofShapePassivitySetting.js @@ -9,7 +9,7 @@ import { useMode } from '@/hooks/useMode' import { usePolygon } from '@/hooks/usePolygon' //지붕형상 수동 설정 -export function useRoofShapePassivitySetting() { +export function useRoofShapePassivitySetting(setShowRoofShapePassivitySettingModal) { const TYPES = { EAVES: 'eaves', GABLE: 'gable', @@ -152,7 +152,7 @@ export function useRoofShapePassivitySetting() { canvas.renderAll() } - const handleSave = (fn) => { + const handleSave = () => { const exceptObjs = canvas.getObjects().filter((obj) => obj.name !== 'outerLine' && obj.parent?.name !== 'outerLine') const lines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') exceptObjs.forEach((obj) => { @@ -169,7 +169,7 @@ export function useRoofShapePassivitySetting() { const roof = drawRoofPolygon(wall) canvas.renderAll() - fn(false) + setShowRoofShapePassivitySettingModal(false) } return { handleSave, handleConfirm, buttons, type, setType, TYPES, offsetRef, pitchRef, handleRollback } } diff --git a/src/hooks/roofcover/useRoofShapeSetting.js b/src/hooks/roofcover/useRoofShapeSetting.js index c0d65a21..9988f7a9 100644 --- a/src/hooks/roofcover/useRoofShapeSetting.js +++ b/src/hooks/roofcover/useRoofShapeSetting.js @@ -8,7 +8,7 @@ import { useMode } from '@/hooks/useMode' import { useLine } from '@/hooks/useLine' // 지붕형상 설정 -export function useRoofShapeSetting() { +export function useRoofShapeSetting(setShowRoofShapeSettingModal) { const [shapeNum, setShapeNum] = useState(1) const [buttonAct, setButtonAct] = useState(1) const { getMessage } = useMessage() @@ -99,11 +99,7 @@ export function useRoofShapeSetting() { { id: 6, name: getMessage('shed') }, ] - /** - * - * @param fn 모달 닫기 위한 함수 - */ - const handleSave = (fn) => { + const handleSave = () => { //기존 wallLine 삭제 let outerLines @@ -243,7 +239,7 @@ export function useRoofShapeSetting() { canvas?.renderAll() roof.drawHelpLine() - fn && fn(false) + setShowRoofShapeSettingModal(false) } const initLineSetting = () => { diff --git a/src/hooks/usePolygon.js b/src/hooks/usePolygon.js index 2d981933..4cd63363 100644 --- a/src/hooks/usePolygon.js +++ b/src/hooks/usePolygon.js @@ -3,6 +3,7 @@ import { useRecoilValue } from 'recoil' import { fabric } from 'fabric' import { getDirectionByPoint } from '@/util/canvas-util' import { QPolygon } from '@/components/fabric/QPolygon' +import { isSamePoint } from '@/util/qpolygon-utils' export const usePolygon = () => { const canvas = useRecoilValue(canvasState) @@ -97,9 +98,214 @@ export const usePolygon = () => { canvas.renderAll() } + //polygon 나누기 + const splitPolygonWithLines = (polygon) => { + const roofs = [] + const allLines = [...polygon.innerLines] + + allLines.forEach((line) => { + line.startPoint = { x: line.x1, y: line.y1 } + line.endPoint = { x: line.x2, y: line.y2 } + }) + + // allLines에 x1,y1,x2,y2를 비교해서 중복되는 값을 제거한다. + allLines.forEach((line, index) => { + const startPoint = line.startPoint + const endPoint = line.endPoint + + allLines.forEach((line2, index2) => { + if (index !== index2) { + if ( + (isSamePoint(startPoint, line2.startPoint) && isSamePoint(endPoint, line2.endPoint)) || + (isSamePoint(endPoint, line2.startPoint) && isSamePoint(startPoint, line2.endPoint)) + ) { + allLines.splice(index2, 1) + } + } + }) + }) + + /** + * 좌표 테스트용 + */ + /*allLines.forEach((line) => { + const text = new fabric.Text(`(${line.startPoint.x},${line.startPoint.y})`, { + left: line.startPoint.x, + top: line.startPoint.y, + fontSize: 15, + }) + + polygon.canvas.add(text) + polygon.canvas.renderAll() + + const text2 = new fabric.Text(`(${line.endPoint.x},${line.endPoint.y})`, { + left: line.endPoint.x, + top: line.endPoint.y, + fontSize: 15, + }) + + polygon.canvas.add(text2) + polygon.canvas.renderAll() + }) + + polygon.points.forEach((point, index) => { + const text = new fabric.Text(`(${point.x},${point.y})`, { + left: point.x, + top: point.y, + fontSize: 15, + }) + + polygon.canvas.add(text) + polygon.canvas.renderAll() + })*/ + /** + * 좌표 테스트용 끝 + */ + + polygon.points.forEach((point, index) => { + allLines.forEach((line) => { + if (line.endPoint.x === point.x && line.endPoint.y === point.y) { + const temp = line.startPoint + line.startPoint = line.endPoint + line.endPoint = temp + } + }) + }) + + polygon.points.forEach((point, index) => { + const routes = [] + + // 시작점은 시작 hip라인의 출발점 + const startPoint = point + // 도착점은 마지막 hip라인의 끝나는 점 + const endPoint = polygon.points[(index + 1) % polygon.points.length] + + const startLine = allLines.find((line) => line.startPoint.x === startPoint.x && line.startPoint.y === startPoint.y) + const endLine = allLines.find((line) => line.startPoint.x === endPoint.x && line.startPoint.y === endPoint.y) + + const arrivalPoint = endLine.endPoint + routes.push(startLine.startPoint) + routes.push(startLine.endPoint) + + //hip끼리 만나는 경우는 아무것도 안해도됨 + if (!isSamePoint(startLine.endPoint, arrivalPoint)) { + // polygon line까지 추가 + const allLinesCopy = [...allLines, ...polygon.lines] + // hip이 만나지 않는 경우 갈 수 있는 길을 다 돌아야함 + let currentPoint = startLine.endPoint + let currentLine = startLine + let movedLines = [] + let subMovedLines = [] + while (!isSamePoint(currentPoint, arrivalPoint)) { + // startHip에서 만나는 출발선 두개. 두개의 선을 출발하여 arrivalPoint에 도착할 때 까지 count를 세고, 더 낮은 count를 가진 길을 선택한다. + let connectedLines = allLinesCopy.filter((line) => isSamePoint(line.startPoint, currentPoint) || isSamePoint(line.endPoint, currentPoint)) + + connectedLines = connectedLines.filter((line) => line !== currentLine) + + connectedLines = connectedLines.filter((line) => !subMovedLines.includes(line)) + + //마지막 선이 endLine의 startPoint와 같은경우 그 전까지 movedLine을 제거한다. + const endLineMeetLineCnt = connectedLines.filter((line) => { + return isSamePoint(line.endPoint, endLine.startPoint) || isSamePoint(line.startPoint, endLine.startPoint) + }).length + + if (endLineMeetLineCnt !== 0) { + movedLines.push(subMovedLines) + + console.log(movedLines, index) + } + + connectedLines = connectedLines.filter((line) => { + return !isSamePoint(line.endPoint, endLine.startPoint) && !isSamePoint(line.startPoint, endLine.startPoint) + }) + + if (connectedLines.length === 0) { + return + } + + let tempPoints = [] + + for (let i = 0; i < connectedLines.length; i++) { + if (isSamePoint(connectedLines[i].startPoint, currentPoint)) { + tempPoints.push({ point: connectedLines[i].endPoint, index: i, line: connectedLines[i] }) + } else { + tempPoints.push({ point: connectedLines[i].startPoint, index: i, line: connectedLines[i] }) + } + } + + //tempPoints에서 arrivalPoint와 가장 가까운 점을 찾는다. + let minDistance = Number.MAX_SAFE_INTEGER + let minIndex = 0 + tempPoints.forEach((tempPoint, index) => { + const distance = Math.sqrt(Math.pow(tempPoint.point.x - arrivalPoint.x, 2) + Math.pow(tempPoint.point.y - arrivalPoint.y, 2)) + if (distance < minDistance) { + minDistance = distance + minIndex = tempPoint.index + } + }) + + currentPoint = tempPoints[minIndex].point + currentLine = tempPoints[minIndex].line + if (currentLine !== startLine) { + subMovedLines.push(currentLine) + } + routes.push(currentPoint) + } + } + + routes.push(endLine.startPoint) + roofs.push(routes) + }) + + // 중복 제거 + roofs.forEach((roofPoint, index) => { + const samePointLengthRoofPoints = roofs.filter((roof) => roof.length === roofPoint.length && roof !== roofPoint) + + samePointLengthRoofPoints.forEach((samePointRoof) => { + if (arraysHaveSamePoints(samePointRoof, roofPoint)) { + roofs.splice(roofs.indexOf(samePointRoof), 1) + } + }) + }) + + roofs.forEach((roofPoint, index) => { + let defense + const direction = getDirectionByPoint(roofPoint[0], roofPoint[roofPoint.length - 1]) + + switch (direction) { + case 'top': + defense = 'east' + break + case 'right': + defense = 'south' + break + case 'bottom': + defense = 'west' + break + case 'left': + defense = 'north' + break + } + + const roof = new QPolygon(roofPoint, { + fontSize: polygon.fontSize, + stroke: 'black', + fill: 'transparent', + strokeWidth: 3, + name: 'roof', + selectable: false, + defense: defense, + }) + + polygon.canvas.add(roof) + polygon.canvas.renderAll() + }) + } + return { addPolygon, addPolygonByLines, removePolygon, + splitPolygonWithLines, } } diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js index 6605e3b3..73b9b285 100644 --- a/src/util/qpolygon-utils.js +++ b/src/util/qpolygon-utils.js @@ -1150,7 +1150,7 @@ export const splitPolygonWithLines = (polygon) => { fill: 'transparent', strokeWidth: 3, name: 'roof', - selectable: false, + selectable: true, defense: defense, }) From d5f5aaa09fe27721fa78ac2a34762a3c3c3e0134 Mon Sep 17 00:00:00 2001 From: basssy Date: Tue, 15 Oct 2024 14:20:39 +0900 Subject: [PATCH 10/68] =?UTF-8?q?=EB=AC=BC=EA=B1=B4=ED=98=84=ED=99=A9=20?= =?UTF-8?q?=EC=9E=84=EC=8B=9C=EC=A0=80=EC=9E=A5=20&=20=EC=84=A4=EA=B3=84?= =?UTF-8?q?=EC=9D=98=EB=A2=B0=20=ED=8C=9D=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/management/Stuff.jsx | 9 +- src/components/management/StuffDetail.jsx | 362 +++++++++--------- src/components/management/StuffQGrid.jsx | 12 +- .../management/StuffSearchCondition.jsx | 1 - .../management/popup/DesignRequestPop.jsx | 5 - .../management/popup/FindAddressPop.jsx | 100 +++-- .../management/popup/FindAddressPopQGrid.jsx | 64 +++- .../management/popup/PlanRequestPop.jsx | 169 ++++++++ src/locales/ja.json | 32 ++ src/locales/ko.json | 32 ++ 10 files changed, 561 insertions(+), 225 deletions(-) delete mode 100644 src/components/management/popup/DesignRequestPop.jsx create mode 100644 src/components/management/popup/PlanRequestPop.jsx diff --git a/src/components/management/Stuff.jsx b/src/components/management/Stuff.jsx index bc0e6779..9a0a73c5 100644 --- a/src/components/management/Stuff.jsx +++ b/src/components/management/Stuff.jsx @@ -53,10 +53,8 @@ export default function Stuff() { const onDoubleClick = (e) => { let objectNo = e.target.innerText if (objectNo.substring(0, 1) === 'R') { - console.log('진짜') router.push(`${pathname}/detail?objectNo=${objectNo.toString()}`) } else { - console.log('임시') router.push(`${pathname}/tempdetail?objectNo=${objectNo.toString()}`) } } @@ -87,7 +85,7 @@ export default function Stuff() { }, { field: 'objectNo', - width: '180px', + minWidth: 230, headerName: getMessage('stuff.gridHeader.objectNo'), cellRenderer: function (params) { if (params.data.objectNo) { @@ -122,7 +120,7 @@ export default function Stuff() { headerName: getMessage('stuff.gridHeader.saleStoreId'), cellStyle: { textAlign: 'left' }, }, - { field: 'saleStoreName', headerName: getMessage('stuff.gridHeader.saleStoreName'), cellStyle: { textAlign: 'left' } }, + { field: 'saleStoreName', minWidth: 300, headerName: getMessage('stuff.gridHeader.saleStoreName'), cellStyle: { textAlign: 'left' } }, { field: 'address', headerName: getMessage('stuff.gridHeader.address'), cellStyle: { textAlign: 'left' } }, { field: 'dispCompanyName', headerName: getMessage('stuff.gridHeader.dispCompanyName'), cellStyle: { textAlign: 'left' } }, { field: 'receiveUser', headerName: getMessage('stuff.gridHeader.receiveUser'), cellStyle: { textAlign: 'left' } }, @@ -159,14 +157,11 @@ export default function Stuff() { if (event.column.colId === 'objectNo') { return } else { - console.log(' 상세이동::::::::', event.data) //T 면 임시 R은 진짜 if (event.data.objectNo) { if (event.data.objectNo.substring(0, 1) === 'R') { - console.log('진짜:::::::::') router.push(`${pathname}/detail?objectNo=${event.data.objectNo.toString()}`) } else { - console.log('임시:::::::::::::::::') router.push(`${pathname}/tempdetail?objectNo=${event.data.objectNo.toString()}`) } } diff --git a/src/components/management/StuffDetail.jsx b/src/components/management/StuffDetail.jsx index 96d2bb11..474e0413 100644 --- a/src/components/management/StuffDetail.jsx +++ b/src/components/management/StuffDetail.jsx @@ -1,19 +1,19 @@ 'use client' -import React, { useState, useEffect } from 'react' +import React, { useState, useEffect, useRef } from 'react' import { useRouter, useSearchParams } from 'next/navigation' import { Button } from '@nextui-org/react' import Select from 'react-dropdown-select' import Link from 'next/link' import { useAxios } from '@/hooks/useAxios' import { globalLocaleStore } from '@/store/localeAtom' -import { isEmptyArray } from '@/util/common-utils' +import { isEmptyArray, isObjectNotEmpty } from '@/util/common-utils' import { useMessage } from '@/hooks/useMessage' import { useForm } from 'react-hook-form' import { useRecoilValue } from 'recoil' import { sessionStore } from '@/store/commonAtom' import FindAddressPop from './popup/FindAddressPop' -import DesignRequestPop from './popup/DesignRequestPop' +import PlanRequestPop from './popup/PlanRequestPop' export default function StuffDetail() { const sessionState = useRecoilValue(sessionStore) @@ -21,7 +21,7 @@ export default function StuffDetail() { const searchParams = useSearchParams() const { getMessage } = useMessage() const globalLocaleState = useRecoilValue(globalLocaleStore) - + const ref = useRef() const { get, post, del } = useAxios(globalLocaleState) //form const formInitValue = { @@ -31,10 +31,12 @@ export default function StuffDetail() { objectName: '', //물건명 objectNameOmit: '', //경칭선택 objectNameKana: '', //물건명 후리가나 - saleStoreId: '', //판매점ID - saleStoreName: '', //판매점명 - otherSaleStoreId: '', - otherSaleStoreName: '', + saleStoreLevel: '', //1차점스토어레벨 + saleStoreId: '', //1차점판매점ID + saleStoreName: '', //1차점판매점명 + otherSaleStoreId: '', //1차점 외 판매점ID + otherSaleStoreName: '', //1차점 외 판매점명 + otherSaleStoreLevel: '', //1차점 외 스토어레벨 zipNo: '', //우편번호 prefId: '', //도도부현 prefName: '', @@ -67,18 +69,19 @@ export default function StuffDetail() { const [windSpeedList, setWindSpeedList] = useState([]) //기준풍속 리스트 const [isFormValid, setIsFormValid] = useState(false) //임시저장, 진짜저장 버튼 컨트롤 - const [showButtonValid, setShowButtonValid] = useState(false) //주소검색 활성화 컨트롤 + const [showAddressButtonValid, setShowAddressButtonValid] = useState(false) //주소검색팝업 활성화 컨트롤 + const [showDesignRequestButtonValid, setShowDesignRequestButtonValid] = useState(false) //설계의뢰팝업 활성화 컨트롤 const objectNo = searchParams.get('objectNo') //url에서 물건번호 꺼내서 바로 set - // const [] //1차판매점 자동완성 선택값 - // const [] //2차판매점 자동완성 선택값 - const [editMode, setEditMode] = useState('NEW') const [detailData, setDetailData] = useState({}) + const [tempDetailData, setTempDetailData] = useState({}) + useEffect(() => { + console.log('objectNo::', objectNo) if (objectNo) { - //console.log('수정화면') + console.log('수정화면') setEditMode('EDIT') if (objectNo.substring(0, 1) === 'R') { @@ -86,38 +89,15 @@ export default function StuffDetail() { setIsFormValid(true) } get({ url: `/api/object/${objectNo}/detail` }).then((res) => { - // console.log('물건번호로 상세 API 호출') + console.log('물건번호로 상세 API 호출') if (res != null) { setDetailData(res) - - // 신규 상세 공통APi - // 도도부현API - get({ url: '/api/object/prefecture/list' }).then((res) => { - if (!isEmptyArray(res)) { - // console.log('도도부현API 결과:::', res) - setPrefCodeList(res) - } - }) - // 임시 1차점 판매점코드 saleStoreId=201TES01 - // T01 - //1차점 : X167 - // get({ url: `/api/object/saleStore/X167/list` }).then((res) => { - get({ url: `/api/object/saleStore/${sessionState?.storeId}/list` }).then((res) => { - if (!isEmptyArray(res)) { - // console.log('판매점 결과:::::', res) - setSaleStoreList(res) - //1차 판매점 자동완성 값 셋팅 - form.setValue('saleStoreId', res[0].saleStoreId) - //1차 판매점 번호 셋팅 - form.setValue('saleStoreName', res[0].saleStoreId) - setOtherSaleStoreList([]) - } - }) } }) } else { // 신규 상세 공통APi // 도도부현API + console.log('신규화면') get({ url: '/api/object/prefecture/list' }).then((res) => { if (!isEmptyArray(res)) { // console.log('신규화면 도도부현API 결과:::', res) @@ -135,28 +115,57 @@ export default function StuffDetail() { //1차점 셀렉트박스 setSaleStoreList(firstList) - //1차 판매점 자동완성 값 셋팅 - //form.setValue('saleStoreId', firstList[0].saleStoreId) - //1차 판매점 번호 셋팅 - form.setValue('saleStoreName', firstList[0]?.saleStoreId) - //1차점 아닌 판매점 셀렉트박스 setOriginOtherSaleStoreList(otherList) setOtherSaleStoreList(otherList) - // form.setValue('otherSaleStoreId', otherList[0].saleStoreId) - form.setValue('otherSaleStoreName', otherList[0]?.saleStoreId) } }) } }, [objectNo]) + useEffect(() => { + if (isObjectNotEmpty(detailData)) { + console.log('상세데이타:::::::', detailData) + // 도도부현API + get({ url: '/api/object/prefecture/list' }).then((res) => { + if (!isEmptyArray(res)) { + // console.log('도도부현API 결과:::', res) + setPrefCodeList(res) + } + }) + + //상세정보로 1차점 목록등 셋팅?? + // 임시 1차점 판매점코드 saleStoreId=201TES01 + // T01 + //1차점 : X167 + // get({ url: `/api/object/saleStore/X167/list` }).then((res) => { + // get({ url: `/api/object/saleStore/${sessionState?.storeId}/list` }).then((res) => { + // if (!isEmptyArray(res)) { + // // console.log('판매점 결과:::::', res) + // setSaleStoreList(res) + // //1차 판매점 자동완성 값 셋팅 + // form.setValue('saleStoreId', res[0].saleStoreId) + // //1차 판매점 번호 셋팅 + // form.setValue('saleStoreName', res[0].saleStoreId) + // setOtherSaleStoreList([]) + // } + // }) + } + }, [detailData]) + + useEffect(() => { + if (isObjectNotEmpty(tempDetailData)) { + console.log('임시저장하고 새로고침했을때:::::::::', tempDetailData) + } + }, [tempDetailData]) + //1차점 변경 이벤트 const onSelectionChange = (key) => { if (!isEmptyArray(key)) { setOtherSaleStoreList(otherSaleStoreList) form.setValue('saleStoreId', key[0].saleStoreId) - form.setValue('saleStoreName', key[0].saleStoreId) - + form.setValue('saleStoreName', key[0].saleStoreName) + form.setValue('saleStoreLevel', key[0].saleStoreLevel) //선택한 1차점 정보로 2차점 list 추리기 //長府工産株式会社 大阪支社 let newOtherSaleStoreList = originOtherSaleStoreList.filter((row) => row.firstAgentId === key[0].saleStoreId) @@ -165,7 +174,13 @@ export default function StuffDetail() { //X누름 form.setValue('saleStoreId', '') form.setValue('saleStoreName', '') + form.setValue('saleStoreLevel', '') + form.setValue('otherSaleStoreId', '') + form.setValue('otherSaleStoreName', '') + form.setValue('otherSaleStoreLevel', '') setOtherSaleStoreList(originOtherSaleStoreList) + //1차점 지웠을때 2차점 자동완성 초기화 + handleClear() } } @@ -173,60 +188,32 @@ export default function StuffDetail() { const onSelectionChange2 = (key) => { if (!isEmptyArray(key)) { form.setValue('otherSaleStoreId', key[0].saleStoreId) - form.setValue('otherSaleStoreId', key[0].saleStoreId) + form.setValue('otherSaleStoreName', key[0].saleStoreName) + form.setValue('otherSaleStoreLevel', key[0].saleStoreLevel) } else { form.setValue('otherSaleStoreId', '') - form.setValue('otherSaleStoreId', '') + form.setValue('otherSaleStoreName', '') + form.setValue('otherSaleStoreLevel', '') } } - // 우편번호 숫자만 체크 - const _zipNo = watch('zipNo') - useEffect(() => { - if (_zipNo !== '' && _zipNo.length === 7 && !_zipNo.match(/\D/g)) { - //setButtonValid(true) + + //1차점 지웠을때 2차점 자동완성 초기화 + const handleClear = () => { + if (ref.current.state.dropDown) { + ref.current.methods.dropDown() } else { - //setButtonValid(false) + ref.current.state.values = [] } - }, [_zipNo]) + } //팝업에서 넘어온 우편정보 const setZipInfo = (info) => { - console.log('팝업에서 넘어온 데이타::::::::', info) - - // const params = { - // zipcode: _zipNo, - // } - - // get({ url: `https://zipcloud.ibsnet.co.jp/api/search?${queryStringFormatter(params)}` }).then((res) => { - // //7830060 - // //9302226 - // //0790177 3개짜리 - // if (res.status === 200) { - // if (res.results != null) { - // console.log('주소검색::', res.results) - // // console.log('prefcode::', res.results[0].prefcode) - // // console.log('address::', res.results[0].address2 + res.results[0].address3) - // setPrefValue(res.results[0].prefcode) - // form.setValue('prefId', res.results[0].prefcode) - // form.setValue('prefName', res.results[0].address1) - // form.setValue('address', res.results[0].address2 + res.results[0].address3) - // } else { - // alert('등록된 우편번호에서 주소를 찾을 수 없습니다. 다시 입력해주세요.') - // form.setValue('prefId', '') - // form.setValue('prefName', '') - // form.setValue('address', '') - // form.setValue('zipNo', '') - // setPrefValue('') - // setAreaIdList([]) - // form.setValue('areaId', '') - // // form.setValue('areaName', '') - // setWindSpeedList([]) - // form.setValue('windSpeed', '') - // } - // } else { - // alert(res.message) - // } - // }) + // console.log('팝업에서 넘어온 데이타::::::::', info) + setPrefValue(info.prefId) + form.setValue('prefId', info.prefId) + form.setValue('prefName', info.address1) + form.setValue('address', info.address2 + info.address3) + form.setValue('zipNo', info.zipNo) } //임시저장 저장 버튼 컨트롤 @@ -251,7 +238,7 @@ export default function StuffDetail() { const _objectName = watch('objectName') const _objectNameOmit = watch('objectNameOmit') const _saleStoreId = watch('saleStoreId') - const _otherSaleStoreId = watch('otherSaleStoreId') + const _saleStoreLevel = watch('saleStoreLevel') const _prefId = watch('prefId') const _address = watch('address') const _areaId = watch('areaId') //new @@ -260,68 +247,56 @@ export default function StuffDetail() { const _installHeight = watch('installHeight') useEffect(() => { - console.log('mode:::::', editMode) if (editMode === 'NEW') { const formData = form.getValues() - console.log('폼::::::::::::', formData) + // console.log('임시저장폼::::::::::::', formData) let errors = {} - if (!_dispCompanyName || _dispCompanyName.trim().length === 0) { + if (!formData.dispCompanyName || formData.dispCompanyName.trim().length === 0) { errors.dispCompanyName = true } - if (!_objectName || _objectName.trim().length === 0) { + if (!formData.objectName || formData.objectName.trim().length === 0) { errors.objectName = true } - if (!_objectNameOmit) { + if (!formData.objectNameOmit) { errors.objectNameOmit = true } - if (!_saleStoreId) { + if (!formData.saleStoreId) { errors.saleStoreId = true } - // if (!_otherSaleStoreId) { - // errors.otherSaleStoreId = true - // } - - if (!_zipNo || _zipNo.length != 7) { - errors.zipCode = true - } - - if (!_prefId) { + if (!formData.prefId) { errors.prefId = true } - if (!_address.trim().length === 0) { - errors.address = true - } - - if (!_areaId) { + if (!formData.areaId) { errors.areaId = true } - if (!_windSpeed) { + if (!formData.windSpeed) { errors.windSpeed = true } - if (!_verticalSnowCover) { + if (!formData.verticalSnowCover) { errors.verticalSnowCover = true } - if (!_installHeight) { + if (!formData.installHeight) { errors.installHeight = true } - console.log('errors::', errors) + // console.log('임시저장용::', errors) setIsFormValid(Object.keys(errors).length === 0) } else { - // console.log('상세일때 폼체크') + console.log('상세일때 폼체크') } }, [ _dispCompanyName, _objectName, _objectNameOmit, _saleStoreId, + _saleStoreLevel, // _otherSaleStoreId, - _zipNo, + // _otherSaleStoreLevel, _prefId, _address, _areaId, @@ -330,24 +305,23 @@ export default function StuffDetail() { _installHeight, ]) - // 주소검색 API - const onSearchPostNumber = () => { - console.log('주소검색클릭!!') + // 주소검색 팝업오픈 + const onSearchPostNumberPopOpen = () => { + setShowAddressButtonValid(true) + } - setShowButtonValid(true) + //설계의뢰 팝업 오픈 + const onSearchDesignRequestPopOpen = () => { + setShowDesignRequestButtonValid(true) } useEffect(() => { if (prefValue !== '') { - console.log('우편번호검색해서 값이왔어:::::::::::', prefValue) // 발전량시뮬레이션 지역 목록 - // /api/object/prefecture/도도부현코드/list get({ url: `/api/object/prefecture/${prefValue}/list` }).then((res) => { if (!isEmptyArray(res)) { - console.log('발전량 시뮬레이션::::::::', res) - //res에 areaId추가됨 - // form.setValue('areaId', res[0].areaId) - form.setValue('areaId', res[0].prefId) + // console.log('발전량 시뮬레이션::::::::', res) + form.setValue('areaId', res[0].areaId) form.setValue('areaName', res[0].prefName) setAreaIdList(res) } @@ -362,13 +336,11 @@ export default function StuffDetail() { useEffect(() => { if (!isEmptyArray(areaIdList)) { - //도도부현넘기는지 발전량시뮬레이션지역 넘기는지 ->도도부현넘기기 - console.log('prefName::', form.watch('prefName')) let _prefName = form.watch('prefName') - //http://localhost:8080/api/object/windSpeed/兵庫県/list + // console.log('기준풍속 가져오는 API', _prefName) get({ url: `/api/object/windSpeed/${_prefName}/list` }).then((res) => { + // console.log('res::', res) if (!isEmptyArray(res)) { - // console.log('기준풍속결과:::::::::', res) setWindSpeedList(res) } }) @@ -409,10 +381,11 @@ export default function StuffDetail() { // 임시저장 const onTempSave = async () => { const formData = form.getValues() - + // console.log('formData::', formData) const params = { - saleStoreId: formData.saleStoreId, - saleStoreName: formData.saleStoreName, + saleStoreId: formData.otherSaleStoreId ? formData.otherSaleStoreId : formData.saleStoreId, + saleStoreName: formData.otherSaleStoreName ? formData.otherSaleStoreName : formData.saleStoreName, + saleStoreLevel: formData.otherSaleStoreLevel ? formData.otherSaleStoreLevel : formData.saleStoreLevel, objectStatusId: formData.objectStatusId, objectName: formData.objectName, objectNameOmit: formData.objectNameOmit, @@ -434,10 +407,19 @@ export default function StuffDetail() { workNo: null, workName: null, } + //1차점 or 2차점 안고르고 임시저장하면 + if (params.saleStoreId == '') { + params.saleStoreId = sessionState.storeId + params.saleStoreLevel = sessionState.storeLevel + } console.log('임시저장params::', params) // return await post({ url: '/api/object/save-object', data: params }).then((res) => { - console.log('res::::::', res) + if (res) { + console.log('임시저장res::::::', res) + setTempDetailData(res) + alert('임시저장 되었습니다. 물건번호를 획득하려면 필수 항목을 모두 입력해 주십시오.') + } }) } @@ -462,7 +444,8 @@ export default function StuffDetail() {
- * 필수 입력항목 + * + {getMessage('stuff.detail.required')}
@@ -471,9 +454,20 @@ export default function StuffDetail() { + + + + - + @@ -565,6 +559,8 @@ export default function StuffDetail() { clearable={true} onChange={onSelectionChange2} disabled={form.watch('saleStoreId') !== '' ? false : true} + onClearAll={handleClear} + ref={ref} >
@@ -581,23 +577,24 @@ export default function StuffDetail() {
- + - + @@ -772,6 +767,21 @@ export default function StuffDetail() { )) || ( <> + +
+
+ * 필수 입력항목 +
+
+
{getMessage('stuff.detail.designNo')} +
+
+ +
+
- 담당자 * + {getMessage('stuff.detail.dispCompanyName')} *
@@ -489,18 +483,18 @@ export default function StuffDetail() {
- +
- +
물건명 후리가나{getMessage('stuff.detail.objectNameKana')}
@@ -521,7 +515,7 @@ export default function StuffDetail() {
- 1차 판매점명 / ID + {getMessage('stuff.detail.saleStoreId')} *
@@ -549,7 +543,7 @@ export default function StuffDetail() {
-
2차 판매점명 / ID
+
{getMessage('stuff.detail.otherSaleStoreId')}
- 우편번호 * + {getMessage('stuff.detail.zipNo')} *
- +
- -
*주소검색 버튼을 클릭한 후, 도도부현 정보를 선택해주십시오.
+
{getMessage('stuff.detail.btn.addressPop.guide')}
- 도도부현 / 주소 * + {getMessage('stuff.detail.prefId')} + *
@@ -620,17 +617,15 @@ export default function StuffDetail() {
- 발전량시뮬레이션지역 * + {getMessage('stuff.detail.areaId')} *
- 기준풍속 * + {getMessage('stuff.detail.windSpeed')} *
@@ -660,14 +655,14 @@ export default function StuffDetail() { })}
- m/s이하 - + {getMessage('stuff.detail.windSpeedSpan')} +
- 수직적설량 * + {getMessage('stuff.detail.verticalSnowCover')} *
@@ -681,14 +676,14 @@ export default function StuffDetail() { cm
- +
- 면조도구분 * + {getMessage('stuff.detail.surfaceType')} *
@@ -702,7 +697,7 @@ export default function StuffDetail() {
- +
@@ -710,7 +705,7 @@ export default function StuffDetail() {
- 설치높이 * + {getMessage('stuff.detail.installHeight')} *
@@ -727,25 +722,25 @@ export default function StuffDetail() {
계약조건{getMessage('stuff.detail.conType')}
- +
- +
메모{getMessage('stuff.detail.remarks')}
- +
+ + + + +
+
+
+ {objectNo.substring(0, 1) === 'R' ? ( <> @@ -788,20 +798,26 @@ export default function StuffDetail() { ) : ( <> + {!isFormValid ? ( + + ) : ( + + )} - )} )} - {showButtonValid && } - + {showAddressButtonValid && } + {showDesignRequestButtonValid && } ) } diff --git a/src/components/management/StuffQGrid.jsx b/src/components/management/StuffQGrid.jsx index f9a65052..a2a47c5d 100644 --- a/src/components/management/StuffQGrid.jsx +++ b/src/components/management/StuffQGrid.jsx @@ -38,7 +38,7 @@ export default function StuffQGrid(props) { flex: 1, sortable: false, suppressMovable: true, - resizable: false, + resizable: true, suppressSizeToFit: false, } }, []) @@ -69,18 +69,12 @@ export default function StuffQGrid(props) { props.getCellDoubleClicked(event) }, []) - //컨텐츠에 따라 컬럼넓이 자동조절 const autoSizeStrategy = useMemo(() => { return { type: 'fitCellContents', } }, []) - const onGridReady = useCallback((event) => { - // 헤더 사이즈 조정 컬럼에 width값으로 계산 - event.api.sizeColumnsToFit() - }, []) - // Fetch data & update rowData state useEffect(() => { gridData ? setRowData(gridData) : '' @@ -97,7 +91,6 @@ export default function StuffQGrid(props) {
물건 목록이 없습니다.'} - // getRowStyle={getRowStyle} getRowClass={getRowClass} + autoSizeAllColumns={true} />
) diff --git a/src/components/management/StuffSearchCondition.jsx b/src/components/management/StuffSearchCondition.jsx index 19981fc5..f88cf3cc 100644 --- a/src/components/management/StuffSearchCondition.jsx +++ b/src/components/management/StuffSearchCondition.jsx @@ -110,7 +110,6 @@ export default function StuffSearchCondition() { //초기화 눌렀을 때 자동완성도.. const handleClear = () => { - // console.log('ref::', ref.current.state.values) if (ref.current.state.dropDown) { ref.current.methods.dropDown() } else { diff --git a/src/components/management/popup/DesignRequestPop.jsx b/src/components/management/popup/DesignRequestPop.jsx deleted file mode 100644 index ae7fd858..00000000 --- a/src/components/management/popup/DesignRequestPop.jsx +++ /dev/null @@ -1,5 +0,0 @@ -import React from 'react' - -export default function DesignRequestPop() { - return
-} diff --git a/src/components/management/popup/FindAddressPop.jsx b/src/components/management/popup/FindAddressPop.jsx index bf276218..157cccb4 100644 --- a/src/components/management/popup/FindAddressPop.jsx +++ b/src/components/management/popup/FindAddressPop.jsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState, useRef } from 'react' +import React, { useState } from 'react' import { useForm } from 'react-hook-form' import { queryStringFormatter } from '@/util/common-utils' import { useAxios } from '@/hooks/useAxios' @@ -6,7 +6,7 @@ import { globalLocaleStore } from '@/store/localeAtom' import { useRecoilValue } from 'recoil' import FindAddressPopQGrid from './FindAddressPopQGrid' import { useMessage } from '@/hooks/useMessage' - +import { isNotEmptyArray } from '@/util/common-utils' export default function FindAddressPop(props) { const globalLocaleState = useRecoilValue(globalLocaleStore) @@ -14,17 +14,37 @@ export default function FindAddressPop(props) { const { getMessage } = useMessage() - const [prefId, setPrefId] = useState('') - const [address1, setAddress1] = useState('') - const [address2, setAddress2] = useState('') - const [address3, setAddress3] = useState('') - const gridRef = useRef() + const [prefId, setPrefId] = useState(null) + const [zipNo, setZipNo] = useState(null) + const [address1, setAddress1] = useState(null) + const [address2, setAddress2] = useState(null) + const [address3, setAddress3] = useState(null) const [gridProps, setGridProps] = useState({ gridData: [], isPageable: false, - gridColumns: [], + gridColumns: [ + { + field: 'address1', + headerName: getMessage('stuff.addressPopup.gridHeader.address1'), + minWidth: 150, + checkboxSelection: true, + showDisabledCheckboxes: true, + }, + { + field: 'address2', + headerName: getMessage('stuff.addressPopup.gridHeader.address2'), + minWidth: 150, + }, + { + field: 'address3', + headerName: getMessage('stuff.addressPopup.gridHeader.address3'), + minWidth: 150, + }, + ], }) + + //검색필드 const formInitValue = { zipNo: '', } @@ -33,38 +53,46 @@ export default function FindAddressPop(props) { }) const form = { register, setValue, getValues, handleSubmit, resetField, control, watch } + //우편번호 검색 const searchPostNum = () => { // //7830060 // //9302226 // //0790177 3개짜리 - console.log(watch('zipNo')) const params = { zipcode: watch('zipNo'), } get({ url: `https://zipcloud.ibsnet.co.jp/api/search?${queryStringFormatter(params)}` }).then((res) => { - console.log('우편조회 API결과:::::', res) if (res.status === 200) { + if (isNotEmptyArray(res.results)) { + setGridProps({ ...gridProps, gridData: res.results }) + } else { + setGridProps({ ...gridProps, gridData: [] }) + } } else { - alert('등록된 우편번호에서 주소를 찾을 수 없습니다. 다시 입력해주세요.') + alert(getMessage('stuff.addressPopup.error.message1')) + setGridProps({ ...gridProps, gridData: [] }) } }) } // 주소적용 클릭 - const zipInfo = () => { - console.log('주소적용 클릭:::::::::') - // setAddress3('3333') - // setAddress3('4444') - // setAddress3('5555') - props.zipInfo({ - zipNo: '3434343', - address1: '3333', - address2: '4444', - address3: '5555', - }) + const applyAddress = () => { + // console.log('주소적용 클릭:::::::::', prefId, address1, address2, address3, zipNo) + if (prefId == null) { + alert('stuff.addressPopup.error.message2') + } else { + props.zipInfo({ + zipNo: zipNo, + address1: address1, + address2: address2, + address3: address3, + prefId: prefId, + }) - props.setShowButtonValid(false) + //팝업닫기 + props.setShowAddressButtonValid(false) + } } //숫자만 @@ -72,13 +100,31 @@ export default function FindAddressPop(props) { let input = e.target input.value = input.value.replace(/[^0-9]/g, '') } + + //그리드에서 선택한 우편정보 + const getSelectedRowdata = (data) => { + if (isNotEmptyArray(data)) { + setAddress1(data[0].address1) + setAddress2(data[0].address2) + setAddress3(data[0].address3) + setPrefId(data[0].prefcode) + setZipNo(data[0].zipcode) + } else { + setAddress1(null) + setAddress2(null) + setAddress3(null) + setPrefId(null) + setZipNo(null) + } + } + return (

{getMessage('stuff.addressPopup.title')}

-
@@ -96,14 +142,14 @@ export default function FindAddressPop(props) {
- +
- -
diff --git a/src/components/management/popup/FindAddressPopQGrid.jsx b/src/components/management/popup/FindAddressPopQGrid.jsx index f5b7c2fa..83544a5a 100644 --- a/src/components/management/popup/FindAddressPopQGrid.jsx +++ b/src/components/management/popup/FindAddressPopQGrid.jsx @@ -1,6 +1,66 @@ import React from 'react' +import { useCallback, useEffect, useMemo, useState } from 'react' +import { AgGridReact } from 'ag-grid-react' + +import 'ag-grid-community/styles/ag-grid.css' +import 'ag-grid-community/styles/ag-theme-quartz.css' export default function FindAddressPopGrid(props) { - const { gridData, gridColumns, isPageable = true, gridRef } = props - return
+ const { gridData, gridColumns, isPageable = true } = props + + const [rowData, setRowData] = useState(null) + + const [gridApi, setGridApi] = useState(null) + + const [colDefs, setColDefs] = useState(gridColumns) + + const defaultColDef = useMemo(() => { + return { + flex: 1, + minWidth: 100, + sortable: false, + suppressMovable: false, + resizable: false, + suppressSizeToFit: false, + } + }, []) + + const rowBuffer = 100 + + useEffect(() => { + gridData ? setRowData(gridData) : '' + }, [gridData]) + + const rowSelection = useMemo(() => { + return { mode: 'singleRow', enableClickSelection: true } + }, []) + + const onGridReady = useCallback( + (params) => { + setGridApi(params.api) + gridData ? setRowData(gridData) : '' + }, + [gridData], + ) + + //부모로 선택한 우편정보 보내기 + const onSelectionChanged = () => { + const selectedData = gridApi.getSelectedRows() + props.getSelectedRowdata(selectedData) + } + + return ( +
+ +
+ ) } diff --git a/src/components/management/popup/PlanRequestPop.jsx b/src/components/management/popup/PlanRequestPop.jsx new file mode 100644 index 00000000..9eee76a3 --- /dev/null +++ b/src/components/management/popup/PlanRequestPop.jsx @@ -0,0 +1,169 @@ +import React, { useState } from 'react' +import { useForm } from 'react-hook-form' +import { queryStringFormatter } from '@/util/common-utils' +import { useAxios } from '@/hooks/useAxios' +import { globalLocaleStore } from '@/store/localeAtom' +import { useRecoilValue } from 'recoil' +import FindAddressPopQGrid from './FindAddressPopQGrid' +import { useMessage } from '@/hooks/useMessage' +import { isNotEmptyArray } from '@/util/common-utils' +import SingleDatePicker from '@/components/common/datepicker/SingleDatePicker' +import dayjs from 'dayjs' +export default function PlanRequestPop(props) { + const globalLocaleState = useRecoilValue(globalLocaleStore) + + const { get } = useAxios(globalLocaleState) + + const { getMessage } = useMessage() + // 검색조건 달력 셋팅 + const [startDate, setStartDate] = useState(dayjs(new Date()).add(-3, 'month').format('YYYY-MM-DD')) + const [endDate, setEndDate] = useState(dayjs(new Date()).format('YYYY-MM-DD')) + + const rangeDatePickerProps1 = { + startDate, //시작일 + setStartDate, + } + + const rangeDatePickerProps2 = { + startDate: endDate, //종료일 + setStartDate: setEndDate, + } + + const [gridProps, setGridProps] = useState({ + gridData: [], + isPageable: false, + gridColumns: [ + { + field: 'address1', + headerName: getMessage('stuff.addressPopup.gridHeader.address1'), + minWidth: 150, + checkboxSelection: true, + showDisabledCheckboxes: true, + }, + { + field: 'address2', + headerName: getMessage('stuff.addressPopup.gridHeader.address2'), + minWidth: 150, + }, + { + field: 'address3', + headerName: getMessage('stuff.addressPopup.gridHeader.address3'), + minWidth: 150, + }, + ], + }) + + //검색필드 + const formInitValue = { + //zipNo: '', + } + + const { register, setValue, getValues, handleSubmit, resetField, control, watch } = useForm({ + defaultValues: formInitValue, + }) + + const form = { register, setValue, getValues, handleSubmit, resetField, control, watch } + + return ( +
+
+
+
+

{getMessage('stuff.planReqPopup.title')}

+ +
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
설계의뢰번호 / 設計依頼番号 +
+ +
+
안건명 / 案件名 +
+ +
+
도도부현 / 都道府県 +
+ +
+
판매대리점명 / 販売代理店名 +
+ +
+
의뢰자명 / 依頼者名 +
+ +
+
상태 / 状態 +
+ +
+
기간검색 / 期間検索 +
+
+
+ + +
+
+ + +
+
+
+ +
+ ~ +
+ +
+
+
+
+
+
+
+
+
+
+
+
+ ) +} diff --git a/src/locales/ja.json b/src/locales/ja.json index eddf5a2a..c2ae55a0 100644 --- a/src/locales/ja.json +++ b/src/locales/ja.json @@ -383,8 +383,40 @@ "stuff.message.periodError": "最大1年間閲覧可能.", "stuff.addressPopup.title": "郵便番号", "stuff.addressPopup.placeholder": "郵便番号の7桁を入力してください。", + "stuff.addressPopup.error.message1": "登録された郵便番号に住所が見つかりません。もう一度入力してください。", + "stuff.addressPopup.error.message2": "住所を選択してください.", + "stuff.addressPopup.gridHeader.address1": "都道府県", + "stuff.addressPopup.gridHeader.address2": "市区町村", + "stuff.addressPopup.gridHeader.address3": "市区町村 以下", "stuff.addressPopup.btn1": "閉じる", "stuff.addressPopup.btn2": "住所適用", + "stuff.planReqPopup.title": "設計依頼のインポート", + "stuff.detail.required": "必須入力項目", + "stuff.detail.designNo": "設計依頼No.", + "stuff.detail.dispCompanyName": "担当者", + "stuff.detail.objectStatusId": "物品区分/物件名", + "stuff.detail.objectStatus0": "新築", + "stuff.detail.objectStatus1": "基軸", + "stuff.detail.objectNameKana": "商品名 ふりがな", + "stuff.detail.saleStoreId": "一次販売店名/ID", + "stuff.detail.otherSaleStoreId": "二次販売店名/ID", + "stuff.detail.zipNo": "郵便番号 ", + "stuff.detail.btn.addressPop": "住所検索", + "stuff.detail.btn.addressPop.guide": "※ 郵便番号7桁を入力した後、アドレス検索ボタンをクリックしてください", + "stuff.detail.prefId": "都道府県 / 住所 ", + "stuff.detail.areaId": "発電量シミュレーション地域 ", + "stuff.detail.windSpeed": "基準風速", + "stuff.detail.windSpeedSpan": "m/s以下", + "stuff.detail.btn.windSpeedPop": "風速選択", + "stuff.detail.verticalSnowCover": "垂直説説", + "stuff.detail.coldRegionFlg": "寒冷地対策施行", + "stuff.detail.surfaceType": "面調図区分", + "stuff.detail.saltAreaFlg": "塩害地域用アイテムの使用", + "stuff.detail.installHeight": "設置高さ", + "stuff.detail.conType": "契約条件", + "stuff.detail.conType0": "余剰", + "stuff.detail.conType1": "全量", + "stuff.detail.remarks": "メモ", "length": "長さ", "height": "高さ", "output": "出力", diff --git a/src/locales/ko.json b/src/locales/ko.json index ef41a4a8..0bd7e448 100644 --- a/src/locales/ko.json +++ b/src/locales/ko.json @@ -387,8 +387,40 @@ "stuff.message.periodError": "최대1년 조회 가능합니다.", "stuff.addressPopup.title": "우편번호", "stuff.addressPopup.placeholder": "우편번호의 7자리를 입력하세요.", + "stuff.addressPopup.error.message1": "등록된 우편번호에서 주소를 찾을 수 없습니다. 다시 입력해주세요.", + "stuff.addressPopup.error.message2": "주소를 선택해주세요.", + "stuff.addressPopup.gridHeader.address1": "도도부현", + "stuff.addressPopup.gridHeader.address2": "시구정촌", + "stuff.addressPopup.gridHeader.address3": "시구정촌 이하", "stuff.addressPopup.btn1": "닫기", "stuff.addressPopup.btn2": "주소적용", + "stuff.planReqPopup.title": "설계의뢰 불러오기", + "stuff.detail.required": "필수 입력항목", + "stuff.detail.designNo": "설계의뢰No.", + "stuff.detail.dispCompanyName": "담당자", + "stuff.detail.objectStatusId": "물건구분/물건명", + "stuff.detail.objectStatus0": "신축", + "stuff.detail.objectStatus1": "기축", + "stuff.detail.objectNameKana": "물건명 후리가나", + "stuff.detail.saleStoreId": "1차 판매점명 / ID", + "stuff.detail.otherSaleStoreId": "2차 판매점명 / ID", + "stuff.detail.zipNo": "우편번호", + "stuff.detail.btn.addressPop": "주소검색", + "stuff.detail.btn.addressPop.guide": "※ 주소검색 버튼을 클릭한 후, 도도부현 정보를 선택해주십시오.", + "stuff.detail.prefId": "도도부현 / 주소", + "stuff.detail.areaId": "발전량시뮬레이션지역", + "stuff.detail.windSpeed": "기준풍속", + "stuff.detail.windSpeedSpan": "m/s이하", + "stuff.detail.btn.windSpeedPop": "풍속선택", + "stuff.detail.verticalSnowCover": "수직적설량", + "stuff.detail.coldRegionFlg": "한랭지대책시행", + "stuff.detail.surfaceType": "면조도구분", + "stuff.detail.saltAreaFlg": "염해지역용아이템사용", + "stuff.detail.installHeight": "설치높이", + "stuff.detail.conType": "계약조건", + "stuff.detail.conType0": "잉여", + "stuff.detail.conType1": "전량", + "stuff.detail.remarks": "메모", "length": "길이", "height": "높이", "output": "출력", From 6c15375a53f27bd22ad8f0a85370691c8f193c03 Mon Sep 17 00:00:00 2001 From: yjnoh Date: Tue, 15 Oct 2024 15:35:00 +0900 Subject: [PATCH 11/68] =?UTF-8?q?=EB=B0=B0=EC=B9=98=EB=A9=B4=20->=20?= =?UTF-8?q?=EC=98=A4=EB=B8=8C=EC=A0=9D=ED=8A=B8=20=EB=B0=B0=EC=B9=98=20->?= =?UTF-8?q?=20=EA=B0=9C=EA=B5=AC,=EA=B7=B8=EB=A6=BC=EC=9E=90=20=EC=9E=91?= =?UTF-8?q?=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/common.js | 4 + src/components/floor-plan/MenuDepth01.jsx | 10 +- .../floor-plan/modal/basic/BasicSetting.jsx | 2 +- .../floor-plan/modal/object/ObjectSetting.jsx | 128 +---------- .../modal/object/type/OpenSpace.jsx | 46 +++- .../floor-plan/modal/object/type/Shadow.jsx | 47 +++- src/hooks/object/useObjectBatch.js | 206 +++++++++++++++++- src/hooks/surface/useSurfaceShapeBatch.js | 34 ++- src/locales/ja.json | 2 +- src/locales/ko.json | 2 +- 10 files changed, 333 insertions(+), 148 deletions(-) diff --git a/src/common/common.js b/src/common/common.js index 9aa7f9f0..09f742f2 100644 --- a/src/common/common.js +++ b/src/common/common.js @@ -96,6 +96,10 @@ export const BATCH_TYPE = { OPENING_TEMP: 'openingTemp', SHADOW: 'shadow', SHADOW_TEMP: 'shadowTemp', + TRIANGLE_DORMER: 'triangleDormer', + TRIANGLE_DORMER_TEMP: 'triangleDormerTemp', + PENTAGON_DORMER: 'pentagonDormer', + PENTAGON_DORMER_TEMP: 'pentagonDormerTemp', } // 오브젝트 배치 > 프리입력, 치수입력 export const INPUT_TYPE = { diff --git a/src/components/floor-plan/MenuDepth01.jsx b/src/components/floor-plan/MenuDepth01.jsx index 21631434..8fcb9599 100644 --- a/src/components/floor-plan/MenuDepth01.jsx +++ b/src/components/floor-plan/MenuDepth01.jsx @@ -5,7 +5,7 @@ import { useEffect, useState } from 'react' import { MENU } from '@/common/common' import { currentMenuState } from '@/store/canvasAtom' import { useSetRecoilState } from 'recoil' - +import { useSurfaceShapeBatch } from '@/hooks/surface/useSurfaceShapeBatch' export default function MenuDepth01(props) { const { setShowOutlineModal, @@ -28,6 +28,9 @@ export default function MenuDepth01(props) { const { getMessage } = useMessage() const [activeMenu, setActiveMenu] = useState() const setCurrentMenu = useSetRecoilState(currentMenuState) + + const { deleteAllSurfacesAndObjects } = useSurfaceShapeBatch() + const onClickMenu = ({ id, menu, name }) => { setActiveMenu(menu) setShowOutlineModal(menu === MENU.ROOF_COVERING.EXTERIOR_WALL_LINE) @@ -60,6 +63,11 @@ export default function MenuDepth01(props) { setShowPlaceShapeDrawingModal(id === 1) setShowPlacementSurfaceSettingModal(id === 2) setShowObjectSettingModal(id === 3) + + //배치면 전체 삭제 + if (id === 4) { + deleteAllSurfacesAndObjects() + } } if (type === 'module') { diff --git a/src/components/floor-plan/modal/basic/BasicSetting.jsx b/src/components/floor-plan/modal/basic/BasicSetting.jsx index c6663cba..de2c28eb 100644 --- a/src/components/floor-plan/modal/basic/BasicSetting.jsx +++ b/src/components/floor-plan/modal/basic/BasicSetting.jsx @@ -1,5 +1,5 @@ import { useMessage } from '@/hooks/useMessage' -import WithDraggable from '@/components/common/draggable/withDraggable' +import WithDraggable from '@/components/common/draggable/WithDraggable' import { useState } from 'react' import Orientation from '@/components/floor-plan/modal/basic/step/Orientation' import Module from '@/components/floor-plan/modal/basic/step/Module' diff --git a/src/components/floor-plan/modal/object/ObjectSetting.jsx b/src/components/floor-plan/modal/object/ObjectSetting.jsx index de7e36c3..07a21f0c 100644 --- a/src/components/floor-plan/modal/object/ObjectSetting.jsx +++ b/src/components/floor-plan/modal/object/ObjectSetting.jsx @@ -3,12 +3,10 @@ import { useState, useRef } from 'react' import { useRecoilValue } from 'recoil' import { useMessage } from '@/hooks/useMessage' -import { INPUT_TYPE, BATCH_TYPE } from '@/common/common' import { useEvent } from '@/hooks/useEvent' import { canvasState } from '@/store/canvasAtom' import { useSwal } from '@/hooks/useSwal' -import { polygonToTurfPolygon, rectToPolygon, pointsToTurfPolygon } from '@/util/canvas-util' -import * as turf from '@turf/turf' +import { useObjectBatch } from '@/hooks/object/useObjectBatch' import WithDraggable from '@/components/common/draggable/WithDraggable' import OpenSpace from '@/components/floor-plan/modal/object/type/OpenSpace' @@ -20,9 +18,8 @@ export default function ObjectSetting({ setShowObjectSettingModal }) { const { getMessage } = useMessage() const canvas = useRecoilValue(canvasState) const [buttonAct, setButtonAct] = useState(1) - const [preObjects, setPreObjects] = useState([]) - const { addCanvasMouseEventListener, initEvent } = useEvent() const { swalFire } = useSwal() + const { applyOpeningAndShadow } = useObjectBatch() /** * 개구배치, 그림자배치 @@ -35,13 +32,8 @@ export default function ObjectSetting({ setShowObjectSettingModal }) { } const applyObject = () => { - setShowObjectSettingModal(false) - - let preObjectsArray = preObjects const surfaceShapePolygons = canvas?.getObjects().filter((obj) => obj.name === 'surfaceShapeBatch') - console.log(preObjectsArray) - if (surfaceShapePolygons.length === 0) { swalFire({ text: '지붕이 없어요 지붕부터 그리세요', icon: 'error' }) return @@ -49,121 +41,7 @@ export default function ObjectSetting({ setShowObjectSettingModal }) { //개구배치, 그림자배치 if (buttonAct === 1 || buttonAct === 2) { - const type = objectPlacement.typeRef.current.find((radio) => radio.checked).value - const isCrossChecked = objectPlacement.isCrossRef.current.checked - - let rect, isDown, origX, origY - let selectedSurface - - //프리입력 - if (type === INPUT_TYPE.FREE) { - addCanvasMouseEventListener('mouse:down', (e) => { - isDown = true - const pointer = canvas.getPointer(e.e) - - surfaceShapePolygons.forEach((surface) => { - if (surface.inPolygon({ x: pointer.x, y: pointer.y })) { - selectedSurface = surface - } - }) - - if (!selectedSurface) { - swalFire({ text: '지붕안에 그려야해요', icon: 'error' }) - setShowObjectSettingModal(true) //메뉴보이고 - initEvent() //이벤트 초기화 - return - } - - origX = pointer.x - origY = pointer.y - - rect = new fabric.Rect({ - left: origX, - top: origY, - originX: 'left', - originY: 'top', - width: 0, - height: 0, - angle: 0, - stroke: 'black', - }) - - //개구냐 그림자냐에 따라 변경 - rect.set({ - fill: buttonAct === 1 ? 'white' : 'rgba(0, 0, 0, 0.3)', - name: buttonAct === 1 ? BATCH_TYPE.OPENING_TEMP : BATCH_TYPE.SHADOW_TEMP, - }) - - canvas?.add(rect) - }) - - addCanvasMouseEventListener('mouse:move', (e) => { - if (!isDown) return - - if (selectedSurface) { - const pointer = canvas.getPointer(e.e) - const width = pointer.x - origX - const height = pointer.y - origY - - rect.set({ width: Math.abs(width), height: Math.abs(height) }) - - if (width < 0) { - rect.set({ left: Math.abs(pointer.x) }) - } - if (height < 0) { - rect.set({ top: Math.abs(pointer.y) }) - } - - canvas?.renderAll() - } - }) - - addCanvasMouseEventListener('mouse:up', (e) => { - if (rect) { - const rectPolygon = pointsToTurfPolygon(rectToPolygon(rect)) - const selectedSurfacePolygon = polygonToTurfPolygon(selectedSurface) - - //지붕 밖으로 그렸을때 - if (!turf.booleanWithin(rectPolygon, selectedSurfacePolygon)) { - swalFire({ text: '지붕안에 그리라고요...', icon: 'error' }) - //일단 지워 - const deleteTarget = canvas?.getObjects().filter((obj) => obj.name === BATCH_TYPE.OPENING_TEMP || obj.name === BATCH_TYPE.SHADOW_TEMP) - canvas?.remove(...deleteTarget) - setShowObjectSettingModal(true) //메뉴보이고 - initEvent() //이벤트 초기화 - return - } - - // if (!isCrossChecked) { - // const isCross = preObjectsArray.some((object) => turf.booleanOverlap(pointsToTurfPolygon(rectToPolygon(object), rectPolygon))) - - // console.log(isCross) - - // if (isCross) { - // swalFire({ text: '겹치기 불가요...', icon: 'error' }) - // const deleteTarget = canvas?.getObjects().filter((obj) => obj.name === BATCH_TYPE.OPENING_TEMP || obj.name === BATCH_TYPE.SHADOW_TEMP) - // canvas?.remove(...deleteTarget) - // setShowObjectSettingModal(true) //메뉴보이고 - // initEvent() //이벤트 초기화 - // return - // } - // } - - isDown = false - const complateName = buttonAct === 1 ? BATCH_TYPE.OPENING : BATCH_TYPE.SHADOW - rect.set({ name: complateName }) - rect.setCoords() - - preObjectsArray.push(rect) - - console.log('preObjectsArray', preObjectsArray) - setPreObjects(preObjectsArray) - - setShowObjectSettingModal(true) //메뉴보이고 - initEvent() - } - }) - } + applyOpeningAndShadow(objectPlacement, buttonAct, surfaceShapePolygons, setShowObjectSettingModal) } } diff --git a/src/components/floor-plan/modal/object/type/OpenSpace.jsx b/src/components/floor-plan/modal/object/type/OpenSpace.jsx index 2ffe9b26..ab4d287d 100644 --- a/src/components/floor-plan/modal/object/type/OpenSpace.jsx +++ b/src/components/floor-plan/modal/object/type/OpenSpace.jsx @@ -1,23 +1,45 @@ -import { forwardRef } from 'react' +import { forwardRef, useState, useEffect } from 'react' import { useMessage } from '@/hooks/useMessage' import { INPUT_TYPE } from '@/common/common' const OpenSpace = forwardRef((props, refs) => { const { getMessage } = useMessage() + const [selectedType, setSelectedType] = useState(INPUT_TYPE.FREE) + + useEffect(() => { + if (selectedType === INPUT_TYPE.FREE) { + refs.widthRef.current.value = 0 + refs.heightRef.current.value = 0 + } + }, [selectedType]) //체크하면 값바꿈 - return (
- (refs.typeRef.current[0] = el)} /> + (refs.typeRef.current[0] = el)} + onClick={() => setSelectedType(INPUT_TYPE.FREE)} + />
- (refs.typeRef.current[1] = el)} /> + (refs.typeRef.current[1] = el)} + onClick={() => setSelectedType(INPUT_TYPE.DIMENSION)} + />
@@ -29,7 +51,13 @@ const OpenSpace = forwardRef((props, refs) => {
- +
mm
@@ -40,7 +68,13 @@ const OpenSpace = forwardRef((props, refs) => {
- +
mm
diff --git a/src/components/floor-plan/modal/object/type/Shadow.jsx b/src/components/floor-plan/modal/object/type/Shadow.jsx index 71bfb6ed..0945d78c 100644 --- a/src/components/floor-plan/modal/object/type/Shadow.jsx +++ b/src/components/floor-plan/modal/object/type/Shadow.jsx @@ -1,20 +1,45 @@ -import { forwardRef } from 'react' +import { forwardRef, useState, useEffect } from 'react' import { useMessage } from '@/hooks/useMessage' import { INPUT_TYPE } from '@/common/common' const Shadow = forwardRef((props, refs) => { const { getMessage } = useMessage() + + const [selectedType, setSelectedType] = useState(INPUT_TYPE.FREE) + + useEffect(() => { + if (selectedType === INPUT_TYPE.FREE) { + refs.widthRef.current.value = 0 + refs.heightRef.current.value = 0 + } + }, [selectedType]) + return (
- (refs.typeRef.current[0] = el)} /> + (refs.typeRef.current[0] = el)} + onClick={() => setSelectedType(INPUT_TYPE.FREE)} + />
- (refs.typeRef.current[1] = el)} /> + (refs.typeRef.current[1] = el)} + onClick={() => setSelectedType(INPUT_TYPE.DIMENSION)} + />
@@ -26,7 +51,13 @@ const Shadow = forwardRef((props, refs) => {
- +
mm
@@ -37,7 +68,13 @@ const Shadow = forwardRef((props, refs) => {
- +
mm
diff --git a/src/hooks/object/useObjectBatch.js b/src/hooks/object/useObjectBatch.js index 1d395d27..33b393b8 100644 --- a/src/hooks/object/useObjectBatch.js +++ b/src/hooks/object/useObjectBatch.js @@ -1,6 +1,210 @@ +'use client' +import { useMessage } from '@/hooks/useMessage' +import { useRecoilState, useRecoilValue } from 'recoil' +import { canvasState } from '@/store/canvasAtom' +import { INPUT_TYPE, BATCH_TYPE } from '@/common/common' +import { useEvent } from '@/hooks/useEvent' +import { polygonToTurfPolygon, rectToPolygon, pointsToTurfPolygon } from '@/util/canvas-util' +import { useSwal } from '@/hooks/useSwal' +import * as turf from '@turf/turf' + export function useObjectBatch() { const { getMessage } = useMessage() const canvas = useRecoilValue(canvasState) + const { addCanvasMouseEventListener, initEvent } = useEvent() + const { swalFire } = useSwal() - const openObjectBatch = () => {} + const applyOpeningAndShadow = (objectPlacement, buttonAct, surfaceShapePolygons, setShowObjectSettingModal) => { + const selectedType = objectPlacement.typeRef.current.find((radio) => radio.checked).value + const isCrossChecked = buttonAct === 1 ? objectPlacement.isCrossRef.current.checked : false + const objTempName = buttonAct === 1 ? BATCH_TYPE.OPENING_TEMP : BATCH_TYPE.SHADOW_TEMP + const objName = buttonAct === 1 ? BATCH_TYPE.OPENING : BATCH_TYPE.SHADOW + + let rect, isDown, origX, origY + let selectedSurface + + //프리입력 + if (selectedType === INPUT_TYPE.FREE) { + addCanvasMouseEventListener('mouse:down', (e) => { + isDown = true + const pointer = canvas.getPointer(e.e) + + surfaceShapePolygons.forEach((surface) => { + if (surface.inPolygon({ x: pointer.x, y: pointer.y })) { + selectedSurface = surface + } + }) + + if (!selectedSurface) { + swalFire({ text: '지붕안에 그려야해요', icon: 'error' }) + initEvent() //이벤트 초기화 + return + } + + origX = pointer.x + origY = pointer.y + + rect = new fabric.Rect({ + left: origX, + top: origY, + originX: 'left', + originY: 'top', + width: 0, + height: 0, + angle: 0, + stroke: 'black', + }) + + //개구냐 그림자냐에 따라 변경 + rect.set({ + fill: buttonAct === 1 ? 'white' : 'rgba(0, 0, 0, 0.4)', + name: objTempName, + }) + + canvas?.add(rect) + }) + + addCanvasMouseEventListener('mouse:move', (e) => { + if (!isDown) return + + if (selectedSurface) { + const pointer = canvas.getPointer(e.e) + const width = pointer.x - origX + const height = pointer.y - origY + + rect.set({ width: Math.abs(width), height: Math.abs(height) }) + + if (width < 0) { + rect.set({ left: Math.abs(pointer.x) }) + } + if (height < 0) { + rect.set({ top: Math.abs(pointer.y) }) + } + + canvas?.renderAll() + } + }) + + addCanvasMouseEventListener('mouse:up', (e) => { + if (rect) { + const rectPolygon = pointsToTurfPolygon(rectToPolygon(rect)) + const selectedSurfacePolygon = polygonToTurfPolygon(selectedSurface) + + //지붕 밖으로 그렸을때 + if (!turf.booleanWithin(rectPolygon, selectedSurfacePolygon)) { + swalFire({ text: '개구를 배치할 수 없습니다.', icon: 'error' }) + //일단 지워 + deleteTempObjects(setShowObjectSettingModal) + return + } + + if (!isCrossChecked) { + const preObjects = canvas?.getObjects().filter((obj) => obj.name === BATCH_TYPE.OPENING || obj.name === BATCH_TYPE.SHADOW) + const preObjectsArray = preObjects.map((obj) => rectToPolygon(obj)) + const isCross = preObjectsArray.some((object) => turf.booleanOverlap(pointsToTurfPolygon(object), rectPolygon)) + + if (isCross) { + swalFire({ text: '겹치기 불가요...', icon: 'error' }) + deleteTempObjects(setShowObjectSettingModal) + return + } + } + + isDown = false + rect.set({ name: objName }) + rect.setCoords() + initEvent() + } + }) + } else if (selectedType === INPUT_TYPE.DIMENSION) { + const width = objectPlacement.widthRef.current.value / 10 + const height = objectPlacement.heightRef.current.value / 10 + + if (width === '' || height === '' || width <= 0 || height <= 0) { + swalFire({ text: getMessage('common.canvas.validate.size'), icon: 'error' }) + return + } + + // setShowObjectSettingModal(false) //메뉴보이고 + addCanvasMouseEventListener('mouse:move', (e) => { + isDown = true + if (!isDown) return + + canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === objTempName)) //움직일때 일단 지워가면서 움직임 + const pointer = canvas.getPointer(e.e) + + surfaceShapePolygons.forEach((surface) => { + if (surface.inPolygon({ x: pointer.x, y: pointer.y })) { + selectedSurface = surface + } + }) + + rect = new fabric.Rect({ + fill: 'white', + stroke: 'black', + strokeWidth: 1, + width: width, + height: height, + left: pointer.x - width / 2, + top: pointer.y - height / 2, + selectable: true, + lockMovementX: true, + lockMovementY: true, + lockRotation: true, + lockScalingX: true, + lockScalingY: true, + }) + + //개구냐 그림자냐에 따라 변경 + rect.set({ + fill: buttonAct === 1 ? 'white' : 'rgba(0, 0, 0, 0.4)', + name: objTempName, + }) + + canvas?.add(rect) + }) + + addCanvasMouseEventListener('mouse:up', (e) => { + if (rect) { + const rectPolygon = pointsToTurfPolygon(rectToPolygon(rect)) + const selectedSurfacePolygon = polygonToTurfPolygon(selectedSurface) + + //지붕 밖으로 그렸을때 + if (!turf.booleanWithin(rectPolygon, selectedSurfacePolygon)) { + swalFire({ text: '개구를 배치할 수 없습니다.', icon: 'error' }) + //일단 지워 + deleteTempObjects(setShowObjectSettingModal) + return + } + + if (!isCrossChecked) { + const preObjects = canvas?.getObjects().filter((obj) => obj.name === BATCH_TYPE.OPENING || obj.name === BATCH_TYPE.SHADOW) + const preObjectsArray = preObjects.map((obj) => rectToPolygon(obj)) + const isCross = preObjectsArray.some((object) => turf.booleanOverlap(pointsToTurfPolygon(object), rectPolygon)) + + if (isCross) { + swalFire({ text: '겹치기 불가요...', icon: 'error' }) + deleteTempObjects(setShowObjectSettingModal) + return + } + } + + isDown = false + rect.set({ name: objName }) + rect.setCoords() + initEvent() + } + }) + } + } + + const deleteTempObjects = (setShowObjectSettingModal) => { + const deleteTarget = canvas?.getObjects().filter((obj) => obj.name === BATCH_TYPE.OPENING_TEMP || obj.name === BATCH_TYPE.SHADOW_TEMP) + canvas?.remove(...deleteTarget) + initEvent() //이벤트 초기화 + } + + return { + applyOpeningAndShadow, + } } diff --git a/src/hooks/surface/useSurfaceShapeBatch.js b/src/hooks/surface/useSurfaceShapeBatch.js index c9e898e9..bcffeba4 100644 --- a/src/hooks/surface/useSurfaceShapeBatch.js +++ b/src/hooks/surface/useSurfaceShapeBatch.js @@ -2,7 +2,7 @@ import { useRecoilValue } from 'recoil' import { canvasState } from '@/store/canvasAtom' -import { MENU } from '@/common/common' +import { MENU, BATCH_TYPE } from '@/common/common' import { getIntersectionPoint, setSurfaceShapePattern } from '@/util/canvas-util' import { degreesToRadians } from '@turf/turf' import { QPolygon } from '@/components/fabric/QPolygon' @@ -131,23 +131,23 @@ export function useSurfaceShapeBatch() { if (surfaceId === 1) { if (length1 === 0) { - swalFire({ text: getMessage('surface.shape.validate.size'), icon: 'error' }) + swalFire({ text: getMessage('common.canvas.validate.size'), icon: 'error' }) check = false } if (length2 === 0) { if (length3 === 0) { - swalFire({ text: getMessage('surface.shape.validate.size'), icon: 'error' }) + swalFire({ text: getMessage('common.canvas.validate.size'), icon: 'error' }) check = false } } } else if ([2, 4].includes(surfaceId)) { if (length1 === 0 || length2 === 0) { - swalFire({ text: getMessage('surface.shape.validate.size'), icon: 'error' }) + swalFire({ text: getMessage('common.canvas.validate.size'), icon: 'error' }) check = false } } else if ([3, 5, 6, 15, 18].includes(surfaceId)) { if (length1 === 0 || length2 === 0 || length3 === 0) { - swalFire({ text: getMessage('surface.shape.validate.size'), icon: 'error' }) + swalFire({ text: getMessage('common.canvas.validate.size'), icon: 'error' }) check = false } if (surfaceId === 3 && length3 > length1) { @@ -173,7 +173,7 @@ export function useSurfaceShapeBatch() { } } else if ([8, 12, 13, 16, 17].includes(surfaceId)) { if (length1 === 0 || length2 === 0 || length3 === 0 || length4 === 0) { - swalFire({ text: getMessage('surface.shape.validate.size'), icon: 'error' }) + swalFire({ text: getMessage('common.canvas.validate.size'), icon: 'error' }) check = false } @@ -201,7 +201,7 @@ export function useSurfaceShapeBatch() { } } else if ([7, 9, 10, 11, 14].includes(surfaceId)) { if (length1 === 0 || length2 === 0 || length3 === 0 || length4 === 0 || length5 === 0) { - swalFire({ text: getMessage('surface.shape.validate.size'), icon: 'error' }) + swalFire({ text: getMessage('common.canvas.validate.size'), icon: 'error' }) check = false } if (surfaceId === 9 || surfaceId === 10 || surfaceId === 11) { @@ -564,7 +564,27 @@ export function useSurfaceShapeBatch() { return points } + + const deleteAllSurfacesAndObjects = () => { + swalFire({ + text: '삭제 ㄱㄱ?', + type: 'confirm', + confirmFn: () => { + canvas?.getObjects().forEach((obj) => { + if (obj.name === MENU.BATCH_CANVAS.SURFACE_SHAPE_BATCH || obj.name === BATCH_TYPE.OPENING || obj.name === BATCH_TYPE.SHADOW) { + canvas?.remove(obj) + } + }) + swalFire({ text: '삭제 완료 되었습니다.' }) + }, + denyFn: () => { + swalFire({ text: '취소되었습니다.' }) + }, + }) + } + return { applySurfaceShape, + deleteAllSurfacesAndObjects, } } diff --git a/src/locales/ja.json b/src/locales/ja.json index c2ae55a0..abdfc11c 100644 --- a/src/locales/ja.json +++ b/src/locales/ja.json @@ -470,7 +470,7 @@ "main.popup.login.validate1": "入力したパスワードが異なります。", "main.popup.login.validate2": "半角10文字以内で入力してください。", "main.popup.login.success": "パスワードが変更されました。", - "surface.shape.validate.size": "寸法を入力してください.", + "common.canvas.validate.size": "寸法を入力してください.", "surface.shape.validate.size.1to2": "①길이는 ②보다 큰 값을 넣어주세요.", "surface.shape.validate.size.1to3": "①길이는 ③보다 큰 값을 넣어주세요.", "surface.shape.validate.size.1to23": "①길이는 ②+③보다 큰 값을 넣어주세요.", diff --git a/src/locales/ko.json b/src/locales/ko.json index 0bd7e448..3e37918b 100644 --- a/src/locales/ko.json +++ b/src/locales/ko.json @@ -474,7 +474,7 @@ "main.popup.login.validate1": "입력한 패스워드가 다릅니다.", "main.popup.login.validate2": "반각 10자 이내로 입력해주세요.", "main.popup.login.success": "비밀번호가 변경되었습니다.", - "surface.shape.validate.size": "사이즈를 입력해 주세요.", + "common.canvas.validate.size": "사이즈를 입력해 주세요.", "surface.shape.validate.size.1to2": "①길이는 ②보다 큰 값을 넣어주세요.", "surface.shape.validate.size.1to3": "①길이는 ③보다 큰 값을 넣어주세요.", "surface.shape.validate.size.1to23": "①길이는 ②+③보다 큰 값을 넣어주세요.", From 41feb2ebec9a540df56f4fd2baa4757fb5b18e92 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Tue, 15 Oct 2024 16:00:54 +0900 Subject: [PATCH 12/68] =?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=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../roofcover/useWallLineOffsetSetting.js | 287 ++++++++++++++---- src/hooks/usePolygon.js | 207 +------------ 2 files changed, 224 insertions(+), 270 deletions(-) diff --git a/src/hooks/roofcover/useWallLineOffsetSetting.js b/src/hooks/roofcover/useWallLineOffsetSetting.js index 882635e0..61d7d4ce 100644 --- a/src/hooks/roofcover/useWallLineOffsetSetting.js +++ b/src/hooks/roofcover/useWallLineOffsetSetting.js @@ -19,7 +19,7 @@ export function useWallLineOffsetSetting() { const arrow1Ref = useRef(null) const arrow2Ref = useRef(null) - const drawLine = (point1, point2, idx) => { + const drawLine = (point1, point2, idx, direction = currentWallLineRef.current.direction) => { const line = addLine([point1.x, point1.y, point2.x, point2.y], { stroke: 'black', strokeWidth: 4, @@ -30,7 +30,7 @@ export function useWallLineOffsetSetting() { y1: point1.y, x2: point2.x, y2: point2.y, - direction: currentWallLineRef.current.direction, + direction: direction, }) line.attributes = { ...currentWallLineRef.current.attributes } @@ -50,6 +50,9 @@ export function useWallLineOffsetSetting() { useEffect(() => { const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') + if (outerLines.length === 0) { + return + } outerLines.forEach((outerLine) => { outerLine.set({ selectable: true }) showLine(outerLine) @@ -94,6 +97,7 @@ export function useWallLineOffsetSetting() { } currentWallLineRef.current = e.target + console.log(currentWallLineRef.current.idx, currentWallLineRef.current.direction) if (type === TYPES.WALL_LINE_EDIT) { addCircleByLine(currentWallLineRef.current) } @@ -193,38 +197,59 @@ export function useWallLineOffsetSetting() { } })[0] const length = Number(length1Ref.current.value) / 10 + const length2 = Number(length2Ref.current.value) / 10 const currentIdx = currentWallLineRef.current.idx + const currentDirection = currentWallLineRef.current.direction let point1, point2, point3 if (radioTypeRef.current === '1') { // 1지점 선택시 방향은 down, right만 가능 if (arrow1Ref.current === 'down') { - point1 = { x: startPoint.x, y: startPoint.y } - point2 = { x: startPoint.x, y: startPoint.y + length } - point3 = { x: endPoint.x, y: endPoint.y } + if (currentDirection === 'top') { + point1 = { x: endPoint.x, y: endPoint.y } + point2 = { x: startPoint.x, y: startPoint.y + length } + point3 = { x: startPoint.x, y: startPoint.y } + } else { + point1 = { x: startPoint.x, y: startPoint.y } + point2 = { x: startPoint.x, y: startPoint.y + length } + point3 = { x: endPoint.x, y: endPoint.y } + } } else { - point1 = { x: startPoint.x, y: startPoint.y } - point2 = { x: startPoint.x + length, y: startPoint.y } - point3 = { x: endPoint.x, y: endPoint.y } + if (currentDirection === 'left') { + point1 = { x: endPoint.x, y: endPoint.y } + point2 = { x: startPoint.x + length, y: startPoint.y } + point3 = { x: startPoint.x, y: startPoint.y } + } else { + point1 = { x: startPoint.x, y: startPoint.y } + point2 = { x: startPoint.x + length, y: startPoint.y } + point3 = { x: endPoint.x, y: endPoint.y } + } } } else { // 2지점일 경우 방향은 up, left만 가능 if (arrow2Ref.current === 'up') { - point1 = { x: startPoint.x, y: startPoint.y } - point2 = { x: startPoint.x, y: startPoint.y - length } - point3 = { x: endPoint.x, y: endPoint.y } + if (currentDirection === 'bottom') { + point1 = { x: startPoint.x, y: startPoint.y } + point2 = { x: endPoint.x, y: endPoint.y - length2 } + point3 = { x: endPoint.x, y: endPoint.y } + } else { + point1 = { x: endPoint.x, y: endPoint.y } + point2 = { x: endPoint.x, y: endPoint.y - length2 } + point3 = { x: startPoint.x, y: startPoint.y } + } } else { - point1 = { x: startPoint.x, y: startPoint.y } - point2 = { x: startPoint.x - length, y: startPoint.y } - point3 = { x: endPoint.x, y: endPoint.y } + if (currentDirection === 'right') { + point1 = { x: startPoint.x, y: startPoint.y } + point2 = { x: endPoint.x - length2, y: endPoint.y } + point3 = { x: endPoint.x, y: endPoint.y } + } else { + point1 = { x: endPoint.x, y: endPoint.y } + point2 = { x: endPoint.x - length2, y: endPoint.y } + point3 = { x: startPoint.x, y: startPoint.y } + } } } - const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') - outerLines.forEach((outerLine) => { - if (outerLine.idx >= currentIdx + 1) { - outerLine.idx = outerLine.idx + 1 - } - }) + rearrangeOuterLine(currentIdx + 1) drawLine(point1, point2, currentIdx) drawLine(point2, point3, currentIdx + 1) @@ -235,10 +260,19 @@ export function useWallLineOffsetSetting() { canvas.renderAll() } + const rearrangeOuterLine = (idxParam) => { + const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') + outerLines.forEach((outerLine) => { + if (outerLine.idx >= idxParam) { + outerLine.idx = outerLine.idx + 1 + } + }) + } + const handleOffsetSave = () => { const direction = currentWallLineRef.current.direction let canDirections = direction === 'left' || direction === 'right' ? ['up', 'down'] : ['left', 'right'] - + const currentIdx = currentWallLineRef.current.idx if (!canDirections.includes(arrow1Ref.current)) { alert('방향을 다시 선택하세요') return @@ -255,72 +289,195 @@ export function useWallLineOffsetSetting() { const nextLine = outerLines.find((line) => line.idx === nextIdx) const length = length1Ref.current.value / 10 - + const currentLineX = Math.floor(Math.max(currentLine.x1, currentLine.x2)) + const currentLineY = Math.floor(Math.max(currentLine.y1, currentLine.y2)) switch (arrow1Ref.current) { case 'up': { - const currentLineY = Math.floor(Math.max(currentLine.y1, currentLine.y2)) - currentLine.set({ y1: currentLineY - length, y2: currentLineY - length }) + if (prevLine.direction === currentLine.direction) { + const newX = + currentLine.direction === 'left' + ? Math.floor(Math.max(currentLine.x1, currentLine.x2)) + : Math.floor(Math.min(currentLine.x1, currentLine.x2)) - if (Math.abs(currentLineY - prevLine.y1) < 2) { - prevLine.set({ y1: currentLineY - length }) + const newPoint1 = { x: newX, y: currentLineY - length } + const newPoint2 = { x: prevLine.x2, y: prevLine.y2 } + rearrangeOuterLine(currentIdx) + drawLine(newPoint1, newPoint2, currentIdx, 'top') + + if (Math.abs(currentLineY - nextLine.y1) < 2) { + nextLine.set({ y1: currentLineY - length }) + } else { + nextLine.set({ y2: currentLineY - length }) + } + } else if (nextLine.direction === currentLine.direction) { + const newX = + currentLine.direction === 'left' + ? Math.floor(Math.min(currentLine.x1, currentLine.x2)) + : Math.floor(Math.max(currentLine.x1, currentLine.x2)) + + const newPoint1 = { x: newX, y: currentLineY - length } + const newPoint2 = { x: nextLine.x1, y: nextLine.y1 } + rearrangeOuterLine(currentIdx + 1) + drawLine(newPoint1, newPoint2, currentIdx + 1, 'top') + + if (Math.abs(currentLineY - prevLine.y1) < 2) { + prevLine.set({ y1: currentLineY - length }) + } else { + prevLine.set({ y2: currentLineY - length }) + } } else { - prevLine.set({ y2: currentLineY - length }) - } - if (Math.abs(currentLineY - nextLine.y1) < 2) { - nextLine.set({ y1: currentLineY - length }) - } else { - nextLine.set({ y2: currentLineY - length }) + if (Math.abs(currentLineY - prevLine.y1) < 2) { + prevLine.set({ y1: prevLine.y1 - length }) + } else { + prevLine.set({ y2: prevLine.y2 - length }) + } + if (Math.abs(currentLineY - nextLine.y1) < 2) { + nextLine.set({ y1: nextLine.y1 - length }) + } else { + nextLine.set({ y2: nextLine.y2 - length }) + } } + currentLine.set({ y1: currentLine.y1 - length, y2: currentLine.y2 - length }) + break } case 'down': { - const currentLineY = Math.floor(Math.max(currentLine.y1, currentLine.y2)) - currentLine.set({ y1: currentLineY + length, y2: currentLineY + length }) + if (prevLine.direction === currentLine.direction) { + const newX = + currentLine.direction === 'left' + ? Math.floor(Math.max(currentLine.x1, currentLine.x2)) + : Math.floor(Math.min(currentLine.x1, currentLine.x2)) + const newPoint1 = { x: newX, y: currentLineY + length } + const newPoint2 = { x: prevLine.x2, y: prevLine.y2 } + rearrangeOuterLine(currentIdx) + drawLine(newPoint1, newPoint2, currentIdx, 'bottom') + if (Math.abs(currentLineY - nextLine.y1) < 2) { + nextLine.set({ y1: currentLineY + length }) + } else { + nextLine.set({ y2: currentLineY + length }) + } + } else if (nextLine.direction === currentLine.direction) { + const newX = + currentLine.direction === 'left' + ? Math.floor(Math.min(currentLine.x1, currentLine.x2)) + : Math.floor(Math.max(currentLine.x1, currentLine.x2)) + const newPoint1 = { x: newX, y: currentLineY + length } + const newPoint2 = { x: nextLine.x1, y: nextLine.y1 } + rearrangeOuterLine(currentIdx + 1) + drawLine(newPoint1, newPoint2, currentIdx + 1, 'bottom') + if (Math.abs(currentLineY - prevLine.y1) < 2) { + prevLine.set({ y1: currentLineY + length }) + } else { + prevLine.set({ y2: currentLineY + length }) + } + } else { + if (Math.abs(currentLineY - prevLine.y1) < 2) { + prevLine.set({ y1: prevLine.y1 + length }) + } else { + prevLine.set({ y2: prevLine.y2 + length }) + } + if (Math.abs(currentLineY - nextLine.y1) < 2) { + nextLine.set({ y1: nextLine.y1 + length }) + } else { + nextLine.set({ y2: nextLine.y2 + length }) + } + } - if (Math.abs(currentLineY - prevLine.y1) < 2) { - prevLine.set({ y1: currentLineY + length }) - } else { - prevLine.set({ y2: currentLineY + length }) - } - if (Math.abs(currentLineY - nextLine.y1) < 2) { - nextLine.set({ y1: currentLineY + length }) - } else { - nextLine.set({ y2: currentLineY + length }) - } + currentLine.set({ y1: currentLine.y1 + length, y2: currentLine.y2 + length }) break } case 'left': { - const currentLineX = Math.floor(Math.max(currentLine.x1, currentLine.x2)) - currentLine.set({ x1: currentLineX - length, x2: currentLineX - length }) + if (prevLine.direction === currentLine.direction) { + const newY = + currentLine.direction === 'top' + ? Math.floor(Math.max(currentLine.y1, currentLine.y2)) + : Math.floor(Math.min(currentLine.y1, currentLine.y2)) + const newPoint1 = { x: currentLineX - length, y: newY } + const newPoint2 = { x: prevLine.x2, y: prevLine.y2 } + rearrangeOuterLine(currentIdx) + drawLine(newPoint1, newPoint2, currentIdx, 'left') + if (Math.abs(currentLineX - nextLine.x1) < 2) { + nextLine.set({ x1: currentLineX - length }) + } else { + nextLine.set({ x2: currentLineX - length }) + } + } else if (nextLine.direction === currentLine.direction) { + const newY = + currentLine.direction === 'top' + ? Math.floor(Math.min(currentLine.y1, currentLine.y2)) + : Math.floor(Math.max(currentLine.y1, currentLine.y2)) + const newPoint1 = { x: currentLineX - length, y: newY } + const newPoint2 = { x: nextLine.x1, y: nextLine.y1 } + rearrangeOuterLine(currentIdx + 1) + drawLine(newPoint1, newPoint2, currentIdx + 1, 'left') + if (Math.abs(currentLineX - prevLine.x1) < 2) { + prevLine.set({ x1: currentLineX - length }) + } else { + prevLine.set({ x2: currentLineX - length }) + } + } else { + if (Math.abs(currentLineX - prevLine.x1) < 2) { + prevLine.set({ x1: prevLine.x1 - length }) + } else { + prevLine.set({ x2: prevLine.x2 - length }) + } - if (Math.abs(currentLineX - prevLine.x1) < 2) { - prevLine.set({ x1: currentLineX - length }) - } else { - prevLine.set({ x2: currentLineX - length }) - } - if (Math.abs(currentLineX - nextLine.x1) < 2) { - nextLine.set({ x1: currentLineX - length }) - } else { - nextLine.set({ x2: currentLineX - length }) + if (Math.abs(currentLineX - nextLine.x1) < 2) { + nextLine.set({ x1: nextLine.x1 - length }) + } else { + nextLine.set({ x2: nextLine.x2 - length }) + } } + + currentLine.set({ x1: currentLine.x1 - length, x2: currentLine.x2 - length }) break } case 'right': { - const currentLineX = Math.floor(Math.max(currentLine.x1, currentLine.x2)) - currentLine.set({ x1: currentLineX + length, x2: currentLineX + length }) + if (prevLine.direction === currentLine.direction) { + const newY = + currentLine.direction === 'top' + ? Math.floor(Math.max(currentLine.y1, currentLine.y2)) + : Math.floor(Math.min(currentLine.y1, currentLine.y2)) + const newPoint1 = { x: currentLineX + length, y: newY } + const newPoint2 = { x: prevLine.x2, y: prevLine.y2 } + rearrangeOuterLine(currentIdx) + drawLine(newPoint1, newPoint2, currentIdx, 'right') + if (Math.abs(currentLineX - nextLine.x1) < 2) { + nextLine.set({ x1: currentLineX + length }) + } else { + nextLine.set({ x2: currentLineX + length }) + } + } else if (nextLine.direction === currentLine.direction) { + const newY = + currentLine.direction === 'top' + ? Math.floor(Math.min(currentLine.y1, currentLine.y2)) + : Math.floor(Math.max(currentLine.y1, currentLine.y2)) + const newPoint1 = { x: currentLineX + length, y: newY } + const newPoint2 = { x: nextLine.x1, y: nextLine.y1 } + rearrangeOuterLine(currentIdx + 1) + drawLine(newPoint1, newPoint2, currentIdx + 1, 'right') - if (Math.abs(currentLineX - prevLine.x1) < 2) { - prevLine.set({ x1: currentLineX + length }) + if (Math.abs(currentLineX - prevLine.x1) < 2) { + prevLine.set({ x1: currentLineX + length }) + } else { + prevLine.set({ x2: currentLineX + length }) + } } else { - prevLine.set({ x2: currentLineX + length }) - } - if (Math.abs(currentLineX - nextLine.x1) < 2) { - nextLine.set({ x1: currentLineX + length }) - } else { - nextLine.set({ x2: currentLineX + length }) + if (Math.abs(currentLineX - prevLine.x1) < 2) { + prevLine.set({ x1: prevLine.x1 + length }) + } else { + prevLine.set({ x2: prevLine.x2 + length }) + } + if (Math.abs(currentLineX - nextLine.x1) < 2) { + nextLine.set({ x1: nextLine.x1 + length }) + } else { + nextLine.set({ x2: nextLine.x2 + length }) + } } + currentLine.set({ x1: currentLine.x1 + length, x2: currentLine.x2 + length }) + break } } diff --git a/src/hooks/usePolygon.js b/src/hooks/usePolygon.js index 4cd63363..7289a0d3 100644 --- a/src/hooks/usePolygon.js +++ b/src/hooks/usePolygon.js @@ -26,6 +26,8 @@ export const usePolygon = () => { } const addPolygonByLines = (lines, options) => { + //lines의 idx를 정렬한다. + lines.sort((a, b) => a.idx - b.idx) const points = createPolygonPointsFromOuterLines(lines) return addPolygon(points, { @@ -98,214 +100,9 @@ export const usePolygon = () => { canvas.renderAll() } - //polygon 나누기 - const splitPolygonWithLines = (polygon) => { - const roofs = [] - const allLines = [...polygon.innerLines] - - allLines.forEach((line) => { - line.startPoint = { x: line.x1, y: line.y1 } - line.endPoint = { x: line.x2, y: line.y2 } - }) - - // allLines에 x1,y1,x2,y2를 비교해서 중복되는 값을 제거한다. - allLines.forEach((line, index) => { - const startPoint = line.startPoint - const endPoint = line.endPoint - - allLines.forEach((line2, index2) => { - if (index !== index2) { - if ( - (isSamePoint(startPoint, line2.startPoint) && isSamePoint(endPoint, line2.endPoint)) || - (isSamePoint(endPoint, line2.startPoint) && isSamePoint(startPoint, line2.endPoint)) - ) { - allLines.splice(index2, 1) - } - } - }) - }) - - /** - * 좌표 테스트용 - */ - /*allLines.forEach((line) => { - const text = new fabric.Text(`(${line.startPoint.x},${line.startPoint.y})`, { - left: line.startPoint.x, - top: line.startPoint.y, - fontSize: 15, - }) - - polygon.canvas.add(text) - polygon.canvas.renderAll() - - const text2 = new fabric.Text(`(${line.endPoint.x},${line.endPoint.y})`, { - left: line.endPoint.x, - top: line.endPoint.y, - fontSize: 15, - }) - - polygon.canvas.add(text2) - polygon.canvas.renderAll() - }) - - polygon.points.forEach((point, index) => { - const text = new fabric.Text(`(${point.x},${point.y})`, { - left: point.x, - top: point.y, - fontSize: 15, - }) - - polygon.canvas.add(text) - polygon.canvas.renderAll() - })*/ - /** - * 좌표 테스트용 끝 - */ - - polygon.points.forEach((point, index) => { - allLines.forEach((line) => { - if (line.endPoint.x === point.x && line.endPoint.y === point.y) { - const temp = line.startPoint - line.startPoint = line.endPoint - line.endPoint = temp - } - }) - }) - - polygon.points.forEach((point, index) => { - const routes = [] - - // 시작점은 시작 hip라인의 출발점 - const startPoint = point - // 도착점은 마지막 hip라인의 끝나는 점 - const endPoint = polygon.points[(index + 1) % polygon.points.length] - - const startLine = allLines.find((line) => line.startPoint.x === startPoint.x && line.startPoint.y === startPoint.y) - const endLine = allLines.find((line) => line.startPoint.x === endPoint.x && line.startPoint.y === endPoint.y) - - const arrivalPoint = endLine.endPoint - routes.push(startLine.startPoint) - routes.push(startLine.endPoint) - - //hip끼리 만나는 경우는 아무것도 안해도됨 - if (!isSamePoint(startLine.endPoint, arrivalPoint)) { - // polygon line까지 추가 - const allLinesCopy = [...allLines, ...polygon.lines] - // hip이 만나지 않는 경우 갈 수 있는 길을 다 돌아야함 - let currentPoint = startLine.endPoint - let currentLine = startLine - let movedLines = [] - let subMovedLines = [] - while (!isSamePoint(currentPoint, arrivalPoint)) { - // startHip에서 만나는 출발선 두개. 두개의 선을 출발하여 arrivalPoint에 도착할 때 까지 count를 세고, 더 낮은 count를 가진 길을 선택한다. - let connectedLines = allLinesCopy.filter((line) => isSamePoint(line.startPoint, currentPoint) || isSamePoint(line.endPoint, currentPoint)) - - connectedLines = connectedLines.filter((line) => line !== currentLine) - - connectedLines = connectedLines.filter((line) => !subMovedLines.includes(line)) - - //마지막 선이 endLine의 startPoint와 같은경우 그 전까지 movedLine을 제거한다. - const endLineMeetLineCnt = connectedLines.filter((line) => { - return isSamePoint(line.endPoint, endLine.startPoint) || isSamePoint(line.startPoint, endLine.startPoint) - }).length - - if (endLineMeetLineCnt !== 0) { - movedLines.push(subMovedLines) - - console.log(movedLines, index) - } - - connectedLines = connectedLines.filter((line) => { - return !isSamePoint(line.endPoint, endLine.startPoint) && !isSamePoint(line.startPoint, endLine.startPoint) - }) - - if (connectedLines.length === 0) { - return - } - - let tempPoints = [] - - for (let i = 0; i < connectedLines.length; i++) { - if (isSamePoint(connectedLines[i].startPoint, currentPoint)) { - tempPoints.push({ point: connectedLines[i].endPoint, index: i, line: connectedLines[i] }) - } else { - tempPoints.push({ point: connectedLines[i].startPoint, index: i, line: connectedLines[i] }) - } - } - - //tempPoints에서 arrivalPoint와 가장 가까운 점을 찾는다. - let minDistance = Number.MAX_SAFE_INTEGER - let minIndex = 0 - tempPoints.forEach((tempPoint, index) => { - const distance = Math.sqrt(Math.pow(tempPoint.point.x - arrivalPoint.x, 2) + Math.pow(tempPoint.point.y - arrivalPoint.y, 2)) - if (distance < minDistance) { - minDistance = distance - minIndex = tempPoint.index - } - }) - - currentPoint = tempPoints[minIndex].point - currentLine = tempPoints[minIndex].line - if (currentLine !== startLine) { - subMovedLines.push(currentLine) - } - routes.push(currentPoint) - } - } - - routes.push(endLine.startPoint) - roofs.push(routes) - }) - - // 중복 제거 - roofs.forEach((roofPoint, index) => { - const samePointLengthRoofPoints = roofs.filter((roof) => roof.length === roofPoint.length && roof !== roofPoint) - - samePointLengthRoofPoints.forEach((samePointRoof) => { - if (arraysHaveSamePoints(samePointRoof, roofPoint)) { - roofs.splice(roofs.indexOf(samePointRoof), 1) - } - }) - }) - - roofs.forEach((roofPoint, index) => { - let defense - const direction = getDirectionByPoint(roofPoint[0], roofPoint[roofPoint.length - 1]) - - switch (direction) { - case 'top': - defense = 'east' - break - case 'right': - defense = 'south' - break - case 'bottom': - defense = 'west' - break - case 'left': - defense = 'north' - break - } - - const roof = new QPolygon(roofPoint, { - fontSize: polygon.fontSize, - stroke: 'black', - fill: 'transparent', - strokeWidth: 3, - name: 'roof', - selectable: false, - defense: defense, - }) - - polygon.canvas.add(roof) - polygon.canvas.renderAll() - }) - } - return { addPolygon, addPolygonByLines, removePolygon, - splitPolygonWithLines, } } From ab3aa6d993698aeb3c729c4411a038a96e55930b Mon Sep 17 00:00:00 2001 From: Daseul Kim Date: Tue, 15 Oct 2024 16:17:41 +0900 Subject: [PATCH 13/68] =?UTF-8?q?refactor:=20=ED=8E=98=EC=9D=B4=EC=A7=80?= =?UTF-8?q?=20=EC=9D=B4=EB=8F=99=20=ED=9B=84=20=EB=B3=B5=EA=B7=80=20?= =?UTF-8?q?=EC=8B=9C=20canvas=20=ED=91=9C=EC=B6=9C=EC=9D=B4=20=EC=95=88?= =?UTF-8?q?=EB=90=98=EB=8A=94=20=ED=98=84=EC=83=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/floor-plan/CanvasFrame.jsx | 2 +- src/components/floor-plan/CanvasLayout.jsx | 27 +++++++++++----------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/components/floor-plan/CanvasFrame.jsx b/src/components/floor-plan/CanvasFrame.jsx index 3b823460..17f0018d 100644 --- a/src/components/floor-plan/CanvasFrame.jsx +++ b/src/components/floor-plan/CanvasFrame.jsx @@ -29,7 +29,7 @@ export default function CanvasFrame({ plan }) { useEffect(() => { loadCanvas() - }, [plan]) + }, [plan, canvas]) useEffect(() => { switch (currentMenu) { diff --git a/src/components/floor-plan/CanvasLayout.jsx b/src/components/floor-plan/CanvasLayout.jsx index 7a3e9323..62cc737b 100644 --- a/src/components/floor-plan/CanvasLayout.jsx +++ b/src/components/floor-plan/CanvasLayout.jsx @@ -25,8 +25,7 @@ export default function CanvasLayout() { const handleCurrentPlan = (newCurrentId) => { // console.log('currentPlan newCurrentId: ', newCurrentId) - - if (!currentCanvasPlan?.id || currentCanvasPlan.id !== newCurrentId) { + if (!currentCanvasPlan || currentCanvasPlan.id !== newCurrentId) { if (currentCanvasPlan?.id && checkModifiedCanvasPlan()) { swalFire({ html: getMessage('common.message.confirm.save') + `
${currentCanvasPlan.name}`, @@ -92,17 +91,19 @@ export default function CanvasLayout() { } useEffect(() => { - getCanvasByObjectNo(sessionState.userId, objectNo).then((res) => { - console.log('canvas 목록 ', res) - if (res.length > 0) { - setInitCanvasPlans(res) - setPlans(res) - handleCurrentPlan(res.at(-1).id) // last 데이터에 포커싱 - setPlanNum(res.length) - } else { - addNewPlan() - } - }) + if (!currentCanvasPlan) { + getCanvasByObjectNo(sessionState.userId, objectNo).then((res) => { + // console.log('canvas 목록 ', res) + if (res.length > 0) { + setInitCanvasPlans(res) + setPlans(res) + handleCurrentPlan(res.at(-1).id) // last 데이터에 포커싱 + setPlanNum(res.length) + } else { + addNewPlan() + } + }) + } }, []) return ( From e5c180225fda37ccd74b544b1c1d8e01e46ea98e Mon Sep 17 00:00:00 2001 From: basssy Date: Tue, 15 Oct 2024 16:29:45 +0900 Subject: [PATCH 14/68] =?UTF-8?q?=EB=AC=BC=EA=B1=B4=ED=98=84=ED=99=A9=20?= =?UTF-8?q?=EC=84=A4=EA=B3=84=EC=9D=98=EB=A2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/management/StuffDetail.jsx | 23 +- .../management/StuffSearchCondition.jsx | 43 ++-- .../management/popup/PlanRequestPop.jsx | 215 ++++++++++++++---- .../management/popup/PlanRequestPopQGrid.jsx | 60 +++++ src/locales/ja.json | 36 ++- src/locales/ko.json | 37 ++- src/store/planReqAtom.js | 22 ++ 7 files changed, 364 insertions(+), 72 deletions(-) create mode 100644 src/components/management/popup/PlanRequestPopQGrid.jsx create mode 100644 src/store/planReqAtom.js diff --git a/src/components/management/StuffDetail.jsx b/src/components/management/StuffDetail.jsx index 474e0413..b64f2a23 100644 --- a/src/components/management/StuffDetail.jsx +++ b/src/components/management/StuffDetail.jsx @@ -455,10 +455,12 @@ export default function StuffDetail() { - {getMessage('stuff.detail.designNo')} + {getMessage('stuff.detail.planReqNo')}
-
+
+ +
@@ -770,7 +772,7 @@ export default function StuffDetail() {
- * 필수 입력항목 + * {getMessage('stuff.detail.required')}
@@ -778,6 +780,21 @@ export default function StuffDetail() { + + + + + +
{getMessage('stuff.detail.planReqNo')} +
+
+ +
+ +
+
diff --git a/src/components/management/StuffSearchCondition.jsx b/src/components/management/StuffSearchCondition.jsx index f88cf3cc..df2d74c8 100644 --- a/src/components/management/StuffSearchCondition.jsx +++ b/src/components/management/StuffSearchCondition.jsx @@ -42,7 +42,6 @@ export default function StuffSearchCondition() { const resetStuffRecoil = useResetRecoilState(stuffSearchState) const [stuffSearch, setStuffSearch] = useRecoilState(stuffSearchState) const [objectNo, setObjectNo] = useState('') //물건번호 - // const [saleStoreId, setSaleStoreId] = useState('') //판매대리점ID 세션에서 가져와서세팅 const [address, setAddress] = useState('') //물건주소 const [objectName, setobjectName] = useState('') //물건명 const [saleStoreName, setSaleStoreName] = useState('') //판매대리점명 @@ -116,6 +115,7 @@ export default function StuffSearchCondition() { ref.current.state.values = [] } } + //판매대리점 자동완성 변경 const onSelectionChange = (key) => { if (!isEmptyArray(key)) { @@ -146,19 +146,19 @@ export default function StuffSearchCondition() {
-

물건현황

+

{getMessage('stuff.search.title')}

@@ -174,13 +174,13 @@ export default function StuffSearchCondition() { - 물건번호 + {getMessage('stuff.search.schObjectNo')}
{ setObjectNo(e.target.value) @@ -189,29 +189,28 @@ export default function StuffSearchCondition() { />
- 판매대리점명 + {getMessage('stuff.search.schSaleStoreName')}
{ - //setSaleStoreId(e.target.value) setSaleStoreName(e.target.value) setStuffSearch({ ...stuffSearch, code: 'S', schSaleStoreName: e.target.value }) }} />
- 물건주소 + {getMessage('stuff.search.schAddress')}
{ setAddress(e.target.value) @@ -222,13 +221,13 @@ export default function StuffSearchCondition() { - 물건명 + {getMessage('stuff.search.schObjectName')}
{ setobjectName(e.target.value) @@ -237,13 +236,13 @@ export default function StuffSearchCondition() { />
- 견적처 + {getMessage('stuff.search.schDispCompanyName')}
{ setDispCompanyName(e.target.value) @@ -252,7 +251,7 @@ export default function StuffSearchCondition() { />
- 판매대리점 선택 + {getMessage('stuff.search.schSelSaleStoreId')} {schSelSaleStoreList?.length > 0 && ( { setReceiveUser(e.target.value) @@ -286,7 +285,7 @@ export default function StuffSearchCondition() { />
- 기간검색 + {getMessage('stuff.search.period')}
@@ -302,7 +301,7 @@ export default function StuffSearchCondition() { setStuffSearch({ ...stuffSearch, code: 'S', schDateType: e.target.value }) }} /> - +
- +
diff --git a/src/components/management/popup/PlanRequestPop.jsx b/src/components/management/popup/PlanRequestPop.jsx index 9eee76a3..66bea1d4 100644 --- a/src/components/management/popup/PlanRequestPop.jsx +++ b/src/components/management/popup/PlanRequestPop.jsx @@ -1,15 +1,21 @@ -import React, { useState } from 'react' +import React, { useState, useRef, useEffect } from 'react' import { useForm } from 'react-hook-form' import { queryStringFormatter } from '@/util/common-utils' import { useAxios } from '@/hooks/useAxios' import { globalLocaleStore } from '@/store/localeAtom' -import { useRecoilValue } from 'recoil' +import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil' import FindAddressPopQGrid from './FindAddressPopQGrid' import { useMessage } from '@/hooks/useMessage' import { isNotEmptyArray } from '@/util/common-utils' import SingleDatePicker from '@/components/common/datepicker/SingleDatePicker' import dayjs from 'dayjs' +import PlanRequestPopQGrid from './PlanRequestPopQGrid' +import { sessionStore } from '@/store/commonAtom' +import { planReqSearchState } from '@/store/planReqAtom' +import Select from 'react-dropdown-select' export default function PlanRequestPop(props) { + const sessionState = useRecoilValue(sessionStore) + const globalLocaleState = useRecoilValue(globalLocaleStore) const { get } = useAxios(globalLocaleState) @@ -29,41 +35,89 @@ export default function PlanRequestPop(props) { setStartDate: setEndDate, } + const ref = useRef() + const resetPlanReqRecoil = useResetRecoilState(planReqSearchState) + + const [planReqSearch, setPlanReqSearch] = useRecoilState(planReqSearchState) + const [schPlanReqNo, setSchPlanReqNo] = useState('') //설계의뢰번호 + const [schTitle, setSchTitle] = useState('') //안건명 + const [schAddress, setSchAddress] = useState('') //도도부현 + const [schSaleStoreName, setSchSaleStoreName] = useState('') //판매대리점명 + const [schPlanReqName, setSchPlanReqName] = useState('') //의뢰자명 + const [schPlanStatCd, setSchPlanStatCd] = useState('') //상태코드 + const [schDateGbn, setSchDateGbn] = useState('S') //기간구분코드(S/R) + + //초기화 + const resetRecoil = () => {} + + //초기화 눌렀을 때 자동완성도.. + const handleClear = () => { + if (ref.current.state.dropDown) { + ref.current.methods.dropDown() + } else { + ref.current.state.values = [] + } + } + + useEffect(() => { + setStartDate(planReqSearch?.schStartDt ? planReqSearch.schStartDt : dayjs(new Date()).add(-3, 'month').format('YYYY-MM-DD')) + setEndDate(planReqSearch?.schEndDt ? planReqSearch.schEndDt : dayjs(new Date()).format('YYYY-MM-DD')) + }, [planReqSearch]) + const [gridProps, setGridProps] = useState({ gridData: [], isPageable: false, gridColumns: [ { - field: 'address1', - headerName: getMessage('stuff.addressPopup.gridHeader.address1'), + field: 'planStatName', + headerName: getMessage('stuff.planReqPopup.gridHeader.planStatName'), minWidth: 150, checkboxSelection: true, showDisabledCheckboxes: true, }, { - field: 'address2', - headerName: getMessage('stuff.addressPopup.gridHeader.address2'), + field: 'planReqNo', + headerName: getMessage('stuff.planReqPopup.gridHeader.planReqNo'), minWidth: 150, }, { - field: 'address3', - headerName: getMessage('stuff.addressPopup.gridHeader.address3'), + field: 'saleStoreId', + headerName: getMessage('stuff.planReqPopup.gridHeader.saleStoreId'), + minWidth: 150, + }, + { + field: 'saleStoreName', + headerName: getMessage('stuff.planReqPopup.gridHeader.saleStoreName'), + minWidth: 150, + }, + { + field: 'title', + headerName: getMessage('stuff.planReqPopup.gridHeader.title'), + minWidth: 150, + }, + { + field: 'address1', + headerName: getMessage('stuff.planReqPopup.gridHeader.address1'), + minWidth: 150, + }, + { + field: 'houseCntName', + headerName: getMessage('stuff.planReqPopup.gridHeader.houseCntName'), + minWidth: 150, + }, + { + field: 'planReqName', + headerName: getMessage('stuff.planReqPopup.gridHeader.planReqName'), + minWidth: 150, + }, + { + field: 'submitDt', + headerName: getMessage('stuff.planReqPopup.gridHeader.submitDt'), minWidth: 150, }, ], }) - //검색필드 - const formInitValue = { - //zipNo: '', - } - - const { register, setValue, getValues, handleSubmit, resetField, control, watch } = useForm({ - defaultValues: formInitValue, - }) - - const form = { register, setValue, getValues, handleSubmit, resetField, control, watch } - return (
@@ -89,68 +143,131 @@ export default function PlanRequestPop(props) { - 설계의뢰번호 / 設計依頼番号 + {getMessage('stuff.planReqPopup.search.planReqNo')}
- + { + setSchPlanReqNo(e.target.value) + setPlanReqSearch({ ...planReqSearch, schPlanReqNo: e.target.value }) + }} + />
- 안건명 / 案件名 + {getMessage('stuff.planReqPopup.search.title')}
- + { + setSchTitle(e.target.value) + setPlanReqSearch({ ...planReqSearch, schTitle: e.target.value }) + }} + />
- 도도부현 / 都道府県 + {getMessage('stuff.planReqPopup.search.address1')}
- + { + setSchAddress(e.target.value) + setPlanReqSearch({ ...planReqSearch, schAddress: e.target.value }) + }} + />
- 판매대리점명 / 販売代理店名 + {getMessage('stuff.planReqPopup.search.saleStoreName')}
- + { + setSchSaleStoreName(e.target.value) + setPlanReqSearch({ ...planReqSearch, schSaleStoreName: e.target.value }) + }} + />
- 의뢰자명 / 依頼者名 + {getMessage('stuff.planReqPopup.search.planReqName')}
- + { + setSchPlanReqName(e.target.value) + setPlanReqSearch({ ...planReqSearch, schPlanReqName: e.target.value }) + }} + />
- 상태 / 状態 + {getMessage('stuff.planReqPopup.search.planStatName')}
- 기간검색 / 期間検索 + {getMessage('stuff.planReqPopup.search.period')}
- - + { + setSchDateGbn(e.target.value) + setPlanReqSearch({ ...planReqSearch, schDateGbn: e.target.value }) + }} + /> +
- - + { + setSchDateGbn(e.target.value) + setPlanReqSearch({ ...planReqSearch, schDateGbn: e.target.value }) + }} + /> +
-
-
- -
- ~ -
- -
+
+
+
+ +
+ ~ +
+
@@ -160,6 +277,16 @@ export default function PlanRequestPop(props) {
+
+
Plan List
+ +
+
+
+ +
diff --git a/src/components/management/popup/PlanRequestPopQGrid.jsx b/src/components/management/popup/PlanRequestPopQGrid.jsx new file mode 100644 index 00000000..8f29cdbe --- /dev/null +++ b/src/components/management/popup/PlanRequestPopQGrid.jsx @@ -0,0 +1,60 @@ +import React from 'react' +import { useCallback, useEffect, useMemo, useState } from 'react' +import { AgGridReact } from 'ag-grid-react' + +import 'ag-grid-community/styles/ag-grid.css' +import 'ag-grid-community/styles/ag-theme-quartz.css' + +export default function PlanRequestPopQGrid(props) { + const { gridData, gridColumns, isPageable = true } = props + + const [rowData, setRowData] = useState(null) + + const [gridApi, setGridApi] = useState(null) + + const [colDefs, setColDefs] = useState(gridColumns) + + const defaultColDef = useMemo(() => { + return { + flex: 1, + minWidth: 100, + sortable: false, + suppressMovable: false, + resizable: false, + suppressSizeToFit: false, + } + }, []) + + const rowBuffer = 100 + + useEffect(() => { + gridData ? setRowData(gridData) : '' + }, [gridData]) + + const rowSelection = useMemo(() => { + return { mode: 'singleRow', enableClickSelection: true } + }, []) + + const onGridReady = useCallback( + (params) => { + setGridApi(params.api) + gridData ? setRowData(gridData) : '' + }, + [gridData], + ) + + return ( +
+ +
+ ) +} diff --git a/src/locales/ja.json b/src/locales/ja.json index abdfc11c..f3e2c650 100644 --- a/src/locales/ja.json +++ b/src/locales/ja.json @@ -392,7 +392,7 @@ "stuff.addressPopup.btn2": "住所適用", "stuff.planReqPopup.title": "設計依頼のインポート", "stuff.detail.required": "必須入力項目", - "stuff.detail.designNo": "設計依頼No.", + "stuff.detail.planReqNo": "設計依頼No.", "stuff.detail.dispCompanyName": "担当者", "stuff.detail.objectStatusId": "物品区分/物件名", "stuff.detail.objectStatus0": "新築", @@ -417,6 +417,40 @@ "stuff.detail.conType0": "余剰", "stuff.detail.conType1": "全量", "stuff.detail.remarks": "メモ", + "stuff.planReqPopup.gridHeader.planStatName": "상태", + "stuff.planReqPopup.gridHeader.planReqNo": "설계의뢰 번호", + "stuff.planReqPopup.gridHeader.saleStoreId": "판매대리점ID", + "stuff.planReqPopup.gridHeader.saleStoreName": "판매대리점명", + "stuff.planReqPopup.gridHeader.title": "안건명", + "stuff.planReqPopup.gridHeader.address1": "도도부현", + "stuff.planReqPopup.gridHeader.houseCntName": "설치가옥수", + "stuff.planReqPopup.gridHeader.planReqName": "의뢰자명", + "stuff.planReqPopup.gridHeader.submitDt": "설계의뢰 제출일", + "stuff.planReqPopup.search.planReqNo": "設計依頼番号", + "stuff.planReqPopup.search.title": "案件名", + "stuff.planReqPopup.search.address1": "都道府県", + "stuff.planReqPopup.search.saleStoreName": "販売代理店名", + "stuff.planReqPopup.search.planReqName": "依頼者名", + "stuff.planReqPopup.search.planStatName": "状態", + "stuff.planReqPopup.search.period": "期間検索", + "stuff.planReqPopup.search.schDateGbnS": "提出日", + "stuff.planReqPopup.search.schDateGbnR": "受付日", + "stuff.planReqPopup.btn1": "閉じる", + "stuff.planReqPopup.btn2": "選択の適用", + "stuff.search.title": "物件状況", + "stuff.search.btn1": "物件登録", + "stuff.search.btn2": "照会", + "stuff.search.btn3": "初期化", + "stuff.search.schObjectNo": "品番", + "stuff.search.schSaleStoreName": "販売代理店名", + "stuff.search.schAddress": "商品アドレス", + "stuff.search.schObjectName": "商品名", + "stuff.search.schDispCompanyName": "見積もり", + "stuff.search.schSelSaleStoreId": "販売代理店の選択", + "stuff.search.schReceiveUser": "担当者", + "stuff.search.period": "期間検索", + "stuff.search.schDateTypeU": "更新日", + "stuff.search.schDateTypeR": "登録日", "length": "長さ", "height": "高さ", "output": "出力", diff --git a/src/locales/ko.json b/src/locales/ko.json index 3e37918b..00edeaaf 100644 --- a/src/locales/ko.json +++ b/src/locales/ko.json @@ -396,7 +396,7 @@ "stuff.addressPopup.btn2": "주소적용", "stuff.planReqPopup.title": "설계의뢰 불러오기", "stuff.detail.required": "필수 입력항목", - "stuff.detail.designNo": "설계의뢰No.", + "stuff.detail.planReqNo": "설계의뢰No.", "stuff.detail.dispCompanyName": "담당자", "stuff.detail.objectStatusId": "물건구분/물건명", "stuff.detail.objectStatus0": "신축", @@ -420,7 +420,40 @@ "stuff.detail.conType": "계약조건", "stuff.detail.conType0": "잉여", "stuff.detail.conType1": "전량", - "stuff.detail.remarks": "메모", + "stuff.planReqPopup.gridHeader.planStatName": "상태", + "stuff.planReqPopup.gridHeader.planReqNo": "설계의뢰 번호", + "stuff.planReqPopup.gridHeader.saleStoreId": "판매대리점ID", + "stuff.planReqPopup.gridHeader.saleStoreName": "판매대리점명", + "stuff.planReqPopup.gridHeader.title": "안건명", + "stuff.planReqPopup.gridHeader.address1": "도도부현", + "stuff.planReqPopup.gridHeader.houseCntName": "설치가옥수", + "stuff.planReqPopup.gridHeader.planReqName": "의뢰자명", + "stuff.planReqPopup.gridHeader.submitDt": "설계의뢰 제출일", + "stuff.planReqPopup.search.planReqNo": "설계의뢰번호", + "stuff.planReqPopup.search.title": "안건명", + "stuff.planReqPopup.search.address1": "도도부현", + "stuff.planReqPopup.search.saleStoreName": "판매대리점명", + "stuff.planReqPopup.search.planReqName": "의뢰자명", + "stuff.planReqPopup.search.planStatName": "상태", + "stuff.planReqPopup.search.period": "기간검색", + "stuff.planReqPopup.search.schDateGbnS": "제출일", + "stuff.planReqPopup.search.schDateGbnR": "접수일", + "stuff.planReqPopup.btn1": "닫기", + "stuff.planReqPopup.btn2": "선택적용", + "stuff.search.title": "물건현황", + "stuff.search.btn1": "신규등록", + "stuff.search.btn2": "조회", + "stuff.search.btn3": "초기화", + "stuff.search.schObjectNo": "물건번호", + "stuff.search.schSaleStoreName": "판매대리점명", + "stuff.search.schAddress": "물건주소", + "stuff.search.schObjectName": "물건명", + "stuff.search.schDispCompanyName": "견적처", + "stuff.search.schSelSaleStoreId": "판매대리점 선택", + "stuff.search.schReceiveUser": "담당자", + "stuff.search.period": "기간검색", + "stuff.search.schDateTypeU": "갱신일", + "stuff.search.schDateTypeR": "등록일", "length": "길이", "height": "높이", "output": "출력", diff --git a/src/store/planReqAtom.js b/src/store/planReqAtom.js new file mode 100644 index 00000000..717d506e --- /dev/null +++ b/src/store/planReqAtom.js @@ -0,0 +1,22 @@ +import { atom } from 'recoil' +import dayjs from 'dayjs' +import { v1 } from 'uuid' +export const planReqSearchState = atom({ + key: `planReqSearchState/${v1()}`, + default: { + saleStoreId: '', //판매점ID 세션 + saleStoreLevel: '', //판매점레벨 세션 + schPlanReqNo: '', //설계의뢰 번호 + schTitle: '', //안건명 + schAddress: '', //도도부현 + schSaleStoreName: '', //판매대리점명 + schPlanReqName: '', //의뢰자명 + schPlanStatCd: '', //상태코드 + schDateGbn: 'S', //기간구분코드(S/R) + schStartDt: dayjs(new Date()).add(-3, 'month').format('YYYY-MM-DD'), //시작일 + schEndDt: dayjs(new Date()).format('YYYY-MM-DD'), //종료일 + startRow: 1, + endRow: 100, + }, + dangerouslyAllowMutability: true, +}) From 0b6b268b6623fea471e733e61a2f09206688a8e1 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Tue, 15 Oct 2024 16:31:15 +0900 Subject: [PATCH 15/68] =?UTF-8?q?=EB=B3=B4=EC=A1=B0=EC=84=A0=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1=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/useAuxiliaryDrawing.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/hooks/roofcover/useAuxiliaryDrawing.js b/src/hooks/roofcover/useAuxiliaryDrawing.js index 2e2e0a1b..5cae2d01 100644 --- a/src/hooks/roofcover/useAuxiliaryDrawing.js +++ b/src/hooks/roofcover/useAuxiliaryDrawing.js @@ -85,6 +85,7 @@ export function useAuxiliaryDrawing(setShowAuxiliaryModal) { // 지붕의 각 꼭지점을 흡착점으로 설정 const roofsPoints = roofs.map((roof) => roof.points).flat() + console.log(roofsPoints) roofAdsorptionPoints.current = [...roofsPoints] addCanvasMouseEventListener('mouse:move', mouseMove) @@ -456,6 +457,7 @@ export function useAuxiliaryDrawing(setShowAuxiliaryModal) { } const mouseDown = (e) => { + addCanvasMouseEventListener('mouse:move', mouseMove) const pointer = getIntersectMousePoint(e) mousePointerArr.current.push(pointer) @@ -487,7 +489,6 @@ export function useAuxiliaryDrawing(setShowAuxiliaryModal) { auxiliaryLines.forEach((line1) => { auxiliaryLines.forEach((line2) => { - const lines = [line1, line2] if (line1 === line2) { return } @@ -619,7 +620,11 @@ export function useAuxiliaryDrawing(setShowAuxiliaryModal) { } const roofBases = canvas.getObjects().find((obj) => obj.name === 'roofBase') - roofBases.innerLines = [...lineHistory.current] + const innerLines = [...lineHistory.current] + + console.log('innerLines', innerLines) + + roofBases.innerLines = [...innerLines] setShowAuxiliaryModal(close) } From b0a0d2a9a55390c80a486ea2b91fcba18aa92149 Mon Sep 17 00:00:00 2001 From: basssy Date: Tue, 15 Oct 2024 16:39:57 +0900 Subject: [PATCH 16/68] =?UTF-8?q?=EB=AC=BC=EA=B1=B4=ED=98=84=ED=99=A9=20?= =?UTF-8?q?=EC=84=A4=EA=B3=84=EC=9D=98=EB=A2=B0=20=ED=8C=9D=EC=97=85=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=EC=B4=88=EA=B8=B0=ED=99=94=20=EB=B2=84?= =?UTF-8?q?=ED=8A=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/management/popup/PlanRequestPop.jsx | 11 +++++++++-- src/locales/ja.json | 7 +++++-- src/locales/ko.json | 8 ++++++-- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/components/management/popup/PlanRequestPop.jsx b/src/components/management/popup/PlanRequestPop.jsx index 66bea1d4..aa6b5492 100644 --- a/src/components/management/popup/PlanRequestPop.jsx +++ b/src/components/management/popup/PlanRequestPop.jsx @@ -130,6 +130,13 @@ export default function PlanRequestPop(props) {
+
+

{getMessage('stuff.planReqPopup.popTitle')}

+
+ + +
+
@@ -284,9 +291,9 @@ export default function PlanRequestPop(props) {
- +
diff --git a/src/locales/ja.json b/src/locales/ja.json index f3e2c650..29ba0725 100644 --- a/src/locales/ja.json +++ b/src/locales/ja.json @@ -417,6 +417,11 @@ "stuff.detail.conType0": "余剰", "stuff.detail.conType1": "全量", "stuff.detail.remarks": "メモ", + "stuff.planReqPopup.popTitle": "設計依頼検索", + "stuff.planReqPopup.btn1": "検索", + "stuff.planReqPopup.btn2": "初期化", + "stuff.planReqPopup.btn3": "閉じる", + "stuff.planReqPopup.btn4": "選択の適用", "stuff.planReqPopup.gridHeader.planStatName": "상태", "stuff.planReqPopup.gridHeader.planReqNo": "설계의뢰 번호", "stuff.planReqPopup.gridHeader.saleStoreId": "판매대리점ID", @@ -435,8 +440,6 @@ "stuff.planReqPopup.search.period": "期間検索", "stuff.planReqPopup.search.schDateGbnS": "提出日", "stuff.planReqPopup.search.schDateGbnR": "受付日", - "stuff.planReqPopup.btn1": "閉じる", - "stuff.planReqPopup.btn2": "選択の適用", "stuff.search.title": "物件状況", "stuff.search.btn1": "物件登録", "stuff.search.btn2": "照会", diff --git a/src/locales/ko.json b/src/locales/ko.json index 00edeaaf..557c2a8c 100644 --- a/src/locales/ko.json +++ b/src/locales/ko.json @@ -420,6 +420,12 @@ "stuff.detail.conType": "계약조건", "stuff.detail.conType0": "잉여", "stuff.detail.conType1": "전량", + "stuff.detail.remarks": "메모", + "stuff.planReqPopup.popTitle": "설계 요청 검색", + "stuff.planReqPopup.btn1": "검색", + "stuff.planReqPopup.btn2": "초기화", + "stuff.planReqPopup.btn3": "닫기", + "stuff.planReqPopup.btn4": "선택적용", "stuff.planReqPopup.gridHeader.planStatName": "상태", "stuff.planReqPopup.gridHeader.planReqNo": "설계의뢰 번호", "stuff.planReqPopup.gridHeader.saleStoreId": "판매대리점ID", @@ -438,8 +444,6 @@ "stuff.planReqPopup.search.period": "기간검색", "stuff.planReqPopup.search.schDateGbnS": "제출일", "stuff.planReqPopup.search.schDateGbnR": "접수일", - "stuff.planReqPopup.btn1": "닫기", - "stuff.planReqPopup.btn2": "선택적용", "stuff.search.title": "물건현황", "stuff.search.btn1": "신규등록", "stuff.search.btn2": "조회", From 41c8fea5946032d03047bfd923668cedc3a9125a Mon Sep 17 00:00:00 2001 From: leeyongjae Date: Tue, 15 Oct 2024 17:12:49 +0900 Subject: [PATCH 17/68] =?UTF-8?q?[QCAST]=20=EB=A1=9C=EA=B7=B8=EC=9D=B8,=20?= =?UTF-8?q?=EA=B0=80=EC=9E=85=EC=8B=A0=EC=B2=AD=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?=EB=B0=8F=20=EB=82=B4=EC=A0=95=EB=B3=B4=ED=8C=9D=EC=97=85,=20?= =?UTF-8?q?=EC=BB=A4=EB=AE=A4=EB=8B=88=ED=8B=B0(=EA=B3=B5=EC=A7=80?= =?UTF-8?q?=EC=82=AC=ED=95=AD,=20FAQ,=20=EC=9E=90=EB=A3=8C=EB=8B=A4?= =?UTF-8?q?=EC=9A=B4=EB=A1=9C=EB=93=9C)=20=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 1 + src/app/community/archive/page.jsx | 5 +- src/app/community/faq/page.jsx | 6 +- src/app/community/notice/page.jsx | 6 +- src/app/join/complete/page.jsx | 16 +- src/app/layout.js | 21 +- src/app/login/page.jsx | 3 +- src/components/auth/Join.jsx | 526 +++++++++--------- src/components/auth/JoinComplete.jsx | 44 ++ src/components/auth/Login.jsx | 404 ++++++++------ src/components/auth/NewLogin.jsx | 244 -------- src/components/community/Archive.jsx | 66 ++- src/components/community/ArchiveTable.jsx | 103 ++++ src/components/community/Faq.jsx | 75 ++- src/components/community/Notice.jsx | 67 ++- src/components/community/Pagination.jsx | 78 +++ src/components/community/Search.jsx | 117 ++++ src/components/community/Table.jsx | 110 ++++ .../community/modal/BoardDetailModal.jsx | 77 +++ src/components/header/Header.jsx | 12 +- src/components/myInfo/UserInfoModal.jsx | 250 +++++++++ src/locales/ja.json | 121 ++-- src/locales/ko.json | 59 +- src/store/boardAtom.js | 14 + src/util/board-utils.js | 46 ++ yarn.lock | 5 + 26 files changed, 1683 insertions(+), 793 deletions(-) create mode 100644 src/components/auth/JoinComplete.jsx delete mode 100644 src/components/auth/NewLogin.jsx create mode 100644 src/components/community/ArchiveTable.jsx create mode 100644 src/components/community/Pagination.jsx create mode 100644 src/components/community/Search.jsx create mode 100644 src/components/community/Table.jsx create mode 100644 src/components/community/modal/BoardDetailModal.jsx create mode 100644 src/components/myInfo/UserInfoModal.jsx create mode 100644 src/store/boardAtom.js create mode 100644 src/util/board-utils.js diff --git a/package.json b/package.json index 260bfe8e..b44c2dda 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "framer-motion": "^11.2.13", "fs": "^0.0.1-security", "iron-session": "^8.0.2", + "js-cookie": "^3.0.5", "mathjs": "^13.0.2", "mssql": "^11.0.1", "next": "14.2.3", diff --git a/src/app/community/archive/page.jsx b/src/app/community/archive/page.jsx index 6917f228..308e02f3 100644 --- a/src/app/community/archive/page.jsx +++ b/src/app/community/archive/page.jsx @@ -7,10 +7,7 @@ export default async function CommunityArchivePage() { return ( <> - -
- -
+ ) } diff --git a/src/app/community/faq/page.jsx b/src/app/community/faq/page.jsx index 2b9d5452..054f9007 100644 --- a/src/app/community/faq/page.jsx +++ b/src/app/community/faq/page.jsx @@ -1,4 +1,3 @@ -import Hero from '@/components/Hero' import Faq from '@/components/community/Faq' import { initCheck } from '@/util/session-util' @@ -7,10 +6,7 @@ export default async function CommunityFaqPage() { return ( <> - -
- -
+ ) } diff --git a/src/app/community/notice/page.jsx b/src/app/community/notice/page.jsx index d2157b20..a3453e64 100644 --- a/src/app/community/notice/page.jsx +++ b/src/app/community/notice/page.jsx @@ -1,4 +1,3 @@ -import Hero from '@/components/Hero' import Notice from '@/components/community/Notice' import { initCheck } from '@/util/session-util' @@ -7,10 +6,7 @@ export default async function CommunityNoticePage() { return ( <> - -
- -
+ ) } diff --git a/src/app/join/complete/page.jsx b/src/app/join/complete/page.jsx index 3f9fc462..3f134e58 100644 --- a/src/app/join/complete/page.jsx +++ b/src/app/join/complete/page.jsx @@ -1,19 +1,9 @@ -'use client' - -import { useMessage } from '@/hooks/useMessage' - -export default function CompletePage() { - const { getMessage } = useMessage() +import JoinComplete from '@/components/auth/JoinComplete' +export default function JoinCompletePage() { return ( <> -
-

{getMessage('join.complete.title')}

-
{getMessage('join.complete.contents')}
-
- {getMessage('join.complete.email_comment')} : {getMessage('join.complete.email')} -
-
+ ) } diff --git a/src/app/layout.js b/src/app/layout.js index 6c04bbec..28626654 100644 --- a/src/app/layout.js +++ b/src/app/layout.js @@ -4,7 +4,6 @@ import { headers } from 'next/headers' import { redirect } from 'next/navigation' import { getSession } from '@/lib/authActions' import RecoilRootWrapper from './RecoilWrapper' -import UIProvider from './UIProvider' import { ToastContainer } from 'react-toastify' @@ -14,6 +13,7 @@ import QModal from '@/components/common/modal/QModal' import './globals.css' import '../styles/style.scss' +import '../styles/contents.scss' import Dimmed from '@/components/ui/Dimmed' // const inter = Inter({ subsets: ['latin'] }) @@ -61,18 +61,21 @@ export default async function RootLayout({ children }) { - {headerPathname !== '/login' ? ( + {headerPathname === '/login' || headerPathname === '/join' ? ( + {children} + ) : (
- -
- - {children} +
+ + {children} +
+
+
+ COPYRIGHT©2024 Hanwha Japan All Rights Reserved.
- +
- ) : ( - {children} )} diff --git a/src/app/login/page.jsx b/src/app/login/page.jsx index 3ff0edd7..0686da2e 100644 --- a/src/app/login/page.jsx +++ b/src/app/login/page.jsx @@ -1,10 +1,9 @@ import Login from '@/components/auth/Login' -import NewLogin from '@/components/auth/NewLogin' export default function LoginPage() { return ( <> - + ) } diff --git a/src/components/auth/Join.jsx b/src/components/auth/Join.jsx index ae100e68..5ae0b5ae 100644 --- a/src/components/auth/Join.jsx +++ b/src/components/auth/Join.jsx @@ -1,30 +1,27 @@ 'use client' import { useAxios } from '@/hooks/useAxios' -import { redirect } from 'next/navigation' +import { useRouter } from 'next/navigation' import { useMessage } from '@/hooks/useMessage' +import Cookies from 'js-cookie' export default function Join() { const { getMessage } = useMessage() const { post } = useAxios() + const router = useRouter() + // 가입 신청 const joinProcess = async (formData) => { + formData.preventDefault() + const param = { - langCd: 'JA', - lastEditUser: formData.get('userId'), storeQcastNm: formData.get('storeQcastNm'), storeQcastNmKana: formData.get('storeQcastNmKana'), postCd: formData.get('postCd'), addr: formData.get('addr'), telNo: formData.get('telNo'), fax: formData.get('fax'), - payTermsCd: 'JB02', - kamId: 'E1101011', - qtCompNm: formData.get('qtCompNm'), - qtPostCd: formData.get('qtPostCd'), - qtAddr: formData.get('qtAddr'), - qtTelNo: formData.get('qtTelNo'), - qtFax: formData.get('qtFax'), + bizNo: formData.get('bizNo'), userInfo: { userId: formData.get('userId'), userNm: formData.get('userNm'), @@ -39,7 +36,8 @@ export default function Join() { await post({ url: '/api/login/v1.0/user/join', data: param }).then((res) => { if (res) { if (res.result.resultCode == 'S') { - redirect('/join/complete') + Cookies.set('joinEmail', formData.get('email'), { expires: 1 }) + router.push('/join/complete') } else { alert(res.result.resultMsg) } @@ -48,275 +46,249 @@ export default function Join() { } return ( -
-

{getMessage('join.title')}

- -
-
- ● {getMessage('join.sub1.title')} (*{getMessage('common.require')}) {getMessage('join.sub1.comment')} +
+
+ +
{getMessage('join.title')}
+
+
+
+

+ {getMessage('join.sub1.title')} (*{getMessage('common.require')}) +

+ {getMessage('join.sub1.comment')} +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ {getMessage('join.sub1.storeQcastNm')} * + +
+ +
+
+ {getMessage('join.sub1.storeQcastNmKana')} * + +
+ +
+
+ {getMessage('join.sub1.postCd')}/{getMessage('join.sub1.addr')} * + +
+
+ +
+
+ +
+
+
+ {getMessage('join.sub1.telNo')} * + +
+ +
+
+ {getMessage('join.sub1.fax')} * + +
+ +
+
{getMessage('join.sub1.bizNo')} +
+ +
+
+
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
{getMessage('join.sub1.storeQcastNm')} * - -
{getMessage('join.sub1.storeQcastNmKana')} * - -
- {getMessage('join.sub1.postCd')}/{getMessage('join.sub1.addr')} * - - - -
{getMessage('join.sub1.telNo')} * - -
{getMessage('join.sub1.fax')} * - -
- -
- ● {getMessage('join.sub2.title')} (*{getMessage('common.require')}) +
+
+
+

+ {getMessage('join.sub2.title')} (*{getMessage('common.require')}) +

+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ {getMessage('join.sub2.userNm')} * + +
+ +
+
{getMessage('join.sub2.userNmKana')} +
+ +
+
+ {getMessage('join.sub2.userId')} * + +
+ +
+
+ {getMessage('join.sub2.email')} * + +
+ +
+
+ {getMessage('join.sub2.telNo')} * + +
+ +
+
+ {getMessage('join.sub2.fax')} * + +
+ +
+
{getMessage('join.sub2.category')} +
+ +
+
+
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
{getMessage('join.sub2.userNm')} * - -
{getMessage('join.sub2.userNmKana')} * - -
{getMessage('join.sub2.userId')} * - -
{getMessage('join.sub2.email')} * - -
{getMessage('join.sub2.telNo')} * - -
{getMessage('join.sub2.fax')} * - -
{getMessage('join.sub2.category')} - -
- -
- ● {getMessage('join.sub3.title')} (*{getMessage('common.require')}) +
+ +
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
{getMessage('join.sub3.qtCompNm')} - -
- {getMessage('join.sub3.qtPostCd')}/{getMessage('join.sub3.qtAddr')} - - - -
{getMessage('join.sub3.qtEmail')} - -
{getMessage('join.sub3.qtTelNo')} - -
{getMessage('join.sub3.qtFax')} - -
-
-
- -
- + +
) } diff --git a/src/components/auth/JoinComplete.jsx b/src/components/auth/JoinComplete.jsx new file mode 100644 index 00000000..c876f20a --- /dev/null +++ b/src/components/auth/JoinComplete.jsx @@ -0,0 +1,44 @@ +'use client' + +import { useMessage } from '@/hooks/useMessage' +import { useRouter } from 'next/navigation' +import { useState } from 'react' +import Cookies from 'js-cookie' + +export default function JoinComplete() { + const router = useRouter() + + const { getMessage } = useMessage() + const [emailText, setEmailText] = useState(Cookies.get('joinEmail')) + + return ( + <> +
+
+
+
+
{getMessage('join.complete.title')}
+
{getMessage('join.complete.contents')}
+
+
+ {getMessage('join.complete.email_comment')}: {emailText} +
+
+
+ +
+
+
+
+
+ + ) +} diff --git a/src/components/auth/Login.jsx b/src/components/auth/Login.jsx index ef9919c7..f6c810fb 100644 --- a/src/components/auth/Login.jsx +++ b/src/components/auth/Login.jsx @@ -1,62 +1,45 @@ 'use client' -import { useState } from 'react' +import { useState, useEffect } from 'react' +import Image from 'next/image' +import Link from 'next/link' +import { useRecoilState } from 'recoil' import { useAxios } from '@/hooks/useAxios' import { setSession } from '@/lib/authActions' -import { redirect } from 'next/navigation' import { useMessage } from '@/hooks/useMessage' - -import { Button, Switch } from '@nextui-org/react' -import { useRecoilState } from 'recoil' import { globalLocaleStore } from '@/store/localeAtom' -import { modalContent, modalState } from '@/store/modalAtom' import { sessionStore } from '@/store/commonAtom' +import { useRouter } from 'next/navigation' + +import Cookies from 'js-cookie' export default function Login() { - const { patch } = useAxios() + const [userId, setUserId] = useState('') + const [checkId, setCheckId] = useState('') + const [checkEmail, setCheckEmail] = useState('') + + useEffect(() => { + if (Cookies.get('chkLoginId')) { + setUserId(Cookies.get('chkLoginId')) + } + }, []) + + const [chkLoginId, setChkLoginId] = useState(Cookies.get('chkLoginId') ? true : false) + const [passwordVisible, setPasswordVisible] = useState(false) + const router = useRouter() const { getMessage } = useMessage() const [globalLocaleState, setGlbalLocaleState] = useRecoilState(globalLocaleStore) const [sessionState, setSessionState] = useRecoilState(sessionStore) - const [isSelected, setIsSelected] = useState(globalLocaleState === 'ko' ? true : false) - const handleSelected = () => { - if (isSelected) { - setGlbalLocaleState('ja') - } else { - setGlbalLocaleState('ko') - } + const [passwordReset, setPasswordReset] = useState(1) - setIsSelected(!isSelected) - } + const { post, patch } = useAxios(globalLocaleState) // login process - const loginProcess = async (formData) => { - const param = { - // langCd: currentLocale - langCd: globalLocaleState, - lastEditUser: formData.get('id'), - loginId: formData.get('id'), - pwd: formData.get('password'), - } - - // await post({ url: '/api/login/v1.0/login', data: param }).then((res) => { - // if (res) { - // if (res.result.resultCode == 'S') { - // // console.log('res.data', res.data) - // // 비밀번호 초기화가 필요한 경우 - // // if (res.data.pwdInitYn != 'Y') { - // // alert('비밀번호 초기화가 필요한 경우') - // // } else { - // setSession(res.data) - // redirect('/') - // // } - // } else { - // alert(res.result.resultMsg) - // } - // } - // }) - + const loginProcess = async (e) => { + e.preventDefault() + const formData = new FormData(e.target) // 임시 로그인 처리 setSession({ userId: 'NEW016610', @@ -71,7 +54,7 @@ export default function Login() { telNo: '336610', fax: null, email: 't10t@naver.com', - pwdInitYn: 'N', + pwdInitYn: 'Y', }) setSessionState({ @@ -87,155 +70,226 @@ export default function Login() { telNo: '336610', fax: null, email: 't10t@naver.com', - pwdInitYn: 'N', + pwdInitYn: 'Y', }) - redirect('/') + // ID SAVE 체크되어 있는 경우, 쿠키 저장 + if (chkLoginId) { + Cookies.set('chkLoginId', formData.get('id'), { expires: 7 }) + } else { + Cookies.remove('chkLoginId') + } + + router.push('/') // 임시 로그인 처리 끝 + + // 로그인 처리 시작 - ** 상단 임시 로그인 추후 삭제 필요 ** + // const param = { + // loginId: formData.get('id'), + // pwd: formData.get('password'), + // } + + // const res = await post({ url: '/api/login/v1.0/login', data: param }) + + // if (res) { + // if (res.result.resultCode == 'S') { + // setSession(res.data) + // setSessionState(res.data) + + // // ID SAVE 체크되어 있는 경우, 쿠키 저장 + // if (chkLoginId) { + // Cookies.set('chkLoginId', formData.get('id'), { expires: 7 }) + // } else { + // Cookies.remove('chkLoginId') + // } + + // router.push('/') + // } else { + // alert(res.result.resultMsg) + // } + // } } // 비밀번호 초기화 관련 - const [open, setOpen] = useRecoilState(modalState) - const [contents, setContent] = useRecoilState(modalContent) - - const initPasswordProcess = async (formData) => { + const initPasswordProcess = async () => { const param = { - langCd: currentLocale, - lastEditUser: formData.get('checkId'), - loginId: formData.get('checkId'), - email: formData.get('checkEmail'), + loginId: checkId, + email: checkEmail, } - await patch({ url: '/api/login/v1.0/user/init-password', data: param }).then((res) => { - if (res) { - if (res.result.resultCode == 'S') { - alert(getMessage('login.init_password.complete_message')) - redirect('/login') - } else { - alert(res.result.resultMsg) - } - } + const res = await patch({ + url: '/api/login/v1.0/user/init-password', + data: param, }) + + if (res) { + if (res.result.resultCode == 'S') { + alert(getMessage('login.init_password.complete_message')) + setCheckId('') + setCheckEmail('') + setPasswordReset(1) + } else { + alert(res.result.resultMsg) + } + } } - const initPasswordContent = ( -
-
-

{getMessage('login.init_password.title')}

-

{getMessage('login.init_password.sub_title')}

-
- -
- -
-
- -
-
- -
-
- -
-
-

- -

-
-
- ) - return ( -
-
-
-

{getMessage('site.name')}

-

{getMessage('site.sub_name')}

-
+
+
+ + react + -
-
-
- -
- + {passwordReset === 1 && ( + <> +
+ +
+ {getMessage('site.name')} + {getMessage('site.sub_name')} +
+
+
+ { + setUserId(e.target.value) + }} + /> + +
+
+ { + setPasswordVisible(passwordVisible) + }} + /> + +
+
+ { + setChkLoginId(e.target.checked) + }} + /> + +
+
+ +
+
+ +
+
+ +
+
+ + {getMessage('login.guide.text')} +
+ {getMessage('login.guide.sub1')} {getMessage('login.guide.join.btn')} + {getMessage('login.guide.sub2')} +
+ + )} + {passwordReset === 2 && ( + <> +
+
+ {getMessage('login.init_password.title')} + {getMessage('login.init_password.sub_title')} +
+
+
+ { + setCheckId(e.target.value) + }} + /> + +
+
+ { + setCheckEmail(e.target.value) + }} + placeholder={getMessage('login.init_password.email.placeholder')} + /> + +
+
+ + +
- -
-
- -
-
- -
-
- -
- -
- - -

- -

- -
- - {isSelected ? 'Current Locale: KO' : 'Current Locale: JA'} - -
-
+ + )}
+
COPYRIGHT©2024 Hanwha Japan All Rights Reserved.
) } diff --git a/src/components/auth/NewLogin.jsx b/src/components/auth/NewLogin.jsx deleted file mode 100644 index 61c02abf..00000000 --- a/src/components/auth/NewLogin.jsx +++ /dev/null @@ -1,244 +0,0 @@ -'use client' - -import { useState, useRef, useEffect } from 'react' -import Image from 'next/image' -import Link from 'next/link' -import { redirect } from 'next/navigation' -import { useRecoilState } from 'recoil' -import { useAxios } from '@/hooks/useAxios' -import { setSession } from '@/lib/authActions' -import { useMessage } from '@/hooks/useMessage' -import { globalLocaleStore } from '@/store/localeAtom' -import { sessionStore } from '@/store/commonAtom' -import { modalContent, modalState } from '@/store/modalAtom' -import '@/styles/style.scss' -import { useRouter } from 'next/navigation' - -export default function NewLogin() { - const [passwordVisible, setPasswordVisible] = useState(false) - const passwordRef = useRef(null) - const router = useRouter() - - useEffect(() => { - setOpen(false) - }, []) - - useEffect(() => { - if (passwordVisible) { - passwordRef.current.type = 'text' - } else { - passwordRef.current.type = 'password' - } - }, [passwordVisible]) - - const { patch } = useAxios() - - const { getMessage } = useMessage() - const [globalLocaleState, setGlbalLocaleState] = useRecoilState(globalLocaleStore) - const [sessionState, setSessionState] = useRecoilState(sessionStore) - const [isSelected, setIsSelected] = useState(globalLocaleState === 'ko' ? true : false) - - const handleSelected = () => { - if (isSelected) { - setGlbalLocaleState('ja') - } else { - setGlbalLocaleState('ko') - } - - setIsSelected(!isSelected) - } - - // login process - const loginProcess = async (formData) => { - const param = { - // langCd: currentLocale - langCd: globalLocaleState, - lastEditUser: formData.get('id'), - loginId: formData.get('id'), - pwd: formData.get('password'), - } - - // await post({ url: '/api/login/v1.0/login', data: param }).then((res) => { - // if (res) { - // if (res.result.resultCode == 'S') { - // // console.log('res.data', res.data) - // // 비밀번호 초기화가 필요한 경우 - // // if (res.data.pwdInitYn != 'Y') { - // // alert('비밀번호 초기화가 필요한 경우') - // // } else { - // setSession(res.data) - // redirect('/') - // // } - // } else { - // alert(res.result.resultMsg) - // } - // } - // }) - - // 임시 로그인 처리 - setSession({ - userId: 'NEW016610', - saleStoreId: null, - name: null, - mail: null, - tel: null, - storeId: 'TEMP02', - userNm: 'ㅇㅇ6610', - userNmKana: '신규사용자 16610', - category: '인상6610', - telNo: '336610', - fax: null, - email: 't10t@naver.com', - pwdInitYn: 'Y', - }) - - setSessionState({ - userId: 'NEW016610', - saleStoreId: null, - name: null, - mail: null, - tel: null, - storeId: 'TEMP02', - userNm: 'ㅇㅇ6610', - userNmKana: '신규사용자 16610', - category: '인상6610', - telNo: '336610', - fax: null, - email: 't10t@naver.com', - pwdInitYn: 'Y', - }) - - // redirect('/') - router.push('/') - // 임시 로그인 처리 끝 - } - - // 비밀번호 초기화 관련 - const [open, setOpen] = useRecoilState(modalState) - const [contents, setContent] = useRecoilState(modalContent) - - const initPasswordProcess = async (formData) => { - const param = { - langCd: currentLocale, - lastEditUser: formData.get('checkId'), - loginId: formData.get('checkId'), - email: formData.get('checkEmail'), - } - - await patch({ url: '/api/login/v1.0/user/init-password', data: param }).then((res) => { - if (res) { - if (res.result.resultCode == 'S') { - alert(getMessage('login.init_password.complete_message')) - redirect('/login') - } else { - alert(res.result.resultMsg) - } - } - }) - } - - const initPasswordContent = ( -
-
-

{getMessage('login.init_password.title')}

-

{getMessage('login.init_password.sub_title')}

-
- -
- -
-
- -
-
- -
-
- -
-
-

- -

-
-
- ) - - return ( -
-
- - react - -
-
-
- Q.CAST III - 太陽光発電システム図面管理サイト -
-
-
- - -
-
- - -
-
- - -
-
- -
-
- パスワードの初期化 -
-
-
-
-
- 当サイトをご利用の際は、事前申請が必要です。 -
- IDがない方は ID申請 クリックしてください。 -
-
-
COPYRIGHT©2024 Hanwha Japan All Rights Reserved.
-
- ) -} diff --git a/src/components/community/Archive.jsx b/src/components/community/Archive.jsx index dcfc737f..768bc500 100644 --- a/src/components/community/Archive.jsx +++ b/src/components/community/Archive.jsx @@ -1,7 +1,71 @@ +'use client' + +import Link from 'next/link' +import Image from 'next/image' + +import Search from '@/components/community/Search' +import ArchiveTable from '@/components/community/ArchiveTable' + +import { useEffect, useState } from 'react' +import { useResetRecoilState } from 'recoil' +import { useMessage } from '@/hooks/useMessage' + +import { searchState } from '@/store/boardAtom' + export default function Archive() { + const { getMessage } = useMessage() + const resetSearch = useResetRecoilState(searchState) + const [isInitialized, setIsInitialized] = useState(false) + + useEffect(() => { + resetSearch() + setIsInitialized(true) + }, []) + + if (!isInitialized) { + return null + } + + const boardType = { + boardTitle: getMessage('board.archive.title'), + subTitle: getMessage('board.archive.sub.title'), + clsCode: 'DOWN', + } + return ( <> -

Community Archive

+
+
+
    +
  • + + {getMessage('board.archive.title')} + +
  • +
+
    +
  • + + react + +
  • +
  • + {getMessage('header.menus.community')} +
  • +
  • + {getMessage('board.archive.title')} +
  • +
+
+
+
+
+
+ + +
+
+
) } diff --git a/src/components/community/ArchiveTable.jsx b/src/components/community/ArchiveTable.jsx new file mode 100644 index 00000000..318badef --- /dev/null +++ b/src/components/community/ArchiveTable.jsx @@ -0,0 +1,103 @@ +'use client' + +import { useEffect, useState } from 'react' +import { useRecoilState } from 'recoil' +import { useAxios } from '@/hooks/useAxios' + +import { searchState } from '@/store/boardAtom' +import { useMessage } from '@/hooks/useMessage' + +import { handleFileDown } from '@/util/board-utils' + +export default function ArchiveTable({ clsCode }) { + const { getMessage } = useMessage() + + // api 조회 관련 + const { get } = useAxios() + const [search, setSearch] = useRecoilState(searchState) + const [boardList, setBoardList] = useState([]) + + // 목록 조회 + useEffect(() => { + async function fetchData() { + const url = `${process.env.NEXT_PUBLIC_API_SERVER_PATH}/api/board/list` + const params = new URLSearchParams({ + schNoticeTpCd: 'QC', + schNoticeClsCd: clsCode, + schTitle: search.searchValue ? search.searchValue : '', + }) + const apiUrl = `${url}?${params.toString()}` + + const resultData = await get({ url: apiUrl }) + + if (resultData) { + if (resultData.result.code === 200) { + setBoardList(resultData.data) + if (resultData.data.length > 0) { + setSearch({ ...search, totalCount: resultData.data[0].totCnt }) + } else { + setSearch({ ...search, totalCount: 0 }) + } + } else { + alert(resultData.result.message) + } + } + } + + fetchData() + }, [search.searchValue]) + + // 상세 파일 목록 조회 + const handleDetailFileListDown = async (noticeNo) => { + const url = `${process.env.NEXT_PUBLIC_API_SERVER_PATH}/api/board/detail` + const params = new URLSearchParams({ + noticeNo: noticeNo, + }) + const apiUrl = `${url}?${params.toString()}` + + const resultData = await get({ url: apiUrl }) + + if (resultData) { + if (resultData.result.code === 200) { + const boardDetailFileList = resultData.data.listFile + + if (boardDetailFileList && Array.isArray(boardDetailFileList)) { + boardDetailFileList.forEach((boardFile) => { + handleFileDown(boardFile) + }) + } + } else { + alert(resultData.result.message) + } + } + } + + return ( + <> +
+ {boardList?.map((board) => ( +
+
+
+ {/* 번호 */} + {board.rowNumber} +
+
+ {/* 제목 */} + {board.title} +
+
+ {/* 등록일 */} + {getMessage('board.sub.updDt')} : {board.uptDt ? board.uptDt : board.regDt} +
+
+
+ {/* 첨부파일 */} + +
+
+ ))} +
+ + ) +} diff --git a/src/components/community/Faq.jsx b/src/components/community/Faq.jsx index 722741b3..17254f5c 100644 --- a/src/components/community/Faq.jsx +++ b/src/components/community/Faq.jsx @@ -1,7 +1,80 @@ +'use client' + +import Link from 'next/link' +import Image from 'next/image' + +import Search from '@/components/community/Search' +import Pagination from '@/components/community/Pagination' +import Table from '@/components/community/Table' + +import { useEffect, useState } from 'react' +import { useResetRecoilState, useRecoilValue, useRecoilState } from 'recoil' +import { useMessage } from '@/hooks/useMessage' + +import { searchState } from '@/store/boardAtom' + export default function Faq() { + const { getMessage } = useMessage() + const resetSearch = useResetRecoilState(searchState) + const [isInitialized, setIsInitialized] = useState(false) + + const search = useRecoilValue(searchState) + const [searchForm, setSearchForm] = useRecoilState(searchState) + + useEffect(() => { + if (search.mainFlag === 'N') { + resetSearch() + } else { + setSearchForm({ ...searchForm, mainFlag: 'N' }) + } + setIsInitialized(true) + }, []) + + if (!isInitialized) { + return null + } + + const boardType = { + boardTitle: getMessage('board.faq.title'), + subTitle: getMessage('board.faq.sub.title'), + clsCode: 'FAQ', + } + return ( <> -

Community FAQ

+
+
+
    +
  • + + {getMessage('board.faq.title')} + +
  • +
+
    +
  • + + react + +
  • +
  • + {getMessage('header.menus.community')} +
  • +
  • + {getMessage('board.faq.title')} +
  • +
+
+
+
+
+
+ + + + + + ) } diff --git a/src/components/community/Notice.jsx b/src/components/community/Notice.jsx index 2ab67c8e..55dfbad1 100644 --- a/src/components/community/Notice.jsx +++ b/src/components/community/Notice.jsx @@ -1,7 +1,72 @@ +'use client' + +import Link from 'next/link' +import Image from 'next/image' + +import Search from '@/components/community/Search' +import Pagination from '@/components/community/Pagination' +import Table from '@/components/community/Table' + +import { useEffect, useState } from 'react' +import { useResetRecoilState } from 'recoil' +import { useMessage } from '@/hooks/useMessage' + +import { searchState } from '@/store/boardAtom' + export default function Notice() { + const { getMessage } = useMessage() + const resetSearch = useResetRecoilState(searchState) + const [isInitialized, setIsInitialized] = useState(false) + + useEffect(() => { + resetSearch() + setIsInitialized(true) + }, []) + + if (!isInitialized) { + return null + } + + const boardType = { + boardTitle: getMessage('board.notice.title'), + subTitle: getMessage('board.notice.sub.title'), + clsCode: 'NOTICE', + } return ( <> -

Community Notice

+
+
+
    +
  • + + {getMessage('board.notice.title')} + +
  • +
+
    +
  • + + + +
  • +
  • + {getMessage('header.menus.community')} +
  • +
  • + {getMessage('board.notice.title')} +
  • +
+
+
+
+
+
+ +
+ + + + ) } diff --git a/src/components/community/Pagination.jsx b/src/components/community/Pagination.jsx new file mode 100644 index 00000000..938f5b0d --- /dev/null +++ b/src/components/community/Pagination.jsx @@ -0,0 +1,78 @@ +'use client' + +import { searchState } from '@/store/boardAtom' +import { useRecoilState, useRecoilValue } from 'recoil' +import { generateBlockPagination } from '@/util/board-utils' + +export default function Pagination() { + const search = useRecoilValue(searchState) + + const [searchForm, setSearchForm] = useRecoilState(searchState) + + const handlePagination = (pageNum) => { + setSearchForm({ ...searchForm, currentPage: pageNum }) + } + + const totalPages = Math.ceil(search.totalCount / search.pageBlock) > 0 ? Math.ceil(search.totalCount / search.pageBlock) : 1 + const allPages = generateBlockPagination(search.currentPage, totalPages, 10) + + return ( + <> +
    +
  1. + +
  2. +
  3. + +
  4. + + {/* 페이지목록 */} + {allPages.map((page, index) => { + return ( +
  5. + +
  6. + ) + })} + +
  7. + +
  8. +
  9. + +
  10. +
+ + ) +} diff --git a/src/components/community/Search.jsx b/src/components/community/Search.jsx new file mode 100644 index 00000000..26ca1883 --- /dev/null +++ b/src/components/community/Search.jsx @@ -0,0 +1,117 @@ +'use client' + +import { searchState } from '@/store/boardAtom' +import { useRecoilState, useRecoilValue } from 'recoil' +import { useState } from 'react' +import { useMessage } from '@/hooks/useMessage' + +export default function Search({ title = '', subTitle = '', isSelectUse = false }) { + const { getMessage } = useMessage() + + const search = useRecoilValue(searchState) + const [searchForm, setSearchForm] = useRecoilState(searchState) + + const [selectPageBlock, setSelectPageBlock] = useState(search.pageBlock) + const [searchValue, setSearchValue] = useState(search.searchValue) + const [searchView, setSearchView] = useState(search.searchValue ? true : false) + const [searchViewText, setSearchViewText] = useState(search.searchValue ? search.searchValue : '') + + // Enter 키 처리 + const handleKeyDown = (e) => { + if (e.key === 'Enter') { + handleSubmit(e) + } + } + + // 조회 값 처리 + const handleSearch = (text, block) => { + if (text !== '') { + setSearchView(true) + setSearchViewText(text) + setSearchForm({ ...searchForm, currentPage: 1, searchValue: text, pageBlock: block, searchFlag: true }) + } else { + setSearchView(false) + setSearchViewText('') + setSearchForm({ ...searchForm, currentPage: 1, searchValue: '', pageBlock: block, searchFlag: !searchForm.searchFlag }) + } + // 조회 후 값 비워주기 + setSearchValue('') + } + + const handleSubmit = (e) => { + e.preventDefault() + handleSearch(searchValue, selectPageBlock) + } + + return ( + <> +
+
+ { + setSearchValue(e.target.value) + }} + onKeyDown={handleKeyDown} + value={searchValue} + /> + +
+ {searchView && ( +
+ {isSelectUse ? ( + <> +
${searchViewText}`, `${search.totalCount}`]), + }} + >
+ + ) : ( + <> +
${searchViewText}`, `${search.totalCount}`]), + }} + >
+ + )} +
+ )} +
+
+
+

{subTitle}

+
    +
  • + {getMessage('board.sub.total')} {search.totalCount} +
  • +
+
+ {isSelectUse && ( +
+
+ +
+
+ )} +
+ + ) +} diff --git a/src/components/community/Table.jsx b/src/components/community/Table.jsx new file mode 100644 index 00000000..f943bc86 --- /dev/null +++ b/src/components/community/Table.jsx @@ -0,0 +1,110 @@ +'use client' + +import { useEffect, useState } from 'react' +import { useRecoilState } from 'recoil' + +import { searchState } from '@/store/boardAtom' + +import { useAxios } from '@/hooks/useAxios' +import { useMessage } from '@/hooks/useMessage' + +import BoardDetailModal from '../community/modal/BoardDetailModal' + +export default function Table({ clsCode }) { + const { getMessage } = useMessage() + + // api 조회 관련 + const { get } = useAxios() + const [search, setSearch] = useRecoilState(searchState) + const [boardList, setBoardList] = useState([]) + + // 팝업 관련 + const [open, setOpen] = useState(false) + const [modalNoticeNo, setModalNoticeNo] = useState('') + + // 목록 조회 + useEffect(() => { + async function fetchData() { + const startRow = (search.currentPage - 1) * search.pageBlock > 0 ? (search.currentPage - 1) * search.pageBlock + 1 : 1 + const endRow = search.currentPage * search.pageBlock + + const url = `/api/board/list` + const params = new URLSearchParams({ + schNoticeTpCd: 'QC', + schNoticeClsCd: clsCode, + schTitle: search.searchValue ? search.searchValue : '', + startRow: startRow, + endRow: endRow, + }) + const apiUrl = `${url}?${params.toString()}` + + const resultData = await get({ url: apiUrl }) + + if (resultData) { + if (resultData.result.code === 200) { + if (resultData.data.length > 0) { + setBoardList(resultData.data) + setSearch({ ...search, totalCount: resultData.data[0].totCnt }) + } else { + setBoardList([]) + setSearch({ ...search, totalCount: 0 }) + } + } else { + alert(resultData.result.message) + } + } + } + + fetchData() + }, [search.currentPage, search.searchValue, search.pageBlock, search.searchFlag]) + + return ( + <> +
+
+ + + + + + + {boardList.length > 0 ? ( + boardList?.map((board) => ( + { + setOpen(true) + setModalNoticeNo(board.noticeNo) + }} + > + + + + + )) + ) : ( + + + + )} + +
+ {/* 번호 */} + {board.rowNumber} + + {/* 제목 */} +
+
{board.title}
+ {board.attachYn && } +
+
+ {/* 등록일 */} + {board.regDt.split(' ')[0]} +
+ {getMessage('common.message.no.data')} +
+
+ {open && } + + ) +} diff --git a/src/components/community/modal/BoardDetailModal.jsx b/src/components/community/modal/BoardDetailModal.jsx new file mode 100644 index 00000000..dec5e09a --- /dev/null +++ b/src/components/community/modal/BoardDetailModal.jsx @@ -0,0 +1,77 @@ +'use client' + +import { useEffect, useState } from 'react' +import { useAxios } from '@/hooks/useAxios' +import { handleFileDown } from '@/util/board-utils' + +export default function BoardDetailModal({ noticeNo, setOpen }) { + // api 조회 관련 + const { get } = useAxios() + const [boardDetail, setBoardDetail] = useState({}) + + useEffect(() => { + // 상세 조회 + const fetchDetail = async (noticeNo) => { + const url = `/api/board/detail` + const params = new URLSearchParams({ + noticeNo: noticeNo, + }) + const apiUrl = `${url}?${params.toString()}` + + const resultData = await get({ url: apiUrl }) + + if (resultData) { + if (resultData.result.code === 200) { + const boardDetail = resultData.data + setBoardDetail(boardDetail) + } else { + alert(resultData.result.message) + } + } + } + + fetchDetail(noticeNo) + }, []) + + return ( + <> +
+
+
+
+ +
+
+
+
{boardDetail.title}
+ + {boardDetail.listFile && ( +
+
첨부파일 목록
+ {boardDetail.listFile.map((boardFile) => ( +
+ +
+ ))} +
+ )} + +
{boardDetail.contents}
+
+
+
+
+
+ + ) +} diff --git a/src/components/header/Header.jsx b/src/components/header/Header.jsx index 65da6051..c9b89658 100644 --- a/src/components/header/Header.jsx +++ b/src/components/header/Header.jsx @@ -12,6 +12,8 @@ import { logout } from '@/lib/authActions' import QSelectBox from '@/components/common/select/QSelectBox' +import UserInfoModal from '@/components/myInfo/UserInfoModal' + export const ToggleonMouse = (e, act, target) => { const listWrap = e.target.closest(target) const ListItem = Array.from(listWrap.childNodes) @@ -28,6 +30,8 @@ export const ToggleonMouse = (e, act, target) => { } export default function Header(props) { + const [userInfoModal, setUserInfoModal] = useState(false) + const { userSession } = props const [sessionState, setSessionState] = useRecoilState(sessionStore) const { getMessage } = useMessage() @@ -137,9 +141,15 @@ export default function Header(props) {
- + { + setUserInfoModal(true) + }} + > + {userInfoModal && }
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
{getMessage('myinfo.info.userId')} +
+ +
+
{getMessage('myinfo.info.nameKana')} +
+ +
+
{getMessage('myinfo.info.name')} +
+ +
+
{getMessage('myinfo.info.password')} +
+
+ { + setPassword(e.target.value) + }} + /> + +
+ {getMessage('myinfo.sub.validation.password')} + +
+
+ {getMessage('myinfo.info.chg.password')} * + +
+
+ { + setChgPwd(e.target.value) + }} + /> +
+ + +
+
{getMessage('myinfo.info.category')} +
+ +
+
{getMessage('myinfo.info.tel')} +
+ +
+
{getMessage('myinfo.info.fax')} +
+ +
+
{getMessage('myinfo.info.mail')} +
+ +
+
+
+
+
+ +
+
+
+
+
+ + ) +} diff --git a/src/locales/ja.json b/src/locales/ja.json index 29ba0725..333ba900 100644 --- a/src/locales/ja.json +++ b/src/locales/ja.json @@ -322,53 +322,88 @@ "commons.south": "立つ", "commons.north": "北", "site.name": "Q.CAST III", - "site.sub_name": "태양광 발전 시스템 도면관리 사이트", - "login": "로그인", - "login.init_password.btn": "비밀번호 초기화 ja", - "login.init_password.title": "비밀번호 초기화", - "login.init_password.sub_title": "비밀번호를 초기화할 아이디와 이메일 주소를 입력해 주세요.", - "login.init_password.complete_message": "비밀번호가 초기화 되었습니다. 초기화된 비밀번호는 아이디와 같습니다.", - "join.title": "Q.CAST3 로그인ID 발행 신청", - "join.sub1.title": "판매대리점 정보", - "join.sub1.comment": "※ 등록되는 리셀러의 회사 이름을 입력하십시오. (2차점은 「○○판매주식회사(2차점:××설비주식회사)」로 기입해 주세요.)", - "join.sub1.storeQcastNm": "판매대리점명", - "join.sub1.storeQcastNm_placeholder": "株式会社エネルギア・ソリューション・アンド・サービス(2次店:山口住機販売有限会社)", - "join.sub1.storeQcastNmKana": "판매대리점명 후리가나", - "join.sub1.storeQcastNmKana_placeholder": "カブシキガイシャエネルギア・ソリューション・アン", - "join.sub1.postCd": "우편번호", - "join.sub1.postCd_placeholder": "숫자 7자리", - "join.sub1.addr": "주소", - "join.sub1.addr_placeholder": "전각50자이내", - "join.sub1.telNo": "전화번호", + "site.sub_name": "太陽光発電システム図面管理サイト", + "board.notice.title": "お知らせ", + "board.notice.sub.title": "お知らせ一覧", + "board.faq.title": "FAQ", + "board.faq.sub.title": "FAQ 一覧", + "board.archive.title": "資料ダウンロード", + "board.archive.sub.title": "文書一覧", + "board.list.header.rownum": "番号", + "board.list.header.title": "タイトル", + "board.list.header.regDt": "登録日", + "board.sub.search.placeholder": "検索語を入力してください。", + "board.sub.search.result": "{0}について、合計{1}件の投稿が検索されました。", + "board.sub.search.archive.result": "{0}について、合計{1}件の文書が検索されました。", + "board.sub.total": "全体", + "board.sub.fileList": "添付ファイル一覧", + "board.sub.updDt": "更新日", + "myinfo.title": "マイプロフィール", + "myinfo.info.userId": "ユーザーID", + "myinfo.info.nameKana": "担当者名ふりがな", + "myinfo.info.name": "担当者名", + "myinfo.info.password": "パスワード", + "myinfo.info.chg.password": "新しいパスワード入力", + "myinfo.info.category": "部署名", + "myinfo.info.tel": "電話番号", + "myinfo.info.fax": "FAX番号", + "myinfo.info.mail": "メールアドレス", + "myinfo.sub.validation.password": "※ 半角10文字以内", + "myinfo.btn.close": "閉じる", + "myinfo.btn.chg.password": "パスワード変更", + "myinfo.btn.chg": "変更", + "myinfo.btn.noChg": "変更しない", + "myinfo.btn.confirm": "確認", + "myinfo.message.validation.password1": "パスワードを入力してください。", + "myinfo.message.validation.password2": "既存のパスワードと同じです。", + "myinfo.message.validation.password3": "半角文字10文字以内で入力してください。", + "myinfo.message.save": "パスワードが変更されました。", + "login": "ログイン", + "login.id.save": "ID保存", + "login.id.placeholder": "IDを入力してください。", + "login.password.placeholder": "パスワードを入力してください。", + "login.guide.text": "当サイトを利用するには、事前申請が必要です。", + "login.guide.sub1": "IDをお持ちでない方は", + "login.guide.sub2": "をクリックしてください。", + "login.guide.join.btn": "ID申請", + "login.init_password.btn": "パスワードリセット", + "login.init_password.btn.back": "前の画面に戻る", + "login.init_password.title": "パスワードリセット", + "login.init_password.sub_title": "パスワードをリセットするIDとメールアドレスを入力してください。", + "login.init_password.complete_message": "パスワードがリセットされました。リセット後のパスワードはIDと同じです。", + "login.init_password.id.placeholder": "IDを入力してください。", + "login.init_password.email.placeholder": "メールアドレスを入力してください。", + "join.title": "Q.CAST3 ログインID発行申請", + "join.sub1.title": "販売代理店情報", + "join.sub1.comment": "※ 登録する販売店の会社名を入力してください。(2次店の場合「○○販売株式会社(2次店:××設備株式会社)」と記載してください。)", + "join.sub1.storeQcastNm": "販売代理店名", + "join.sub1.storeQcastNm_placeholder": "株式会社 エナジー ギア ソリューション アンド サービス(2次店: 山口重機販売有限会社)", + "join.sub1.storeQcastNmKana": "販売代理店名ふりがな", + "join.sub1.storeQcastNmKana_placeholder": "株式会社 エナジー ギア ソリューション", + "join.sub1.postCd": "郵便番号", + "join.sub1.postCd_placeholder": "7桁の数字", + "join.sub1.addr": "住所", + "join.sub1.addr_placeholder": "全角50文字以内", + "join.sub1.telNo": "電話番号", "join.sub1.telNo_placeholder": "00-0000-0000", - "join.sub1.fax": "FAX 번호", + "join.sub1.fax": "FAX番号", "join.sub1.fax_placeholder": "00-0000-0000", - "join.sub2.title": "담당자 정보", - "join.sub2.userNm": "담당자명", - "join.sub2.userNmKana": "담당자명 후리가나", - "join.sub2.userId": "신청 ID", - "join.sub2.email": "이메일 주소", - "join.sub2.telNo": "전화번호", + "join.sub1.bizNo": "法人番号", + "join.sub2.title": "担当者情報", + "join.sub2.userNm": "担当者名", + "join.sub2.userNmKana": "担当者名ふりがな", + "join.sub2.userId": "申請ID", + "join.sub2.email": "メールアドレス", + "join.sub2.telNo": "電話番号", "join.sub2.telNo_placeholder": "00-0000-0000", - "join.sub2.fax": "FAX 번호", + "join.sub2.fax": "FAX番号", "join.sub2.fax_placeholder": "00-0000-0000", - "join.sub2.category": "부서명", - "join.sub3.title": "견적서 제출용 회사정보", - "join.sub3.qtCompNm": "회사명", - "join.sub3.qtPostCd": "우편번호", - "join.sub3.qtPostCd_placeholder": "숫자 7자리", - "join.sub3.qtAddr": "주소", - "join.sub3.qtAddr_placeholder": "전각50자이내", - "join.sub3.qtEmail": "이메일 주소", - "join.sub3.qtTelNo": "전화번호", - "join.sub3.qtTelNo_placeholder": "00-0000-0000", - "join.sub3.qtFax": "FAX 번호", - "join.sub3.qtFax_placeholder": "00-0000-0000", - "join.btn.approval_request": "ID 승인요청", - "join.complete.title": "Q.CAST3 로그인ID 발행신청 완료", - "join.complete.contents": "※ 신청한 ID가 승인되면, 담당자 정보에 입력한 이메일 주소로 로그인 관련 안내 메일이 전송됩니다.", - "join.complete.email_comment": "담당자 이메일 주소", - "join.complete.email": "test@naver.com", + "join.sub2.category": "部署名", + "join.btn.login_page": "ログイン画面に移動", + "join.btn.approval_request": "ID承認申請", + "join.complete.title": "Q.CAST3 ログインID発行申請完了", + "join.complete.contents": "※ 申請したIDが承認されると、担当者情報に入力されたメールアドレスにログイン案内メールが送信されます。", + "join.complete.email_comment": "担当者メールアドレス", "stuff.gridHeader.lastEditDatetime": "更新日時", "stuff.gridHeader.objectNo": "品番", "stuff.gridHeader.planTotCnt": "プラン数", diff --git a/src/locales/ko.json b/src/locales/ko.json index 557c2a8c..a67eb769 100644 --- a/src/locales/ko.json +++ b/src/locales/ko.json @@ -327,11 +327,56 @@ "commons.north": "북", "site.name": "Q.CAST III", "site.sub_name": "태양광 발전 시스템 도면관리 사이트", + "board.notice.title": "공지사항", + "board.notice.sub.title": "공지사항 목록", + "board.faq.title": "FAQ", + "board.faq.sub.title": "FAQ 목록", + "board.archive.title": "자료 다운로드", + "board.archive.sub.title": "문서 목록", + "board.list.header.rownum": "번호", + "board.list.header.title": "제목", + "board.list.header.regDt": "등록일", + "board.sub.search.placeholder": "검색어를 입력하세요.", + "board.sub.search.result": "{0}에 대해 총 {1}건의 글이 검색 되었습니다.", + "board.sub.search.archive.result": "{0}에 대해 총 {1}건의 문서가 검색 되었습니다.", + "board.sub.total": "전체", + "board.sub.fileList": "첨부파일 목록", + "board.sub.updDt": "업데이트", + "myinfo.title": "My profile", + "myinfo.info.userId": "사용자ID", + "myinfo.info.nameKana": "담당자명 후리가나", + "myinfo.info.name": "담당자명", + "myinfo.info.password": "비밀번호", + "myinfo.info.chg.password": "변경 비밀번호 입력", + "myinfo.info.category": "부서명", + "myinfo.info.tel": "전화번호", + "myinfo.info.fax": "FAX번호", + "myinfo.info.mail": "이메일 주소", + "myinfo.sub.validation.password": "※ 반각10자 이내", + "myinfo.btn.close": "닫기", + "myinfo.btn.chg.password": "비밀번호 변경", + "myinfo.btn.chg": "변경", + "myinfo.btn.noChg": "변경안함", + "myinfo.btn.confirm": "확인", + "myinfo.message.validation.password1": "비밀번호를 입력하세요.", + "myinfo.message.validation.password2": "기존 비밀번호와 동일합니다.", + "myinfo.message.validation.password3": "반각 문자 10자이내여야 합니다.", + "myinfo.message.save": "비밀번호가 변경되었습니다.", "login": "로그인", + "login.id.save": "ID Save", + "login.id.placeholder": "아이디를 입력해주세요.", + "login.password.placeholder": "비밀번호를 입력해주세요.", + "login.guide.text": "당 사이트를 이용할 때는, 사전 신청이 필요합니다.", + "login.guide.sub1": "ID가 없는 분은", + "login.guide.sub2": "을 클릭해주십시오.", + "login.guide.join.btn": "ID신청", "login.init_password.btn": "비밀번호 초기화", + "login.init_password.btn.back": "이전 화면으로", "login.init_password.title": "비밀번호 초기화", "login.init_password.sub_title": "비밀번호를 초기화할 아이디와 이메일 주소를 입력해 주세요.", "login.init_password.complete_message": "비밀번호가 초기화 되었습니다. 초기화된 비밀번호는 아이디와 같습니다.", + "login.init_password.id.placeholder": "ID를 입력하세요.", + "login.init_password.email.placeholder": "이메일을 입력하세요.", "join.title": "Q.CAST3 로그인ID 발행 신청", "join.sub1.title": "판매대리점 정보", "join.sub1.comment": "※ 등록되는 리셀러의 회사 이름을 입력하십시오. (2차점은 「○○판매주식회사(2차점:××설비주식회사)」로 기입해 주세요.)", @@ -347,6 +392,7 @@ "join.sub1.telNo_placeholder": "00-0000-0000", "join.sub1.fax": "FAX 번호", "join.sub1.fax_placeholder": "00-0000-0000", + "join.sub1.bizNo": "법인번호", "join.sub2.title": "담당자 정보", "join.sub2.userNm": "담당자명", "join.sub2.userNmKana": "담당자명 후리가나", @@ -357,22 +403,11 @@ "join.sub2.fax": "FAX 번호", "join.sub2.fax_placeholder": "00-0000-0000", "join.sub2.category": "부서명", - "join.sub3.title": "견적서 제출용 회사정보", - "join.sub3.qtCompNm": "회사명", - "join.sub3.qtPostCd": "우편번호", - "join.sub3.qtPostCd_placeholder": "숫자 7자리", - "join.sub3.qtAddr": "주소", - "join.sub3.qtAddr_placeholder": "전각50자이내", - "join.sub3.qtEmail": "이메일 주소", - "join.sub3.qtTelNo": "전화번호", - "join.sub3.qtTelNo_placeholder": "00-0000-0000", - "join.sub3.qtFax": "FAX 번호", - "join.sub3.qtFax_placeholder": "00-0000-0000", + "join.btn.login_page": "로그인 화면으로 이동", "join.btn.approval_request": "ID 승인요청", "join.complete.title": "Q.CAST3 로그인ID 발행신청 완료", "join.complete.contents": "※ 신청한 ID가 승인되면, 담당자 정보에 입력한 이메일 주소로 로그인 관련 안내 메일이 전송됩니다.", "join.complete.email_comment": "담당자 이메일 주소", - "join.complete.email": "test@naver.com", "stuff.gridHeader.lastEditDatetime": "갱신일시", "stuff.gridHeader.objectNo": "물건번호", "stuff.gridHeader.planTotCnt": "플랜 수", diff --git a/src/store/boardAtom.js b/src/store/boardAtom.js new file mode 100644 index 00000000..3444eba0 --- /dev/null +++ b/src/store/boardAtom.js @@ -0,0 +1,14 @@ +import { atom } from 'recoil' + +export const searchState = atom({ + key: 'searchState', + default: { + currentPage: 1, + totalPage: 1, + pageBlock: 100, + totalCount: 0, + searchValue: '', + mainFlag: 'N', + searchFlag: false, + }, +}) diff --git a/src/util/board-utils.js b/src/util/board-utils.js new file mode 100644 index 00000000..515555dd --- /dev/null +++ b/src/util/board-utils.js @@ -0,0 +1,46 @@ +import { useAxios } from '@/hooks/useAxios' + +// 파일 다운로드 +export const handleFileDown = async (file) => { + const { get } = useAxios() + + const url = `/api/board/file/download` + const params = new URLSearchParams({ + encodeFileNo: file.encodeFileNo, + }) + const apiUrl = `${url}?${params.toString()}` + const resultData = await get({ url: apiUrl }) + + if (resultData) { + const blob = new Blob([resultData]) + const fileUrl = window.URL.createObjectURL(blob) + const link = document.createElement('a') + + link.href = fileUrl + link.download = file.srcFileNm + document.body.appendChild(link) + link.click() + link.remove() + window.URL.revokeObjectURL(url) + } +} + +// 페이지 번호 생성 +export const generateBlockPagination = (currentPage, totalPages, pageBlock) => { + const currentBlock = Math.ceil(currentPage / pageBlock) + + let startPage = (currentBlock - 1) * pageBlock + 1 + let endPage = startPage + pageBlock - 1 + + if (endPage > totalPages) { + endPage = totalPages + } + + let pageArr = [] + + for (let i = startPage; i <= endPage; i++) { + pageArr.push(i) + } + + return pageArr +} diff --git a/yarn.lock b/yarn.lock index 1ffd6b98..50679622 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5189,6 +5189,11 @@ jiti@^1.21.0: resolved "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz" integrity sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w== +js-cookie@^3.0.5: + version "3.0.5" + resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-3.0.5.tgz#0b7e2fd0c01552c58ba86e0841f94dc2557dcdbc" + integrity sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw== + js-md4@^0.3.2: version "0.3.2" resolved "https://registry.npmjs.org/js-md4/-/js-md4-0.3.2.tgz" From 277dc6bfd364c603683b5d5bcc461d446fe1e1bd Mon Sep 17 00:00:00 2001 From: leeyongjae Date: Tue, 15 Oct 2024 17:47:18 +0900 Subject: [PATCH 18/68] =?UTF-8?q?[QCAST]=20=EA=B0=80=EC=9E=85=EC=8B=A0?= =?UTF-8?q?=EC=B2=AD=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/auth/Join.jsx | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/components/auth/Join.jsx b/src/components/auth/Join.jsx index 5ae0b5ae..ff64225c 100644 --- a/src/components/auth/Join.jsx +++ b/src/components/auth/Join.jsx @@ -11,8 +11,9 @@ export default function Join() { const router = useRouter() // 가입 신청 - const joinProcess = async (formData) => { - formData.preventDefault() + const joinProcess = async (e) => { + e.preventDefault() + const formData = new FormData(e.target) const param = { storeQcastNm: formData.get('storeQcastNm'), @@ -197,7 +198,7 @@ export default function Join() {
- +
@@ -215,7 +216,7 @@ export default function Join() {
- +
@@ -225,7 +226,7 @@ export default function Join() {
- +
@@ -241,6 +242,7 @@ export default function Join() { name="userTelNo" className="input-light" placeholder={getMessage('join.sub2.telNo_placeholder')} + required />
@@ -257,6 +259,7 @@ export default function Join() { name="userFax" className="input-light" placeholder={getMessage('join.sub1.fax_placeholder')} + required />
From cf786db086feb1a475d5be640b6cc0977d049ab2 Mon Sep 17 00:00:00 2001 From: basssy Date: Tue, 15 Oct 2024 17:50:15 +0900 Subject: [PATCH 19/68] =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=ED=9B=84?= =?UTF-8?q?=20=EB=A9=94=EC=9D=B8=ED=99=94=EB=A9=B4=20FAQ=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=20=ED=99=94=EB=A9=B4=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Main.jsx | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/src/components/Main.jsx b/src/components/Main.jsx index 69c90071..76676cd1 100644 --- a/src/components/Main.jsx +++ b/src/components/Main.jsx @@ -13,6 +13,7 @@ import { stuffSearchState } from '@/store/stuffAtom' import { useForm } from 'react-hook-form' import '@/styles/contents.scss' import ChangePasswordPop from './main/ChangePasswordPop' +import { searchState } from '@/store/boardAtom' export default function MainPage() { const [sessionState, setSessionState] = useRecoilState(sessionStore) @@ -36,6 +37,8 @@ export default function MainPage() { const [stuffSearch, setStuffSearch] = useRecoilState(stuffSearchState) + const [searchForm, setSearchForm] = useRecoilState(searchState) + useEffect(() => { if (sessionState.pwdInitYn === 'Y') { fetchObjectList() @@ -72,14 +75,8 @@ export default function MainPage() { }) router.push('/management/stuff') } else { - alert('작업중') - return - - //FAQ일떄 - //faq리코일에 - //searchValue= e.target.value - //mainFlag:'Y' - // router.push('/community/faq') + setSearchForm({ ...searchForm, searchValue: searchTxt, mainFlag: 'Y' }) + router.push('/community/faq') } } } @@ -100,13 +97,8 @@ export default function MainPage() { router.push('/management/stuff') } else { - alert('작업중') - return - //FAQ일떄 - //faq리코일에 - //searchValue= e.target.value - //mainFlag:'Y' - // router.push('/community/faq') + setSearchForm({ ...searchForm, searchValue: searchTxt, mainFlag: 'Y' }) + router.push('/community/faq') } } From da26423cc2b78f693d63f8b9b9efd17b0b2a09fc Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Tue, 15 Oct 2024 17:58:38 +0900 Subject: [PATCH 20/68] =?UTF-8?q?offset=20=3D>=20width=EB=A1=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/roofcover/useAuxiliaryDrawing.js | 12 ++++++------ src/hooks/roofcover/useRoofShapeSetting.js | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/hooks/roofcover/useAuxiliaryDrawing.js b/src/hooks/roofcover/useAuxiliaryDrawing.js index 5cae2d01..cb33f407 100644 --- a/src/hooks/roofcover/useAuxiliaryDrawing.js +++ b/src/hooks/roofcover/useAuxiliaryDrawing.js @@ -26,6 +26,7 @@ import { import { calculateDistance, calculateIntersection, distanceBetweenPoints, findClosestPoint } from '@/util/canvas-util' import { fabric } from 'fabric' import { useAdsorptionPoint } from '@/hooks/useAdsorptionPoint' +import { useSwal } from '@/hooks/useSwal' // 보조선 작성 export function useAuxiliaryDrawing(setShowAuxiliaryModal) { @@ -34,6 +35,7 @@ export function useAuxiliaryDrawing(setShowAuxiliaryModal) { const { getIntersectMousePoint } = useMouse() const { addLine, removeLine } = useLine() const { tempGridMode } = useTempGrid() + const { swalFire } = useSwal() const { getAdsorptionPoints } = useAdsorptionPoint() const adsorptionRange = useRecoilValue(adsorptionRangeState) @@ -80,18 +82,19 @@ export function useAuxiliaryDrawing(setShowAuxiliaryModal) { // innerLines가 있을경우 삭제 const roofs = canvas?.getObjects().filter((obj) => obj.name === 'roofBase') if (roofs.length === 0) { + swalFire({ text: '지붕형상이 없습니다.' }) + setShowAuxiliaryModal(false) return } // 지붕의 각 꼭지점을 흡착점으로 설정 const roofsPoints = roofs.map((roof) => roof.points).flat() - console.log(roofsPoints) roofAdsorptionPoints.current = [...roofsPoints] addCanvasMouseEventListener('mouse:move', mouseMove) addCanvasMouseEventListener('mouse:down', mouseDown) addDocumentEventListener('contextmenu', document, cutAuxiliary) - addDocumentEventListener('keydown', document, keydown) + addDocumentEventListener('keydown', document, keydown[type]) return () => { canvas.remove(...canvas.getObjects().filter((obj) => obj.name === 'innerPoint')) @@ -120,7 +123,6 @@ export function useAuxiliaryDrawing(setShowAuxiliaryModal) { const keydown = { outerLine: (e) => { - console.log(123) if (mousePointerArr.current.length === 0) { return } @@ -622,11 +624,9 @@ export function useAuxiliaryDrawing(setShowAuxiliaryModal) { const roofBases = canvas.getObjects().find((obj) => obj.name === 'roofBase') const innerLines = [...lineHistory.current] - console.log('innerLines', innerLines) - roofBases.innerLines = [...innerLines] - setShowAuxiliaryModal(close) + setShowAuxiliaryModal(false) } return { diff --git a/src/hooks/roofcover/useRoofShapeSetting.js b/src/hooks/roofcover/useRoofShapeSetting.js index 9988f7a9..1984632d 100644 --- a/src/hooks/roofcover/useRoofShapeSetting.js +++ b/src/hooks/roofcover/useRoofShapeSetting.js @@ -384,7 +384,7 @@ export function useRoofShapeSetting(setShowRoofShapeSettingModal) { // 벽 attributes = { type: LINE_TYPE.WALLLINE.WALL, - offset: hasSleeve === '0' ? 0 : sleeveOffset / 10, + width: hasSleeve === '0' ? 0 : sleeveOffset / 10, } break } @@ -412,7 +412,7 @@ export function useRoofShapeSetting(setShowRoofShapeSettingModal) { // 한쪽흐름 attributes = { type: LINE_TYPE.WALLLINE.SHED, - offset: shedWidth / 10, + width: shedWidth / 10, } break } From b97f372f62484fac53391b00c4ada30389e25d64 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Tue, 15 Oct 2024 18:00:37 +0900 Subject: [PATCH 21/68] =?UTF-8?q?sleeve=20=EC=86=8D=EC=84=B1=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/roofcover/useRoofShapeSetting.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/hooks/roofcover/useRoofShapeSetting.js b/src/hooks/roofcover/useRoofShapeSetting.js index 1984632d..e8bf3684 100644 --- a/src/hooks/roofcover/useRoofShapeSetting.js +++ b/src/hooks/roofcover/useRoofShapeSetting.js @@ -385,6 +385,7 @@ export function useRoofShapeSetting(setShowRoofShapeSettingModal) { attributes = { type: LINE_TYPE.WALLLINE.WALL, width: hasSleeve === '0' ? 0 : sleeveOffset / 10, + sleeve: hasSleeve === '1', } break } From b18280803d501c368de9af14260395e8b9c71103 Mon Sep 17 00:00:00 2001 From: leeyongjae Date: Wed, 16 Oct 2024 09:27:49 +0900 Subject: [PATCH 22/68] =?UTF-8?q?[QCAST]=20promisePatch=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EB=B0=8F=20promise=20=EC=82=AC=EC=9A=A9=20?= =?UTF-8?q?=EB=B6=80=EB=B6=84(=EA=B0=80=EC=9E=85,=20=EB=B9=84=EB=B0=80?= =?UTF-8?q?=EB=B2=88=ED=98=B8=EB=B3=80=EA=B2=BD,=20=ED=8C=8C=EC=9D=BC?= =?UTF-8?q?=EB=8B=A4=EC=9A=B4=EB=A1=9C=EB=93=9C=20=EB=93=B1)=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/auth/Join.jsx | 24 +++++---- src/components/auth/Login.jsx | 69 +++++++++++++------------ src/components/myInfo/UserInfoModal.jsx | 24 +++++---- src/hooks/useAxios.js | 6 ++- src/util/board-utils.js | 32 +++++++----- 5 files changed, 88 insertions(+), 67 deletions(-) diff --git a/src/components/auth/Join.jsx b/src/components/auth/Join.jsx index ff64225c..aa2e21ca 100644 --- a/src/components/auth/Join.jsx +++ b/src/components/auth/Join.jsx @@ -7,7 +7,7 @@ import Cookies from 'js-cookie' export default function Join() { const { getMessage } = useMessage() - const { post } = useAxios() + const { promisePost } = useAxios() const router = useRouter() // 가입 신청 @@ -34,16 +34,20 @@ export default function Join() { }, } - await post({ url: '/api/login/v1.0/user/join', data: param }).then((res) => { - if (res) { - if (res.result.resultCode == 'S') { - Cookies.set('joinEmail', formData.get('email'), { expires: 1 }) - router.push('/join/complete') - } else { - alert(res.result.resultMsg) + await promisePost({ url: '/api/login/v1.0/user/join', data: param }) + .then((res) => { + if (res) { + if (res.result.resultCode == 'S') { + Cookies.set('joinEmail', formData.get('email'), { expires: 1 }) + router.push('/join/complete') + } else { + alert(res.result.resultMsg) + } } - } - }) + }) + .catch((error) => { + alert(error.response.data.message) + }) } return ( diff --git a/src/components/auth/Login.jsx b/src/components/auth/Login.jsx index f6c810fb..69d02e21 100644 --- a/src/components/auth/Login.jsx +++ b/src/components/auth/Login.jsx @@ -34,7 +34,7 @@ export default function Login() { const [passwordReset, setPasswordReset] = useState(1) - const { post, patch } = useAxios(globalLocaleState) + const { promisePost, promisePatch } = useAxios(globalLocaleState) // login process const loginProcess = async (e) => { @@ -88,26 +88,27 @@ export default function Login() { // loginId: formData.get('id'), // pwd: formData.get('password'), // } - - // const res = await post({ url: '/api/login/v1.0/login', data: param }) - - // if (res) { - // if (res.result.resultCode == 'S') { - // setSession(res.data) - // setSessionState(res.data) - - // // ID SAVE 체크되어 있는 경우, 쿠키 저장 - // if (chkLoginId) { - // Cookies.set('chkLoginId', formData.get('id'), { expires: 7 }) - // } else { - // Cookies.remove('chkLoginId') + // await promisePost({ url: '/api/login/v1.0/login', data: param }) + // .then((res) => { + // if (res) { + // if (res.result.resultCode == 'S') { + // setSession(res.data) + // setSessionState(res.data) + // // ID SAVE 체크되어 있는 경우, 쿠키 저장 + // if (chkLoginId) { + // Cookies.set('chkLoginId', formData.get('id'), { expires: 7 }) + // } else { + // Cookies.remove('chkLoginId') + // } + // router.push('/') + // } else { + // alert(res.result.resultMsg) + // } // } - - // router.push('/') - // } else { - // alert(res.result.resultMsg) - // } - // } + // }) + // .catch((error) => { + // alert(error.response.data.message) + // }) } // 비밀번호 초기화 관련 @@ -117,21 +118,25 @@ export default function Login() { email: checkEmail, } - const res = await patch({ + await promisePatch({ url: '/api/login/v1.0/user/init-password', data: param, }) - - if (res) { - if (res.result.resultCode == 'S') { - alert(getMessage('login.init_password.complete_message')) - setCheckId('') - setCheckEmail('') - setPasswordReset(1) - } else { - alert(res.result.resultMsg) - } - } + .then((res) => { + if (res) { + if (res.result.resultCode == 'S') { + alert(getMessage('login.init_password.complete_message')) + setCheckId('') + setCheckEmail('') + setPasswordReset(1) + } else { + alert(res.result.resultMsg) + } + } + }) + .catch((error) => { + alert(error.response.data.message) + }) } return ( diff --git a/src/components/myInfo/UserInfoModal.jsx b/src/components/myInfo/UserInfoModal.jsx index 715504fc..d2627034 100644 --- a/src/components/myInfo/UserInfoModal.jsx +++ b/src/components/myInfo/UserInfoModal.jsx @@ -8,7 +8,7 @@ export default function UserInfoModal({ userId, userInfoModal, setUserInfoModal const { getMessage } = useMessage() // api 조회 관련 - const { get, patch } = useAxios() + const { get, promisePatch } = useAxios() const [info, setInfo] = useState({ userId: '', password: '', @@ -69,16 +69,20 @@ export default function UserInfoModal({ userId, userInfoModal, setUserInfoModal const params = { loginId: info.userId, chgType: 'C', pwd: password, chgPwd: chgPwd } - await patch({ url: '/api/login/v1.0/user/change-password', data: params }).then((res) => { - if (res) { - if (res.result.resultCode === 'S') { - alert(getMessage('myinfo.message.save')) - setChkChgPwd(false) - } else { - alert(res.result.resultMsg) + await promisePatch({ url: '/api/login/v1.0/user/change-password', data: params }) + .then((res) => { + if (res) { + if (res.result.resultCode === 'S') { + alert(getMessage('myinfo.message.save')) + setChkChgPwd(false) + } else { + alert(res.result.resultMsg) + } } - } - }) + }) + .catch((error) => { + alert(error.response.data.message) + }) } return ( diff --git a/src/hooks/useAxios.js b/src/hooks/useAxios.js index 39b71769..c125c28a 100644 --- a/src/hooks/useAxios.js +++ b/src/hooks/useAxios.js @@ -80,6 +80,10 @@ export function useAxios(lang = '') { .catch(console.error) } + const promisePatch = async ({ url, data }) => { + return await getInstances(url).patch(url, data) + } + const del = async ({ url }) => { return await getInstances(url) .delete(url) @@ -91,5 +95,5 @@ export function useAxios(lang = '') { return await getInstances(url).delete(url) } - return { get, promiseGet, post, promisePost, put, promisePut, patch, del, promiseDel } + return { get, promiseGet, post, promisePost, put, promisePut, patch, promisePatch, del, promiseDel } } diff --git a/src/util/board-utils.js b/src/util/board-utils.js index 515555dd..cdb177c6 100644 --- a/src/util/board-utils.js +++ b/src/util/board-utils.js @@ -2,27 +2,31 @@ import { useAxios } from '@/hooks/useAxios' // 파일 다운로드 export const handleFileDown = async (file) => { - const { get } = useAxios() + const { promiseGet } = useAxios() const url = `/api/board/file/download` const params = new URLSearchParams({ encodeFileNo: file.encodeFileNo, }) const apiUrl = `${url}?${params.toString()}` - const resultData = await get({ url: apiUrl }) + await promiseGet({ url: apiUrl }) + .then((resultData) => { + if (resultData) { + const blob = new Blob([resultData]) + const fileUrl = window.URL.createObjectURL(blob) + const link = document.createElement('a') - if (resultData) { - const blob = new Blob([resultData]) - const fileUrl = window.URL.createObjectURL(blob) - const link = document.createElement('a') - - link.href = fileUrl - link.download = file.srcFileNm - document.body.appendChild(link) - link.click() - link.remove() - window.URL.revokeObjectURL(url) - } + link.href = fileUrl + link.download = file.srcFileNm + document.body.appendChild(link) + link.click() + link.remove() + window.URL.revokeObjectURL(url) + } + }) + .catch((error) => { + alert(error.response.data.message) + }) } // 페이지 번호 생성 From 4782ea4931bfa8bbb98459fa02c6b46f6a4b9e17 Mon Sep 17 00:00:00 2001 From: minsik Date: Wed, 16 Oct 2024 09:46:16 +0900 Subject: [PATCH 23/68] =?UTF-8?q?-=20=ED=9A=8C=EB=A1=9C=20=EB=B0=8F=20?= =?UTF-8?q?=EA=B0=80=EB=8C=80=20=EC=84=A4=EC=A0=95=20=EB=AA=A8=EB=8B=AC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/floor-plan/CanvasMenu.jsx | 2 + src/components/floor-plan/FloorPlan.jsx | 4 + src/components/floor-plan/MenuDepth01.jsx | 4 + .../circuitTrestle/CircuitTrestleSetting.jsx | 57 +++ .../circuitTrestle/step/CircuitAllocation.jsx | 25 ++ .../step/PowerConditionalSelect.jsx | 146 +++++++ .../modal/circuitTrestle/step/StepUp.jsx | 408 ++++++++++++++++++ .../step/type/AutoCircuitAllocation.jsx | 17 + .../step/type/PassivityCircuitAllocation.jsx | 104 +++++ src/locales/ja.json | 19 + src/locales/ko.json | 20 + 11 files changed, 806 insertions(+) create mode 100644 src/components/floor-plan/modal/circuitTrestle/CircuitTrestleSetting.jsx create mode 100644 src/components/floor-plan/modal/circuitTrestle/step/CircuitAllocation.jsx create mode 100644 src/components/floor-plan/modal/circuitTrestle/step/PowerConditionalSelect.jsx create mode 100644 src/components/floor-plan/modal/circuitTrestle/step/StepUp.jsx create mode 100644 src/components/floor-plan/modal/circuitTrestle/step/type/AutoCircuitAllocation.jsx create mode 100644 src/components/floor-plan/modal/circuitTrestle/step/type/PassivityCircuitAllocation.jsx diff --git a/src/components/floor-plan/CanvasMenu.jsx b/src/components/floor-plan/CanvasMenu.jsx index ab445ca9..f552f530 100644 --- a/src/components/floor-plan/CanvasMenu.jsx +++ b/src/components/floor-plan/CanvasMenu.jsx @@ -48,6 +48,7 @@ export default function CanvasMenu(props) { setShowWallLineOffsetSettingModal, setShowRoofAllocationSettingModal, setShowBasicSettingModal, + setShowCircuitTrestleSettingModal, setShowPropertiesSettingModal, } = props @@ -105,6 +106,7 @@ export default function CanvasMenu(props) { setShowRoofAllocationSettingModal, setShowObjectSettingModal, setShowBasicSettingModal, + setShowCircuitTrestleSettingModal, setShowPropertiesSettingModal, type, } diff --git a/src/components/floor-plan/FloorPlan.jsx b/src/components/floor-plan/FloorPlan.jsx index 1de51962..b8ea7784 100644 --- a/src/components/floor-plan/FloorPlan.jsx +++ b/src/components/floor-plan/FloorPlan.jsx @@ -27,6 +27,7 @@ import RoofShapePassivitySetting from '@/components/floor-plan/modal/roofShape/R import MovementSetting from '@/components/floor-plan/modal/movement/MovementSetting' import RoofAllocationSetting from '@/components/floor-plan/modal/roofAllocation/RoofAllocationSetting' import BasicSetting from '@/components/floor-plan/modal/basic/BasicSetting' +import CircuitTrestleSetting from '@/components/floor-plan/modal/circuitTrestle/CircuitTrestleSetting' export default function FloorPlan() { const [showCanvasSettingModal, setShowCanvasSettingModal] = useState(false) @@ -45,6 +46,7 @@ export default function FloorPlan() { const [showWallLineOffsetSettingModal, setShowWallLineOffsetSettingModal] = useState(false) const [showRoofAllocationSettingModal, setShowRoofAllocationSettingModal] = useState(false) const [showBasicSettingModal, setShowBasicSettingModal] = useState(false) + const [showCircuitTrestleSettingModal, setShowCircuitTrestleSettingModal] = useState(false) const globalLocaleState = useRecoilValue(globalLocaleStore) const { get } = useAxios(globalLocaleState) @@ -84,6 +86,7 @@ export default function FloorPlan() { setShowWallLineOffsetSettingModal, setShowRoofAllocationSettingModal, setShowBasicSettingModal, + setShowCircuitTrestleSettingModal, setShowPropertiesSettingModal, } @@ -159,6 +162,7 @@ export default function FloorPlan() { {showObjectSettingModal && } {showPlacementSurfaceSettingModal && } {showBasicSettingModal && } + {showCircuitTrestleSettingModal && }
diff --git a/src/components/floor-plan/MenuDepth01.jsx b/src/components/floor-plan/MenuDepth01.jsx index 21631434..d862853a 100644 --- a/src/components/floor-plan/MenuDepth01.jsx +++ b/src/components/floor-plan/MenuDepth01.jsx @@ -23,6 +23,7 @@ export default function MenuDepth01(props) { setShowRoofAllocationSettingModal, setShowObjectSettingModal, setShowBasicSettingModal, + setShowCircuitTrestleSettingModal, setShowPropertiesSettingModal, } = props const { getMessage } = useMessage() @@ -44,6 +45,7 @@ export default function MenuDepth01(props) { setShowRoofAllocationSettingModal(id === 7) setShowPlaceShapeDrawingModal(false) setShowPropertiesSettingModal(false) + setShowCircuitTrestleSettingModal(false) } if (type === 'surface') { @@ -56,6 +58,7 @@ export default function MenuDepth01(props) { setShowWallLineOffsetSettingModal(false) setShowRoofAllocationSettingModal(false) setShowPropertiesSettingModal(false) + setShowCircuitTrestleSettingModal(false) setShowSlopeSettingModal(id === 0) setShowPlaceShapeDrawingModal(id === 1) setShowPlacementSurfaceSettingModal(id === 2) @@ -73,6 +76,7 @@ export default function MenuDepth01(props) { setShowRoofAllocationSettingModal(false) setShowPropertiesSettingModal(false) setShowBasicSettingModal(id === 0) + setShowCircuitTrestleSettingModal(id === 1) } } diff --git a/src/components/floor-plan/modal/circuitTrestle/CircuitTrestleSetting.jsx b/src/components/floor-plan/modal/circuitTrestle/CircuitTrestleSetting.jsx new file mode 100644 index 00000000..1c67ea7f --- /dev/null +++ b/src/components/floor-plan/modal/circuitTrestle/CircuitTrestleSetting.jsx @@ -0,0 +1,57 @@ +import WithDraggable from '@/components/common/draggable/withDraggable' +import { useState } from 'react' +import PowerConditionalSelect from '@/components/floor-plan/modal/circuitTrestle/step/PowerConditionalSelect' +import CircuitAllocation from '@/components/floor-plan/modal/circuitTrestle/step/CircuitAllocation' +import StepUp from '@/components/floor-plan/modal/circuitTrestle/step/StepUp' +import { useMessage } from '@/hooks/useMessage' + +export default function CircuitTrestleSetting({ setShowCircuitTrestleSettingModal }) { + const { getMessage } = useMessage() + const [tabNum, setTabNum] = useState(1) + const [circuitAllocationType, setCircuitAllocationType] = useState(1) + const circuitProps = { + circuitAllocationType, + setCircuitAllocationType, + } + return ( + +
+
+

{getMessage('modal.circuit.trestle.setting')}

+ +
+
+
+
+ {getMessage('modal.circuit.trestle.setting.power.conditional.select')} +
+ +
+ {getMessage('modal.circuit.trestle.setting.circuit.allocation')} +
+ +
{getMessage('modal.circuit.trestle.setting.step.up.allocation')}
+
+ {tabNum === 1 && } + {tabNum === 2 && } + {tabNum === 3 && } +
+ {tabNum !== 1 && ( + + )} + {tabNum !== 3 && ( + + )} + {tabNum === 3 && } +
+
+
+
+ ) +} diff --git a/src/components/floor-plan/modal/circuitTrestle/step/CircuitAllocation.jsx b/src/components/floor-plan/modal/circuitTrestle/step/CircuitAllocation.jsx new file mode 100644 index 00000000..b980eeb4 --- /dev/null +++ b/src/components/floor-plan/modal/circuitTrestle/step/CircuitAllocation.jsx @@ -0,0 +1,25 @@ +import AutoCircuitAllocation from '@/components/floor-plan/modal/circuitTrestle/step/type/AutoCircuitAllocation' +import PassivityCircuitAllocation from '@/components/floor-plan/modal/circuitTrestle/step/type/PassivityCircuitAllocation' +import { useMessage } from '@/hooks/useMessage' + +export default function CircuitAllocation(props) { + const { getMessage } = useMessage() + const { circuitAllocationType, setCircuitAllocationType } = props + return ( + <> +
+ + +
+
+
{getMessage('modal.circuit.trestle.setting.circuit.allocation')}
+ {circuitAllocationType === 1 && } + {circuitAllocationType === 2 && } +
+ + ) +} diff --git a/src/components/floor-plan/modal/circuitTrestle/step/PowerConditionalSelect.jsx b/src/components/floor-plan/modal/circuitTrestle/step/PowerConditionalSelect.jsx new file mode 100644 index 00000000..fe2d168b --- /dev/null +++ b/src/components/floor-plan/modal/circuitTrestle/step/PowerConditionalSelect.jsx @@ -0,0 +1,146 @@ +import QSelectBox from '@/components/common/select/QSelectBox' +import { useMessage } from '@/hooks/useMessage' +import { useState } from 'react' + +const SelectOption01 = [{ name: '0' }, { name: '0' }, { name: '0' }, { name: '0' }] + +export default function PowerConditionalSelect({ setTabNum }) { + const { getMessage } = useMessage() + const [selectedRowIndex, setSelectedRowIndex] = useState(null) + const [powerConditions, setPowerConditions] = useState([]) + const seriesData = { + header: [ + { name: getMessage('명칭'), width: '15%', prop: 'name', type: 'color-box' }, + { + name: `${getMessage('modal.circuit.trestle.setting.power.conditional.select.rated.output')} (kW)`, + width: '10%', + prop: 'ratedOutput', + }, + { + name: `${getMessage('modal.circuit.trestle.setting.power.conditional.select.circuit.amount')}`, + width: '10%', + prop: 'circuitAmount', + }, + { + name: `${getMessage('modal.circuit.trestle.setting.power.conditional.select.max.connection')}`, + width: '10%', + prop: 'maxConnection', + }, + { + name: `${getMessage('modal.circuit.trestle.setting.power.conditional.select.max.overload')}`, + width: '10%', + prop: 'maxOverload', + }, + { + name: `${getMessage('modal.circuit.trestle.setting.power.conditional.select.output.current')}`, + width: '10%', + prop: 'outputCurrent', + }, + ], + rows: [ + { + name: { name: 'PCSオプションマスター', color: '#AA6768' }, + ratedOutput: { name: '2' }, + circuitAmount: { name: '2' }, + maxConnection: { name: '-' }, + maxOverload: { name: '-' }, + outputCurrent: { name: '-' }, + }, + { + name: { name: 'HQJP-KA40-5', color: '#AA6768' }, + ratedOutput: { name: '2' }, + circuitAmount: { name: '2' }, + maxConnection: { name: '-' }, + maxOverload: { name: '-' }, + outputCurrent: { name: '-' }, + }, + { + name: { name: 'Re.RISE-G3 440', color: '#AA6768' }, + ratedOutput: { name: '2' }, + circuitAmount: { name: '2' }, + maxConnection: { name: '-' }, + maxOverload: { name: '-' }, + outputCurrent: { name: '-' }, + }, + ], + } + return ( + <> +
+ 分類 (余剰) +
+ +
+ 寒冷地仕様 +
+
+
+
+
+ + +
+
+ + +
+
+
+
+
+
+
+
+ + + + {seriesData.header.map((header) => ( + + ))} + + + + {seriesData.rows.map((row, index) => ( + setSelectedRowIndex(index)} className={index === selectedRowIndex ? 'on' : ''}> + {seriesData.header.map((header) => ( + + ))} + + ))} + +
+ {header.name} +
{row[header.prop].name}
+
+
+
+ + +
+
+ HQJP-KA40-5 + HQJP-KA40-5 + HQJP-KA40-5 +
+
+
+
+
+ + +
+
+ + +
+
+ {/*
*/} + {/* */} + {/*
*/} + + ) +} diff --git a/src/components/floor-plan/modal/circuitTrestle/step/StepUp.jsx b/src/components/floor-plan/modal/circuitTrestle/step/StepUp.jsx new file mode 100644 index 00000000..d8130a0a --- /dev/null +++ b/src/components/floor-plan/modal/circuitTrestle/step/StepUp.jsx @@ -0,0 +1,408 @@ +import QSelectBox from '@/components/common/select/QSelectBox' +import { useMessage } from '@/hooks/useMessage' + +const SelectOption01 = [{ name: '0' }, { name: '0' }, { name: '0' }, { name: '0' }] + +export default function StepUp({}) { + const { getMessage } = useMessage() + return ( + <> +
+
{getMessage('modal.circuit.trestle.setting.step.up.allocation')}
+
+
+
+
+
HQJP-KA55-5
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
シリアル枚数総回路数
100
100
100
100
100
+
+
+
+
+
接続する
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
名称回路数昇圧回路数
KTN-CBD4C40
KTN-CBD4C40
KTN-CBD4C40
+
+
+
+ + +
+
+ HQJP-KA40-5 +
+
+
+
+
昇圧オプション
+
+ + + + + + + + + + + + + + + + + +
名称昇圧回路数
--
--
+
+
+
+ + +
+
+ HQJP-KA40-5 +
+
+
+
+
+ 綿調道区分 +
+ +
+ 回路 + (二重昇圧回路数 +
+ +
+ 回路) +
+
+
+
+
+
+
HQJP-KA55-5
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
シリアル枚数総回路数
100
100
100
100
100
+
+
+
+
+
接続する
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
名称回路数昇圧回路数
KTN-CBD4C40
KTN-CBD4C40
KTN-CBD4C40
+
+
+
+ + +
+
+ HQJP-KA40-5 +
+
+
+
+
昇圧オプション
+
+ + + + + + + + + + + + + + + + + +
名称昇圧回路数
--
--
+
+
+
+ + +
+
+ HQJP-KA40-5 +
+
+
+
+
+ 綿調道区分 +
+ +
+ 回路 + (二重昇圧回路数 +
+ +
+ 回路) +
+
+
+
+
+
+
HQJP-KA55-5
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
シリアル枚数総回路数
100
100
100
100
100
+
+
+
+
+
接続する
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
名称回路数昇圧回路数
KTN-CBD4C40
KTN-CBD4C40
KTN-CBD4C40
+
+
+
+ + +
+
+ HQJP-KA40-5 +
+
+
+
+
昇圧オプション
+
+ + + + + + + + + + + + + + + + + +
名称昇圧回路数
--
--
+
+
+
+ + +
+
+ HQJP-KA40-5 +
+
+
+
+
+ 綿調道区分 +
+ +
+ 回路 + (二重昇圧回路数 +
+ +
+ 回路) +
+
+
+
+
+
+ + モニターの選択 + +
+ +
+
+
+
+ {/*
*/} + {/* */} + {/* */} + {/*
*/} + + ) +} diff --git a/src/components/floor-plan/modal/circuitTrestle/step/type/AutoCircuitAllocation.jsx b/src/components/floor-plan/modal/circuitTrestle/step/type/AutoCircuitAllocation.jsx new file mode 100644 index 00000000..026bbaee --- /dev/null +++ b/src/components/floor-plan/modal/circuitTrestle/step/type/AutoCircuitAllocation.jsx @@ -0,0 +1,17 @@ +import { useMessage } from '@/hooks/useMessage' + +export default function AutoCircuitAllocation() { + const { getMessage } = useMessage() + return ( +
+
+
+
+ + +
+
+
+
+ ) +} diff --git a/src/components/floor-plan/modal/circuitTrestle/step/type/PassivityCircuitAllocation.jsx b/src/components/floor-plan/modal/circuitTrestle/step/type/PassivityCircuitAllocation.jsx new file mode 100644 index 00000000..5233d992 --- /dev/null +++ b/src/components/floor-plan/modal/circuitTrestle/step/type/PassivityCircuitAllocation.jsx @@ -0,0 +1,104 @@ +import { useMessage } from '@/hooks/useMessage' + +export default function PassivityCircuitAllocation() { + const { getMessage } = useMessage() + const moduleData = { + header: [ + { name: getMessage('屋根面'), prop: 'roofShape' }, + { + name: getMessage('Q.TRON M-G2'), + prop: 'moduleName', + }, + { + name: getMessage('発電量 (kW)'), + prop: 'powerGeneration', + }, + ], + rows: [ + { + roofShape: { name: 'M 1' }, + moduleName: { name: '8' }, + powerGeneration: { name: '3,400' }, + }, + { + roofShape: { name: 'M 1' }, + moduleName: { name: '8' }, + powerGeneration: { name: '3,400' }, + }, + ], + } + return ( + <> +
+
+
{getMessage('modal.circuit.trestle.setting.circuit.allocation.passivity')}
+
{getMessage('modal.circuit.trestle.setting.circuit.allocation.passivity.info')}
+
+ + + + {moduleData.header.map((header) => ( + + ))} + + + + {moduleData.rows.map((row, index) => ( + + {moduleData.header.map((header) => ( + + ))} + + ))} + +
{header.name}
{row[header.prop].name}
+
+
+
+
+
+
+
+
{getMessage('modal.circuit.trestle.setting.circuit.allocation.passivity.selected.power.conditional')}
+
+
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+
+
+
+ + {getMessage('modal.circuit.trestle.setting.circuit.allocation.passivity.circuit.num')} + +
+ +
+
+
+
+ + + +
+
+ + ) +} diff --git a/src/locales/ja.json b/src/locales/ja.json index f8f15c2e..bf2fb5ef 100644 --- a/src/locales/ja.json +++ b/src/locales/ja.json @@ -122,6 +122,24 @@ "modal.module.basic.setting.passivity.placement": "手動配置", "modal.module.basic.setting.auto.placement": "設定値に自動配置", "plan.menu.module.circuit.setting.circuit.trestle.setting": "回路と架台の設定", + "modal.circuit.trestle.setting": "回路と架台設定", + "modal.circuit.trestle.setting.power.conditional.select": "パワーコンディショナーを選択", + "modal.circuit.trestle.setting.power.conditional.select.name": "名称", + "modal.circuit.trestle.setting.power.conditional.select.rated.output": "定格出力", + "modal.circuit.trestle.setting.power.conditional.select.circuit.amount": "回路数", + "modal.circuit.trestle.setting.power.conditional.select.max.connection": "最大接続枚数", + "modal.circuit.trestle.setting.power.conditional.select.max.overload": "過積最大枚数", + "modal.circuit.trestle.setting.power.conditional.select.output.current": "出力電流", + "modal.circuit.trestle.setting.circuit.allocation": "回路割り当て", + "modal.circuit.trestle.setting.circuit.allocation.auto": "自動回路割り当て", + "modal.circuit.trestle.setting.circuit.allocation.passivity": "手動回路割当", + "modal.circuit.trestle.setting.circuit.allocation.passivity.info": "同じ回路のモジュールを選択状態にした後、 [番号確認]ボタンを押すと番号が割り当てられます。", + "modal.circuit.trestle.setting.circuit.allocation.passivity.selected.power.conditional": "選択したパワーコンディショナー", + "modal.circuit.trestle.setting.circuit.allocation.passivity.circuit.num": "設定する回路番号 (1~)", + "modal.circuit.trestle.setting.circuit.allocation.passivity.selected.power.conditional.reset": "選択されたパワーコンディショナーの回路番号の初期化", + "modal.circuit.trestle.setting.circuit.allocation.passivity.all.power.conditional.reset": "すべての回路番号の初期化", + "modal.circuit.trestle.setting.circuit.allocation.passivity.circuit.num.fix": "番号確定", + "modal.circuit.trestle.setting.step.up.allocation": "昇圧設定", "plan.menu.module.circuit.setting.plan.orientation": "図面方位の適用", "plan.menu.estimate": "見積", "plan.menu.estimate.roof.alloc": "屋根面の割り当て", @@ -167,6 +185,7 @@ "modal.grid.copy.save": "保存", "modal.common.save": "保存", "modal.common.add": "追加", + "modal.common.prev": "以前", "modal.canvas.setting.font.plan.edit": "フォントとサイズの変更", "modal.canvas.setting.font.plan.edit.word": "文字フォントの変更", "modal.canvas.setting.font.plan.edit.flow": "フロー方向フォントの変更", diff --git a/src/locales/ko.json b/src/locales/ko.json index ee31c6c3..f3bd3daf 100644 --- a/src/locales/ko.json +++ b/src/locales/ko.json @@ -126,6 +126,24 @@ "modal.module.basic.setting.passivity.placement": "수동 배치", "modal.module.basic.setting.auto.placement": "설정값으로 자동 배치", "plan.menu.module.circuit.setting.circuit.trestle.setting": "회로 및 가대 설정", + "modal.circuit.trestle.setting": "회로 및 가대설정", + "modal.circuit.trestle.setting.power.conditional.select": "파워컨디셔너 선택", + "modal.circuit.trestle.setting.power.conditional.select.name": "명칭", + "modal.circuit.trestle.setting.power.conditional.select.rated.output": "정격출력", + "modal.circuit.trestle.setting.power.conditional.select.circuit.amount": "회로수", + "modal.circuit.trestle.setting.power.conditional.select.max.connection": "최대접속매수", + "modal.circuit.trestle.setting.power.conditional.select.max.overload": "과적최대매수", + "modal.circuit.trestle.setting.power.conditional.select.output.current": "출력전류", + "modal.circuit.trestle.setting.circuit.allocation": "회로 할당", + "modal.circuit.trestle.setting.circuit.allocation.auto": "자동 회로 할당", + "modal.circuit.trestle.setting.circuit.allocation.passivity": "수동 회로 할당", + "modal.circuit.trestle.setting.circuit.allocation.passivity.info": "동일한 회로의 모듈을 선택 상태로 만든 후 [번호 확정] 버튼을 누르면 번호가 할당됩니다.", + "modal.circuit.trestle.setting.circuit.allocation.passivity.selected.power.conditional": "선택된 파워컨디셔너", + "modal.circuit.trestle.setting.circuit.allocation.passivity.circuit.num": "설정할 회로번호(1~)", + "modal.circuit.trestle.setting.circuit.allocation.passivity.selected.power.conditional.reset": "선택된 파워컨디셔너의 회로번호 초기화", + "modal.circuit.trestle.setting.circuit.allocation.passivity.all.power.conditional.reset": "모든 회로번호 초기화", + "modal.circuit.trestle.setting.circuit.allocation.passivity.circuit.num.fix": "번호 확정", + "modal.circuit.trestle.setting.step.up.allocation": "승압 설정", "plan.menu.module.circuit.setting.plan.orientation": "도면 방위 적용", "plan.menu.estimate": "견적서", "plan.menu.estimate.roof.alloc": "지붕면 할당", @@ -171,6 +189,8 @@ "modal.grid.copy.save": "저장", "modal.common.save": "저장", "modal.common.add": "추가", + "modal.common.prev": "이전", + "modal.common.next": "다음", "modal.canvas.setting.font.plan.edit": "글꼴 및 크기 변경", "modal.canvas.setting.font.plan.edit.word": "문자 글꼴 변경", "modal.canvas.setting.font.plan.edit.flow": "흐름 방향 글꼴 변경", From 8d0e2b59a0b2dea115f042bc562bc28b1a2561b1 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Wed, 16 Oct 2024 10:13:54 +0900 Subject: [PATCH 24/68] =?UTF-8?q?POLYGON=5FTYPE=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 | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/common/common.js b/src/common/common.js index 09f742f2..ebb394d3 100644 --- a/src/common/common.js +++ b/src/common/common.js @@ -106,3 +106,8 @@ export const INPUT_TYPE = { FREE: 'free', DIMENSION: 'dimension', } + +export const POLYGON_TYPE = { + ROOF: 'roof', + TRESTLE: 'trestle', +} From e172b2cd334dc785a4d10764de6b2b29e62873eb Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Wed, 16 Oct 2024 10:26:30 +0900 Subject: [PATCH 25/68] =?UTF-8?q?=EB=B0=B0=EC=B9=98=EB=A9=B4=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../placementShape/PlacementShapeDrawing.jsx | 11 +- src/hooks/surface/usePlacementShapeDrawing.js | 825 ++++++++++++++++++ src/store/placementShapeDrawingAtom.js | 70 ++ 3 files changed, 903 insertions(+), 3 deletions(-) create mode 100644 src/hooks/surface/usePlacementShapeDrawing.js create mode 100644 src/store/placementShapeDrawingAtom.js diff --git a/src/components/floor-plan/modal/placementShape/PlacementShapeDrawing.jsx b/src/components/floor-plan/modal/placementShape/PlacementShapeDrawing.jsx index c705b0eb..62bbab5d 100644 --- a/src/components/floor-plan/modal/placementShape/PlacementShapeDrawing.jsx +++ b/src/components/floor-plan/modal/placementShape/PlacementShapeDrawing.jsx @@ -8,6 +8,7 @@ import Diagonal from '@/components/floor-plan/modal/lineTypes/Diagonal' import { useOuterLineWall } from '@/hooks/roofcover/useOuterLineWall' import { OUTER_LINE_TYPE } from '@/store/outerLineAtom' import OuterLineWall from '@/components/floor-plan/modal/lineTypes/OuterLineWall' +import { usePlacementShapeDrawing } from '@/hooks/surface/usePlacementShapeDrawing' export default function PlacementShapeDrawing({ setShowPlaceShapeDrawingModal }) { const { getMessage } = useMessage() @@ -45,7 +46,7 @@ export default function PlacementShapeDrawing({ setShowPlaceShapeDrawingModal }) outerLineDiagonalLengthRef, handleRollback, handleFix, - } = useOuterLineWall() + } = usePlacementShapeDrawing(setShowPlaceShapeDrawingModal) const outerLineProps = { length1, @@ -145,8 +146,12 @@ export default function PlacementShapeDrawing({ setShowPlaceShapeDrawingModal })
- - + +
diff --git a/src/hooks/surface/usePlacementShapeDrawing.js b/src/hooks/surface/usePlacementShapeDrawing.js new file mode 100644 index 00000000..b07a8031 --- /dev/null +++ b/src/hooks/surface/usePlacementShapeDrawing.js @@ -0,0 +1,825 @@ +import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil' +import { + adsorptionPointAddModeState, + adsorptionPointModeState, + adsorptionRangeState, + canvasState, + dotLineIntervalSelector, + verticalHorizontalModeState, +} from '@/store/canvasAtom' +import { useEvent } from '@/hooks/useEvent' +import { useMouse } from '@/hooks/useMouse' +import { useLine } from '@/hooks/useLine' +import { useTempGrid } from '@/hooks/useTempGrid' +import { useEffect, useRef } from 'react' +import { distanceBetweenPoints, setSurfaceShapePattern } from '@/util/canvas-util' +import { fabric } from 'fabric' +import { calculateAngle } from '@/util/qpolygon-utils' +import { + placementShapeDrawingAngle1State, + placementShapeDrawingAngle2State, + placementShapeDrawingArrow1State, + placementShapeDrawingArrow2State, + placementShapeDrawingDiagonalState, + placementShapeDrawingFixState, + placementShapeDrawingLength1State, + placementShapeDrawingLength2State, + placementShapeDrawingPointsState, + placementShapeDrawingTypeState, +} from '@/store/placementShapeDrawingAtom' +import { usePolygon } from '@/hooks/usePolygon' +import { POLYGON_TYPE } from '@/common/common' + +// 면형상 배치 +export function usePlacementShapeDrawing(setShowPlaceShapeDrawingModal) { + const canvas = useRecoilValue(canvasState) + const { addCanvasMouseEventListener, addDocumentEventListener, removeAllMouseEventListeners, removeAllDocumentEventListeners, removeMouseEvent } = + useEvent() + const { getIntersectMousePoint } = useMouse() + const { addLine, removeLine } = useLine() + const { addPolygonByLines } = usePolygon() + const { tempGridMode } = useTempGrid() + + const verticalHorizontalMode = useRecoilValue(verticalHorizontalModeState) + const adsorptionPointAddMode = useRecoilValue(adsorptionPointAddModeState) + const adsorptionPointMode = useRecoilValue(adsorptionPointModeState) + const adsorptionRange = useRecoilValue(adsorptionRangeState) + const interval = useRecoilValue(dotLineIntervalSelector) // 가로 세로 간격 + + const length1Ref = useRef(null) + const length2Ref = useRef(null) + const angle1Ref = useRef(null) + const angle2Ref = useRef(null) + const [length1, setLength1] = useRecoilState(placementShapeDrawingLength1State) + const [length2, setLength2] = useRecoilState(placementShapeDrawingLength2State) + const [arrow1, setArrow1] = useRecoilState(placementShapeDrawingArrow1State) + const [arrow2, setArrow2] = useRecoilState(placementShapeDrawingArrow2State) + const [points, setPoints] = useRecoilState(placementShapeDrawingPointsState) + const [type, setType] = useRecoilState(placementShapeDrawingTypeState) + const [angle1, setAngle1] = useRecoilState(placementShapeDrawingAngle1State) + const [angle2, setAngle2] = useRecoilState(placementShapeDrawingAngle2State) + const [outerLineDiagonalLength, setOuterLineDiagonalLength] = useRecoilState(placementShapeDrawingDiagonalState) + const setOuterLineFix = useSetRecoilState(placementShapeDrawingFixState) + const arrow1Ref = useRef(arrow1) + const arrow2Ref = useRef(arrow2) + + const outerLineDiagonalLengthRef = useRef(null) + + const isFix = useRef(false) + + useEffect(() => { + if (adsorptionPointAddMode || tempGridMode) { + return + } + + addCanvasMouseEventListener('mouse:down', mouseDown) + clear() + }, [verticalHorizontalMode, points, adsorptionPointAddMode, adsorptionPointMode, adsorptionRange, interval, tempGridMode]) + + useEffect(() => { + arrow1Ref.current = arrow1 + }, [arrow1]) + + useEffect(() => { + arrow2Ref.current = arrow2 + }, [arrow2]) + + useEffect(() => { + clear() + addDocumentEventListener('keydown', document, keydown[type]) + }, [type]) + + const clear = () => { + setLength1(0) + setLength2(0) + + setArrow1('') + setArrow2('') + + setAngle1(0) + setAngle2(0) + + setOuterLineDiagonalLength(0) + } + + const mouseDown = (e) => { + let pointer = getIntersectMousePoint(e) + + if (points.length === 0) { + setPoints((prev) => [...prev, pointer]) + } else { + const lastPoint = points[points.length - 1] + let newPoint = { x: pointer.x, y: pointer.y } + const length = distanceBetweenPoints(lastPoint, newPoint) + if (verticalHorizontalMode) { + const vector = { + x: pointer.x - points[points.length - 1].x, + y: pointer.y - points[points.length - 1].y, + } + const slope = Math.abs(vector.y / vector.x) // 기울기 계산 + + let scaledVector + if (slope >= 1) { + // 기울기가 1 이상이면 x축 방향으로 그림 + scaledVector = { + x: 0, + y: vector.y >= 0 ? Number(length) : -Number(length), + } + } else { + // 기울기가 1 미만이면 y축 방향으로 그림 + scaledVector = { + x: vector.x >= 0 ? Number(length) : -Number(length), + y: 0, + } + } + + const verticalLength = scaledVector.y + const horizontalLength = scaledVector.x + + newPoint = { + x: lastPoint.x + horizontalLength, + y: lastPoint.y + verticalLength, + } + } + setPoints((prev) => [...prev, newPoint]) + } + } + + useEffect(() => { + canvas + ?.getObjects() + .filter((obj) => obj.name === 'placementShapeDrawingLine' || obj.name === 'helpGuideLine') + .forEach((obj) => { + removeLine(obj) + }) + + canvas?.remove(canvas?.getObjects().find((obj) => obj.name === 'placementShapeDrawingStartPoint')) + + if (points.length === 0) { + setOuterLineFix(true) + removeAllDocumentEventListeners() + return + } + + addDocumentEventListener('keydown', document, keydown[type]) + + if (points.length === 1) { + const point = new fabric.Circle({ + radius: 5, + fill: 'transparent', + stroke: 'red', + left: points[0].x - 5, + top: points[0].y - 5, + selectable: false, + name: 'placementShapeDrawingStartPoint', + }) + + canvas?.add(point) + } else { + setOuterLineFix(false) + canvas + .getObjects() + .filter((obj) => obj.name === 'placementShapeDrawingPoint') + .forEach((obj) => { + canvas.remove(obj) + }) + points.forEach((point, idx) => { + const circle = new fabric.Circle({ + left: point.x, + top: point.y, + visible: false, + name: 'placementShapeDrawingPoint', + }) + canvas.add(circle) + }) + points.forEach((point, idx) => { + if (idx === 0) { + return + } + drawLine(points[idx - 1], point, idx) + }) + + const lastPoint = points[points.length - 1] + const firstPoint = points[0] + + if (isFix.current) { + removeAllMouseEventListeners() + removeAllDocumentEventListeners() + + const lines = canvas?.getObjects().filter((obj) => obj.name === 'placementShapeDrawingLine') + const roof = addPolygonByLines(lines, { + stroke: 'black', + strokeWidth: 3, + selectable: true, + name: POLYGON_TYPE.ROOF, + }) + + setSurfaceShapePattern(roof) + + lines.forEach((line) => { + removeLine(line) + }) + + canvas?.renderAll() + setShowPlaceShapeDrawingModal(false) + } + + if (points.length < 3) { + return + } + + /*if (lastPoint.x === firstPoint.x && lastPoint.y === firstPoint.y) { + return + } + + if (lastPoint.x === firstPoint.x || lastPoint.y === firstPoint.y) { + let isAllRightAngle = true + + const firstPoint = points[0] + + points.forEach((point, idx) => { + if (idx === 0 || !isAllRightAngle) { + return + } + + const angle = calculateAngle(point, firstPoint) + if (angle % 90 !== 0) { + isAllRightAngle = false + } + }) + + if (isAllRightAngle) { + return + } + const line = new QLine([lastPoint.x, lastPoint.y, firstPoint.x, firstPoint.y], { + stroke: 'grey', + strokeWidth: 1, + selectable: false, + name: 'helpGuideLine', + }) + + canvas?.add(line) + addLineText(line) + } else { + const guideLine1 = new QLine([lastPoint.x, lastPoint.y, lastPoint.x, firstPoint.y], { + stroke: 'grey', + strokeWidth: 1, + strokeDashArray: [1, 1, 1], + name: 'helpGuideLine', + }) + + const guideLine2 = new QLine([guideLine1.x2, guideLine1.y2, firstPoint.x, firstPoint.y], { + stroke: 'grey', + strokeWidth: 1, + strokeDashArray: [1, 1, 1], + name: 'helpGuideLine', + }) + if (guideLine1.length > 0) { + canvas?.add(guideLine1) + addLineText(guideLine1) + } + + canvas?.add(guideLine2) + + addLineText(guideLine2) + }*/ + } + }, [points]) + + const drawLine = (point1, point2, idx) => { + addLine([point1.x, point1.y, point2.x, point2.y], { + stroke: 'black', + strokeWidth: 3, + idx: idx, + selectable: true, + name: 'placementShapeDrawingLine', + x1: point1.x, + y1: point1.y, + x2: point2.x, + y2: point2.y, + }) + } + + // 직각 완료될 경우 확인 + const checkRightAngle = (direction) => { + const activeElem = document.activeElement + + const canDirection = + direction === '↓' || direction === '↑' + ? arrow1Ref.current === '←' || arrow1Ref.current === '→' + : arrow1Ref.current === '↓' || arrow1Ref.current === '↑' + + if (activeElem === length1Ref.current || activeElem === angle1Ref.current) { + setArrow1(direction) + arrow1Ref.current = direction + length2Ref.current.focus() + } else if (activeElem === length2Ref.current || activeElem === angle2Ref.current) { + if (!canDirection) { + return + } + setArrow2(direction) + arrow2Ref.current = direction + } + + const length1Num = Number(length1Ref.current.value) / 10 + const length2Num = Number(length2Ref.current.value) / 10 + + if (points.length === 0) { + return + } + + if (length1Num === 0 || length2Num === 0 || arrow1Ref.current === '' || arrow2Ref.current === '') { + return + } + + if (arrow1Ref.current === '↓' && arrow2Ref.current === '→') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x + length2Num, y: prev[prev.length - 1].y + length1Num }] + }) + } else if (arrow1Ref.current === '↓' && arrow2Ref.current === '←') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x - length2Num, y: prev[prev.length - 1].y + length1Num }] + }) + } else if (arrow1Ref.current === '↑' && arrow2Ref.current === '→') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x + length2Num, y: prev[prev.length - 1].y - length1Num }] + }) + } else if (arrow1Ref.current === '↑' && arrow2Ref.current === '←') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x - length2Num, y: prev[prev.length - 1].y - length1Num }] + }) + } else if (arrow1Ref.current === '→' && arrow2Ref.current === '↓') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x + length1Num, y: prev[prev.length - 1].y + length2Num }] + }) + } else if (arrow1Ref.current === '→' && arrow2Ref.current === '↑') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x + length1Num, y: prev[prev.length - 1].y - length2Num }] + }) + } else if (arrow1Ref.current === '←' && arrow2Ref.current === '↓') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x - length1Num, y: prev[prev.length - 1].y + length2Num }] + }) + } else if (arrow1Ref.current === '←' && arrow2Ref.current === '↑') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x - length1Num, y: prev[prev.length - 1].y - length2Num }] + }) + } + } + + //이구배 완료될 경우 확인 ↓, ↑, ←, → + const checkDoublePitch = (direction) => { + const activeElem = document.activeElement + + const canDirection = + direction === '↓' || direction === '↑' + ? arrow1Ref.current === '←' || arrow1Ref.current === '→' + : arrow1Ref.current === '↓' || arrow1Ref.current === '↑' + + if (activeElem === length1Ref.current || activeElem === angle1Ref.current) { + setArrow1(direction) + arrow1Ref.current = direction + angle2Ref.current.focus() + } else if (activeElem === length2Ref.current || activeElem === angle2Ref.current) { + if (!canDirection) { + return + } + setArrow2(direction) + arrow2Ref.current = direction + } + + const angle1Value = angle1Ref.current.value + const angle2Value = angle2Ref.current.value + const length1Value = length1Ref.current.value + const length2Value = length2Ref.current.value + + const arrow1Value = arrow1Ref.current + const arrow2Value = arrow2Ref.current + + if (angle1Value !== 0 && length1Value !== 0 && angle2Value !== 0 && arrow1Value !== '' && arrow2Value !== '') { + if (arrow1Value === '↓' && arrow2Value === '→') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x + length1Value / 10, y: prev[prev.length - 1].y + length2Value / 10 }] + }) + } else if (arrow1Value === '↓' && arrow2Value === '←') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x - length1Value / 10, y: prev[prev.length - 1].y + length2Value / 10 }] + }) + } else if (arrow1Value === '↑' && arrow2Value === '→') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x + length1Value / 10, y: prev[prev.length - 1].y - length2Value / 10 }] + }) + } else if (arrow1Value === '↑' && arrow2Value === '←') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x - length1Value / 10, y: prev[prev.length - 1].y - length2Value / 10 }] + }) + } else if (arrow1Value === '→' && arrow2Value === '↓') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x + length2Value / 10, y: prev[prev.length - 1].y + length1Value / 10 }] + }) + } else if (arrow1Value === '→' && arrow2Value === '↑') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x + length2Value / 10, y: prev[prev.length - 1].y - length1Value / 10 }] + }) + } else if (arrow1Value === '←' && arrow2Value === '↓') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x - length2Value / 10, y: prev[prev.length - 1].y + length1Value / 10 }] + }) + } else if (arrow1Value === '←' && arrow2Value === '↑') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x - length2Value / 10, y: prev[prev.length - 1].y - length1Value / 10 }] + }) + } + + angle1Ref.current.focus() + } + } + + //대각선 완료될 경우 확인 + const checkDiagonal = (direction) => { + const activeElem = document.activeElement + const diagonalLength = outerLineDiagonalLengthRef.current.value // 대각선 길이 + + const length1Value = length1Ref.current.value + + if (diagonalLength <= length1Value) { + alert('대각선 길이는 직선 길이보다 길어야 합니다.') + return + } + + const canDirection = + direction === '↓' || direction === '↑' + ? arrow1Ref.current === '←' || arrow1Ref.current === '→' + : arrow1Ref.current === '↓' || arrow1Ref.current === '↑' + + if (activeElem === length1Ref.current) { + setArrow1(direction) + arrow1Ref.current = direction + } else if (activeElem === length2Ref.current || activeElem === angle2Ref.current) { + if (!canDirection) { + return + } + setArrow2(direction) + arrow2Ref.current = direction + } + + const arrow1Value = arrow1Ref.current + const arrow2Value = arrow2Ref.current + + const getLength2 = () => { + return Math.floor(Math.sqrt(diagonalLength ** 2 - length1Value ** 2)) + } + + const length2Value = getLength2() + + if (diagonalLength !== 0 && length1Value !== 0 && arrow1Value !== '') { + setLength2(getLength2()) + length2Ref.current.focus() + } + + if (length1Value !== 0 && length2Value !== 0 && arrow1Value !== '' && arrow2Value !== '') { + if (arrow1Value === '↓' && arrow2Value === '→') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x + length2Value / 10, y: prev[prev.length - 1].y + length1Value / 10 }] + }) + } else if (arrow1Value === '↓' && arrow2Value === '←') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x - length2Value / 10, y: prev[prev.length - 1].y + length1Value / 10 }] + }) + } else if (arrow1Value === '↑' && arrow2Value === '→') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x + length2Value / 10, y: prev[prev.length - 1].y - length1Value / 10 }] + }) + } else if (arrow1Value === '↑' && arrow2Value === '←') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x - length2Value / 10, y: prev[prev.length - 1].y - length1Value / 10 }] + }) + } else if (arrow1Value === '→' && arrow2Value === '↓') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x + length1Value / 10, y: prev[prev.length - 1].y + length2Value / 10 }] + }) + } else if (arrow1Value === '→' && arrow2Value === '↑') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [ + ...prev, + { + x: prev[prev.length - 1].x + length1Value / 10, + y: prev[prev.length - 1].y - length2Value / 10, + }, + ] + }) + } + } + } + + const keydown = { + outerLine: (e) => { + if (points.length === 0) { + return + } + // 포커스가 length1에 있지 않으면 length1에 포커스를 줌 + const activeElem = document.activeElement + if (activeElem !== length1Ref.current) { + length1Ref.current.focus() + } + + const key = e.key + + if (!length1Ref.current) { + return + } + + const lengthNum = Number(length1Ref.current.value) / 10 + if (lengthNum === 0) { + return + } + switch (key) { + case 'Down': // IE/Edge에서 사용되는 값 + case 'ArrowDown': { + setArrow1('↓') + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x, y: prev[prev.length - 1].y + lengthNum }] + }) + break + } + case 'Up': // IE/Edge에서 사용되는 값 + case 'ArrowUp': + setArrow1('↑') + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x, y: prev[prev.length - 1].y - lengthNum }] + }) + break + case 'Left': // IE/Edge에서 사용되는 값 + case 'ArrowLeft': + setArrow1('←') + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x - lengthNum, y: prev[prev.length - 1].y }] + }) + break + case 'Right': // IE/Edge에서 사용되는 값 + case 'ArrowRight': + setArrow1('→') + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x + lengthNum, y: prev[prev.length - 1].y }] + }) + break + } + }, + rightAngle: (e) => { + if (points.length === 0) { + return + } + const key = e.key + + const activeElem = document.activeElement + if (activeElem !== length1Ref.current && activeElem !== length2Ref.current) { + length1Ref.current.focus() + } + + switch (key) { + case 'Down': // IE/Edge에서 사용되는 값 + case 'ArrowDown': { + checkRightAngle('↓') + break + } + case 'Up': // IE/Edge에서 사용되는 값 + case 'ArrowUp': + checkRightAngle('↑') + break + case 'Left': // IE/Edge에서 사용되는 값 + case 'ArrowLeft': + checkRightAngle('←') + break + case 'Right': // IE/Edge에서 사용되는 값 + case 'ArrowRight': + checkRightAngle('→') + break + case 'Enter': + break + } + }, + doublePitch: (e) => { + if (points.length === 0) { + return + } + const key = e.key + switch (key) { + case 'Down': // IE/Edge에서 사용되는 값 + case 'ArrowDown': { + checkDoublePitch('↓') + break + } + case 'Up': // IE/Edge에서 사용되는 값 + case 'ArrowUp': + checkDoublePitch('↑') + break + case 'Left': // IE/Edge에서 사용되는 값 + case 'ArrowLeft': + checkDoublePitch('←') + break + case 'Right': // IE/Edge에서 사용되는 값 + case 'ArrowRight': + checkDoublePitch('→') + break + } + }, + angle: (e) => { + if (points.length === 0) { + return + } + const key = e.key + switch (key) { + case 'Enter': { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + const lastPoint = prev[prev.length - 1] + const length = length1Ref.current.value / 10 + const angle = angle1Ref.current.value + //lastPoint로부터 angle1만큼의 각도로 length1만큼의 길이를 가지는 선을 그림 + const radian = (angle * Math.PI) / 180 + + const x = lastPoint.x + length * Math.cos(radian) + const y = lastPoint.y - length * Math.sin(radian) + return [...prev, { x, y }] + }) + } + } + }, + diagonalLine: (e) => { + if (points.length === 0) { + return + } + + const key = e.key + switch (key) { + case 'Down': // IE/Edge에서 사용되는 값 + case 'ArrowDown': { + checkDiagonal('↓') + break + } + case 'Up': // IE/Edge에서 사용되는 값 + case 'ArrowUp': + checkDiagonal('↑') + break + case 'Left': // IE/Edge에서 사용되는 값 + case 'ArrowLeft': + checkDiagonal('←') + break + case 'Right': // IE/Edge에서 사용되는 값 + case 'ArrowRight': + checkDiagonal('→') + break + } + }, + } + + /** + * 일변전으로 돌아가기 + */ + const handleRollback = () => { + //points의 마지막 요소를 제거 + setPoints((prev) => prev.slice(0, prev.length - 1)) + } + + const handleFix = () => { + if (points.length < 3) { + return + } + + let isAllRightAngle = true + + const firstPoint = points[0] + + points.forEach((point, idx) => { + if (idx === 0 || !isAllRightAngle) { + return + } + + const angle = calculateAngle(point, firstPoint) + if (angle % 90 !== 0) { + isAllRightAngle = false + } + }) + + if (isAllRightAngle) { + alert('부정확한 다각형입니다.') + return + } + + setPoints((prev) => { + return [...prev, { x: prev[0].x, y: prev[0].y }] + }) + + isFix.current = true + } + + return { + points, + setPoints, + length1, + setLength1, + length2, + setLength2, + length1Ref, + length2Ref, + arrow1, + setArrow1, + arrow2, + setArrow2, + arrow1Ref, + arrow2Ref, + angle1, + setAngle1, + angle1Ref, + angle2, + setAngle2, + angle2Ref, + outerLineDiagonalLength, + setOuterLineDiagonalLength, + outerLineDiagonalLengthRef, + type, + setType, + handleFix, + handleRollback, + } +} diff --git a/src/store/placementShapeDrawingAtom.js b/src/store/placementShapeDrawingAtom.js new file mode 100644 index 00000000..b7a65107 --- /dev/null +++ b/src/store/placementShapeDrawingAtom.js @@ -0,0 +1,70 @@ +import { atom } from 'recoil' + +export const OUTER_LINE_TYPE = { + OUTER_LINE: 'outerLine', // 외벽선 + RIGHT_ANGLE: 'rightAngle', // 직각 + DOUBLE_PITCH: 'doublePitch', + ANGLE: 'angle', // 각도 + DIAGONAL_LINE: 'diagonalLine', // 대각선 +} + +/** + * 외벽선 작성에서 사용하는 recoilState + */ + +export const placementShapeDrawingLength1State = atom({ + //길이1 + key: 'placementShapeDrawingLength1State', + default: 0, +}) + +export const placementShapeDrawingLength2State = atom({ + // 길이2 + key: 'placementShapeDrawingLength2State', + default: 0, +}) + +export const placementShapeDrawingArrow1State = atom({ + // 방향1 + key: 'placementShapeDrawingArrow1State', + default: '', +}) + +export const placementShapeDrawingArrow2State = atom({ + // 방향2 + key: 'placementShapeDrawingArrow2State', + default: '', +}) + +export const placementShapeDrawingAngle1State = atom({ + // 각도1 + key: 'placementShapeDrawingAngle1State', + default: 0, +}) + +export const placementShapeDrawingAngle2State = atom({ + // 각도2 + key: 'placementShapeDrawingAngle2State', + default: 0, +}) + +export const placementShapeDrawingDiagonalState = atom({ + // 대각선 + key: 'placementShapeDrawingDiagonalState', + default: 0, +}) + +export const placementShapeDrawingTypeState = atom({ + key: 'placementShapeDrawingTypeState', + default: OUTER_LINE_TYPE.OUTER_LINE, +}) + +export const placementShapeDrawingPointsState = atom({ + key: 'placementShapeDrawingPointsState', + default: [], +}) + +export const placementShapeDrawingFixState = atom({ + key: 'placementShapeDrawingFixState', + default: false, +}) From 0bc97221a2321a8ac349141e93d8442eca187fa4 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Wed, 16 Oct 2024 10:55:48 +0900 Subject: [PATCH 26/68] =?UTF-8?q?validation=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../modal/roofShape/RoofShapePassivitySetting.jsx | 9 +++++++-- .../modal/wallLineOffset/WallLineOffsetSetting.jsx | 2 +- src/hooks/roofcover/useEavesGableEdit.js | 14 +++++++++++++- src/hooks/roofcover/useOuterLineWall.js | 1 + src/hooks/roofcover/useRoofAllocationSetting.js | 13 ++++++++++++- .../roofcover/useRoofShapePassivitySetting.js | 13 +++++++++++++ src/hooks/roofcover/useRoofShapeSetting.js | 12 ++++++++++++ src/hooks/roofcover/useWallLineOffsetSetting.js | 13 +++++++++++-- 8 files changed, 70 insertions(+), 7 deletions(-) diff --git a/src/components/floor-plan/modal/roofShape/RoofShapePassivitySetting.jsx b/src/components/floor-plan/modal/roofShape/RoofShapePassivitySetting.jsx index 8eaa4a49..44e56828 100644 --- a/src/components/floor-plan/modal/roofShape/RoofShapePassivitySetting.jsx +++ b/src/components/floor-plan/modal/roofShape/RoofShapePassivitySetting.jsx @@ -36,8 +36,13 @@ export default function RoofShapePassivitySetting({ setShowRoofShapePassivitySet
- {buttons.map((button) => ( - ))} diff --git a/src/components/floor-plan/modal/wallLineOffset/WallLineOffsetSetting.jsx b/src/components/floor-plan/modal/wallLineOffset/WallLineOffsetSetting.jsx index a013934c..7e5230b1 100644 --- a/src/components/floor-plan/modal/wallLineOffset/WallLineOffsetSetting.jsx +++ b/src/components/floor-plan/modal/wallLineOffset/WallLineOffsetSetting.jsx @@ -20,7 +20,7 @@ export default function WallLineOffsetSetting({ setShowWallLineOffsetSettingModa length2Ref, handleSave, wallLineEditRef, - } = useWallLineOffsetSetting() + } = useWallLineOffsetSetting(setShowWallLineOffsetSettingModal) const wallLineProps = { length1Ref, diff --git a/src/hooks/roofcover/useEavesGableEdit.js b/src/hooks/roofcover/useEavesGableEdit.js index 96ec864f..083d8e03 100644 --- a/src/hooks/roofcover/useEavesGableEdit.js +++ b/src/hooks/roofcover/useEavesGableEdit.js @@ -6,9 +6,11 @@ import { useEvent } from '@/hooks/useEvent' import { LINE_TYPE } from '@/common/common' import { useLine } from '@/hooks/useLine' import { useMode } from '@/hooks/useMode' +import { outerLineFixState } from '@/store/outerLineAtom' +import { useSwal } from '@/hooks/useSwal' // 처마.케라바 변경 -export function useEavesGableEdit() { +export function useEavesGableEdit(setShowEavesGableEditModal) { const canvas = useRecoilValue(canvasState) const { getMessage } = useMessage() const { addCanvasMouseEventListener, initEvent } = useEvent() @@ -21,6 +23,7 @@ export function useEavesGableEdit() { const [type, setType] = useState(TYPES.EAVES) const typeRef = useRef(TYPES.EAVES) const { removeLine } = useLine() + const { swalFire } = useSwal() const { drawRoofPolygon } = useMode() @@ -28,6 +31,7 @@ export function useEavesGableEdit() { const offsetRef = useRef(null) const widthRef = useRef(null) const radioTypeRef = useRef('1') // 각 페이지에서 사용하는 radio type + const outerLineFix = useRecoilValue(outerLineFixState) const buttonMenu = [ { id: 1, name: getMessage('eaves'), type: TYPES.EAVES }, @@ -36,6 +40,14 @@ export function useEavesGableEdit() { { id: 4, name: getMessage('shed'), type: TYPES.SHED }, ] + useEffect(() => { + const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') + if (!outerLineFix || outerLines.length === 0) { + swalFire({ text: '외벽선이 없습니다.' }) + setShowEavesGableEditModal(false) + } + }, []) + useEffect(() => { const wallLines = canvas.getObjects().filter((obj) => obj.name === 'wallLine') wallLines.forEach((wallLine) => { diff --git a/src/hooks/roofcover/useOuterLineWall.js b/src/hooks/roofcover/useOuterLineWall.js index 269ef24f..07dab81e 100644 --- a/src/hooks/roofcover/useOuterLineWall.js +++ b/src/hooks/roofcover/useOuterLineWall.js @@ -207,6 +207,7 @@ export function useOuterLineWall(setShowOutlineModal) { removeAllMouseEventListeners() removeAllDocumentEventListeners() canvas?.renderAll() + setOuterLineFix(true) setShowOutlineModal(false) } diff --git a/src/hooks/roofcover/useRoofAllocationSetting.js b/src/hooks/roofcover/useRoofAllocationSetting.js index fe321c1e..9a299ca3 100644 --- a/src/hooks/roofcover/useRoofAllocationSetting.js +++ b/src/hooks/roofcover/useRoofAllocationSetting.js @@ -1,13 +1,16 @@ import { useRecoilValue } from 'recoil' import { canvasState } from '@/store/canvasAtom' -import { useState } from 'react' +import { useEffect, useState } from 'react' import { setSurfaceShapePattern } from '@/util/canvas-util' import { splitPolygonWithLines } from '@/util/qpolygon-utils' +import { useSwal } from '@/hooks/useSwal' // 지붕면 할당 export function useRoofAllocationSetting(setShowRoofAllocationSettingModal) { const canvas = useRecoilValue(canvasState) + const { swalFire } = useSwal() + const roofMaterials = [ { id: 'A', @@ -71,6 +74,14 @@ export function useRoofAllocationSetting(setShowRoofAllocationSettingModal) { const [selectedRoofMaterial, setSelectedRoofMaterial] = useState(roofMaterials[0]) + useEffect(() => { + const roofBases = canvas.getObjects().filter((obj) => obj.name === 'roofBase') + if (roofBases.length === 0) { + swalFire({ text: '할당할 지붕이 없습니다.' }) + setShowRoofAllocationSettingModal(false) + } + }, []) + const onAddRoofMaterial = () => { setValues([...values, selectedRoofMaterial]) } diff --git a/src/hooks/roofcover/useRoofShapePassivitySetting.js b/src/hooks/roofcover/useRoofShapePassivitySetting.js index 8efb53c6..217e71bc 100644 --- a/src/hooks/roofcover/useRoofShapePassivitySetting.js +++ b/src/hooks/roofcover/useRoofShapePassivitySetting.js @@ -7,6 +7,8 @@ import { useEvent } from '@/hooks/useEvent' import { LINE_TYPE } from '@/common/common' import { useMode } from '@/hooks/useMode' import { usePolygon } from '@/hooks/usePolygon' +import { outerLineFixState } from '@/store/outerLineAtom' +import { useSwal } from '@/hooks/useSwal' //지붕형상 수동 설정 export function useRoofShapePassivitySetting(setShowRoofShapePassivitySettingModal) { @@ -18,6 +20,7 @@ export function useRoofShapePassivitySetting(setShowRoofShapePassivitySettingMod const canvas = useRecoilValue(canvasState) const { getMessage } = useMessage() const { showLine, hideLine } = useLine() + const { swalFire } = useSwal() const { addCanvasMouseEventListener, initEvent } = useEvent() const { drawRoofPolygon } = useMode() const { addPolygonByLines } = usePolygon() @@ -36,6 +39,16 @@ export function useRoofShapePassivitySetting(setShowRoofShapePassivitySettingMod { id: 3, name: getMessage('windage'), type: TYPES.SHED }, ] + const outerLineFix = useRecoilValue(outerLineFixState) + + useEffect(() => { + const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') + if (!outerLineFix || outerLines.length === 0) { + swalFire({ text: '외벽선이 없습니다.' }) + setShowRoofShapePassivitySettingModal(false) + } + }, []) + useEffect(() => { addCanvasMouseEventListener('mouse:down', mouseDown) const wallLines = canvas.getObjects().filter((obj) => obj.name === 'wallLine') diff --git a/src/hooks/roofcover/useRoofShapeSetting.js b/src/hooks/roofcover/useRoofShapeSetting.js index e8bf3684..ddf94e9c 100644 --- a/src/hooks/roofcover/useRoofShapeSetting.js +++ b/src/hooks/roofcover/useRoofShapeSetting.js @@ -6,11 +6,14 @@ import { LINE_TYPE, MENU } from '@/common/common' import { usePolygon } from '@/hooks/usePolygon' import { useMode } from '@/hooks/useMode' import { useLine } from '@/hooks/useLine' +import { outerLineFixState } from '@/store/outerLineAtom' +import { useSwal } from '@/hooks/useSwal' // 지붕형상 설정 export function useRoofShapeSetting(setShowRoofShapeSettingModal) { const [shapeNum, setShapeNum] = useState(1) const [buttonAct, setButtonAct] = useState(1) + const { swalFire } = useSwal() const { getMessage } = useMessage() const canvas = useRecoilValue(canvasState) const { addPolygonByLines } = usePolygon() @@ -28,9 +31,18 @@ export function useRoofShapeSetting(setShowRoofShapeSettingModal) { const { hideLine, showLine } = useLine() const setCurrentMenu = useSetRecoilState(currentMenuState) + const outerLineFix = useRecoilValue(outerLineFixState) const history = useRef([]) + useEffect(() => { + const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') + if (!outerLineFix || outerLines.length === 0) { + swalFire({ text: '외벽선이 없습니다.' }) + setShowRoofShapeSettingModal(false) + } + }, []) + useEffect(() => { if (shapeNum !== 4) { return diff --git a/src/hooks/roofcover/useWallLineOffsetSetting.js b/src/hooks/roofcover/useWallLineOffsetSetting.js index 61d7d4ce..6f65b58e 100644 --- a/src/hooks/roofcover/useWallLineOffsetSetting.js +++ b/src/hooks/roofcover/useWallLineOffsetSetting.js @@ -4,12 +4,14 @@ import { useEffect, useRef, useState } from 'react' import { useMessage } from '@/hooks/useMessage' import { useEvent } from '@/hooks/useEvent' import { useLine } from '@/hooks/useLine' +import { useSwal } from '@/hooks/useSwal' // 외벽선 편집 및 오프셋 -export function useWallLineOffsetSetting() { +export function useWallLineOffsetSetting(setShowWallLineOffsetSettingModal) { const canvas = useRecoilValue(canvasState) const { showLine, addLine } = useLine() const { getMessage } = useMessage() + const { swalFire } = useSwal() const { addCanvasMouseEventListener, initEvent } = useEvent() const wallLineEditRef = useRef(null) const length1Ref = useRef(null) @@ -19,6 +21,8 @@ export function useWallLineOffsetSetting() { const arrow1Ref = useRef(null) const arrow2Ref = useRef(null) + const [isLoading, setIsLoading] = useState(false) + const drawLine = (point1, point2, idx, direction = currentWallLineRef.current.direction) => { const line = addLine([point1.x, point1.y, point2.x, point2.y], { stroke: 'black', @@ -51,6 +55,8 @@ export function useWallLineOffsetSetting() { useEffect(() => { const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') if (outerLines.length === 0) { + swalFire({ text: '외벽선이 없습니다.' }) + setShowWallLineOffsetSettingModal(false) return } outerLines.forEach((outerLine) => { @@ -69,7 +75,7 @@ export function useWallLineOffsetSetting() { addCircleByLine(currentWallLineRef.current) addCanvasMouseEventListener('mouse:down', mouseDown) - + setIsLoading(true) return () => { removeOuterLineEditCircle() canvas.discardActiveObject() @@ -78,6 +84,9 @@ export function useWallLineOffsetSetting() { }, []) useEffect(() => { + if (!isLoading) { + return + } removeOuterLineEditCircle() addCanvasMouseEventListener('mouse:down', mouseDown) if (type === TYPES.WALL_LINE_EDIT) { From 7a548fbcce4f756bfbfb98741eb19f3888c395c5 Mon Sep 17 00:00:00 2001 From: Daseul Kim Date: Wed, 16 Oct 2024 11:14:28 +0900 Subject: [PATCH 27/68] =?UTF-8?q?feat:=20=EC=8B=A0=EA=B7=9C=20canvas=20pla?= =?UTF-8?q?n=20=EC=83=9D=EC=84=B1=20=EC=8B=9C=20=EB=B3=B5=EC=A0=9C=20?= =?UTF-8?q?=EC=97=AC=EB=B6=80=20=EC=84=A0=ED=83=9D=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/floor-plan/CanvasLayout.jsx | 43 +++++++++++++++++++--- src/hooks/usePlan.js | 6 +++ 2 files changed, 43 insertions(+), 6 deletions(-) diff --git a/src/components/floor-plan/CanvasLayout.jsx b/src/components/floor-plan/CanvasLayout.jsx index 62cc737b..c2c54251 100644 --- a/src/components/floor-plan/CanvasLayout.jsx +++ b/src/components/floor-plan/CanvasLayout.jsx @@ -21,7 +21,7 @@ export default function CanvasLayout() { const { getMessage } = useMessage() const { swalFire } = useSwal() - const { getCanvasByObjectNo, delCanvasById, checkModifiedCanvasPlan, saveCanvas } = usePlan() + const { getCanvasByObjectNo, delCanvasById, checkModifiedCanvasPlan, saveCanvas, currentCanvasData } = usePlan() const handleCurrentPlan = (newCurrentId) => { // console.log('currentPlan newCurrentId: ', newCurrentId) @@ -84,12 +84,25 @@ export default function CanvasLayout() { } } - const addNewPlan = () => { + const addEmptyPlan = () => { setPlans([...plans, { id: planNum, name: `Plan ${planNum + 1}`, objectNo: `${objectNo}` }]) handleCurrentPlan(planNum) setPlanNum(planNum + 1) } + const addCopyPlan = () => { + const copyPlan = { + id: planNum, + name: `Plan ${planNum + 1}`, + objectNo: `${objectNo}`, + userId: sessionState.userId, + canvasStatus: currentCanvasData(), + } + setPlans((plans) => [...plans, copyPlan]) + handleCurrentPlan(planNum) + setPlanNum(planNum + 1) + } + useEffect(() => { if (!currentCanvasPlan) { getCanvasByObjectNo(sessionState.userId, objectNo).then((res) => { @@ -100,7 +113,7 @@ export default function CanvasLayout() { handleCurrentPlan(res.at(-1).id) // last 데이터에 포커싱 setPlanNum(res.length) } else { - addNewPlan() + addEmptyPlan() } }) } @@ -128,9 +141,27 @@ export default function CanvasLayout() { ))}
- + {plans.length < 10 && ( + + )}
plan.isCurrent === true)} />
diff --git a/src/hooks/usePlan.js b/src/hooks/usePlan.js index 427fe511..6d74cd93 100644 --- a/src/hooks/usePlan.js +++ b/src/hooks/usePlan.js @@ -72,6 +72,11 @@ export function usePlan() { // }, 1000) } + const currentCanvasData = () => { + removeMouseLines() + return addCanvas() + } + /** * 실시간 캔버스 상태와 DB에 저장된 캔버스 상태를 비교하여 수정 여부를 판단 */ @@ -231,6 +236,7 @@ export function usePlan() { return { canvas, removeMouseLines, + currentCanvasData, saveCanvas, addCanvas, checkModifiedCanvasPlan, From 309658ef84c59cda95f4008df5c45fec1a0cfb5f Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Wed, 16 Oct 2024 12:52:56 +0900 Subject: [PATCH 28/68] =?UTF-8?q?=ED=82=A4=20=EA=B4=80=EB=A0=A8=20?= =?UTF-8?q?=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/floor-plan/CanvasFrame.jsx | 6 +++--- src/components/floor-plan/CanvasLayout.jsx | 6 +++++- src/components/floor-plan/CanvasMenu.jsx | 6 +++++- .../modal/placementShape/PlacementShapeDrawing.jsx | 8 ++++++-- 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/components/floor-plan/CanvasFrame.jsx b/src/components/floor-plan/CanvasFrame.jsx index 17f0018d..f94962d9 100644 --- a/src/components/floor-plan/CanvasFrame.jsx +++ b/src/components/floor-plan/CanvasFrame.jsx @@ -160,9 +160,9 @@ export default function CanvasFrame({ plan }) { {contextMenu.map((menus, index) => ( -
    - {menus.map((menu) => ( -
  • {menu.name}
  • +
      + {menus.map((menu, idx) => ( +
    • {menu.name}
    • ))}
    ))} diff --git a/src/components/floor-plan/CanvasLayout.jsx b/src/components/floor-plan/CanvasLayout.jsx index 62cc737b..c674fe39 100644 --- a/src/components/floor-plan/CanvasLayout.jsx +++ b/src/components/floor-plan/CanvasLayout.jsx @@ -111,7 +111,11 @@ export default function CanvasLayout() {
    {plans.map((plan) => ( -
    - {types.map((type) => ( - ))} From ba899a327973262618f6c2dcb1ba8718b0085b3e Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Wed, 16 Oct 2024 13:21:26 +0900 Subject: [PATCH 29/68] =?UTF-8?q?lengthText=EB=8A=94=20=EC=9D=B4=EB=8F=99?= =?UTF-8?q?=20=EA=B0=80=EB=8A=A5=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/roofcover/useRoofShapeSetting.js | 3 +++ src/hooks/useCanvasEvent.js | 2 ++ 2 files changed, 5 insertions(+) diff --git a/src/hooks/roofcover/useRoofShapeSetting.js b/src/hooks/roofcover/useRoofShapeSetting.js index ddf94e9c..1722e287 100644 --- a/src/hooks/roofcover/useRoofShapeSetting.js +++ b/src/hooks/roofcover/useRoofShapeSetting.js @@ -244,6 +244,9 @@ export function useRoofShapeSetting(setShowRoofShapeSettingModal) { } } + // 기존 wallLine 제거 + canvas?.remove(canvas.getObjects().filter((obj) => obj.name === 'wallLine')) + const polygon = addPolygonByLines(outerLines, { name: 'wallLine' }) polygon.lines = [...outerLines] diff --git a/src/hooks/useCanvasEvent.js b/src/hooks/useCanvasEvent.js index 55a34cb2..49705917 100644 --- a/src/hooks/useCanvasEvent.js +++ b/src/hooks/useCanvasEvent.js @@ -77,6 +77,8 @@ export function useCanvasEvent() { if (target.name === 'lengthText') { const x = target.left const y = target.top + target.lockMovementX = false + target.lockMovementY = false // Add a property to store the previous value const previousValue = target.text target.on('selected', (e) => { From eebc85e352019d49111de2dbc90e096e497cbb4f Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Wed, 16 Oct 2024 14:00:59 +0900 Subject: [PATCH 30/68] =?UTF-8?q?text=20=EC=9D=BC=EA=B4=84=EC=B2=98?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/useCanvasEvent.js | 9 ++++++++- src/store/canvasAtom.js | 2 +- src/util/qpolygon-utils.js | 3 ++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/hooks/useCanvasEvent.js b/src/hooks/useCanvasEvent.js index 49705917..cf3b6244 100644 --- a/src/hooks/useCanvasEvent.js +++ b/src/hooks/useCanvasEvent.js @@ -1,6 +1,6 @@ import { useState } from 'react' import { useRecoilState, useRecoilValue } from 'recoil' -import { canvasSizeState, currentObjectState } from '@/store/canvasAtom' +import { canvasSizeState, currentObjectState, fontFamilyState, fontSizeState } from '@/store/canvasAtom' import { QPolygon } from '@/components/fabric/QPolygon' // 캔버스에 필요한 이벤트 @@ -8,6 +8,8 @@ export function useCanvasEvent() { const [canvas, setCanvasForEvent] = useState(null) const [currentObject, setCurrentObject] = useRecoilState(currentObjectState) const canvasSize = useRecoilValue(canvasSizeState) + const fontSize = useRecoilValue(fontSizeState) + const fontFamily = useRecoilValue(fontFamilyState) // 기본적인 이벤트 필요시 추가 const attachDefaultEventOnCanvas = () => { @@ -74,6 +76,11 @@ export function useCanvasEvent() { }) } + if (target.type.toLowerCase().includes('text')) { + target.set({ fontSize }) + target.set({ fontFamily }) + } + if (target.name === 'lengthText') { const x = target.left const y = target.top diff --git a/src/store/canvasAtom.js b/src/store/canvasAtom.js index c622283b..7806b8f2 100644 --- a/src/store/canvasAtom.js +++ b/src/store/canvasAtom.js @@ -35,7 +35,7 @@ export const fontFamilyState = atom({ export const fontSizeState = atom({ key: 'fontSizeState', - default: 16, + default: 20, }) export const canvasSizeState = atom({ diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js index 73b9b285..063d9d4d 100644 --- a/src/util/qpolygon-utils.js +++ b/src/util/qpolygon-utils.js @@ -3,6 +3,7 @@ import { QLine } from '@/components/fabric/QLine' import { calculateIntersection, distanceBetweenPoints, findClosestPoint, getDirectionByPoint } from '@/util/canvas-util' import { QPolygon } from '@/components/fabric/QPolygon' import * as turf from '@turf/turf' +import { POLYGON_TYPE } from '@/common/common' const TWO_PI = Math.PI * 2 @@ -1149,7 +1150,7 @@ export const splitPolygonWithLines = (polygon) => { stroke: 'black', fill: 'transparent', strokeWidth: 3, - name: 'roof', + name: POLYGON_TYPE.ROOF, selectable: true, defense: defense, }) From 8b570a66de4f49cb11f9247e68d907019887bd4d Mon Sep 17 00:00:00 2001 From: minsik Date: Wed, 16 Oct 2024 15:10:37 +0900 Subject: [PATCH 31/68] =?UTF-8?q?-=20useContextMenu=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/useContextMenu.js | 153 ++++++++++++++++++++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 src/hooks/useContextMenu.js diff --git a/src/hooks/useContextMenu.js b/src/hooks/useContextMenu.js new file mode 100644 index 00000000..2cca39f3 --- /dev/null +++ b/src/hooks/useContextMenu.js @@ -0,0 +1,153 @@ +import { useRecoilValue } from 'recoil' +import { currentMenuState } from '@/store/canvasAtom' +import { useEffect, useState } from 'react' +import { MENU } from '@/common/common' + +export function useContextMenu() { + const currentMenu = useRecoilValue(currentMenuState) + const [contextMenu, setContextMenu] = useState([[]]) + + useEffect(() => { + switch (currentMenu) { + case MENU.PLAN_DRAWING: + setContextMenu([ + [ + { + id: 'gridMove', + name: '그리드 이동', + }, + { + id: 'gridCopy', + name: '그리드 복사', + }, + { + id: 'gridColorEdit', + name: '그리드 색 변경', + }, + { + id: 'remove', + name: '삭제', + }, + { + id: 'removeAll', + name: '전체 삭제', + }, + ], + ]) + break + case MENU.ROOF_COVERING.EXTERIOR_WALL_LINE: + case MENU.ROOF_COVERING.ROOF_SHAPE_SETTINGS: + case MENU.ROOF_COVERING.ROOF_SHAPE_PASSIVITY_SETTINGS: + case MENU.ROOF_COVERING.ROOF_SHAPE_EDITING: + case MENU.ROOF_COVERING.HELP_LINE_DRAWING: + case MENU.ROOF_COVERING.EAVES_KERAVA_EDIT: + case MENU.ROOF_COVERING.MOVEMENT_SHAPE_UPDOWN: + case MENU.ROOF_COVERING.OUTLINE_EDIT_OFFSET: + case MENU.ROOF_COVERING.ROOF_SHAPE_ALLOC: + case MENU.ROOF_COVERING.DEFAULT: + setContextMenu([ + [ + { + id: 'roofMaterialPlacement', + name: '지붕재 배치', + }, + { + id: 'roofMaterialRemove', + name: '지붕재 삭제', + }, + { + id: 'roofMaterialRemoveAll', + name: '지붕재 전체 삭제', + }, + { + id: 'selectMove', + name: '선택・이동', + }, + { + id: 'wallLineRemove', + name: '외벽선 삭제', + }, + ], + [ + { + id: 'sizeEdit', + name: '사이즈 변경', + }, + { + id: 'auxiliaryMove', + name: '보조선 이동(M)', + }, + { + id: 'auxiliaryCopy', + name: '보조선 복사(C)', + }, + { + id: 'auxiliaryRemove', + name: '보조선 삭제(D)', + }, + { + id: 'auxiliaryVerticalBisector', + name: '보조선 수직이등분선', + }, + { + id: 'auxiliaryCut', + name: '보조선 절삭', + }, + { + id: 'auxiliaryRemoveAll', + name: '보조선 전체 삭제', + }, + ], + ]) + break + case MENU.BATCH_CANVAS.SLOPE_SETTING: + case MENU.BATCH_CANVAS.BATCH_DRAWING: + case MENU.BATCH_CANVAS.SURFACE_SHAPE_BATCH: + case MENU.BATCH_CANVAS.OBJECT_BATCH: + case MENU.BATCH_CANVAS.ALL_REMOVE: + case MENU.BATCH_CANVAS.DEFAULT: + setContextMenu([ + [ + { + id: 'sizeEdit', + name: '사이즈 변경', + }, + { + id: 'remove', + name: '삭제(D)', + }, + { + id: 'move', + name: '이동(M)', + }, + { + id: 'copy', + name: '복사(C)', + }, + ], + [ + { + id: 'roofMaterialEdit', + name: '지붕재 변경', + }, + { + id: 'linePropertyEdit', + name: '각 변 속성 변경', + }, + { + id: 'flowDirectionEdit', + name: '흐름 방향 변경', + }, + ], + ]) + break + default: + setContextMenu([]) + break + } + }, [currentMenu]) + + return { + contextMenu, + } +} From 66acf8060f8d42aff3a5c8ae3def22dae940b8f6 Mon Sep 17 00:00:00 2001 From: minsik Date: Wed, 16 Oct 2024 15:10:49 +0900 Subject: [PATCH 32/68] =?UTF-8?q?-=20=EB=B3=B4=EC=A1=B0=EC=84=A0=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99,=20=EC=82=AC=EC=9D=B4=EC=A6=88=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20=EB=AA=A8=EB=8B=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../modal/auxiliary/AuxiliaryMove.jsx | 47 ++++++++++++++ .../modal/auxiliary/AuxiliarySize.jsx | 61 +++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 src/components/floor-plan/modal/auxiliary/AuxiliaryMove.jsx create mode 100644 src/components/floor-plan/modal/auxiliary/AuxiliarySize.jsx diff --git a/src/components/floor-plan/modal/auxiliary/AuxiliaryMove.jsx b/src/components/floor-plan/modal/auxiliary/AuxiliaryMove.jsx new file mode 100644 index 00000000..65925cbd --- /dev/null +++ b/src/components/floor-plan/modal/auxiliary/AuxiliaryMove.jsx @@ -0,0 +1,47 @@ +import { useMessage } from '@/hooks/useMessage' +import WithDraggable from '@/components/common/draggable/WithDraggable' + +export default function AuxiliaryMove({ setShowAuxiliaryModal }) { + const { getMessage } = useMessage() + return ( + +
    +
    +

    補助線の移動

    + +
    +
    +
    移動する方向を入力してください
    +
    +
    +
    +

    長さ

    +
    +
    + +
    + mm +
    +
    +
    + +
    + mm +
    +
    +
    + + + + +
    +
    +
    +
    + +
    +
    +
    +
    + ) +} diff --git a/src/components/floor-plan/modal/auxiliary/AuxiliarySize.jsx b/src/components/floor-plan/modal/auxiliary/AuxiliarySize.jsx new file mode 100644 index 00000000..baa9fafa --- /dev/null +++ b/src/components/floor-plan/modal/auxiliary/AuxiliarySize.jsx @@ -0,0 +1,61 @@ +import { useMessage } from '@/hooks/useMessage' +import WithDraggable from '@/components/common/draggable/WithDraggable' + +export default function AuxiliarySize({ setShowAuxiliaryModal }) { + const { getMessage } = useMessage() + return ( + +
    +
    +

    補助線サイズ変更

    + +
    +
    +
    +
    + + +
    +
    +
    + +
    + mm +
    +
    + 長さ +
    + +
    + mm +
    +
    +
    +
    + + +
    +
    +
    + +
    + mm +
    +
    + 長さ +
    + +
    + mm +
    +
    +
    + +
    +
    +
    +
    + ) +} From a4daba58634705c905a6e5d5ad56794fff8311a5 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Wed, 16 Oct 2024 15:18:17 +0900 Subject: [PATCH 33/68] =?UTF-8?q?roofPolygon=EC=9D=98=20originX,originY=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/roofcover/useRoofAllocationSetting.js | 3 ++- src/hooks/surface/usePlacementShapeDrawing.js | 6 +++++- src/util/qpolygon-utils.js | 5 ++++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/hooks/roofcover/useRoofAllocationSetting.js b/src/hooks/roofcover/useRoofAllocationSetting.js index 9a299ca3..6533fe79 100644 --- a/src/hooks/roofcover/useRoofAllocationSetting.js +++ b/src/hooks/roofcover/useRoofAllocationSetting.js @@ -2,7 +2,7 @@ import { useRecoilValue } from 'recoil' import { canvasState } from '@/store/canvasAtom' import { useEffect, useState } from 'react' import { setSurfaceShapePattern } from '@/util/canvas-util' -import { splitPolygonWithLines } from '@/util/qpolygon-utils' +import { drawDirectionArrow, splitPolygonWithLines } from '@/util/qpolygon-utils' import { useSwal } from '@/hooks/useSwal' // 지붕면 할당 @@ -112,6 +112,7 @@ export function useRoofAllocationSetting(setShowRoofAllocationSettingModal) { roofs.forEach((roof) => { setSurfaceShapePattern(roof) + drawDirectionArrow(roof) }) setShowRoofAllocationSettingModal(false) } diff --git a/src/hooks/surface/usePlacementShapeDrawing.js b/src/hooks/surface/usePlacementShapeDrawing.js index b07a8031..4df37618 100644 --- a/src/hooks/surface/usePlacementShapeDrawing.js +++ b/src/hooks/surface/usePlacementShapeDrawing.js @@ -14,7 +14,7 @@ import { useTempGrid } from '@/hooks/useTempGrid' import { useEffect, useRef } from 'react' import { distanceBetweenPoints, setSurfaceShapePattern } from '@/util/canvas-util' import { fabric } from 'fabric' -import { calculateAngle } from '@/util/qpolygon-utils' +import { calculateAngle, drawDirectionArrow } from '@/util/qpolygon-utils' import { placementShapeDrawingAngle1State, placementShapeDrawingAngle2State, @@ -212,9 +212,13 @@ export function usePlacementShapeDrawing(setShowPlaceShapeDrawingModal) { strokeWidth: 3, selectable: true, name: POLYGON_TYPE.ROOF, + originX: 'center', + originY: 'center', + direction: 'south', }) setSurfaceShapePattern(roof) + drawDirectionArrow(roof) lines.forEach((line) => { removeLine(line) diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js index 063d9d4d..83b6693f 100644 --- a/src/util/qpolygon-utils.js +++ b/src/util/qpolygon-utils.js @@ -1151,8 +1151,11 @@ export const splitPolygonWithLines = (polygon) => { fill: 'transparent', strokeWidth: 3, name: POLYGON_TYPE.ROOF, + originX: 'center', + originY: 'center', selectable: true, defense: defense, + direction: defense, }) polygon.canvas.add(roof) @@ -3058,7 +3061,7 @@ export const drawDirectionArrow = (polygon) => { polygon.canvas.remove(polygon.arrow) } - let centerPoint = { x: polygon.width / 2 + polygon.left, y: polygon.height / 2 + polygon.top } + let centerPoint = { x: polygon.left, y: polygon.top } let stickeyPoint const polygonMaxX = Math.max(...polygon.getCurrentPoints().map((point) => point.x)) From c4a513f003505d027611df5a4f4ffd4d177bc521 Mon Sep 17 00:00:00 2001 From: basssy Date: Wed, 16 Oct 2024 15:30:13 +0900 Subject: [PATCH 34/68] =?UTF-8?q?=EB=AC=BC=EA=B1=B4=EB=AA=A9=EB=A1=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 1 + src/components/management/StuffDetail.jsx | 178 ++++++++++++------ .../management/StuffSearchCondition.jsx | 48 +++-- src/store/stuffAtom.js | 4 + yarn.lock | 99 +++++++++- 5 files changed, 250 insertions(+), 80 deletions(-) diff --git a/package.json b/package.json index b44c2dda..d7214b51 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "prisma": "^5.18.0", "react-color-palette": "^7.2.2", "react-dropdown-select": "^4.11.3", + "react-select": "^5.8.1", "sass": "^1.77.8", "tailwindcss": "^3.4.1" } diff --git a/src/components/management/StuffDetail.jsx b/src/components/management/StuffDetail.jsx index b64f2a23..67c6cc39 100644 --- a/src/components/management/StuffDetail.jsx +++ b/src/components/management/StuffDetail.jsx @@ -1,13 +1,13 @@ 'use client' import React, { useState, useEffect, useRef } from 'react' -import { useRouter, useSearchParams } from 'next/navigation' +import { useRouter, useSearchParams, usePathname } from 'next/navigation' import { Button } from '@nextui-org/react' -import Select from 'react-dropdown-select' +import Select from 'react-select' import Link from 'next/link' import { useAxios } from '@/hooks/useAxios' import { globalLocaleStore } from '@/store/localeAtom' -import { isEmptyArray, isObjectNotEmpty } from '@/util/common-utils' +import { isEmptyArray, isNotEmptyArray, isObjectNotEmpty } from '@/util/common-utils' import { useMessage } from '@/hooks/useMessage' import { useForm } from 'react-hook-form' import { useRecoilValue } from 'recoil' @@ -18,11 +18,12 @@ export default function StuffDetail() { const sessionState = useRecoilValue(sessionStore) const router = useRouter() + const pathname = usePathname() const searchParams = useSearchParams() const { getMessage } = useMessage() const globalLocaleState = useRecoilValue(globalLocaleStore) const ref = useRef() - const { get, post, del } = useAxios(globalLocaleState) + const { get, post, del, promisePost } = useAxios(globalLocaleState) //form const formInitValue = { // 물건번호 T...(임시) R...(진짜) @@ -76,10 +77,9 @@ export default function StuffDetail() { const [editMode, setEditMode] = useState('NEW') const [detailData, setDetailData] = useState({}) - const [tempDetailData, setTempDetailData] = useState({}) - useEffect(() => { console.log('objectNo::', objectNo) + if (objectNo) { console.log('수정화면') setEditMode('EDIT') @@ -97,13 +97,13 @@ export default function StuffDetail() { } else { // 신규 상세 공통APi // 도도부현API - console.log('신규화면') get({ url: '/api/object/prefecture/list' }).then((res) => { if (!isEmptyArray(res)) { // console.log('신규화면 도도부현API 결과:::', res) setPrefCodeList(res) } }) + // 임시 1차점 판매점코드 saleStoreId=201TES01 // T01 //1차점 : X167 @@ -112,7 +112,6 @@ export default function StuffDetail() { if (!isEmptyArray(res)) { const firstList = res.filter((row) => row.saleStoreLevel === '1') const otherList = res.filter((row) => row.saleStoreLevel !== '1') - //1차점 셀렉트박스 setSaleStoreList(firstList) //1차점 아닌 판매점 셀렉트박스 @@ -126,6 +125,7 @@ export default function StuffDetail() { useEffect(() => { if (isObjectNotEmpty(detailData)) { console.log('상세데이타:::::::', detailData) + // 도도부현API get({ url: '/api/object/prefecture/list' }).then((res) => { if (!isEmptyArray(res)) { @@ -138,37 +138,31 @@ export default function StuffDetail() { // 임시 1차점 판매점코드 saleStoreId=201TES01 // T01 //1차점 : X167 - // get({ url: `/api/object/saleStore/X167/list` }).then((res) => { - // get({ url: `/api/object/saleStore/${sessionState?.storeId}/list` }).then((res) => { - // if (!isEmptyArray(res)) { - // // console.log('판매점 결과:::::', res) - // setSaleStoreList(res) - // //1차 판매점 자동완성 값 셋팅 - // form.setValue('saleStoreId', res[0].saleStoreId) - // //1차 판매점 번호 셋팅 - // form.setValue('saleStoreName', res[0].saleStoreId) - // setOtherSaleStoreList([]) - // } - // }) + get({ url: `/api/object/saleStore/T01/list` }).then((res) => { + // get({ url: `/api/object/saleStore/${sessionState?.storeId}/list` }).then((res) => { + if (!isEmptyArray(res)) { + const firstList = res.filter((row) => row.saleStoreLevel === '1') + const otherList = res.filter((row) => row.saleStoreLevel !== '1') + //1차점 셀렉트박스 + setSaleStoreList(firstList) + //1차점 아닌 판매점 셀렉트박스 + setOriginOtherSaleStoreList(otherList) + setOtherSaleStoreList(otherList) + } + }) } }, [detailData]) - useEffect(() => { - if (isObjectNotEmpty(tempDetailData)) { - console.log('임시저장하고 새로고침했을때:::::::::', tempDetailData) - } - }, [tempDetailData]) - //1차점 변경 이벤트 const onSelectionChange = (key) => { - if (!isEmptyArray(key)) { + if (isObjectNotEmpty(key)) { setOtherSaleStoreList(otherSaleStoreList) - form.setValue('saleStoreId', key[0].saleStoreId) - form.setValue('saleStoreName', key[0].saleStoreName) - form.setValue('saleStoreLevel', key[0].saleStoreLevel) + form.setValue('saleStoreId', key.saleStoreId) + form.setValue('saleStoreName', key.saleStoreName) + form.setValue('saleStoreLevel', key.saleStoreLevel) //선택한 1차점 정보로 2차점 list 추리기 //長府工産株式会社 大阪支社 - let newOtherSaleStoreList = originOtherSaleStoreList.filter((row) => row.firstAgentId === key[0].saleStoreId) + let newOtherSaleStoreList = originOtherSaleStoreList.filter((row) => row.firstAgentId === key.saleStoreId) setOtherSaleStoreList(newOtherSaleStoreList) } else { //X누름 @@ -186,10 +180,10 @@ export default function StuffDetail() { //2차점 변경 이벤트 const onSelectionChange2 = (key) => { - if (!isEmptyArray(key)) { - form.setValue('otherSaleStoreId', key[0].saleStoreId) - form.setValue('otherSaleStoreName', key[0].saleStoreName) - form.setValue('otherSaleStoreLevel', key[0].saleStoreLevel) + if (isObjectNotEmpty(key)) { + form.setValue('otherSaleStoreId', key.saleStoreId) + form.setValue('otherSaleStoreName', key.saleStoreName) + form.setValue('otherSaleStoreLevel', key.saleStoreLevel) } else { form.setValue('otherSaleStoreId', '') form.setValue('otherSaleStoreName', '') @@ -199,10 +193,8 @@ export default function StuffDetail() { //1차점 지웠을때 2차점 자동완성 초기화 const handleClear = () => { - if (ref.current.state.dropDown) { - ref.current.methods.dropDown() - } else { - ref.current.state.values = [] + if (ref.current) { + ref.current.clearValue() } } @@ -410,15 +402,13 @@ export default function StuffDetail() { //1차점 or 2차점 안고르고 임시저장하면 if (params.saleStoreId == '') { params.saleStoreId = sessionState.storeId - params.saleStoreLevel = sessionState.storeLevel + params.saleStoreLevel = sessionState.storeLvl } - console.log('임시저장params::', params) - // return - await post({ url: '/api/object/save-object', data: params }).then((res) => { - if (res) { - console.log('임시저장res::::::', res) - setTempDetailData(res) + + await promisePost({ url: '/api/object/save-object', data: params }).then((res) => { + if (res.status === 201) { alert('임시저장 되었습니다. 물건번호를 획득하려면 필수 항목을 모두 입력해 주십시오.') + router.push(`${pathname}?objectNo=${res.data.objectNo.toString()}`) } }) } @@ -528,13 +518,11 @@ export default function StuffDetail() {
    + getOptionLabel={(x) => x.saleStoreName} + getOptionValue={(x) => x.saleStoreId} + isClearable={true} + />
    @@ -553,6 +541,15 @@ export default function StuffDetail() {
    + > */}
    + + + {getMessage('stuff.detail.dispCompanyName')} * + + +
    + {/* */} +
    + + + + + 물건구분/물건명 * + + +
    +
    + + +
    +
    + + +
    +
    + +
    +
    + +
    +
    + + + + {getMessage('stuff.detail.objectNameKana')} + +
    + +
    + + + + +
    +
    + {getMessage('stuff.detail.saleStoreId')} + * +
    +
    +
    + + +
    +
    + {/* */} +
    +
    + +
    +
    + +
    diff --git a/src/components/management/StuffSearchCondition.jsx b/src/components/management/StuffSearchCondition.jsx index df2d74c8..8bd739b4 100644 --- a/src/components/management/StuffSearchCondition.jsx +++ b/src/components/management/StuffSearchCondition.jsx @@ -4,7 +4,8 @@ import React, { useEffect, useRef, useState } from 'react' import { useAxios } from '@/hooks/useAxios' import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil' import { appMessageStore, globalLocaleStore } from '@/store/localeAtom' -import Select from 'react-dropdown-select' +// import Select from 'react-dropdown-select' +import Select from 'react-select' import KO from '@/locales/ko.json' import JA from '@/locales/ja.json' import { stuffSearchState } from '@/store/stuffAtom' @@ -73,13 +74,16 @@ export default function StuffSearchCondition() { startRow: stuffSearch?.startRow ? stuffSearch.startRow : 1, endRow: stuffSearch?.endRow ? stuffSearch.endRow : 100, schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'R', + selObject: { + label: stuffSearch.selObject.label, + value: stuffSearch.selObject.value, + }, }) } //초기화 const resetRecoil = () => { setObjectNo('') - //setSaleStoreId('') //세션정보 setAddress('') setobjectName('') setSaleStoreName('') @@ -100,7 +104,10 @@ export default function StuffSearchCondition() { // get({ url: `/api/object/saleStore/201TES01/list` }).then((res) => { get({ url: `/api/object/saleStore/${sessionState?.storeId}/list` }).then((res) => { if (!isEmptyArray(res)) { - // console.log('판매점 결과:::::', res) + res.map((row) => { + row.value = row.saleStoreId + row.label = row.saleStoreName + }) setSchSelSaleStoreList(res) } }) @@ -109,18 +116,21 @@ export default function StuffSearchCondition() { //초기화 눌렀을 때 자동완성도.. const handleClear = () => { - if (ref.current.state.dropDown) { - ref.current.methods.dropDown() - } else { - ref.current.state.values = [] + if (ref.current) { + ref.current.clearValue() } } //판매대리점 자동완성 변경 const onSelectionChange = (key) => { - if (!isEmptyArray(key)) { - setSchSelSaleStoreId(key[0].saleStoreId) - setStuffSearch({ ...stuffSearch, code: 'S', schSelSaleStoreId: key[0].saleStoreId }) + if (isObjectNotEmpty(key)) { + setSchSelSaleStoreId(key.saleStoreId) + setStuffSearch({ + ...stuffSearch, + code: 'S', + schSelSaleStoreId: key.saleStoreId, + selObject: { value: key.saleStoreId, label: key.saleStoreName }, + }) } else { setSchSelSaleStoreId('') setStuffSearch({ ...stuffSearch, schSelSaleStoreId: '' }) @@ -255,17 +265,15 @@ export default function StuffSearchCondition() { {schSelSaleStoreList?.length > 0 && ( + options={schSelSaleStoreList} + onChange={onSelectionChange} + getOptionLabel={(x) => x.saleStoreName} + getOptionValue={(x) => x.saleStoreId} + defaultValue={stuffSearch?.selObject?.value ? stuffSearch?.selObject : null} + isDisabled={sessionState?.storeLvl === '1' ? false : true} + isClearable={true} + /> )} diff --git a/src/store/stuffAtom.js b/src/store/stuffAtom.js index c47321ae..ceb5def0 100644 --- a/src/store/stuffAtom.js +++ b/src/store/stuffAtom.js @@ -18,6 +18,10 @@ export const stuffSearchState = atom({ startRow: 1, endRow: 100, schSortType: 'R', //정렬조건 (R:최근등록일 U:최근수정일) + selObject: { + value: '', + label: '', + }, }, dangerouslyAllowMutability: true, }) diff --git a/yarn.lock b/yarn.lock index 50679622..1472f5ac 100644 --- a/yarn.lock +++ b/yarn.lock @@ -216,7 +216,7 @@ dependencies: "@babel/types" "^7.25.7" -"@babel/runtime@^7.12.5", "@babel/runtime@^7.18.3": +"@babel/runtime@^7.12.0", "@babel/runtime@^7.12.5", "@babel/runtime@^7.18.3", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.7": version "7.25.7" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.25.7.tgz#7ffb53c37a8f247c8c4d335e89cdf16a2e0d0fb6" integrity sha512-FjoyLe754PMiYsFaN5C94ttGiOmBNYTf6pLr4xXHAT5uctHb092PBszndLDR5XA/jghQvn4n7JMHl7dmTgbm9w== @@ -273,7 +273,7 @@ resolved "https://registry.npmjs.org/@bedrock-layout/use-stateful-ref/-/use-stateful-ref-1.4.1.tgz" integrity sha512-4eKO2KdQEXcR5LI4QcxqlJykJUDQJWDeWYAukIn6sRQYoabcfI5kDl61PUi6FR6o8VFgQ8IEP7HleKqWlSe8SQ== -"@emotion/babel-plugin@^11.11.0": +"@emotion/babel-plugin@^11.11.0", "@emotion/babel-plugin@^11.12.0": version "11.12.0" resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.12.0.tgz#7b43debb250c313101b3f885eba634f1d723fcc2" integrity sha512-y2WQb+oP8Jqvvclh8Q55gLUyb7UFvgv7eJfsj7td5TToBrIUtPay2kMrZi4xjq9qw2vD0ZR5fSho0yqoFgX7Rw== @@ -290,7 +290,7 @@ source-map "^0.5.7" stylis "4.2.0" -"@emotion/cache@^11.11.0": +"@emotion/cache@^11.11.0", "@emotion/cache@^11.13.0", "@emotion/cache@^11.4.0": version "11.13.1" resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.13.1.tgz#fecfc54d51810beebf05bf2a161271a1a91895d7" integrity sha512-iqouYkuEblRcXmylXIwwOodiEK5Ifl7JcX7o6V4jI3iW4mLXX3dmt5xwBtIkJiQEXFAI+pC8X0i67yiPkH9Ucw== @@ -332,7 +332,21 @@ "@emotion/weak-memoize" "^0.3.1" hoist-non-react-statics "^3.3.1" -"@emotion/serialize@^1.1.2", "@emotion/serialize@^1.2.0": +"@emotion/react@^11.8.1": + version "11.13.3" + resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.13.3.tgz#a69d0de2a23f5b48e0acf210416638010e4bd2e4" + integrity sha512-lIsdU6JNrmYfJ5EbUCf4xW1ovy5wKQ2CkPRM4xogziOxH1nXxBSjpC9YqbFAP7circxMfYp+6x676BqWcEiixg== + dependencies: + "@babel/runtime" "^7.18.3" + "@emotion/babel-plugin" "^11.12.0" + "@emotion/cache" "^11.13.0" + "@emotion/serialize" "^1.3.1" + "@emotion/use-insertion-effect-with-fallbacks" "^1.1.0" + "@emotion/utils" "^1.4.0" + "@emotion/weak-memoize" "^0.4.0" + hoist-non-react-statics "^3.3.1" + +"@emotion/serialize@^1.1.2", "@emotion/serialize@^1.2.0", "@emotion/serialize@^1.3.1": version "1.3.2" resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.3.2.tgz#e1c1a2e90708d5d85d81ccaee2dfeb3cc0cccf7a" integrity sha512-grVnMvVPK9yUVE6rkKfAJlYZgo0cu3l9iMC77V7DW6E1DUIrU68pSEXRmFZFOFB1QFo57TncmOcvcbMDWsL4yA== @@ -365,7 +379,7 @@ resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.10.0.tgz#2af2f7c7e5150f497bdabd848ce7b218a27cf745" integrity sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg== -"@emotion/use-insertion-effect-with-fallbacks@^1.0.1": +"@emotion/use-insertion-effect-with-fallbacks@^1.0.1", "@emotion/use-insertion-effect-with-fallbacks@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.1.0.tgz#1a818a0b2c481efba0cf34e5ab1e0cb2dcb9dfaf" integrity sha512-+wBOcIV5snwGgI2ya3u99D7/FJquOIniQT1IKyDsBmEgwvpxMNeS65Oib7OnE2d2aY+3BU4OiH+0Wchf8yk3Hw== @@ -400,6 +414,14 @@ "@floating-ui/core" "^1.6.0" "@floating-ui/utils" "^0.2.7" +"@floating-ui/dom@^1.0.1": + version "1.6.11" + resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.6.11.tgz#8631857838d34ee5712339eb7cbdfb8ad34da723" + integrity sha512-qkMCxSR24v2vGkhYDo/UzxfJN3D4syqSjyuTFz6C7XcpU1pASPRieNI0Kj5VP3/503mOfYiGY891ugBX1GlABQ== + dependencies: + "@floating-ui/core" "^1.6.0" + "@floating-ui/utils" "^0.2.8" + "@floating-ui/react-dom@^2.1.1": version "2.1.1" resolved "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.1.tgz" @@ -421,6 +443,11 @@ resolved "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.7.tgz" integrity sha512-X8R8Oj771YRl/w+c1HqAC1szL8zWQRwFvgDwT129k9ACdBoud/+/rX9V0qiMl6LWUdP9voC2nDVZYPMQQsb6eA== +"@floating-ui/utils@^0.2.8": + version "0.2.8" + resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.8.tgz#21a907684723bbbaa5f0974cf7730bd797eb8e62" + integrity sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig== + "@formatjs/ecma402-abstract@2.0.0": version "2.0.0" resolved "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.0.0.tgz" @@ -4085,6 +4112,26 @@ resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.2.tgz#5950e50960793055845e956c427fc2b0d70c5239" integrity sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw== +"@types/prop-types@*": + version "15.7.13" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.13.tgz#2af91918ee12d9d32914feb13f5326658461b451" + integrity sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA== + +"@types/react-transition-group@^4.4.0": + version "4.4.11" + resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.11.tgz#d963253a611d757de01ebb241143b1017d5d63d5" + integrity sha512-RM05tAniPZ5DZPzzNFP+DmrcOdD0efDUxMy3145oljWSl3x9ZV5vhme98gTxFrj2lhXvmGNnUiuDyJgY9IKkNA== + dependencies: + "@types/react" "*" + +"@types/react@*": + version "18.3.11" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.3.11.tgz#9d530601ff843ee0d7030d4227ea4360236bd537" + integrity sha512-r6QZ069rFTjrEYgFdOck1gK7FLVsgJE7tTz0pQBczlBNUhBNk0MQH4UbnFSwjpQLMkLzgqvBBa+qGpLje16eTQ== + dependencies: + "@types/prop-types" "*" + csstype "^3.0.2" + "@types/readable-stream@^4.0.0": version "4.0.15" resolved "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-4.0.15.tgz" @@ -4677,6 +4724,14 @@ dlv@^1.1.3: resolved "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz" integrity sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA== +dom-helpers@^5.0.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902" + integrity sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA== + dependencies: + "@babel/runtime" "^7.8.7" + csstype "^3.0.2" + domexception@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz" @@ -5421,6 +5476,11 @@ mathjs@^13.0.2: tiny-emitter "^2.1.0" typed-function "^4.2.1" +memoize-one@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-6.0.0.tgz#b2591b871ed82948aee4727dc6abceeeac8c1045" + integrity sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw== + merge2@^1.3.0: version "1.4.1" resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz" @@ -5800,7 +5860,7 @@ process@^0.11.10: resolved "https://registry.npmjs.org/process/-/process-0.11.10.tgz" integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== -prop-types@^15.7.2, prop-types@^15.8.1: +prop-types@^15.6.0, prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1: version "15.8.1" resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -5951,6 +6011,21 @@ react-responsive-modal@^6.4.2: body-scroll-lock "^3.1.5" classnames "^2.3.1" +react-select@^5.8.1: + version "5.8.1" + resolved "https://registry.yarnpkg.com/react-select/-/react-select-5.8.1.tgz#3284a93b7633b5e893306b2a8007ea0f793e62b9" + integrity sha512-RT1CJmuc+ejqm5MPgzyZujqDskdvB9a9ZqrdnVLsvAHjJ3Tj0hELnLeVPQlmYdVKCdCpxanepl6z7R5KhXhWzg== + dependencies: + "@babel/runtime" "^7.12.0" + "@emotion/cache" "^11.4.0" + "@emotion/react" "^11.8.1" + "@floating-ui/dom" "^1.0.1" + "@types/react-transition-group" "^4.4.0" + memoize-one "^6.0.0" + prop-types "^15.6.0" + react-transition-group "^4.3.0" + use-isomorphic-layout-effect "^1.1.2" + react-style-singleton@^2.2.1: version "2.2.1" resolved "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz" @@ -5976,6 +6051,16 @@ react-toastify@^10.0.5: dependencies: clsx "^2.1.0" +react-transition-group@^4.3.0: + version "4.4.5" + resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.5.tgz#e53d4e3f3344da8521489fbef8f2581d42becdd1" + integrity sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g== + dependencies: + "@babel/runtime" "^7.5.5" + dom-helpers "^5.0.1" + loose-envify "^1.4.0" + prop-types "^15.6.2" + react@^18: version "18.3.1" resolved "https://registry.npmjs.org/react/-/react-18.3.1.tgz" @@ -6549,7 +6634,7 @@ use-composed-ref@^1.3.0: resolved "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.3.0.tgz" integrity sha512-GLMG0Jc/jiKov/3Ulid1wbv3r54K9HlMW29IWcDFPEqFkSO2nS0MuefWgMJpeHQ9YJeXDL3ZUF+P3jdXlZX/cQ== -use-isomorphic-layout-effect@^1.1.1: +use-isomorphic-layout-effect@^1.1.1, use-isomorphic-layout-effect@^1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz" integrity sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA== From 8ffdf5dca9469a9ca350ed4e435c6c16ff09b6f0 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Wed, 16 Oct 2024 17:16:58 +0900 Subject: [PATCH 35/68] =?UTF-8?q?lengthText,=20=ED=99=94=EC=82=B4=ED=91=9C?= =?UTF-8?q?=20=EC=A2=8C=ED=91=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/fabric/QPolygon.js | 20 ++++++++++- src/hooks/useMode.js | 1 + src/util/qpolygon-utils.js | 56 +++++++++++++++---------------- 3 files changed, 48 insertions(+), 29 deletions(-) diff --git a/src/components/fabric/QPolygon.js b/src/components/fabric/QPolygon.js index 360c9ddc..7c027c2f 100644 --- a/src/components/fabric/QPolygon.js +++ b/src/components/fabric/QPolygon.js @@ -201,7 +201,25 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { const dy = end.y - start.y const length = Number(Math.sqrt(dx * dx + dy * dy).toFixed(1)) * 10 - const midPoint = new fabric.Point((start.x + end.x) / 2, (start.y + end.y) / 2) + let midPoint + + switch (this.direction) { + case 'north': + midPoint = new fabric.Point((start.x + end.x) / 2, (start.y + end.y) / 2 - 30) + break + case 'west': + midPoint = new fabric.Point((start.x + end.x) / 2 - 30, (start.y + end.y) / 2) + break + case 'south': + midPoint = new fabric.Point((start.x + end.x) / 2, (start.y + end.y) / 2 + 30) + break + case 'east': + midPoint = new fabric.Point((start.x + end.x) / 2 + 30, (start.y + end.y) / 2) + break + default: + midPoint = new fabric.Point((start.x + end.x) / 2, (start.y + end.y) / 2) + break + } const degree = (Math.atan2(dy, dx) * 180) / Math.PI diff --git a/src/hooks/useMode.js b/src/hooks/useMode.js index 04ec7685..858c13f7 100644 --- a/src/hooks/useMode.js +++ b/src/hooks/useMode.js @@ -1694,6 +1694,7 @@ export function useMode() { const polygon = createRoofPolygon(wall.points) const originPolygon = new QPolygon(wall.points, { fontSize: 0 }) + originPolygon.setViewLengthText(false) let offsetPolygon let result = createMarginPolygon(polygon, wall.lines).vertices diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js index 83b6693f..07b46bc4 100644 --- a/src/util/qpolygon-utils.js +++ b/src/util/qpolygon-utils.js @@ -3072,58 +3072,58 @@ export const drawDirectionArrow = (polygon) => { switch (direction) { case 'north': points = [ - { x: centerPoint.x, y: polygonMinY - 20 }, - { x: centerPoint.x + 20, y: polygonMinY - 20 }, + { x: centerPoint.x, y: polygonMinY - 50 }, { x: centerPoint.x + 20, y: polygonMinY - 50 }, - { x: centerPoint.x + 50, y: polygonMinY - 50 }, - { x: centerPoint.x, y: polygonMinY - 80 }, - { x: centerPoint.x - 50, y: polygonMinY - 50 }, + { x: centerPoint.x + 20, y: polygonMinY - 80 }, + { x: centerPoint.x + 50, y: polygonMinY - 80 }, + { x: centerPoint.x, y: polygonMinY - 110 }, + { x: centerPoint.x - 50, y: polygonMinY - 80 }, + { x: centerPoint.x - 20, y: polygonMinY - 80 }, { x: centerPoint.x - 20, y: polygonMinY - 50 }, - { x: centerPoint.x - 20, y: polygonMinY - 20 }, ] - stickeyPoint = { x: centerPoint.x, y: polygonMinY - 80 } + stickeyPoint = { x: centerPoint.x, y: polygonMinY - 110 } break case 'south': points = [ - { x: centerPoint.x, y: polygonMaxY + 20 }, - { x: centerPoint.x + 20, y: polygonMaxY + 20 }, + { x: centerPoint.x, y: polygonMaxY + 50 }, { x: centerPoint.x + 20, y: polygonMaxY + 50 }, - { x: centerPoint.x + 50, y: polygonMaxY + 50 }, - { x: centerPoint.x, y: polygonMaxY + 80 }, - { x: centerPoint.x - 50, y: polygonMaxY + 50 }, + { x: centerPoint.x + 20, y: polygonMaxY + 80 }, + { x: centerPoint.x + 50, y: polygonMaxY + 80 }, + { x: centerPoint.x, y: polygonMaxY + 110 }, + { x: centerPoint.x - 50, y: polygonMaxY + 80 }, + { x: centerPoint.x - 20, y: polygonMaxY + 80 }, { x: centerPoint.x - 20, y: polygonMaxY + 50 }, - { x: centerPoint.x - 20, y: polygonMaxY + 20 }, ] - stickeyPoint = { x: centerPoint.x, y: polygonMaxY + 80 } + stickeyPoint = { x: centerPoint.x, y: polygonMaxY + 110 } break case 'west': points = [ - { x: polygonMinX - 20, y: centerPoint.y }, - { x: polygonMinX - 20, y: centerPoint.y + 20 }, + { x: polygonMinX - 50, y: centerPoint.y }, { x: polygonMinX - 50, y: centerPoint.y + 20 }, - { x: polygonMinX - 50, y: centerPoint.y + 50 }, - { x: polygonMinX - 80, y: centerPoint.y }, - { x: polygonMinX - 50, y: centerPoint.y - 50 }, + { x: polygonMinX - 80, y: centerPoint.y + 20 }, + { x: polygonMinX - 80, y: centerPoint.y + 50 }, + { x: polygonMinX - 110, y: centerPoint.y }, + { x: polygonMinX - 80, y: centerPoint.y - 50 }, + { x: polygonMinX - 80, y: centerPoint.y - 20 }, { x: polygonMinX - 50, y: centerPoint.y - 20 }, - { x: polygonMinX - 20, y: centerPoint.y - 20 }, ] - stickeyPoint = { x: polygonMinX - 80, y: centerPoint.y } + stickeyPoint = { x: polygonMinX - 110, y: centerPoint.y } break case 'east': points = [ - { x: polygonMaxX + 20, y: centerPoint.y }, - { x: polygonMaxX + 20, y: centerPoint.y + 20 }, + { x: polygonMaxX + 50, y: centerPoint.y }, { x: polygonMaxX + 50, y: centerPoint.y + 20 }, - { x: polygonMaxX + 50, y: centerPoint.y + 50 }, - { x: polygonMaxX + 80, y: centerPoint.y }, - { x: polygonMaxX + 50, y: centerPoint.y - 50 }, + { x: polygonMaxX + 80, y: centerPoint.y + 20 }, + { x: polygonMaxX + 80, y: centerPoint.y + 50 }, + { x: polygonMaxX + 110, y: centerPoint.y }, + { x: polygonMaxX + 80, y: centerPoint.y - 50 }, + { x: polygonMaxX + 80, y: centerPoint.y - 20 }, { x: polygonMaxX + 50, y: centerPoint.y - 20 }, - { x: polygonMaxX + 20, y: centerPoint.y - 20 }, ] - stickeyPoint = { x: polygonMaxX + 80, y: centerPoint.y } + stickeyPoint = { x: polygonMaxX + 110, y: centerPoint.y } break } From 82f3af907f0dbd2d3cf0b85efc1653b70f2d5df9 Mon Sep 17 00:00:00 2001 From: leeyongjae Date: Wed, 16 Oct 2024 17:49:10 +0900 Subject: [PATCH 36/68] =?UTF-8?q?Q.order,=20Q.mosubi=20=EC=9E=90=EB=8F=99?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EC=B6=94=EA=B0=80/session=20?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=20=EC=B6=94=EA=B0=80/response=20=EB=A9=94?= =?UTF-8?q?=EC=8B=9C=EC=A7=80=20=EC=B2=98=EB=A6=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.development | 5 +- .env.production | 5 +- src/app/layout.js | 2 + src/components/auth/Join.jsx | 4 +- src/components/auth/Login.jsx | 68 +++++++++++++++++-- src/components/community/ArchiveTable.jsx | 4 +- .../community/modal/BoardDetailModal.jsx | 4 +- src/components/header/Header.jsx | 46 +++++++++++-- src/components/myInfo/UserInfoModal.jsx | 8 +-- src/lib/authActions.js | 2 + src/util/board-utils.js | 6 +- 11 files changed, 125 insertions(+), 29 deletions(-) diff --git a/.env.development b/.env.development index 468efc67..933db5d3 100644 --- a/.env.development +++ b/.env.development @@ -8,4 +8,7 @@ DATABASE_URL="sqlserver://mssql.devgrr.kr:1433;database=qcast;user=qcast;passwor SESSION_SECRET="i3iHH1yp2/2SpQSIySQ4bpyc4g0D+zCF9FAn5xUG0+Y=" -NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_bV5zuYMyyIYFlOb3" \ No newline at end of file +NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_bV5zuYMyyIYFlOb3" + +NEXT_PUBLIC_Q_ORDER_AUTO_LOGIN_URL="http://q-order-local.q-cells.jp:8120/eos/login/autoLogin" +NEXT_PUBLIC_Q_MUSUBI_AUTO_LOGIN_URL="http://q-musubi-local.q-cells.jp:8120/qm/login/autoLogin" \ No newline at end of file diff --git a/.env.production b/.env.production index 2c41bf76..9bad1719 100644 --- a/.env.production +++ b/.env.production @@ -6,4 +6,7 @@ DATABASE_URL="" SESSION_SECRET="i3iHH1yp2/2SpQSIySQ4bpyc4g0D+zCF9FAn5xUG0+Y=" -NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_bV5zuYMyyIYFlOb3" \ No newline at end of file +NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_bV5zuYMyyIYFlOb3" + +NEXT_PUBLIC_Q_ORDER_AUTO_LOGIN_URL="http://q-order-local.q-cells.jp:8120/eos/login/autoLogin" +NEXT_PUBLIC_Q_MUSUBI_AUTO_LOGIN_URL="http://q-musubi-local.q-cells.jp:8120/qm/login/autoLogin" \ No newline at end of file diff --git a/src/app/layout.js b/src/app/layout.js index 28626654..d27522fe 100644 --- a/src/app/layout.js +++ b/src/app/layout.js @@ -48,6 +48,8 @@ export default async function RootLayout({ children }) { telNo: session.telNo, fax: session.fax, email: session.email, + storeLvl: session.storeLvl, + groupId: session.groupId, pwdInitYn: session.pwdInitYn, isLoggedIn: session.isLoggedIn, } diff --git a/src/components/auth/Join.jsx b/src/components/auth/Join.jsx index aa2e21ca..3412efdb 100644 --- a/src/components/auth/Join.jsx +++ b/src/components/auth/Join.jsx @@ -37,11 +37,11 @@ export default function Join() { await promisePost({ url: '/api/login/v1.0/user/join', data: param }) .then((res) => { if (res) { - if (res.result.resultCode == 'S') { + if (res.data.result.resultCode == 'S') { Cookies.set('joinEmail', formData.get('email'), { expires: 1 }) router.push('/join/complete') } else { - alert(res.result.resultMsg) + alert(res.data.result.resultMsg) } } }) diff --git a/src/components/auth/Login.jsx b/src/components/auth/Login.jsx index 69d02e21..c43341e3 100644 --- a/src/components/auth/Login.jsx +++ b/src/components/auth/Login.jsx @@ -13,7 +13,59 @@ import { useRouter } from 'next/navigation' import Cookies from 'js-cookie' +import { useSearchParams } from 'next/navigation' + export default function Login() { + //////////////////////////////////////////////////////////////////////////////// + // 자동 로그인 작업진행중 + const initParams = useSearchParams() + const autoLoginParam = initParams.get('autoLoginParam1') + useEffect(() => { + if (autoLoginParam) { + autoLoginProcess(autoLoginParam) + } + }, []) + const autoLoginProcess = async (autoLoginParam) => { + setSession({ + userId: autoLoginParam, + saleStoreId: null, + name: null, + mail: null, + tel: null, + storeId: 'TEMP02', + userNm: 'ㅇㅇ6610', + userNmKana: '신규사용자 16610', + category: '인상6610', + telNo: '336610', + fax: null, + email: 't10t@naver.com', + pwdInitYn: 'Y', + storeLvl: '2', + groupId: '70000', + }) + + setSessionState({ + userId: autoLoginParam, + saleStoreId: null, + name: null, + mail: null, + tel: null, + storeId: 'TEMP02', + userNm: 'ㅇㅇ6610', + userNmKana: '신규사용자 16610', + category: '인상6610', + telNo: '336610', + fax: null, + email: 't10t@naver.com', + pwdInitYn: 'Y', + storeLvl: '2', + groupId: '70000', + }) + + router.push('/') + } + //////////////////////////////////////////////////////////////////////////////// + const [userId, setUserId] = useState('') const [checkId, setCheckId] = useState('') const [checkEmail, setCheckEmail] = useState('') @@ -55,6 +107,8 @@ export default function Login() { fax: null, email: 't10t@naver.com', pwdInitYn: 'Y', + storeLvl: '1', + groupId: '60000', }) setSessionState({ @@ -71,6 +125,8 @@ export default function Login() { fax: null, email: 't10t@naver.com', pwdInitYn: 'Y', + storeLvl: '1', + groupId: '60000', }) // ID SAVE 체크되어 있는 경우, 쿠키 저장 @@ -91,9 +147,9 @@ export default function Login() { // await promisePost({ url: '/api/login/v1.0/login', data: param }) // .then((res) => { // if (res) { - // if (res.result.resultCode == 'S') { - // setSession(res.data) - // setSessionState(res.data) + // if (res.data.result.resultCode === 'S') { + // setSession(res.data.data) + // setSessionState(res.data.data) // // ID SAVE 체크되어 있는 경우, 쿠키 저장 // if (chkLoginId) { // Cookies.set('chkLoginId', formData.get('id'), { expires: 7 }) @@ -102,7 +158,7 @@ export default function Login() { // } // router.push('/') // } else { - // alert(res.result.resultMsg) + // alert(res.data.result.resultMsg) // } // } // }) @@ -124,13 +180,13 @@ export default function Login() { }) .then((res) => { if (res) { - if (res.result.resultCode == 'S') { + if (res.data.result.resultCode == 'S') { alert(getMessage('login.init_password.complete_message')) setCheckId('') setCheckEmail('') setPasswordReset(1) } else { - alert(res.result.resultMsg) + alert(res.data.result.resultMsg) } } }) diff --git a/src/components/community/ArchiveTable.jsx b/src/components/community/ArchiveTable.jsx index 318badef..ef6b303b 100644 --- a/src/components/community/ArchiveTable.jsx +++ b/src/components/community/ArchiveTable.jsx @@ -20,7 +20,7 @@ export default function ArchiveTable({ clsCode }) { // 목록 조회 useEffect(() => { async function fetchData() { - const url = `${process.env.NEXT_PUBLIC_API_SERVER_PATH}/api/board/list` + const url = `/api/board/list` const params = new URLSearchParams({ schNoticeTpCd: 'QC', schNoticeClsCd: clsCode, @@ -49,7 +49,7 @@ export default function ArchiveTable({ clsCode }) { // 상세 파일 목록 조회 const handleDetailFileListDown = async (noticeNo) => { - const url = `${process.env.NEXT_PUBLIC_API_SERVER_PATH}/api/board/detail` + const url = `/api/board/detail` const params = new URLSearchParams({ noticeNo: noticeNo, }) diff --git a/src/components/community/modal/BoardDetailModal.jsx b/src/components/community/modal/BoardDetailModal.jsx index dec5e09a..ec324966 100644 --- a/src/components/community/modal/BoardDetailModal.jsx +++ b/src/components/community/modal/BoardDetailModal.jsx @@ -35,7 +35,7 @@ export default function BoardDetailModal({ noticeNo, setOpen }) { return ( <> -
    +
    @@ -57,7 +57,7 @@ export default function BoardDetailModal({ noticeNo, setOpen }) {
    첨부파일 목록
    {boardDetail.listFile.map((boardFile) => ( -
    +
    diff --git a/src/components/header/Header.jsx b/src/components/header/Header.jsx index c9b89658..c12d237e 100644 --- a/src/components/header/Header.jsx +++ b/src/components/header/Header.jsx @@ -13,6 +13,8 @@ import { logout } from '@/lib/authActions' import QSelectBox from '@/components/common/select/QSelectBox' import UserInfoModal from '@/components/myInfo/UserInfoModal' +import { useAxios } from '@/hooks/useAxios' +import { globalLocaleStore } from '@/store/localeAtom' export const ToggleonMouse = (e, act, target) => { const listWrap = e.target.closest(target) @@ -44,12 +46,42 @@ export default function Header(props) { const dimmedState = useRecoilValue(dimmedStore) const isDimmed = dimmedState ? 'opacity-50 bg-black' : '' - const SelectOptions = [ - { id: 0, name: 'オンライン保証シ', link: '' }, - { id: 1, name: 'ステム', link: '' }, - { id: 2, name: 'TEST1', link: 'https://www.weather.go.kr/w/index.do' }, - { id: 3, name: 'TEST2', link: 'https://www.google.com' }, - ] + // Link 이동 자동 로그인 + const [globalLocaleState, setGlbalLocaleState] = useRecoilState(globalLocaleStore) + const { promisePost } = useAxios(globalLocaleState) + + const qOrderUrl = process.env.NEXT_PUBLIC_Q_ORDER_AUTO_LOGIN_URL + const qMusubiUrl = process.env.NEXT_PUBLIC_Q_MUSUBI_AUTO_LOGIN_URL + + const [SelectOptions, setSelectOptions] = useState( + userSession.groupId === '60000' ? [{ id: 0, name: 'Q.ORDER', link: `${qOrderUrl}` }] : [{ id: 1, name: 'Q.Musubi', link: `${qMusubiUrl}` }], + ) + + const getAutoLoginParam = async () => { + await promisePost({ url: '/api/login/v1.0/user/login/autoLoginEncryptData', data: { loginId: userSession.userId } }) + .then((res) => { + if (res) { + setSelectOptions( + userSession.groupId === '60000' + ? [{ id: 0, name: 'Q.ORDER', link: `${qOrderUrl}?autoLoginParam1=${encodeURIComponent(res.data)}` }] + : [{ id: 1, name: 'Q.Musubi', link: `${qMusubiUrl}?autoLoginParam1=${encodeURIComponent(res.data)}` }], + ) + setSelected( + userSession.groupId === '60000' + ? { id: 0, name: 'Q.ORDER', link: `${qOrderUrl}?autoLoginParam1=${encodeURIComponent(res.data)}` } + : { id: 1, name: 'Q.Musubi', link: `${qMusubiUrl}?autoLoginParam1=${encodeURIComponent(res.data)}` }, + ) + } + }) + .catch((error) => { + alert(error.response.data.message) + }) + } + + useEffect(() => { + getAutoLoginParam() + }, [userSession]) + const menus = [ { id: 0, name: 'header.menus.home', url: '/', children: [] }, { @@ -157,7 +189,7 @@ export default function Header(props) {
    - +
    )} - {tabNum !== 3 && } + {/*{tabNum !== 3 && }*/} {tabNum !== 3 && (
    -
    -
    -
    -
    -
    割り当て設定
    -
    -
    -
    -
    - - {getMessage('modal.module.basic.setting.pitch.module.row.amount')} - -
    - -
    - mm -
    -
    - - {getMessage('modal.module.basic.setting.pitch.module.row.margin')} - -
    - -
    - mm -
    -
    - - {getMessage('modal.module.basic.setting.pitch.module.column.amount')} - -
    - -
    - mm -
    -
    - - {getMessage('modal.module.basic.setting.pitch.module.column.margin')} - -
    - -
    - mm -
    -
    -
    -
    -
    -
    {getMessage('modal.module.basic.setting.pitch.module.allocation.setting.info')}
    -
    -
    -
    -
    -
    ) } diff --git a/src/components/floor-plan/modal/circuitTrestle/CircuitTrestleSetting.jsx b/src/components/floor-plan/modal/circuitTrestle/CircuitTrestleSetting.jsx index 1c67ea7f..659fb54a 100644 --- a/src/components/floor-plan/modal/circuitTrestle/CircuitTrestleSetting.jsx +++ b/src/components/floor-plan/modal/circuitTrestle/CircuitTrestleSetting.jsx @@ -1,9 +1,9 @@ -import WithDraggable from '@/components/common/draggable/withDraggable' import { useState } from 'react' import PowerConditionalSelect from '@/components/floor-plan/modal/circuitTrestle/step/PowerConditionalSelect' import CircuitAllocation from '@/components/floor-plan/modal/circuitTrestle/step/CircuitAllocation' import StepUp from '@/components/floor-plan/modal/circuitTrestle/step/StepUp' import { useMessage } from '@/hooks/useMessage' +import WithDraggable from '@/components/common/draggable/withDraggable' export default function CircuitTrestleSetting({ setShowCircuitTrestleSettingModal }) { const { getMessage } = useMessage() diff --git a/src/components/floor-plan/modal/circuitTrestle/step/PowerConditionalSelect.jsx b/src/components/floor-plan/modal/circuitTrestle/step/PowerConditionalSelect.jsx index fe2d168b..9eadeb7f 100644 --- a/src/components/floor-plan/modal/circuitTrestle/step/PowerConditionalSelect.jsx +++ b/src/components/floor-plan/modal/circuitTrestle/step/PowerConditionalSelect.jsx @@ -115,12 +115,17 @@ export default function PowerConditionalSelect({ setTabNum }) {
    -
    - HQJP-KA40-5 - HQJP-KA40-5 - HQJP-KA40-5 + + HQJP-KA40-5 + + + HQJP-KA40-5 + + + HQJP-KA40-5 +
    @@ -136,11 +141,6 @@ export default function PowerConditionalSelect({ setTabNum }) {
    - {/*
    */} - {/* */} - {/*
    */} ) } diff --git a/src/components/floor-plan/modal/circuitTrestle/step/StepUp.jsx b/src/components/floor-plan/modal/circuitTrestle/step/StepUp.jsx index d8130a0a..6a11ed71 100644 --- a/src/components/floor-plan/modal/circuitTrestle/step/StepUp.jsx +++ b/src/components/floor-plan/modal/circuitTrestle/step/StepUp.jsx @@ -81,10 +81,11 @@ export default function StepUp({}) {
    -
    - HQJP-KA40-5 + + HQJP-KA40-5 +
    @@ -113,10 +114,11 @@ export default function StepUp({}) {
    -
    - HQJP-KA40-5 + + HQJP-KA40-5 +
    @@ -206,10 +208,11 @@ export default function StepUp({}) {
    -
    - HQJP-KA40-5 + + HQJP-KA40-5 +
@@ -238,10 +241,11 @@ export default function StepUp({}) {
-
- HQJP-KA40-5 + + HQJP-KA40-5 +
@@ -331,10 +335,11 @@ export default function StepUp({}) {
-
- HQJP-KA40-5 + + HQJP-KA40-5 +
@@ -363,10 +368,11 @@ export default function StepUp({}) {
-
- HQJP-KA40-5 + + HQJP-KA40-5 +
diff --git a/src/components/floor-plan/modal/object/type/TriangleDormer.jsx b/src/components/floor-plan/modal/object/type/TriangleDormer.jsx index 8cfa55e4..33154aab 100644 --- a/src/components/floor-plan/modal/object/type/TriangleDormer.jsx +++ b/src/components/floor-plan/modal/object/type/TriangleDormer.jsx @@ -35,17 +35,6 @@ export default function TriangleDormer() {
-
-
{getMessage('width')}
-
-
-
- -
- mm -
-
-
{getMessage('slope')}
From 9411cf60239a73f426ad88b3c5cb01ff7bb12ee9 Mon Sep 17 00:00:00 2001 From: minsik Date: Wed, 16 Oct 2024 17:52:59 +0900 Subject: [PATCH 40/68] =?UTF-8?q?=EC=98=A4=EB=B8=8C=EC=A0=9D=ED=8A=B8?= =?UTF-8?q?=EC=84=A4=EC=A0=95=20=EC=82=BC=EA=B0=81=ED=98=95=20=EB=8F=84?= =?UTF-8?q?=EB=A8=B8=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/static/images/canvas/object_img01.svg | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/public/static/images/canvas/object_img01.svg b/public/static/images/canvas/object_img01.svg index a4ebac98..1d72f00b 100644 --- a/public/static/images/canvas/object_img01.svg +++ b/public/static/images/canvas/object_img01.svg @@ -1,5 +1,5 @@ - - + + @@ -9,13 +9,8 @@ - - - - - - + From c3f56caaa68a06cf71487ae7e3b740741c69db93 Mon Sep 17 00:00:00 2001 From: basssy Date: Wed, 16 Oct 2024 17:53:57 +0900 Subject: [PATCH 41/68] =?UTF-8?q?=EB=AC=BC=EA=B1=B4=20=EB=93=B1=EB=A1=9D?= =?UTF-8?q?=ED=99=94=EB=A9=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/management/StuffDetail.jsx | 67 +++++++--- .../management/popup/FindAddressPop.jsx | 7 +- .../management/popup/PlanRequestPop.jsx | 1 - .../management/popup/WindSelectPop.jsx | 115 ++++++++++++++++++ src/locales/ja.json | 8 ++ src/locales/ko.json | 8 ++ 6 files changed, 185 insertions(+), 21 deletions(-) create mode 100644 src/components/management/popup/WindSelectPop.jsx diff --git a/src/components/management/StuffDetail.jsx b/src/components/management/StuffDetail.jsx index 67c6cc39..9b8bfd4a 100644 --- a/src/components/management/StuffDetail.jsx +++ b/src/components/management/StuffDetail.jsx @@ -14,6 +14,7 @@ import { useRecoilValue } from 'recoil' import { sessionStore } from '@/store/commonAtom' import FindAddressPop from './popup/FindAddressPop' import PlanRequestPop from './popup/PlanRequestPop' +import WindSelectPop from './popup/WindSelectPop' export default function StuffDetail() { const sessionState = useRecoilValue(sessionStore) @@ -43,7 +44,6 @@ export default function StuffDetail() { prefName: '', address: '', //주소 areaId: '', //발전량시뮬레이션지역id - // areaName: '', //발전량시뮬레이션지역명 windSpeed: '', //기준풍속 verticalSnowCover: '', //수직적설량NEW coldRegionFlg: false, //한랭지대책시행(true : 1 / false : 0) @@ -68,10 +68,11 @@ export default function StuffDetail() { const [areaIdList, setAreaIdList] = useState([]) //발전시뮬레이션 리스트 - const [windSpeedList, setWindSpeedList] = useState([]) //기준풍속 리스트 + // const [windSpeedList, setWindSpeedList] = useState([]) //기준풍속 리스트 팝업으로이동 const [isFormValid, setIsFormValid] = useState(false) //임시저장, 진짜저장 버튼 컨트롤 const [showAddressButtonValid, setShowAddressButtonValid] = useState(false) //주소검색팝업 활성화 컨트롤 const [showDesignRequestButtonValid, setShowDesignRequestButtonValid] = useState(false) //설계의뢰팝업 활성화 컨트롤 + const [showWindSpeedButtonValid, setShowWindSpeedButtonValid] = useState(false) //풍속선택팝업 활성화 컨트롤 const objectNo = searchParams.get('objectNo') //url에서 물건번호 꺼내서 바로 set const [editMode, setEditMode] = useState('NEW') @@ -208,6 +209,14 @@ export default function StuffDetail() { form.setValue('zipNo', info.zipNo) } + //팝업에서 넘어온 설계의뢰 정보 + const setPlanReqInfo = (info) => {} + + //팝업에서 넘어온 바람정보 + const setWindSppedInfo = (info) => { + form.setValue('windSpeed', info.windSpeed) + } + //임시저장 저장 버튼 컨트롤 // dispCompanyName: '', //담당자 // objectName: '', //물건명 @@ -307,6 +316,16 @@ export default function StuffDetail() { setShowDesignRequestButtonValid(true) } + // 풍속선택 팝업 오픈 + const onSearchWindSpeedPopOpen = () => { + const prefName = form.watch('prefName') + if (prefName === '') { + alert(getMessage('stuff.windSelectPopup.error.message1')) + } else { + setShowWindSpeedButtonValid(true) + } + } + useEffect(() => { if (prefValue !== '') { // 발전량시뮬레이션 지역 목록 @@ -326,18 +345,18 @@ export default function StuffDetail() { form.setValue('areaId', e.target.value) } - useEffect(() => { - if (!isEmptyArray(areaIdList)) { - let _prefName = form.watch('prefName') - // console.log('기준풍속 가져오는 API', _prefName) - get({ url: `/api/object/windSpeed/${_prefName}/list` }).then((res) => { - // console.log('res::', res) - if (!isEmptyArray(res)) { - setWindSpeedList(res) - } - }) - } - }, [areaIdList]) + // useEffect(() => { + // if (!isEmptyArray(areaIdList)) { + // let _prefName = form.watch('prefName') + // // console.log('기준풍속 가져오는 API', _prefName) + // get({ url: `/api/object/windSpeed/${_prefName}/list` }).then((res) => { + // // console.log('res::', res) + // if (!isEmptyArray(res)) { + // setWindSpeedList(res) + // } + // }) + // } + // }, [areaIdList]) //필수값 다 입력했을때 const onValid = (data) => { @@ -643,7 +662,10 @@ export default function StuffDetail() {
-
+
+ +
+ {/*
-
+
*/} {getMessage('stuff.detail.windSpeedSpan')} - +
@@ -803,7 +827,7 @@ export default function StuffDetail() { - 물건구분/물건명 * + {getMessage('stuff.detail.objectStatusId')} *
@@ -906,7 +930,12 @@ export default function StuffDetail() { )} {showAddressButtonValid && } - {showDesignRequestButtonValid && } + {showDesignRequestButtonValid && ( + + )} + {showWindSpeedButtonValid && ( + + )} ) } diff --git a/src/components/management/popup/FindAddressPop.jsx b/src/components/management/popup/FindAddressPop.jsx index 157cccb4..b3702ded 100644 --- a/src/components/management/popup/FindAddressPop.jsx +++ b/src/components/management/popup/FindAddressPop.jsx @@ -68,6 +68,7 @@ export default function FindAddressPop(props) { if (isNotEmptyArray(res.results)) { setGridProps({ ...gridProps, gridData: res.results }) } else { + alert(getMessage('stuff.addressPopup.error.message1')) setGridProps({ ...gridProps, gridData: [] }) } } else { @@ -80,7 +81,7 @@ export default function FindAddressPop(props) { const applyAddress = () => { // console.log('주소적용 클릭:::::::::', prefId, address1, address2, address3, zipNo) if (prefId == null) { - alert('stuff.addressPopup.error.message2') + alert(getMessage('stuff.addressPopup.error.message2')) } else { props.zipInfo({ zipNo: zipNo, @@ -99,6 +100,10 @@ export default function FindAddressPop(props) { const handleKeyUp = (e) => { let input = e.target input.value = input.value.replace(/[^0-9]/g, '') + + if (e.key === 'Enter') { + searchPostNum() + } } //그리드에서 선택한 우편정보 diff --git a/src/components/management/popup/PlanRequestPop.jsx b/src/components/management/popup/PlanRequestPop.jsx index aa6b5492..224519d0 100644 --- a/src/components/management/popup/PlanRequestPop.jsx +++ b/src/components/management/popup/PlanRequestPop.jsx @@ -12,7 +12,6 @@ import dayjs from 'dayjs' import PlanRequestPopQGrid from './PlanRequestPopQGrid' import { sessionStore } from '@/store/commonAtom' import { planReqSearchState } from '@/store/planReqAtom' -import Select from 'react-dropdown-select' export default function PlanRequestPop(props) { const sessionState = useRecoilValue(sessionStore) diff --git a/src/components/management/popup/WindSelectPop.jsx b/src/components/management/popup/WindSelectPop.jsx new file mode 100644 index 00000000..db1795f8 --- /dev/null +++ b/src/components/management/popup/WindSelectPop.jsx @@ -0,0 +1,115 @@ +import React, { useState, useEffect } from 'react' +import { useMessage } from '@/hooks/useMessage' +import { useAxios } from '@/hooks/useAxios' +import { globalLocaleStore } from '@/store/localeAtom' +import { useRecoilValue } from 'recoil' +import { isEmptyArray, isNotEmptyArray } from '@/util/common-utils' +export default function WindSelectPop(props) { + const globalLocaleState = useRecoilValue(globalLocaleStore) + const { promiseGet } = useAxios(globalLocaleState) + const [windSpeedList, setWindSpeedList] = useState([]) + const [windSpeed, setWindSpeed] = useState(null) + const { getMessage } = useMessage() + + //선택한 라디오 값 세팅 + const handleChangeRadio = (e) => { + setWindSpeed(e.target.value) + } + + //적용 + const applyWindSpeed = () => { + if (windSpeed == null) { + alert(getMessage('stuff.windSelectPopup.error.message2')) + } else { + props.windSpeedInfo({ windSpeed: windSpeed }) + + //팝업닫기 + props.setShowWindSpeedButtonValid(false) + } + } + + useEffect(() => { + if (props.prefName !== '') { + promiseGet({ url: `/api/object/windSpeed/${props.prefName}/list` }).then((res) => { + if (res.status === 200) { + if (!isEmptyArray(res.data)) { + setWindSpeedList(res.data) + } + } + }) + } + }, [props]) + + return ( +
+
+
+
+

{getMessage('stuff.windSelectPopup.title')}

+ +
+
+
+
+ + + + + + + + + + + +
{getMessage('stuff.windSelectPopup.search.address1')} +
+ +
+
+
+
+ + + + + + + + + + {windSpeedList.map((row, index) => { + // console.log('row:::', row) + return ( + + + + + + ) + })} + +
{getMessage('stuff.windSelectPopup.table.selected')}{getMessage('stuff.windSelectPopup.table.windspeed')}Remarks
+
+ + +
+
{row.windSpeed}{row.remarks}
+
+
+
+ + +
+
+
+
+
+ ) +} diff --git a/src/locales/ja.json b/src/locales/ja.json index 330d7fe9..8d1319b6 100644 --- a/src/locales/ja.json +++ b/src/locales/ja.json @@ -508,6 +508,14 @@ "stuff.search.period": "期間検索", "stuff.search.schDateTypeU": "更新日", "stuff.search.schDateTypeR": "登録日", + "stuff.windSelectPopup.title": "風速選択", + "stuff.windSelectPopup.table.selected": "選択", + "stuff.windSelectPopup.table.windspeed": "風速", + "stuff.windSelectPopup.error.message1": "住所を先に検索してください", + "stuff.windSelectPopup.error.message2": "風速を選択してください。", + "stuff.windSelectPopup.search.address1": "県", + "stuff.windSelectPopup.btn1": "閉じる", + "stuff.windSelectPopup.btn2": "選択", "length": "長さ", "height": "高さ", "output": "出力", diff --git a/src/locales/ko.json b/src/locales/ko.json index d6b8849b..884a8772 100644 --- a/src/locales/ko.json +++ b/src/locales/ko.json @@ -513,6 +513,14 @@ "stuff.search.period": "기간검색", "stuff.search.schDateTypeU": "갱신일", "stuff.search.schDateTypeR": "등록일", + "stuff.windSelectPopup.title": "풍속선택", + "stuff.windSelectPopup.table.selected": "선택", + "stuff.windSelectPopup.table.windspeed": "풍속", + "stuff.windSelectPopup.error.message1": "주소를 먼저 검색해주세요.", + "stuff.windSelectPopup.error.message2": "풍속을 선택해주세요.", + "stuff.windSelectPopup.search.address1": "현", + "stuff.windSelectPopup.btn1": "닫기", + "stuff.windSelectPopup.btn2": "선택", "length": "길이", "height": "높이", "output": "출력", From 78f892e4e3ad210313752232e446f3e077a5fdf1 Mon Sep 17 00:00:00 2001 From: minsik Date: Wed, 16 Oct 2024 17:55:48 +0900 Subject: [PATCH 42/68] =?UTF-8?q?useContextMenu=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/useContextMenu.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/hooks/useContextMenu.js b/src/hooks/useContextMenu.js index 2cca39f3..665617ef 100644 --- a/src/hooks/useContextMenu.js +++ b/src/hooks/useContextMenu.js @@ -2,10 +2,13 @@ import { useRecoilValue } from 'recoil' import { currentMenuState } from '@/store/canvasAtom' import { useEffect, useState } from 'react' import { MENU } from '@/common/common' +import AuxiliaryMove from '@/components/floor-plan/modal/auxiliary/AuxiliaryMove' +import AuxiliarySize from '@/components/floor-plan/modal/auxiliary/AuxiliarySize' export function useContextMenu() { const currentMenu = useRecoilValue(currentMenuState) const [contextMenu, setContextMenu] = useState([[]]) + const [currentContextMenu, setCurrentContextMenu] = useState(null) useEffect(() => { switch (currentMenu) { @@ -72,10 +75,12 @@ export function useContextMenu() { { id: 'sizeEdit', name: '사이즈 변경', + component: , }, { id: 'auxiliaryMove', name: '보조선 이동(M)', + component: , }, { id: 'auxiliaryCopy', @@ -149,5 +154,7 @@ export function useContextMenu() { return { contextMenu, + currentContextMenu, + setCurrentContextMenu, } } From 9d15682fe5ae305aa6d4d746aecb66532a9d3b73 Mon Sep 17 00:00:00 2001 From: minsik Date: Wed, 16 Oct 2024 17:56:02 +0900 Subject: [PATCH 43/68] =?UTF-8?q?=EB=B3=B4=EC=A1=B0=EC=84=A0=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99,=20=EC=82=AC=EC=9D=B4=EC=A6=88=20=EB=B3=80=EA=B2=BD?= =?UTF-8?q?=20=EB=AA=A8=EB=8B=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/floor-plan/CanvasFrame.jsx | 141 ++---------------- .../modal/auxiliary/AuxiliaryMove.jsx | 8 +- .../modal/auxiliary/AuxiliarySize.jsx | 4 +- 3 files changed, 19 insertions(+), 134 deletions(-) diff --git a/src/components/floor-plan/CanvasFrame.jsx b/src/components/floor-plan/CanvasFrame.jsx index 17f0018d..f34d1cb6 100644 --- a/src/components/floor-plan/CanvasFrame.jsx +++ b/src/components/floor-plan/CanvasFrame.jsx @@ -1,19 +1,23 @@ 'use client' -import { useEffect, useRef, useState } from 'react' +import { useEffect, useRef } from 'react' import { useCanvas } from '@/hooks/useCanvas' import { useEvent } from '@/hooks/useEvent' import QContextMenu from '@/components/common/context-menu/QContextMenu' +import { useContextMenu } from '@/hooks/useContextMenu' import { useRecoilValue } from 'recoil' -import { currentMenuState } from '@/store/canvasAtom' -import { MENU } from '@/common/common' +import { currentObjectState } from '@/store/canvasAtom' export default function CanvasFrame({ plan }) { const canvasRef = useRef(null) const { canvas } = useCanvas('canvas') - const currentMenu = useRecoilValue(currentMenuState) - const [contextMenu, setContextMenu] = useState([[]]) + const { contextMenu, currentContextMenu, setCurrentContextMenu } = useContextMenu() + const currentObject = useRecoilValue(currentObjectState) + + useEffect(() => { + console.log(currentObject) + }, [currentObject]) useEvent() const loadCanvas = () => { @@ -31,129 +35,7 @@ export default function CanvasFrame({ plan }) { loadCanvas() }, [plan, canvas]) - useEffect(() => { - switch (currentMenu) { - case MENU.PLAN_DRAWING: - setContextMenu([ - [ - { - name: '그리드 이동', - }, - { - name: '그리드 복사', - }, - { - name: '그리드 색 변경', - }, - { - name: '삭제', - }, - { - name: '전체 삭제', - }, - ], - ]) - break - case MENU.ROOF_COVERING.EXTERIOR_WALL_LINE: - case MENU.ROOF_COVERING.ROOF_SHAPE_SETTINGS: - case MENU.ROOF_COVERING.ROOF_SHAPE_PASSIVITY_SETTINGS: - case MENU.ROOF_COVERING.ROOF_SHAPE_EDITING: - case MENU.ROOF_COVERING.HELP_LINE_DRAWING: - case MENU.ROOF_COVERING.EAVES_KERAVA_EDIT: - case MENU.ROOF_COVERING.MOVEMENT_SHAPE_UPDOWN: - case MENU.ROOF_COVERING.OUTLINE_EDIT_OFFSET: - case MENU.ROOF_COVERING.ROOF_SHAPE_ALLOC: - case MENU.ROOF_COVERING.DEFAULT: - console.log('지붕덮개') - setContextMenu([ - [ - { - name: '지붕재 배치', - }, - { - name: '지붕재 삭제', - }, - { - name: '지붕재 전체 삭제', - }, - { - name: '선택・이동', - }, - { - name: '외벽선 삭제', - }, - ], - [ - { - name: '사이즈 변경', - }, - { - name: '보조선 이동(M)', - }, - { - name: '보조선 복사(C)', - }, - { - name: '보조선 삭제(D)', - }, - { - name: '보조선 수직이등분선', - }, - { - name: '보조선 절삭', - }, - { - name: '보조선 전체 삭제', - }, - ], - ]) - break - case MENU.BATCH_CANVAS.SLOPE_SETTING: - case MENU.BATCH_CANVAS.BATCH_DRAWING: - case MENU.BATCH_CANVAS.SURFACE_SHAPE_BATCH: - case MENU.BATCH_CANVAS.OBJECT_BATCH: - case MENU.BATCH_CANVAS.ALL_REMOVE: - case MENU.BATCH_CANVAS.DEFAULT: - console.log('배치면') - setContextMenu([ - [ - { - name: '사이즈 변경', - }, - { - name: '삭제(D)', - }, - { - name: '이동(M)', - }, - { - name: '복사(C)', - }, - ], - [ - { - name: '지붕재 변경', - }, - { - name: '각 변 속성 변경', - }, - { - name: '흐름 방향 변경', - }, - ], - ]) - break - default: - console.log('default') - setContextMenu([]) - break - } - }, [currentMenu]) - - useEffect(() => { - console.log('currentMenu', currentMenu) - console.log('contextMenu', contextMenu) - }, [contextMenu]) + const onClickContextMenu = (index) => {} return (
@@ -162,11 +44,12 @@ export default function CanvasFrame({ plan }) { {contextMenu.map((menus, index) => (
    {menus.map((menu) => ( -
  • {menu.name}
  • +
  • setCurrentContextMenu(menu)}>{menu.name}
  • ))}
))} + {currentContextMenu?.component}
) } diff --git a/src/components/floor-plan/modal/auxiliary/AuxiliaryMove.jsx b/src/components/floor-plan/modal/auxiliary/AuxiliaryMove.jsx index 65925cbd..28674c07 100644 --- a/src/components/floor-plan/modal/auxiliary/AuxiliaryMove.jsx +++ b/src/components/floor-plan/modal/auxiliary/AuxiliaryMove.jsx @@ -1,14 +1,16 @@ import { useMessage } from '@/hooks/useMessage' import WithDraggable from '@/components/common/draggable/WithDraggable' -export default function AuxiliaryMove({ setShowAuxiliaryModal }) { +export default function AuxiliaryMove({ setCurrentContextMenu }) { const { getMessage } = useMessage() return ( - +

補助線の移動

- +
移動する方向を入力してください
diff --git a/src/components/floor-plan/modal/auxiliary/AuxiliarySize.jsx b/src/components/floor-plan/modal/auxiliary/AuxiliarySize.jsx index baa9fafa..c562e8dd 100644 --- a/src/components/floor-plan/modal/auxiliary/AuxiliarySize.jsx +++ b/src/components/floor-plan/modal/auxiliary/AuxiliarySize.jsx @@ -1,14 +1,14 @@ import { useMessage } from '@/hooks/useMessage' import WithDraggable from '@/components/common/draggable/WithDraggable' -export default function AuxiliarySize({ setShowAuxiliaryModal }) { +export default function AuxiliarySize({ setCurrentContextMenu }) { const { getMessage } = useMessage() return (

補助線サイズ変更

-
From 99f9a50ad515b469300ce549ca32405224c67ad2 Mon Sep 17 00:00:00 2001 From: minsik Date: Wed, 16 Oct 2024 17:56:16 +0900 Subject: [PATCH 44/68] =?UTF-8?q?id=20->=20key=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../floor-plan/modal/roofShape/RoofShapePassivitySetting.jsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/floor-plan/modal/roofShape/RoofShapePassivitySetting.jsx b/src/components/floor-plan/modal/roofShape/RoofShapePassivitySetting.jsx index 8eaa4a49..ec1684a6 100644 --- a/src/components/floor-plan/modal/roofShape/RoofShapePassivitySetting.jsx +++ b/src/components/floor-plan/modal/roofShape/RoofShapePassivitySetting.jsx @@ -1,4 +1,3 @@ -import { useState } from 'react' import WithDraggable from '@/components/common/draggable/WithDraggable' import Eaves from '@/components/floor-plan/modal/roofShape/passivity/Eaves' import Gable from '@/components/floor-plan/modal/roofShape/passivity/Gable' @@ -37,7 +36,7 @@ export default function RoofShapePassivitySetting({ setShowRoofShapePassivitySet
{buttons.map((button) => ( - ))} From d7463556fe2211b8520b94a854804c1ae6abd9a4 Mon Sep 17 00:00:00 2001 From: yoosangwook Date: Wed, 16 Oct 2024 18:18:40 +0900 Subject: [PATCH 45/68] feat: Add QPagination Component --- src/components/Playground.jsx | 12 ++++++ .../common/pagination/QPagination.jsx | 37 +++++++++++++++++++ src/hooks/usePagination.js | 30 +++++++++++++++ 3 files changed, 79 insertions(+) create mode 100644 src/components/common/pagination/QPagination.jsx create mode 100644 src/hooks/usePagination.js diff --git a/src/components/Playground.jsx b/src/components/Playground.jsx index 5d54c858..a882bdd9 100644 --- a/src/components/Playground.jsx +++ b/src/components/Playground.jsx @@ -20,6 +20,7 @@ import Image from 'next/image' import QInput from './common/input/Qinput' import QSelect from './common/select/QSelect' +import QPagination from './common/pagination/QPagination' export default function Playground() { const [useCadFile, setUseCadFile] = useRecoilState(useCadFileState) @@ -41,6 +42,7 @@ export default function Playground() { const [radioInput, setRadioInput] = useState('') const [checkboxInput, setCheckboxInput] = useState([]) const [selectedValue, setSelectedValue] = useState('') + useEffect(() => { console.log('textInput:', textInput) }, [textInput]) @@ -130,6 +132,13 @@ export default function Playground() { }) } + const paginationProps = { + pageNo: 1, + pageSize: 10, + pagePerBlock: 10, + totalCount: 501, + } + return ( <>
@@ -290,6 +299,9 @@ export default function Playground() { Sweetalert - confirm
+
+ +
) diff --git a/src/components/common/pagination/QPagination.jsx b/src/components/common/pagination/QPagination.jsx new file mode 100644 index 00000000..4175036b --- /dev/null +++ b/src/components/common/pagination/QPagination.jsx @@ -0,0 +1,37 @@ +import usePagination from '@/hooks/usePagination' + +export default function QPagination(props) { + const { currentPage, pageRange, changePage, totalPages } = usePagination(props) + + return ( +
    +
  1. + +
  2. +
  3. + +
  4. + {pageRange.map((page) => ( +
  5. + +
  6. + ))} +
  7. + +
  8. +
  9. + +
  10. +
+ ) +} diff --git a/src/hooks/usePagination.js b/src/hooks/usePagination.js new file mode 100644 index 00000000..818275e1 --- /dev/null +++ b/src/hooks/usePagination.js @@ -0,0 +1,30 @@ +import { useState } from 'react' + +/** + * 페이지네이션 훅 + * @param {number} pageNo 현재 페이지 {optional} + * @param {number} pageSize 페이지당 아이템 수 {optional} + * @param {number} pagePerBlock 페이지 그룹 수 {optional} + * @param {number} totalCount 전체 아이템 수 {required} + * @returns + */ +const usePagination = ({ pageNo = 1, pageSize = 10, pagePerBlock = 10, totalCount = 0 }) => { + const [currentPage, setCurrentPage] = useState(pageNo) + const changePage = (page) => { + setCurrentPage(page) + } + + const pageGroup = Math.floor((currentPage - 1) / pagePerBlock) + 1 + const totalPages = Math.ceil(totalCount / pageSize) + const pages = Array.from({ length: totalPages }, (_, i) => i + 1) + const startPage = Math.max(1, (pageGroup - 1) * pagePerBlock + 1) + const endPage = Math.min(totalPages, pageGroup * pagePerBlock) + const pageRange = Array.from({ length: endPage !== totalPages ? pagePerBlock : endPage - startPage + 1 }, (_, i) => { + if (i + startPage > endPage) return + return i + startPage + }) + + return { currentPage, pageRange, changePage, totalPages } +} + +export default usePagination From aab9084f1b6d42dd4e62361086ccbd9cc021f9ff Mon Sep 17 00:00:00 2001 From: yoosangwook Date: Thu, 17 Oct 2024 09:54:15 +0900 Subject: [PATCH 46/68] refactor: modify QPagination MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 그룹 변경 이벤트 수정 - 외부 함수 주입 추가 --- src/components/Playground.jsx | 3 +++ .../common/pagination/QPagination.jsx | 18 ++++++++++++------ src/hooks/usePagination.js | 2 +- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/components/Playground.jsx b/src/components/Playground.jsx index a882bdd9..6393158d 100644 --- a/src/components/Playground.jsx +++ b/src/components/Playground.jsx @@ -137,6 +137,9 @@ export default function Playground() { pageSize: 10, pagePerBlock: 10, totalCount: 501, + handleChangePage: (page) => { + console.log('page', page) + }, } return ( diff --git a/src/components/common/pagination/QPagination.jsx b/src/components/common/pagination/QPagination.jsx index 4175036b..54a61541 100644 --- a/src/components/common/pagination/QPagination.jsx +++ b/src/components/common/pagination/QPagination.jsx @@ -1,36 +1,42 @@ import usePagination from '@/hooks/usePagination' export default function QPagination(props) { - const { currentPage, pageRange, changePage, totalPages } = usePagination(props) + const { handleChangePage, pagePerBlock = 10 } = props + const { currentPage, changePage, pageGroup, totalPages, pages, startPage, endPage, pageRange } = usePagination(props) + + const handlePage = (page) => { + handleChangePage(page) + changePage(page) + } return (
  1. - +
  2. {pageRange.map((page) => (
  3. - +
  4. ))}
  5. - +
) diff --git a/src/hooks/usePagination.js b/src/hooks/usePagination.js index 818275e1..4fc3a959 100644 --- a/src/hooks/usePagination.js +++ b/src/hooks/usePagination.js @@ -24,7 +24,7 @@ const usePagination = ({ pageNo = 1, pageSize = 10, pagePerBlock = 10, totalCoun return i + startPage }) - return { currentPage, pageRange, changePage, totalPages } + return { currentPage, changePage, pageGroup, totalPages, pages, startPage, endPage, pageRange } } export default usePagination From e7d988bba6b83abf75cb98144ffb5ca55f5cc92c Mon Sep 17 00:00:00 2001 From: yoosangwook Date: Thu, 17 Oct 2024 09:58:06 +0900 Subject: [PATCH 47/68] refactor: QPagination component to modify handleChangePage default value --- src/components/common/pagination/QPagination.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/common/pagination/QPagination.jsx b/src/components/common/pagination/QPagination.jsx index 54a61541..7eb6638a 100644 --- a/src/components/common/pagination/QPagination.jsx +++ b/src/components/common/pagination/QPagination.jsx @@ -1,7 +1,7 @@ import usePagination from '@/hooks/usePagination' export default function QPagination(props) { - const { handleChangePage, pagePerBlock = 10 } = props + const { handleChangePage = () => {}, pagePerBlock = 10 } = props const { currentPage, changePage, pageGroup, totalPages, pages, startPage, endPage, pageRange } = usePagination(props) const handlePage = (page) => { From 6d6a44698a8174e408a3e454172aa2b61607bc3d Mon Sep 17 00:00:00 2001 From: basssy Date: Thu, 17 Oct 2024 10:31:29 +0900 Subject: [PATCH 48/68] =?UTF-8?q?=EB=AC=BC=EA=B1=B4=20=EC=83=81=EC=84=B8?= =?UTF-8?q?=ED=99=94=EB=A9=B4=20=ED=88=B4=ED=8C=81=20=EB=A9=94=EC=84=B8?= =?UTF-8?q?=EC=A7=80=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/management/StuffDetail.jsx | 24 +++++++------------ .../management/popup/FindAddressPopQGrid.jsx | 6 +---- .../management/popup/PlanRequestPopQGrid.jsx | 6 +---- src/locales/ja.json | 1 + src/locales/ko.json | 1 + 5 files changed, 12 insertions(+), 26 deletions(-) diff --git a/src/components/management/StuffDetail.jsx b/src/components/management/StuffDetail.jsx index 9b8bfd4a..7167fc63 100644 --- a/src/components/management/StuffDetail.jsx +++ b/src/components/management/StuffDetail.jsx @@ -488,7 +488,7 @@ export default function StuffDetail() { - 물건구분/물건명 * + {getMessage('stuff.detail.objectStatusId')} *
@@ -529,7 +529,9 @@ export default function StuffDetail() { {getMessage('stuff.detail.saleStoreId')} *
-
+
+ {getMessage('stuff.detail.tooltip.saleStoreId')} +
@@ -553,7 +555,9 @@ export default function StuffDetail() {
{getMessage('stuff.detail.otherSaleStoreId')}
-
+
+ {getMessage('stuff.detail.tooltip.saleStoreId')} +
@@ -568,18 +572,6 @@ export default function StuffDetail() { isDisabled={form.watch('saleStoreId') !== '' ? false : true} isClearable={true} /> - {/* */}
-
+
{/*
diff --git a/src/components/management/popup/FindAddressPopQGrid.jsx b/src/components/management/popup/FindAddressPopQGrid.jsx index 83544a5a..b9d1bb90 100644 --- a/src/components/management/popup/FindAddressPopQGrid.jsx +++ b/src/components/management/popup/FindAddressPopQGrid.jsx @@ -31,10 +31,6 @@ export default function FindAddressPopGrid(props) { gridData ? setRowData(gridData) : '' }, [gridData]) - const rowSelection = useMemo(() => { - return { mode: 'singleRow', enableClickSelection: true } - }, []) - const onGridReady = useCallback( (params) => { setGridApi(params.api) @@ -57,7 +53,7 @@ export default function FindAddressPopGrid(props) { rowData={rowData} columnDefs={colDefs} defaultColDef={defaultColDef} - rowSelection={rowSelection} + rowSelection={'singleRow'} pagination={isPageable} onSelectionChanged={onSelectionChanged} /> diff --git a/src/components/management/popup/PlanRequestPopQGrid.jsx b/src/components/management/popup/PlanRequestPopQGrid.jsx index 8f29cdbe..b7dca164 100644 --- a/src/components/management/popup/PlanRequestPopQGrid.jsx +++ b/src/components/management/popup/PlanRequestPopQGrid.jsx @@ -31,10 +31,6 @@ export default function PlanRequestPopQGrid(props) { gridData ? setRowData(gridData) : '' }, [gridData]) - const rowSelection = useMemo(() => { - return { mode: 'singleRow', enableClickSelection: true } - }, []) - const onGridReady = useCallback( (params) => { setGridApi(params.api) @@ -51,7 +47,7 @@ export default function PlanRequestPopQGrid(props) { rowData={rowData} columnDefs={colDefs} defaultColDef={defaultColDef} - rowSelection={rowSelection} + rowSelection={'singleRow'} pagination={isPageable} // onSelectionChanged={onSelectionChanged} /> diff --git a/src/locales/ja.json b/src/locales/ja.json index 8d1319b6..2603d356 100644 --- a/src/locales/ja.json +++ b/src/locales/ja.json @@ -471,6 +471,7 @@ "stuff.detail.conType0": "余剰", "stuff.detail.conType1": "全量", "stuff.detail.remarks": "メモ", + "stuff.detail.tooltip.saleStoreId": "販売代理店または販売代理店IDを1文字以上入力してください", "stuff.planReqPopup.popTitle": "設計依頼検索", "stuff.planReqPopup.btn1": "検索", "stuff.planReqPopup.btn2": "初期化", diff --git a/src/locales/ko.json b/src/locales/ko.json index 884a8772..2741f5e1 100644 --- a/src/locales/ko.json +++ b/src/locales/ko.json @@ -476,6 +476,7 @@ "stuff.detail.conType0": "잉여", "stuff.detail.conType1": "전량", "stuff.detail.remarks": "메모", + "stuff.detail.tooltip.saleStoreId": "판매대리점 또는 판매대리점ID를 1자 이상 입력하세요", "stuff.planReqPopup.popTitle": "설계 요청 검색", "stuff.planReqPopup.btn1": "검색", "stuff.planReqPopup.btn2": "초기화", From ee34bd549c6f40f1215456b3dd638fd17e8dd0bc Mon Sep 17 00:00:00 2001 From: leeyongjae Date: Thu, 17 Oct 2024 10:32:55 +0900 Subject: [PATCH 49/68] =?UTF-8?q?Q.CAST=20=EC=9E=90=EB=8F=99=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EC=9D=B8=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=EB=B9=84?= =?UTF-8?q?=EB=B0=80=EB=B2=88=ED=98=B8=20=EB=B3=80=EA=B2=BD=20=ED=8C=9D?= =?UTF-8?q?=EC=97=85=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/auth/Login.jsx | 71 +++++++++---------------- src/components/myInfo/UserInfoModal.jsx | 49 ++++++++++++++--- src/locales/ja.json | 2 + src/locales/ko.json | 2 + 4 files changed, 71 insertions(+), 53 deletions(-) diff --git a/src/components/auth/Login.jsx b/src/components/auth/Login.jsx index c43341e3..28416284 100644 --- a/src/components/auth/Login.jsx +++ b/src/components/auth/Login.jsx @@ -16,8 +16,7 @@ import Cookies from 'js-cookie' import { useSearchParams } from 'next/navigation' export default function Login() { - //////////////////////////////////////////////////////////////////////////////// - // 자동 로그인 작업진행중 + // 자동 로그인 const initParams = useSearchParams() const autoLoginParam = initParams.get('autoLoginParam1') useEffect(() => { @@ -26,45 +25,27 @@ export default function Login() { } }, []) const autoLoginProcess = async (autoLoginParam) => { - setSession({ - userId: autoLoginParam, - saleStoreId: null, - name: null, - mail: null, - tel: null, - storeId: 'TEMP02', - userNm: 'ㅇㅇ6610', - userNmKana: '신규사용자 16610', - category: '인상6610', - telNo: '336610', - fax: null, - email: 't10t@naver.com', - pwdInitYn: 'Y', - storeLvl: '2', - groupId: '70000', - }) - - setSessionState({ - userId: autoLoginParam, - saleStoreId: null, - name: null, - mail: null, - tel: null, - storeId: 'TEMP02', - userNm: 'ㅇㅇ6610', - userNmKana: '신규사용자 16610', - category: '인상6610', - telNo: '336610', - fax: null, - email: 't10t@naver.com', - pwdInitYn: 'Y', - storeLvl: '2', - groupId: '70000', - }) - - router.push('/') + await promisePost({ url: '/api/login/v1.0/user/login/autoLoginDecryptData', data: { loginId: autoLoginParam } }) + .then((res) => { + if (res) { + if (res.data) { + post({ url: '/api/login/v1.0/user', data: { loginId: res.data } }).then((response) => { + if (response) { + const result = { ...response, storeLvl: response.groupId === '60000' ? '1' : '2', pwdInitYn: 'Y' } + setSession(result) + setSessionState(result) + router.push('/') + } else { + router.push('/login') + } + }) + } + } + }) + .catch((error) => { + router.push('/login') + }) } - //////////////////////////////////////////////////////////////////////////////// const [userId, setUserId] = useState('') const [checkId, setCheckId] = useState('') @@ -86,12 +67,14 @@ export default function Login() { const [passwordReset, setPasswordReset] = useState(1) - const { promisePost, promisePatch } = useAxios(globalLocaleState) + const { promisePost, promisePatch, post } = useAxios(globalLocaleState) // login process const loginProcess = async (e) => { e.preventDefault() const formData = new FormData(e.target) + + /////////////////////////////////////////////////////////// // 임시 로그인 처리 setSession({ userId: 'NEW016610', @@ -110,7 +93,6 @@ export default function Login() { storeLvl: '1', groupId: '60000', }) - setSessionState({ userId: 'NEW016610', saleStoreId: null, @@ -128,16 +110,14 @@ export default function Login() { storeLvl: '1', groupId: '60000', }) - - // ID SAVE 체크되어 있는 경우, 쿠키 저장 if (chkLoginId) { Cookies.set('chkLoginId', formData.get('id'), { expires: 7 }) } else { Cookies.remove('chkLoginId') } - router.push('/') // 임시 로그인 처리 끝 + /////////////////////////////////////////////////////////// // 로그인 처리 시작 - ** 상단 임시 로그인 추후 삭제 필요 ** // const param = { @@ -173,7 +153,6 @@ export default function Login() { loginId: checkId, email: checkEmail, } - await promisePatch({ url: '/api/login/v1.0/user/init-password', data: param, diff --git a/src/components/myInfo/UserInfoModal.jsx b/src/components/myInfo/UserInfoModal.jsx index 9f8b0721..7643913c 100644 --- a/src/components/myInfo/UserInfoModal.jsx +++ b/src/components/myInfo/UserInfoModal.jsx @@ -8,7 +8,7 @@ export default function UserInfoModal({ userId, userInfoModal, setUserInfoModal const { getMessage } = useMessage() // api 조회 관련 - const { get, promisePatch } = useAxios() + const { get, promisePatch, promisePost } = useAxios() const [info, setInfo] = useState({ userId: '', name: '', @@ -25,6 +25,7 @@ export default function UserInfoModal({ userId, userInfoModal, setUserInfoModal const [showPwd, setShowPwd] = useState(false) const [chkChgPwd, setChkChgPwd] = useState(false) const [chgPwd, setChgPwd] = useState('') + const pwdInput = useRef() const chgPwdInput = useRef() useEffect(() => { @@ -54,7 +55,7 @@ export default function UserInfoModal({ userId, userInfoModal, setUserInfoModal const handleChangePassword = async () => { if (chgPwd === '') { chgPwdInput.current.focus() - return alert(getMessage('myinfo.message.validation.password1')) + return alert(getMessage('myinfo.message.validation.password4')) } if (password === chgPwd) { chgPwdInput.current.focus() @@ -73,6 +74,8 @@ export default function UserInfoModal({ userId, userInfoModal, setUserInfoModal if (res.data.result.resultCode === 'S') { alert(getMessage('myinfo.message.save')) setChkChgPwd(false) + setPassword(chgPwd) + setChgPwd('') } else { alert(res.data.result.resultMsg) } @@ -83,6 +86,40 @@ export default function UserInfoModal({ userId, userInfoModal, setUserInfoModal }) } + // 비밀번호 변경 버튼 클릭 시, + const checkPasswordProcess = async (e) => { + if (password === '') { + pwdInput.current.focus() + return alert(getMessage('myinfo.message.validation.password1')) + } + + const param = { + loginId: userId, + pwd: password, + } + await promisePost({ url: '/api/login/v1.0/login', data: param }) + .then((res) => { + if (res) { + if (res.data.result.resultCode === 'S') { + setChkChgPwd(true) + setTimeout(() => { + chgPwdInput.current.focus() + }, 10) + } else { + alert(getMessage('myinfo.message.password.error')) + setChkChgPwd(false) + setChgPwd('') + setTimeout(() => { + pwdInput.current.focus() + }, 10) + } + } + }) + .catch((error) => { + alert(error.response.data.message) + }) + } + return ( <>
@@ -141,6 +178,7 @@ export default function UserInfoModal({ userId, userInfoModal, setUserInfoModal { setPassword(e.target.value) }} @@ -151,11 +189,8 @@ export default function UserInfoModal({ userId, userInfoModal, setUserInfoModal
- - - + + + +
) -} +}) + +export default TriangleDormer diff --git a/src/components/floor-plan/modal/placementSurface/PlacementSurfaceSetting.jsx b/src/components/floor-plan/modal/placementSurface/PlacementSurfaceSetting.jsx index d2c3022a..e667667e 100644 --- a/src/components/floor-plan/modal/placementSurface/PlacementSurfaceSetting.jsx +++ b/src/components/floor-plan/modal/placementSurface/PlacementSurfaceSetting.jsx @@ -4,17 +4,29 @@ import { useEffect, useState, useRef } from 'react' import Image from 'next/image' import PlacementSurface from '@/components/floor-plan/modal/placementSurface/PlacementSurface' import { useSurfaceShapeBatch } from '@/hooks/surface/useSurfaceShapeBatch' +import { useRecoilValue } from 'recoil' +import { canvasState } from '@/store/canvasAtom' +import { POLYGON_TYPE } from '@/common/common' export default function PlacementSurfaceSetting({ setShowPlacementSurfaceSettingModal }) { const { getMessage } = useMessage() - const [selectedType, setSelectedType] = useState() const [rotate, setRotate] = useState(0) const [xInversion, setXInversion] = useState(false) const [yInversion, setYInversion] = useState(false) + const canvas = useRecoilValue(canvasState) const { applySurfaceShape } = useSurfaceShapeBatch() + const surfaceShapePolygons = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF) + + //오브젝트 배치에서 넘어오면 면형상 선택 가능 + useEffect(() => { + surfaceShapePolygons.forEach((obj) => { + obj.set({ selectable: true }) + }) + }, []) + const surfaceRefs = { length1: useRef(null), length2: useRef(null), diff --git a/src/hooks/object/useObjectBatch.js b/src/hooks/object/useObjectBatch.js index 33b393b8..7229f470 100644 --- a/src/hooks/object/useObjectBatch.js +++ b/src/hooks/object/useObjectBatch.js @@ -4,9 +4,18 @@ import { useRecoilState, useRecoilValue } from 'recoil' import { canvasState } from '@/store/canvasAtom' import { INPUT_TYPE, BATCH_TYPE } from '@/common/common' import { useEvent } from '@/hooks/useEvent' -import { polygonToTurfPolygon, rectToPolygon, pointsToTurfPolygon } from '@/util/canvas-util' +import { + polygonToTurfPolygon, + rectToPolygon, + triangleToPolygon, + pointsToTurfPolygon, + splitDormerTriangle, + setSurfaceShapePattern, +} from '@/util/canvas-util' import { useSwal } from '@/hooks/useSwal' import * as turf from '@turf/turf' +import { QPolygon } from '@/components/fabric/QPolygon' +import { drawDirectionArrow } from '@/util/qpolygon-utils' export function useObjectBatch() { const { getMessage } = useMessage() @@ -14,11 +23,11 @@ export function useObjectBatch() { const { addCanvasMouseEventListener, initEvent } = useEvent() const { swalFire } = useSwal() - const applyOpeningAndShadow = (objectPlacement, buttonAct, surfaceShapePolygons, setShowObjectSettingModal) => { + const applyOpeningAndShadow = (objectPlacement, buttonAct, surfaceShapePolygons) => { const selectedType = objectPlacement.typeRef.current.find((radio) => radio.checked).value const isCrossChecked = buttonAct === 1 ? objectPlacement.isCrossRef.current.checked : false - const objTempName = buttonAct === 1 ? BATCH_TYPE.OPENING_TEMP : BATCH_TYPE.SHADOW_TEMP const objName = buttonAct === 1 ? BATCH_TYPE.OPENING : BATCH_TYPE.SHADOW + const objTempName = buttonAct === 1 ? BATCH_TYPE.OPENING_TEMP : BATCH_TYPE.SHADOW_TEMP let rect, isDown, origX, origY let selectedSurface @@ -94,7 +103,7 @@ export function useObjectBatch() { if (!turf.booleanWithin(rectPolygon, selectedSurfacePolygon)) { swalFire({ text: '개구를 배치할 수 없습니다.', icon: 'error' }) //일단 지워 - deleteTempObjects(setShowObjectSettingModal) + deleteTempObjects() return } @@ -105,7 +114,7 @@ export function useObjectBatch() { if (isCross) { swalFire({ text: '겹치기 불가요...', icon: 'error' }) - deleteTempObjects(setShowObjectSettingModal) + deleteTempObjects() return } } @@ -173,7 +182,7 @@ export function useObjectBatch() { if (!turf.booleanWithin(rectPolygon, selectedSurfacePolygon)) { swalFire({ text: '개구를 배치할 수 없습니다.', icon: 'error' }) //일단 지워 - deleteTempObjects(setShowObjectSettingModal) + deleteTempObjects() return } @@ -184,7 +193,7 @@ export function useObjectBatch() { if (isCross) { swalFire({ text: '겹치기 불가요...', icon: 'error' }) - deleteTempObjects(setShowObjectSettingModal) + deleteTempObjects() return } } @@ -198,13 +207,209 @@ export function useObjectBatch() { } } - const deleteTempObjects = (setShowObjectSettingModal) => { - const deleteTarget = canvas?.getObjects().filter((obj) => obj.name === BATCH_TYPE.OPENING_TEMP || obj.name === BATCH_TYPE.SHADOW_TEMP) + const applyDormers = (dormerPlacement, buttonAct, surfaceShapePolygons) => { + const dormerName = buttonAct === 3 ? BATCH_TYPE.TRIANGLE_DORMER : BATCH_TYPE.PENTAGON_DORMER + const dormerTempName = buttonAct === 3 ? BATCH_TYPE.TRIANGLE_DORMER_TEMP : BATCH_TYPE.PENTAGON_DORMER_TEMP + const height = dormerPlacement.heightRef.current.value / 10 + const pitch = dormerPlacement.pitchRef.current.value + const directionRef = dormerPlacement.directionRef.current + const offsetRef = dormerPlacement.offsetRef.current.value === '' ? 0 : parseInt(dormerPlacement.offsetRef.current.value) / 10 + + let dormer, dormerOffset, isDown, selectedSurface + + console.log('dormerPlacement', dormerPlacement) + + if (height === '' || pitch === '' || height <= 0 || pitch <= 0) { + swalFire({ text: getMessage('common.canvas.validate.size'), icon: 'error' }) + return + } + + //삼각형 도머 + if (buttonAct === 3) { + const bottomLength = height / (pitch * 0.25) + const bottomOffsetLength = (height + offsetRef) / (pitch * 0.25) + + console.log(bottomOffsetLength) + + addCanvasMouseEventListener('mouse:move', (e) => { + isDown = true + if (!isDown) return + + canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === dormerTempName)) //움직일때 일단 지워가면서 움직임 + const pointer = canvas.getPointer(e.e) + + surfaceShapePolygons.forEach((surface) => { + if (surface.inPolygon({ x: pointer.x, y: pointer.y })) { + selectedSurface = surface + } + }) + + let angle = 0 + if (directionRef === 'left') { + //서 + angle = 90 + } else if (directionRef === 'right') { + //동 + angle = 270 + } else if (directionRef === 'up') { + //북 + angle = 180 + } + + dormer = new fabric.Triangle({ + fill: 'white', + stroke: 'red', + strokeDashArray: [5, 5], + strokeWidth: 1, + width: bottomLength * 2, + height: height, + left: pointer.x, + top: pointer.y, + selectable: true, + lockMovementX: true, + lockMovementY: true, + lockRotation: true, + lockScalingX: true, + lockScalingY: true, + name: dormerTempName, + originX: 'center', + originY: 'top', + angle: angle, + }) + canvas?.add(dormer) + + if (offsetRef > 0) { + dormerOffset = new fabric.Triangle({ + fill: 'gray', + stroke: 'red', + strokeDashArray: [5, 5], + strokeWidth: 1, + width: bottomOffsetLength * 2, + height: height + offsetRef, + left: pointer.x, + top: pointer.y, + selectable: true, + lockMovementX: true, + lockMovementY: true, + lockRotation: true, + lockScalingX: true, + lockScalingY: true, + name: dormerTempName, + originX: 'center', + originY: 'top', + angle: angle, + }) + canvas?.add(dormerOffset) + } + }) + + addCanvasMouseEventListener('mouse:up', (e) => { + if (dormer) { + // const trianglePolygon = pointsToTurfPolygon(triangleToPolygon(dormer)) + // const selectedSurfacePolygon = polygonToTurfPolygon(selectedSurface) + + // //지붕 밖으로 그렸을때 + // if (!turf.booleanWithin(trianglePolygon, selectedSurfacePolygon)) { + // swalFire({ text: '개구를 배치할 수 없습니다.', icon: 'error' }) + // //일단 지워 + // deleteTempObjects() + // return + // } + + //각도 추가 + let originAngle = 0 //기본 남쪽 + let direction = 'south' + + if (directionRef === 'left') { + //서 + originAngle = 90 + direction = 'west' + } else if (directionRef === 'right') { + //동 + originAngle = 270 + direction = 'east' + } else if (directionRef === 'up') { + //북 + originAngle = 180 + direction = 'north' + } + + let splitedTriangle = offsetRef > 0 ? splitDormerTriangle(dormerOffset, directionRef) : splitDormerTriangle(dormer, directionRef) + canvas?.remove(offsetRef > 0 ? dormerOffset : dormer) + + if (offsetRef > 0) + dormer.set({ + name: dormerName, + stroke: 'black', + strokeWidth: 1, + strokeDashArray: [0], + }) //오프셋이 있을땐 같이 도머로 만든다 + + const leftTriangle = new QPolygon(splitedTriangle[0], { + fill: 'transparent', + stroke: 'black', + strokeWidth: 1, + selectable: true, + lockMovementX: true, // X 축 이동 잠금 + lockMovementY: true, // Y 축 이동 잠금 + lockRotation: true, // 회전 잠금 + viewLengthText: true, + fontSize: 14, + direction: direction, + originX: 'center', + originY: 'center', + name: dormerName, + }) + + const rightTriangle = new QPolygon(splitedTriangle[1], { + fill: 'transparent', + stroke: 'black', + strokeWidth: 1, + selectable: true, + lockMovementX: true, // X 축 이동 잠금 + lockMovementY: true, // Y 축 이동 잠금 + lockRotation: true, // 회전 잠금 + viewLengthText: true, + fontSize: 14, + direction: direction, + originX: 'center', + originY: 'center', + name: dormerName, + }) + + canvas?.add(leftTriangle) + canvas?.add(rightTriangle) + + //패턴 + setSurfaceShapePattern(leftTriangle) + setSurfaceShapePattern(rightTriangle) + //방향 + drawDirectionArrow(leftTriangle) + drawDirectionArrow(rightTriangle) + + isDown = false + initEvent() + } + }) + } + } + + const deleteTempObjects = () => { + const deleteTarget = canvas + ?.getObjects() + .filter( + (obj) => + obj.name === BATCH_TYPE.OPENING_TEMP || + obj.name === BATCH_TYPE.SHADOW_TEMP || + obj.name === BATCH_TYPE.TRIANGLE_DORMER_TEMP || + obj.name === BATCH_TYPE.PENTAGON_DORMER_TEMP, + ) canvas?.remove(...deleteTarget) initEvent() //이벤트 초기화 } return { applyOpeningAndShadow, + applyDormers, } } diff --git a/src/hooks/surface/useSurfaceShapeBatch.js b/src/hooks/surface/useSurfaceShapeBatch.js index bcffeba4..7eb421cc 100644 --- a/src/hooks/surface/useSurfaceShapeBatch.js +++ b/src/hooks/surface/useSurfaceShapeBatch.js @@ -2,11 +2,10 @@ import { useRecoilValue } from 'recoil' import { canvasState } from '@/store/canvasAtom' -import { MENU, BATCH_TYPE } from '@/common/common' +import { MENU, BATCH_TYPE, POLYGON_TYPE } from '@/common/common' import { getIntersectionPoint, setSurfaceShapePattern } from '@/util/canvas-util' import { degreesToRadians } from '@turf/turf' import { QPolygon } from '@/components/fabric/QPolygon' -import { fabric } from 'fabric' import { useSwal } from '@/hooks/useSwal' import { useMessage } from '@/hooks/useMessage' import { useEvent } from '@/hooks/useEvent' @@ -116,7 +115,7 @@ export function useSurfaceShapeBatch() { addCanvasMouseEventListener('mouse:down', (e) => { isDrawing = false - obj.set('name', MENU.BATCH_CANVAS.SURFACE_SHAPE_BATCH) + obj.set('name', POLYGON_TYPE.ROOF) initEvent() setSurfaceShapePattern(obj) setShowPlacementSurfaceSettingModal(true) @@ -567,19 +566,25 @@ export function useSurfaceShapeBatch() { const deleteAllSurfacesAndObjects = () => { swalFire({ - text: '삭제 ㄱㄱ?', + text: '배치면 내용을 전부 삭제하시겠습니까?', type: 'confirm', confirmFn: () => { canvas?.getObjects().forEach((obj) => { - if (obj.name === MENU.BATCH_CANVAS.SURFACE_SHAPE_BATCH || obj.name === BATCH_TYPE.OPENING || obj.name === BATCH_TYPE.SHADOW) { + if ( + obj.name === POLYGON_TYPE.ROOF || + obj.name === BATCH_TYPE.OPENING || + obj.name === BATCH_TYPE.SHADOW || + obj.name === BATCH_TYPE.TRIANGLE_DORMER || + obj.name === BATCH_TYPE.PENTAGON_DORMER + ) { canvas?.remove(obj) } }) swalFire({ text: '삭제 완료 되었습니다.' }) }, - denyFn: () => { - swalFire({ text: '취소되었습니다.' }) - }, + // denyFn: () => { + // swalFire({ text: '취소되었습니다.', icon: 'error' }) + // }, }) } diff --git a/src/util/canvas-util.js b/src/util/canvas-util.js index 6b232b95..17c30f07 100644 --- a/src/util/canvas-util.js +++ b/src/util/canvas-util.js @@ -744,6 +744,79 @@ export const polygonToTurfPolygon = (polygon) => { ) } +export const splitDormerTriangle = (triangle, direction) => { + const halfWidth = triangle.width / 2 + + let leftPoints = [] + let rightPoints = [] + let leftPointOffset = [] + let rightPointOffset = [] + + if (direction === 'down') { + leftPoints = [ + { x: triangle.left, y: triangle.top }, + { x: triangle.left - halfWidth, y: triangle.top + triangle.height }, + { x: triangle.left, y: triangle.top + triangle.height }, + ] + + rightPoints = [ + { x: triangle.left, y: triangle.top }, + { x: triangle.left, y: triangle.top + triangle.height }, + { x: triangle.left + halfWidth, y: triangle.top + triangle.height }, + ] + } else if (direction === 'up') { + leftPoints = [ + { x: triangle.left, y: triangle.top }, + { x: triangle.left - halfWidth, y: triangle.top - triangle.height }, + { x: triangle.left, y: triangle.top - triangle.height }, + ] + + rightPoints = [ + { x: triangle.left, y: triangle.top }, + { x: triangle.left, y: triangle.top - triangle.height }, + { x: triangle.left + halfWidth, y: triangle.top - triangle.height }, + ] + } else if (direction === 'left') { + leftPoints = [ + { x: triangle.left, y: triangle.top }, + { x: triangle.left - triangle.height, y: triangle.top - halfWidth }, + { x: triangle.left - triangle.height, y: triangle.top }, + ] + + rightPoints = [ + { x: triangle.left, y: triangle.top }, + { x: triangle.left - triangle.height, y: triangle.top }, + { x: triangle.left - triangle.height, y: triangle.top + halfWidth }, + ] + } else if (direction === 'right') { + leftPoints = [ + { x: triangle.left, y: triangle.top }, + { x: triangle.left + triangle.height, y: triangle.top }, + { x: triangle.left + triangle.height, y: triangle.top + triangle.height }, + ] + + rightPoints = [ + { x: triangle.left, y: triangle.top }, + { x: triangle.left + triangle.height, y: triangle.top }, + { x: triangle.left + triangle.height, y: triangle.top - triangle.height }, + ] + } + + return [leftPoints, rightPoints] +} + +export const triangleToPolygon = (triangle) => { + const points = [] + const halfWidth = triangle.width / 2 + const height = triangle.height + + points.push({ x: triangle.left + halfWidth, y: triangle.top }) + points.push({ x: triangle.left, y: triangle.top + height }) + points.push({ x: triangle.left + triangle.width, y: triangle.top + height }) + + return points +} + export const rectToPolygon = (rect) => { const points = [] const left = rect.left From 07e11f3148fa52883aeac0b34def2c45e49c15e3 Mon Sep 17 00:00:00 2001 From: yjnoh Date: Thu, 17 Oct 2024 10:50:23 +0900 Subject: [PATCH 51/68] =?UTF-8?q?=EC=98=A4=EB=A5=98=20=EC=BB=A4=EB=B0=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../floor-plan/modal/circuitTrestle/CircuitTrestleSetting.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/floor-plan/modal/circuitTrestle/CircuitTrestleSetting.jsx b/src/components/floor-plan/modal/circuitTrestle/CircuitTrestleSetting.jsx index d1e4a864..f165bde0 100644 --- a/src/components/floor-plan/modal/circuitTrestle/CircuitTrestleSetting.jsx +++ b/src/components/floor-plan/modal/circuitTrestle/CircuitTrestleSetting.jsx @@ -4,7 +4,6 @@ import PowerConditionalSelect from '@/components/floor-plan/modal/circuitTrestle import CircuitAllocation from '@/components/floor-plan/modal/circuitTrestle/step/CircuitAllocation' import StepUp from '@/components/floor-plan/modal/circuitTrestle/step/StepUp' import { useMessage } from '@/hooks/useMessage' -import WithDraggable from '@/components/common/draggable/withDraggable' export default function CircuitTrestleSetting({ setShowCircuitTrestleSettingModal }) { const { getMessage } = useMessage() From 63b500e0c0a5469c3ee3bb7cae899d1aefb13f60 Mon Sep 17 00:00:00 2001 From: basssy Date: Thu, 17 Oct 2024 13:18:50 +0900 Subject: [PATCH 52/68] =?UTF-8?q?=EB=AC=BC=EA=B1=B4=ED=98=84=ED=99=A9=20?= =?UTF-8?q?=ED=99=94=EB=A9=B4=20=ED=8E=98=EC=9D=B4=EC=A7=95=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/management/Stuff.jsx | 165 ++++++++---------- .../management/StuffSearchCondition.jsx | 2 +- 2 files changed, 78 insertions(+), 89 deletions(-) diff --git a/src/components/management/Stuff.jsx b/src/components/management/Stuff.jsx index 9a0a73c5..cedf1975 100644 --- a/src/components/management/Stuff.jsx +++ b/src/components/management/Stuff.jsx @@ -15,6 +15,7 @@ import { convertNumberToPriceDecimal } from '@/util/common-utils' import { appMessageStore, globalLocaleStore } from '@/store/localeAtom' import KO from '@/locales/ko.json' import JA from '@/locales/ja.json' +import QPagination from '../common/pagination/QPagination' import '@/styles/grid.scss' import { sessionStore } from '@/store/commonAtom' @@ -24,9 +25,9 @@ export default function Stuff() { const stuffSearchParams = useRecoilValue(stuffSearchState) const [stuffSearch, setStuffSearch] = useRecoilState(stuffSearchState) const { getMessage } = useMessage() - const [curPage, setCurPage] = useState(1) //현재 페이지 번호 - const [defaultSize, setDefaultSize] = useState(100) //페이지 당 게시물 수 - const [gridCount, setGridCount] = useState(0) //총 갯수 + const [pageNo, setPageNo] = useState(1) //현재 페이지 번호 + const [pageSize, setPageSize] = useState(100) //페이지 당 게시물 수 + const [totalCount, setTotalCount] = useState(0) //총 갯수 const [defaultSortType, setDefaultSortType] = useState('R') const globalLocaleState = useRecoilValue(globalLocaleStore) @@ -62,10 +63,6 @@ export default function Stuff() { const [gridProps, setGridProps] = useState({ gridData: [], isPageable: false, - // sets 10 rows per page (default is 100) - // paginationPageSize: 100, - // allows the user to select the page size from a predefined list of page sizes - // paginationPageSizeSelector: [100, 200, 300, 400], gridColumns: [ { field: 'lastEditDatetime', @@ -228,56 +225,42 @@ export default function Stuff() { // 진입시 그리드 데이터 조회 useEffect(() => { if (isObjectNotEmpty(sessionState)) { + //물건 메뉴 눌러서 최초 진입 sessionState if (stuffSearchParams?.code === 'S') { const params = { - schObjectNo: '', - schAddress: '', - schObjectName: '', - schSaleStoreName: '', - schReceiveUser: '', - schDispCompanyName: '', - schDateType: 'U', + schObjectNo: stuffSearchParams?.schObjectNo, + schAddress: stuffSearchParams?.schAddress, + schObjectName: stuffSearchParams?.schObjectName, + schSaleStoreName: stuffSearchParams?.schSaleStoreName, + schReceiveUser: stuffSearchParams?.schReceiveUser, + schDispCompanyName: stuffSearchParams?.schDispCompanyName, + schDateType: stuffSearchParams.schDateType, schFromDt: dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD'), schToDt: dayjs(new Date()).format('YYYY-MM-DD'), - startRow: (curPage - 1) * defaultSize + 1, - endRow: curPage * defaultSize, + startRow: (pageNo - 1) * pageSize + 1, + endRow: pageNo * pageSize, schSelSaleStoreId: '', - schSortType: 'R', + schSortType: stuffSearchParams.schSortType, } async function fetchData() { - //api에 넘길값 startRow, endRow - // let startRow - // let endRow - // startRow = (curPage - 1) * size + 1 - // endRow = curPage * size - // console.log('startrow::', startRow) - // console.log('endRow::', endRow) - - // let curPage - // let totalpage - // let totalCount - // let size - // let pageCount - - // console.log('화면진입 세션정보::::::::::', sessionState) - // const apiUrl = `/api/object/list?saleStoreId=201TES01&${queryStringFormatter(params)}` - // const apiUrl = `/api/object/list?saleStoreId=X167&${queryStringFormatter(params)}` - const apiUrl = `/api/object/list?saleStoreId=${sessionState?.storeId}&${queryStringFormatter(params)}` + // const apiUrl = `/api/object/list?saleStoreId=${sessionState?.storeId}&${queryStringFormatter(params)}` + const apiUrl = `/api/object/list?saleStoreId=T01&${queryStringFormatter(params)}` await get({ url: apiUrl, }).then((res) => { if (!isEmptyArray(res)) { setGridProps({ ...gridProps, gridData: res, count: res[0].totCnt }) - setGridCount(res[0].totCnt) + setTotalCount(res[0].totCnt) } }) } fetchData() } else { + //메인화면에서 진입 const params = { - schObjectNo: '', + schObjectNo: stuffSearchParams.schObjectNo, schAddress: '', schObjectName: '', schSaleStoreName: '', @@ -286,30 +269,13 @@ export default function Stuff() { schDateType: 'U', schFromDt: dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD'), schToDt: dayjs(new Date()).format('YYYY-MM-DD'), - startRow: (curPage - 1) * defaultSize + 1, - endRow: curPage * defaultSize, + startRow: (pageNo - 1) * pageSize + 1, + endRow: pageNo * pageSize, schSelSaleStoreId: '', schSortType: 'R', } async function fetchData() { - //api에 넘길값 startRow, endRow - // let startRow - // let endRow - // startRow = (curPage - 1) * size + 1 - // endRow = curPage * size - // console.log('startrow::', startRow) - // console.log('endRow::', endRow) - - // let curPage - // let totalpage - // let totalCount - // let size - // let pageCount - - // console.log('화면진입 세션정보::::::::::', sessionState) - // const apiUrl = `/api/object/list?saleStoreId=201TES01&${queryStringFormatter(params)}` - // const apiUrl = `/api/object/list?saleStoreId=X167&${queryStringFormatter(params)}` const apiUrl = `/api/object/list?saleStoreId=${sessionState?.storeId}&${queryStringFormatter(params)}` await get({ @@ -317,34 +283,36 @@ export default function Stuff() { }).then((res) => { if (!isEmptyArray(res)) { setGridProps({ ...gridProps, gridData: res, count: res[0].totCnt }) - setGridCount(res[0].totCnt) + setTotalCount(res[0].totCnt) } }) } fetchData() } } - }, [sessionState]) + }, [pageNo, sessionState]) useEffect(() => { if (stuffSearchParams?.code === 'E') { - //console.log('조회누름::::::::', stuffSearchParams) - stuffSearchParams.startRow = (curPage - 1) * defaultSize + 1 - stuffSearchParams.endRow = curPage * defaultSize + //console.log('조회누름::::::::', stuffSearchParams, sessionState) + // stuffSearchParams.startRow = (pageNo - 1) * pageSize + 1 + // stuffSearchParams.endRow = pageNo * pageSize + stuffSearchParams.startRow = 1 + stuffSearchParams.endRow = 1 * pageSize stuffSearchParams.schSortType = defaultSortType + + setPageNo(1) + async function fetchData() { - // console.log('조회누름 세션정보:::::::::::::', sessionState) - // const apiUrl = `/api/object/list?saleStoreId=201TES01&${queryStringFormatter(stuffSearchParams)}` - // const apiUrl = `/api/object/list?saleStoreId=X167&${queryStringFormatter(stuffSearchParams)}` - const apiUrl = `/api/object/list?saleStoreId=${sessionState?.storeId}&${queryStringFormatter(stuffSearchParams)}` + const apiUrl = `/api/object/list?saleStoreId=T01&${queryStringFormatter(stuffSearchParams)}` + // const apiUrl = `/api/object/list?saleStoreId=${sessionState?.storeId}&${queryStringFormatter(stuffSearchParams)}` await get({ url: apiUrl }).then((res) => { - // console.log('검색조건 변경 조회 API결과:::::::', res) if (!isEmptyArray(res)) { setGridProps({ ...gridProps, gridData: res, count: res[0].totCnt }) - setGridCount(res[0].totCnt) + setTotalCount(res[0].totCnt) } else { setGridProps({ ...gridProps, gridData: [], count: 0 }) - setGridCount(0) + setTotalCount(0) } }) } @@ -354,53 +322,58 @@ export default function Stuff() { //페이지 갯수 변경 이벤트 const onChangePerPage = (e) => { - let startRow = (curPage - 1) * e.target.value + 1 + let startRow = (1 - 1) * e.target.value + 1 stuffSearchParams.startRow = startRow - stuffSearchParams.endRow = curPage * e.target.value - setDefaultSize(e.target.value) + stuffSearchParams.endRow = 1 * e.target.value + setPageSize(e.target.value) setStuffSearch({ ...stuffSearch, code: 'S', startRow: startRow, - endRow: curPage * e.target.value, + endRow: 1 * e.target.value, }) - // console.log('페이지 갯수 변경 때 셋팅된 검색조건:::', stuffSearchParams) - // console.log('페이지 갯수 변경 때 sessionState:::', sessionState) - // const apiUrl = `/api/object/list?saleStoreId=201TES01&${queryStringFormatter(stuffSearchParams)}` - // const apiUrl = `/api/object/list?saleStoreId=X167&${queryStringFormatter(stuffSearchParams)}` - const apiUrl = `/api/object/list?saleStoreId=${sessionState?.storeId}&${queryStringFormatter(stuffSearchParams)}` + + setPageNo(1) + // const apiUrl = `/api/object/list?saleStoreId=${sessionState?.storeId}&${queryStringFormatter(stuffSearchParams)}` + const apiUrl = `/api/object/list?saleStoreId=T01&${queryStringFormatter(stuffSearchParams)}` get({ url: apiUrl }).then((res) => { if (!isEmptyArray(res)) { setGridProps({ ...gridProps, gridData: res, count: res[0].totCnt }) - setGridCount(res[0].totCnt) + setTotalCount(res[0].totCnt) } else { setGridProps({ ...gridProps, gridData: [], count: 0 }) - setGridCount(0) + setTotalCount(0) } }) } //최근 등록일 수정일 정렬 이벤트 const onChangeSortType = (e) => { + let startRow = (1 - 1) * pageSize + 1 + stuffSearchParams.startRow = startRow + stuffSearchParams.endRow = 1 * pageSize + stuffSearchParams.schSortType = e.target.value - // console.log('셋팅된 검색조건:::', stuffSearchParams) setDefaultSortType(e.target.value) setStuffSearch({ ...stuffSearch, code: 'S', + startRow: startRow, + endRow: 1 * pageSize, schSortType: e.target.value, }) - // console.log('정렬 변경시 세션정보::::::::::::', sessionState) - // const apiUrl = `/api/object/list?saleStoreId=201TES01&${queryStringFormatter(stuffSearchParams)}` - // const apiUrl = `/api/object/list?saleStoreId=X167&${queryStringFormatter(stuffSearchParams)}` - const apiUrl = `/api/object/list?saleStoreId=${sessionState?.storeId}&${queryStringFormatter(stuffSearchParams)}` + + setPageNo(1) + + const apiUrl = `/api/object/list?saleStoreId=T01&${queryStringFormatter(stuffSearchParams)}` + // const apiUrl = `/api/object/list?saleStoreId=${sessionState?.storeId}&${queryStringFormatter(stuffSearchParams)}` get({ url: apiUrl }).then((res) => { if (!isEmptyArray(res)) { setGridProps({ ...gridProps, gridData: res, count: res[0].totCnt }) - setGridCount(res[0].totCnt) + setTotalCount(res[0].totCnt) } else { setGridProps({ ...gridProps, gridData: [], count: 0 }) - setGridCount(0) + setTotalCount(0) } }) } @@ -413,6 +386,20 @@ export default function Stuff() { } }, [globalLocaleState]) + // 페이징 현재페이지 변경 + const handleChangePage = (page) => { + stuffSearchParams.code = 'S' + + setStuffSearch({ + ...stuffSearch, + code: 'S', + startRow: (page - 1) * pageSize + 1, + endRow: page * pageSize, + }) + + setPageNo(page) + } + return ( <> {/* 퍼블시작 */} @@ -423,7 +410,7 @@ export default function Stuff() {
  • 전체 - {convertNumberToPriceDecimal(gridCount)} + {convertNumberToPriceDecimal(totalCount)}
  • 선택 @@ -450,7 +437,9 @@ export default function Stuff() {
    -
    페이징 컴포넌트예정
    +
    + +
diff --git a/src/components/management/StuffSearchCondition.jsx b/src/components/management/StuffSearchCondition.jsx index 8bd739b4..a8f86718 100644 --- a/src/components/management/StuffSearchCondition.jsx +++ b/src/components/management/StuffSearchCondition.jsx @@ -323,7 +323,7 @@ export default function StuffSearchCondition() { setStuffSearch({ ...stuffSearch, code: 'S', schDateType: e.target.value }) }} /> - +
From 429832b8b7a09e44dc7ee061114516bae083d82b Mon Sep 17 00:00:00 2001 From: yoosangwook Date: Thu, 17 Oct 2024 13:34:11 +0900 Subject: [PATCH 53/68] refactor: Refactor usePagination hook to include useEffect --- src/hooks/usePagination.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/hooks/usePagination.js b/src/hooks/usePagination.js index 4fc3a959..7d76d293 100644 --- a/src/hooks/usePagination.js +++ b/src/hooks/usePagination.js @@ -1,4 +1,4 @@ -import { useState } from 'react' +import { useEffect, useState } from 'react' /** * 페이지네이션 훅 @@ -14,6 +14,10 @@ const usePagination = ({ pageNo = 1, pageSize = 10, pagePerBlock = 10, totalCoun setCurrentPage(page) } + useEffect(() => { + setCurrentPage(pageNo) + }, [pageNo]) + const pageGroup = Math.floor((currentPage - 1) / pagePerBlock) + 1 const totalPages = Math.ceil(totalCount / pageSize) const pages = Array.from({ length: totalPages }, (_, i) => i + 1) From b57c6d47a9268aabe920e271a9f3fd355397b2e0 Mon Sep 17 00:00:00 2001 From: yoosangwook Date: Thu, 17 Oct 2024 13:42:55 +0900 Subject: [PATCH 54/68] refactor: Refactor useAxios hook in Settings component --- src/components/Settings.jsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/Settings.jsx b/src/components/Settings.jsx index 43288dbe..51680711 100644 --- a/src/components/Settings.jsx +++ b/src/components/Settings.jsx @@ -3,7 +3,8 @@ import React, { useEffect, useState } from 'react' import { Button } from '@nextui-org/react' -import { get, post } from '@/lib/Axios' +import { useAxios } from '@/hooks/useAxios' + import { useRecoilState } from 'recoil' import { customSettingsState } from '@/store/canvasAtom' import { modalContent, modalState } from '@/store/modalAtom' @@ -20,6 +21,8 @@ export default function Settings() { const [open, setOpen] = useRecoilState(modalState) const [contents, setContent] = useRecoilState(modalContent) + const { get, post } = useAxios() + const handleSavePopup = () => { console.log('color ', color) } From 24b4556db7f20c6dbf441b9b95cb2a19e0dcd13a Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Thu, 17 Oct 2024 14:17:12 +0900 Subject: [PATCH 55/68] =?UTF-8?q?line,=20polygon=20=EB=B3=80=ED=99=98=20?= =?UTF-8?q?=EC=8B=9C=20=EA=B8=B8=EC=9D=B4=20=EC=95=88=EB=A7=9E=EB=8A=94=20?= =?UTF-8?q?=EA=B2=83=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/fabric/QLine.js | 4 +-- src/components/fabric/QPolygon.js | 4 +-- src/components/floor-plan/CanvasFrame.jsx | 4 --- src/components/floor-plan/CanvasMenu.jsx | 7 ++-- src/hooks/roofcover/useAuxiliaryDrawing.js | 32 ++++++++++++----- .../roofcover/useRoofAllocationSetting.js | 6 +++- .../roofcover/useRoofShapePassivitySetting.js | 35 ++++++++++++++++--- src/hooks/roofcover/useRoofShapeSetting.js | 9 +++-- src/hooks/useEvent.js | 1 - 9 files changed, 75 insertions(+), 27 deletions(-) diff --git a/src/components/fabric/QLine.js b/src/components/fabric/QLine.js index ae3c6c62..616143fe 100644 --- a/src/components/fabric/QLine.js +++ b/src/components/fabric/QLine.js @@ -75,7 +75,7 @@ export const QLine = fabric.util.createClass(fabric.Line, { const y2 = this.top + this.height * scaleY const dx = x2 - x1 const dy = y2 - y1 - this.length = Number(Math.sqrt(dx * dx + dy * dy).toFixed(0)) + this.length = Number(Math.sqrt(dx * dx + dy * dy).toFixed(1)) }, addLengthText() { @@ -150,7 +150,7 @@ export const QLine = fabric.util.createClass(fabric.Line, { getLength() { //10배 곱해진 값 return - return Number(this.length.toFixed(1) * 10) + return Number(this.length.toFixed(0) * 10) }, setViewLengthText(bool) { diff --git a/src/components/fabric/QPolygon.js b/src/components/fabric/QPolygon.js index 7c027c2f..64c897c6 100644 --- a/src/components/fabric/QPolygon.js +++ b/src/components/fabric/QPolygon.js @@ -199,7 +199,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { const end = points[(i + 1) % points.length] const dx = end.x - start.x const dy = end.y - start.y - const length = Number(Math.sqrt(dx * dx + dy * dy).toFixed(1)) * 10 + const length = Number(Math.sqrt(dx * dx + dy * dy).toFixed(0)) * 10 let midPoint @@ -224,7 +224,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { const degree = (Math.atan2(dy, dx) * 180) / Math.PI // Create new text object if it doesn't exist - const text = new fabric.Text(length.toFixed(0), { + const text = new fabric.Text(length.toString(), { left: midPoint.x, top: midPoint.y, fontSize: this.fontSize, diff --git a/src/components/floor-plan/CanvasFrame.jsx b/src/components/floor-plan/CanvasFrame.jsx index f34d1cb6..d6a4e179 100644 --- a/src/components/floor-plan/CanvasFrame.jsx +++ b/src/components/floor-plan/CanvasFrame.jsx @@ -13,11 +13,7 @@ export default function CanvasFrame({ plan }) { const canvasRef = useRef(null) const { canvas } = useCanvas('canvas') const { contextMenu, currentContextMenu, setCurrentContextMenu } = useContextMenu() - const currentObject = useRecoilValue(currentObjectState) - useEffect(() => { - console.log(currentObject) - }, [currentObject]) useEvent() const loadCanvas = () => { diff --git a/src/components/floor-plan/CanvasMenu.jsx b/src/components/floor-plan/CanvasMenu.jsx index ae8cc79d..96115d98 100644 --- a/src/components/floor-plan/CanvasMenu.jsx +++ b/src/components/floor-plan/CanvasMenu.jsx @@ -19,6 +19,7 @@ import { MENU } from '@/common/common' import KO from '@/locales/ko.json' import JA from '@/locales/ja.json' import { settingModalFirstOptionsState } from '@/store/settingAtom' +import { placementShapeDrawingPointsState } from '@/store/placementShapeDrawingAtom' const canvasMenus = [ { index: 0, name: 'plan.menu.plan.drawing', icon: 'con00', title: MENU.PLAN_DRAWING }, @@ -58,7 +59,8 @@ export default function CanvasMenu(props) { const [verticalHorizontalMode, setVerticalHorizontalMode] = useRecoilState(verticalHorizontalModeState) const [appMessageState, setAppMessageState] = useRecoilState(appMessageStore) const setCurrentMenu = useSetRecoilState(currentMenuState) - const setPoints = useSetRecoilState(outerLinePointsState) + const setOuterLinePoints = useSetRecoilState(outerLinePointsState) + const setPlacementPoints = useSetRecoilState(placementShapeDrawingPointsState) const [canvasZoom, setCanvasZoom] = useRecoilState(canvasZoomState) const [currentCanvasPlan, setcurrentCanvasPlan] = useRecoilState(currentCanvasPlanState) @@ -140,7 +142,8 @@ export default function CanvasMenu(props) { } const handleClear = () => { - setPoints([]) + setOuterLinePoints([]) + setPlacementPoints([]) canvas?.clear() } diff --git a/src/hooks/roofcover/useAuxiliaryDrawing.js b/src/hooks/roofcover/useAuxiliaryDrawing.js index cb33f407..94a7e989 100644 --- a/src/hooks/roofcover/useAuxiliaryDrawing.js +++ b/src/hooks/roofcover/useAuxiliaryDrawing.js @@ -23,10 +23,11 @@ import { outerLineLength2State, outerLineTypeState, } from '@/store/outerLineAtom' -import { calculateDistance, calculateIntersection, distanceBetweenPoints, findClosestPoint } from '@/util/canvas-util' +import { calculateDistance, calculateIntersection, distanceBetweenPoints, findClosestPoint, polygonToTurfPolygon } from '@/util/canvas-util' import { fabric } from 'fabric' import { useAdsorptionPoint } from '@/hooks/useAdsorptionPoint' import { useSwal } from '@/hooks/useSwal' +import { booleanPointInPolygon } from '@turf/turf' // 보조선 작성 export function useAuxiliaryDrawing(setShowAuxiliaryModal) { @@ -76,6 +77,8 @@ export function useAuxiliaryDrawing(setShowAuxiliaryModal) { useEffect(() => { typeRef.current = type + clear() + addDocumentEventListener('keydown', document, keydown[type]) }, [type]) useEffect(() => { @@ -103,12 +106,8 @@ export function useAuxiliaryDrawing(setShowAuxiliaryModal) { } }, []) - useEffect(() => { - clear() - addDocumentEventListener('keydown', document, keydown[type]) - }, [type]) - const clear = () => { + addCanvasMouseEventListener('mouse:move', mouseMove) setLength1(0) setLength2(0) @@ -459,8 +458,9 @@ export function useAuxiliaryDrawing(setShowAuxiliaryModal) { } const mouseDown = (e) => { - addCanvasMouseEventListener('mouse:move', mouseMove) + canvas.renderAll() const pointer = getIntersectMousePoint(e) + console.log(pointer) mousePointerArr.current.push(pointer) if (mousePointerArr.current.length === 2) { @@ -621,10 +621,24 @@ export function useAuxiliaryDrawing(setShowAuxiliaryModal) { return } - const roofBases = canvas.getObjects().find((obj) => obj.name === 'roofBase') + const roofBases = canvas.getObjects().filter((obj) => obj.name === 'roofBase') const innerLines = [...lineHistory.current] - roofBases.innerLines = [...innerLines] + roofBases.forEach((roofBase) => { + const roofInnerLines = innerLines.filter((line) => { + const turfPolygon = polygonToTurfPolygon(roofBase) + + // innerLines의 두 점이 모두 polygon 안에 있는지 확인 + const inPolygon1 = booleanPointInPolygon([line.x1, line.y1], turfPolygon) + const inPolygon2 = booleanPointInPolygon([line.x2, line.y2], turfPolygon) + + if (inPolygon1 && inPolygon2) { + return true + } + }) + + roofBase.innerLines = [...roofInnerLines] + }) setShowAuxiliaryModal(false) } diff --git a/src/hooks/roofcover/useRoofAllocationSetting.js b/src/hooks/roofcover/useRoofAllocationSetting.js index 6533fe79..bc55c921 100644 --- a/src/hooks/roofcover/useRoofAllocationSetting.js +++ b/src/hooks/roofcover/useRoofAllocationSetting.js @@ -95,7 +95,11 @@ export function useRoofAllocationSetting(setShowRoofAllocationSettingModal) { const roofBases = canvas.getObjects().filter((obj) => obj.name === 'roofBase') const wallLines = canvas.getObjects().filter((obj) => obj.name === 'wallLine') roofBases.forEach((roofBase) => { - splitPolygonWithLines(roofBase) + try { + splitPolygonWithLines(roofBase) + } catch (e) { + return + } roofBase.innerLines.forEach((line) => { canvas.remove(line) diff --git a/src/hooks/roofcover/useRoofShapePassivitySetting.js b/src/hooks/roofcover/useRoofShapePassivitySetting.js index 217e71bc..83ecf7ed 100644 --- a/src/hooks/roofcover/useRoofShapePassivitySetting.js +++ b/src/hooks/roofcover/useRoofShapePassivitySetting.js @@ -33,6 +33,9 @@ export function useRoofShapePassivitySetting(setShowRoofShapePassivitySettingMod const [type, setType] = useState(TYPES.EAVES) + const isFix = useRef(false) + const initLines = useRef([]) + const buttons = [ { id: 1, name: getMessage('eaves'), type: TYPES.EAVES }, { id: 2, name: getMessage('gable'), type: TYPES.GABLE }, @@ -52,9 +55,11 @@ export function useRoofShapePassivitySetting(setShowRoofShapePassivitySettingMod useEffect(() => { addCanvasMouseEventListener('mouse:down', mouseDown) const wallLines = canvas.getObjects().filter((obj) => obj.name === 'wallLine') - canvas.remove(wallLines) + + canvas?.remove(...wallLines) const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') + initLines.current = outerLines.map((line) => ({ ...line })) outerLines.forEach((outerLine, idx) => { if (idx === 0) { currentLineRef.current = outerLine @@ -66,6 +71,7 @@ export function useRoofShapePassivitySetting(setShowRoofShapePassivitySettingMod canvas?.renderAll() return () => { + handleLineToPolygon() canvas?.discardActiveObject() initEvent() } @@ -166,6 +172,14 @@ export function useRoofShapePassivitySetting(setShowRoofShapePassivitySettingMod } const handleSave = () => { + isFix.current = true + handleLineToPolygon() + + setShowRoofShapePassivitySettingModal(false) + } + + const handleLineToPolygon = () => { + const roofBases = canvas.getObjects().filter((obj) => obj.name === 'roofBase') const exceptObjs = canvas.getObjects().filter((obj) => obj.name !== 'outerLine' && obj.parent?.name !== 'outerLine') const lines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') exceptObjs.forEach((obj) => { @@ -176,13 +190,26 @@ export function useRoofShapePassivitySetting(setShowRoofShapePassivitySettingMod hideLine(line) }) - const wall = addPolygonByLines(lines, { name: 'wallLine', fill: 'transparent', stroke: 'black' }) + let wall + + if (isFix.current) { + wall = addPolygonByLines(lines, { name: 'wallLine', fill: 'transparent', stroke: 'black' }) + } else { + // 그냥 닫을 경우 처리 + wall = addPolygonByLines([...initLines.current], { name: 'wallLine', fill: 'transparent', stroke: 'black' }) + lines.forEach((line, idx) => { + line.attributes = initLines.current[idx].attributes + }) + } wall.lines = [...lines] - + // 기존 그려진 지붕이 없다면 + if (roofBases.length === 0) { + return + } const roof = drawRoofPolygon(wall) canvas.renderAll() - setShowRoofShapePassivitySettingModal(false) } + return { handleSave, handleConfirm, buttons, type, setType, TYPES, offsetRef, pitchRef, handleRollback } } diff --git a/src/hooks/roofcover/useRoofShapeSetting.js b/src/hooks/roofcover/useRoofShapeSetting.js index 1722e287..016cd365 100644 --- a/src/hooks/roofcover/useRoofShapeSetting.js +++ b/src/hooks/roofcover/useRoofShapeSetting.js @@ -244,8 +244,13 @@ export function useRoofShapeSetting(setShowRoofShapeSettingModal) { } } - // 기존 wallLine 제거 - canvas?.remove(canvas.getObjects().filter((obj) => obj.name === 'wallLine')) + // 기존 wallLine, roofBase 제거 + canvas + .getObjects() + .filter((obj) => obj.name === 'wallLine' || obj.name === 'roofBase') + .forEach((line) => { + canvas.remove(line) + }) const polygon = addPolygonByLines(outerLines, { name: 'wallLine' }) polygon.lines = [...outerLines] diff --git a/src/hooks/useEvent.js b/src/hooks/useEvent.js index 639b61b3..d14fa7e5 100644 --- a/src/hooks/useEvent.js +++ b/src/hooks/useEvent.js @@ -43,7 +43,6 @@ export function useEvent() { //default Event 추가 addCanvasMouseEventListener('mouse:move', defaultMouseMoveEvent) addCanvasMouseEventListener('mouse:out', defaultMouseOutEvent) - addDocumentEventListener('keydown', document, defaultKeyboardEvent) addDocumentEventListener('contextmenu', document, defaultContextMenuEvent) if (adsorptionPointAddMode) { addCanvasMouseEventListener('mouse:down', adsorptionPointAddModeStateEvent) From 7f8a862dc8c9cc38db9adb3f7be68dcf409fe837 Mon Sep 17 00:00:00 2001 From: basssy Date: Thu, 17 Oct 2024 14:48:51 +0900 Subject: [PATCH 56/68] =?UTF-8?q?=EB=AC=BC=EA=B1=B4=ED=98=84=ED=99=A9=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/management/Stuff.jsx | 10 +- .../management/StuffSearchCondition.jsx | 6 -- .../management/popup/PlanRequestPop.jsx | 96 ++++++++++++++++--- src/locales/ja.json | 5 + src/locales/ko.json | 5 + 5 files changed, 97 insertions(+), 25 deletions(-) diff --git a/src/components/management/Stuff.jsx b/src/components/management/Stuff.jsx index cedf1975..30fab787 100644 --- a/src/components/management/Stuff.jsx +++ b/src/components/management/Stuff.jsx @@ -406,14 +406,14 @@ export default function Stuff() {
-

물건목록

+

{getMessage('stuff.search.grid.title')}

  • - 전체 + {getMessage('stuff.search.grid.all')} {convertNumberToPriceDecimal(totalCount)}
  • - 선택 + {getMessage('stuff.search.grid.selected')} {convertNumberToPriceDecimal(selectedRowDataCount)}
@@ -421,8 +421,8 @@ export default function Stuff() {
diff --git a/src/components/management/StuffSearchCondition.jsx b/src/components/management/StuffSearchCondition.jsx index a8f86718..cf310719 100644 --- a/src/components/management/StuffSearchCondition.jsx +++ b/src/components/management/StuffSearchCondition.jsx @@ -99,7 +99,6 @@ export default function StuffSearchCondition() { useEffect(() => { if (isObjectNotEmpty(sessionState)) { - // console.log('판매대리점 리스트 가져오기 위한 세션정보::::::::', sessionState) // storeId가 T01 이거나 1차점일때만 판매대리점 선택 활성화 // get({ url: `/api/object/saleStore/201TES01/list` }).then((res) => { get({ url: `/api/object/saleStore/${sessionState?.storeId}/list` }).then((res) => { @@ -190,7 +189,6 @@ export default function StuffSearchCondition() { { setObjectNo(e.target.value) @@ -205,7 +203,6 @@ export default function StuffSearchCondition() { { setSaleStoreName(e.target.value) @@ -220,7 +217,6 @@ export default function StuffSearchCondition() { { setAddress(e.target.value) @@ -237,7 +233,6 @@ export default function StuffSearchCondition() { { setobjectName(e.target.value) @@ -252,7 +247,6 @@ export default function StuffSearchCondition() { { setDispCompanyName(e.target.value) diff --git a/src/components/management/popup/PlanRequestPop.jsx b/src/components/management/popup/PlanRequestPop.jsx index 224519d0..46afad46 100644 --- a/src/components/management/popup/PlanRequestPop.jsx +++ b/src/components/management/popup/PlanRequestPop.jsx @@ -12,6 +12,9 @@ import dayjs from 'dayjs' import PlanRequestPopQGrid from './PlanRequestPopQGrid' import { sessionStore } from '@/store/commonAtom' import { planReqSearchState } from '@/store/planReqAtom' +import { isObjectNotEmpty } from '@/util/common-utils' + +import Select from 'react-select' export default function PlanRequestPop(props) { const sessionState = useRecoilValue(sessionStore) @@ -20,6 +23,7 @@ export default function PlanRequestPop(props) { const { get } = useAxios(globalLocaleState) const { getMessage } = useMessage() + const ref = useRef() // 검색조건 달력 셋팅 const [startDate, setStartDate] = useState(dayjs(new Date()).add(-3, 'month').format('YYYY-MM-DD')) const [endDate, setEndDate] = useState(dayjs(new Date()).format('YYYY-MM-DD')) @@ -34,7 +38,6 @@ export default function PlanRequestPop(props) { setStartDate: setEndDate, } - const ref = useRef() const resetPlanReqRecoil = useResetRecoilState(planReqSearchState) const [planReqSearch, setPlanReqSearch] = useRecoilState(planReqSearchState) @@ -47,14 +50,42 @@ export default function PlanRequestPop(props) { const [schDateGbn, setSchDateGbn] = useState('S') //기간구분코드(S/R) //초기화 - const resetRecoil = () => {} + const resetRecoil = () => { + console.log('초기화') + setSchPlanReqNo('') + setSchTitle('') + setSchAddress('') + setSchSaleStoreName('') + setSchPlanReqName('') + setSchDateGbn('S') + setStartDate(dayjs(new Date()).add(-3, 'month').format('YYYY-MM-DD')) + setEndDate(dayjs(new Date()).format('YYYY-MM-DD')) + setSchPlanStatCd('') + handleClear() //셀렉트 자동완성 초기화 + resetPlanReqRecoil() + } - //초기화 눌렀을 때 자동완성도.. + //셀렉트 자동완성 초기화 const handleClear = () => { - if (ref.current.state.dropDown) { - ref.current.methods.dropDown() + if (ref.current) { + ref.current.clearValue() + } + } + + // 상태 검색조건 변경 + const onSelectionChange = (key) => { + //임시작업 + console.log('E::::::::', key) + if (isObjectNotEmpty(key)) { + setSchPlanStatCd(key.value) + setPlanReqSearch({ + ...planReqSearch, + schPlanStatCd: key.value, + }) } else { - ref.current.state.values = [] + //X누름 + setSchPlanStatCd('') + setPlanReqSearch({ ...planReqSearch, schPlanStatCd: '' }) } } @@ -63,6 +94,11 @@ export default function PlanRequestPop(props) { setEndDate(planReqSearch?.schEndDt ? planReqSearch.schEndDt : dayjs(new Date()).format('YYYY-MM-DD')) }, [planReqSearch]) + // 조회 + const onSubmit = () => { + console.log('조회!!!!', planReqSearch) + } + const [gridProps, setGridProps] = useState({ gridData: [], isPageable: false, @@ -117,6 +153,25 @@ export default function PlanRequestPop(props) { ], }) + const tempList = [ + { + label: '완료', + value: 'C', + }, + { + label: '저장', + value: 'I', + }, + { + label: '접수', + value: 'R', + }, + { + label: '제출', + value: 'S', + }, + ] + return (
@@ -132,8 +187,12 @@ export default function PlanRequestPop(props) {

{getMessage('stuff.planReqPopup.popTitle')}

- - + +
@@ -223,13 +282,22 @@ export default function PlanRequestPop(props) { {getMessage('stuff.planReqPopup.search.planStatName')} -
- - - - - + + + + + */} + { setReceiveUser(e.target.value) diff --git a/src/components/management/popup/PlanRequestPop.jsx b/src/components/management/popup/PlanRequestPop.jsx index 46afad46..dbd979eb 100644 --- a/src/components/management/popup/PlanRequestPop.jsx +++ b/src/components/management/popup/PlanRequestPop.jsx @@ -1,6 +1,5 @@ import React, { useState, useRef, useEffect } from 'react' import { useForm } from 'react-hook-form' -import { queryStringFormatter } from '@/util/common-utils' import { useAxios } from '@/hooks/useAxios' import { globalLocaleStore } from '@/store/localeAtom' import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil' @@ -12,17 +11,23 @@ import dayjs from 'dayjs' import PlanRequestPopQGrid from './PlanRequestPopQGrid' import { sessionStore } from '@/store/commonAtom' import { planReqSearchState } from '@/store/planReqAtom' -import { isObjectNotEmpty } from '@/util/common-utils' +import { isObjectNotEmpty, queryStringFormatter } from '@/util/common-utils' import Select from 'react-select' +import QPagination from '@/components/common/pagination/QPagination' export default function PlanRequestPop(props) { + const [pageNo, setPageNo] = useState(1) //현재 페이지 번호 + const [pageSize, setPageSize] = useState(20) //페이지 당 게시물 개수 + const [totalCount, setTotalCount] = useState(0) //총 갯수 + const sessionState = useRecoilValue(sessionStore) const globalLocaleState = useRecoilValue(globalLocaleStore) - const { get } = useAxios(globalLocaleState) + const { get, promiseGet } = useAxios(globalLocaleState) const { getMessage } = useMessage() + //Select ref const ref = useRef() // 검색조건 달력 셋팅 const [startDate, setStartDate] = useState(dayjs(new Date()).add(-3, 'month').format('YYYY-MM-DD')) @@ -51,7 +56,6 @@ export default function PlanRequestPop(props) { //초기화 const resetRecoil = () => { - console.log('초기화') setSchPlanReqNo('') setSchTitle('') setSchAddress('') @@ -75,7 +79,7 @@ export default function PlanRequestPop(props) { // 상태 검색조건 변경 const onSelectionChange = (key) => { //임시작업 - console.log('E::::::::', key) + // console.log('E::::::::', key) if (isObjectNotEmpty(key)) { setSchPlanStatCd(key.value) setPlanReqSearch({ @@ -95,8 +99,56 @@ export default function PlanRequestPop(props) { }, [planReqSearch]) // 조회 - const onSubmit = () => { - console.log('조회!!!!', planReqSearch) + const onSubmit = (page, type) => { + const params = { + saleStoreId: 'T100', + saleStoreLevel: '1', + // saleStoreId: sessionState?.storeId, + // saleStoreLevel: sessionState?.storeLvl, + schPlanReqNo: planReqSearch?.schPlanReqNo ? planReqSearch.schPlanReqNo : schPlanReqNo, + schTitle: planReqSearch?.schTitle ? planReqSearch.schTitle : schTitle, + schAddress: planReqSearch?.schAddress ? planReqSearch.schAddress : schAddress, + schSaleStoreName: planReqSearch?.schSaleStoreName ? planReqSearch.schSaleStoreName : schSaleStoreName, + schPlanReqName: planReqSearch?.schPlanReqName ? planReqSearch.schPlanReqName : schPlanReqName, + schPlanStatCd: planReqSearch?.schPlanStatCd ? planReqSearch.schPlanStatCd : schPlanStatCd, + schDateGbn: planReqSearch?.schDateGbn ? planReqSearch.schDateGbn : schDateGbn, + // schStartDt: dayjs(startDate).format('YYYY-MM-DD'), + // schEndDt: dayjs(endDate).format('YYYY-MM-DD'), + startRow: type === 'S' ? (1 - 1) * pageSize + 1 : (page - 1) * pageSize + 1, + endRow: type === 'S' ? 1 * pageSize : page * pageSize, + } + if (type === 'S') { + setPageNo(1) + } else { + setPageNo(page) + } + + const apiUrl = `/api/object/planReq/list?${queryStringFormatter(params)}` + promiseGet({ url: apiUrl }).then((res) => { + if (res.status === 200) { + if (isNotEmptyArray(res.data.data)) { + setGridProps({ ...gridProps, gridData: res.data.data, gridCount: res.data.data[0].totCnt }) + setTotalCount(res.data.data[0].totCnt) + } else { + setGridProps({ ...gridProps, gridData: [], gridCount: 0 }) + setTotalCount(0) + } + } else { + setGridProps({ ...gridProps, gridData: [], gridCount: 0 }) + setTotalCount(0) + } + }) + } + // 페이징 현재페이지 변경 + const handleChangePage = (page) => { + setPlanReqSearch({ + ...planReqSearch, + startRow: (page - 1) * pageSize + 1, + endRow: page * pageSize, + }) + setPageNo(page) + + onSubmit(page, 'P') } const [gridProps, setGridProps] = useState({ @@ -187,7 +239,12 @@ export default function PlanRequestPop(props) {

{getMessage('stuff.planReqPopup.popTitle')}

-