Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/skeleton-dev

This commit is contained in:
ysCha 2025-12-15 17:50:27 +09:00
commit accba862de
7 changed files with 152 additions and 115 deletions

View File

@ -32,6 +32,7 @@ import { useEvent } from '@/hooks/useEvent'
import { compasDegAtom } from '@/store/orientationAtom' import { compasDegAtom } from '@/store/orientationAtom'
import { hotkeyStore } from '@/store/hotkeyAtom' import { hotkeyStore } from '@/store/hotkeyAtom'
import { usePopup } from '@/hooks/usePopup' import { usePopup } from '@/hooks/usePopup'
import { outerLinePointsState } from '@/store/outerLineAtom'
export default function CanvasFrame() { export default function CanvasFrame() {
const canvasRef = useRef(null) const canvasRef = useRef(null)
@ -45,6 +46,7 @@ export default function CanvasFrame() {
const totalDisplay = useRecoilValue(totalDisplaySelector) // const totalDisplay = useRecoilValue(totalDisplaySelector) //
const { setIsGlobalLoading } = useContext(QcastContext) const { setIsGlobalLoading } = useContext(QcastContext)
const resetModuleStatisticsState = useResetRecoilState(moduleStatisticsState) const resetModuleStatisticsState = useResetRecoilState(moduleStatisticsState)
const resetOuterLinePoints = useResetRecoilState(outerLinePointsState)
const resetMakersState = useResetRecoilState(makersState) const resetMakersState = useResetRecoilState(makersState)
const resetSelectedMakerState = useResetRecoilState(selectedMakerState) const resetSelectedMakerState = useResetRecoilState(selectedMakerState)
const resetSeriesState = useResetRecoilState(seriesState) const resetSeriesState = useResetRecoilState(seriesState)
@ -137,6 +139,7 @@ export default function CanvasFrame() {
const resetRecoilData = () => { const resetRecoilData = () => {
// resetModuleStatisticsState() // resetModuleStatisticsState()
resetOuterLinePoints()
resetMakersState() resetMakersState()
resetSelectedMakerState() resetSelectedMakerState()
resetSeriesState() resetSeriesState()

View File

@ -1,4 +1,4 @@
import { useState, useEffect, useRef } from 'react' import { useEffect, useRef, useState } from 'react'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import WithDraggable from '@/components/common/draggable/WithDraggable' import WithDraggable from '@/components/common/draggable/WithDraggable'
import { useRecoilValue } from 'recoil' import { useRecoilValue } from 'recoil'
@ -15,8 +15,8 @@ export default function DormerOffset(props) {
const { closePopup } = usePopup() const { closePopup } = usePopup()
const [arrow1, setArrow1] = useState(null) const [arrow1, setArrow1] = useState(null)
const [arrow2, setArrow2] = useState(null) const [arrow2, setArrow2] = useState(null)
const arrow1LengthRef = useRef() const arrow1LengthRef = useRef(0)
const arrow2LengthRef = useRef() const arrow2LengthRef = useRef(0)
const [arrow1Length, setArrow1Length] = useState(0) const [arrow1Length, setArrow1Length] = useState(0)
const [arrow2Length, setArrow2Length] = useState(0) const [arrow2Length, setArrow2Length] = useState(0)
@ -59,12 +59,12 @@ export default function DormerOffset(props) {
name="" name=""
label="" label=""
className="input-origin block" className="input-origin block"
value={arrow1LengthRef.current.value} value={arrow1LengthRef.current.value ?? 0}
ref={arrow1LengthRef} ref={arrow1LengthRef}
onChange={(value) => setArrow1Length(value)} onChange={(value) => setArrow1Length(value)}
options={{ options={{
allowNegative: false, allowNegative: false,
allowDecimal: false allowDecimal: false,
}} }}
/> />
</div> </div>

View File

@ -170,8 +170,8 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla
setCurrentRoof({ setCurrentRoof({
...selectedRoofMaterial, ...selectedRoofMaterial,
pitch: currentRoof?.pitch, // pitch: currentRoof?.pitch,
angle: currentRoof?.angle, // angle: currentRoof?.angle,
index: 0, index: 0,
planNo: currentRoof.planNo, planNo: currentRoof.planNo,
roofSizeSet: String(currentRoof.roofSizeSet), roofSizeSet: String(currentRoof.roofSizeSet),
@ -353,19 +353,21 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla
value={index === 0 ? currentRoof?.pitch || '0' : currentRoof?.angle || '0'} value={index === 0 ? currentRoof?.pitch || '0' : currentRoof?.angle || '0'}
onChange={(value) => { onChange={(value) => {
if (index === 0) { if (index === 0) {
const num = value === '' ? '' : Number(value) const pitch = value === '' ? '' : Number(value);
const angle = pitch === '' ? '' : getDegreeByChon(pitch);
setCurrentRoof(prev => ({ setCurrentRoof(prev => ({
...prev, ...prev,
pitch: num === '' ? '' : num, pitch,
angle: num === '' ? '' : getDegreeByChon(num), angle
})) }));
} else { } else {
const num = value === '' ? '' : Number(value) const angle = value === '' ? '' : Number(value);
const pitch = angle === '' ? '' : getChonByDegree(angle);
setCurrentRoof(prev => ({ setCurrentRoof(prev => ({
...prev, ...prev,
pitch: num === '' ? '' : getChonByDegree(num), pitch,
angle: num === '' ? '' : num, angle
})) }));
} }
}} }}
options={{ options={{
@ -514,13 +516,17 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla
{/*/>*/} {/*/>*/}
<CalculatorInput <CalculatorInput
id="" id=""
name={'hajebichi'} name="hajebichi"
label="" label=""
className="input-origin block" className="input-origin block"
ref={roofRef.hajebichi} ref={roofRef.hajebichi}
value={currentRoof?.hajebichi||0} value={currentRoof?.hajebichi || '0'}
onChange={(value) => { onChange={(value) => {
setCurrentRoof({ ...currentRoof, value }) const hajebichi = value === '' ? '' : Number(value);
setCurrentRoof(prev => ({
...prev,
hajebichi
}));
}} }}
readOnly={currentRoof?.roofPchAuth === 'R'} readOnly={currentRoof?.roofPchAuth === 'R'}
disabled={currentRoof?.roofSizeSet === '3'} disabled={currentRoof?.roofSizeSet === '3'}

View File

@ -264,7 +264,6 @@ export default function Simulator() {
style={{ width: '30%' }} style={{ width: '30%' }}
className="select-light" className="select-light"
value={pwrGnrSimType} value={pwrGnrSimType}
defaultValue={`D`}
onChange={(e) => { onChange={(e) => {
handleChartChangeData(e.target.value) handleChartChangeData(e.target.value)
setPwrGnrSimType(e.target.value) setPwrGnrSimType(e.target.value)
@ -337,7 +336,6 @@ export default function Simulator() {
{moduleInfoList.length > 0 ? ( {moduleInfoList.length > 0 ? (
moduleInfoList.map((moduleInfo) => { moduleInfoList.map((moduleInfo) => {
return ( return (
<>
<tr key={moduleInfo.itemId}> <tr key={moduleInfo.itemId}>
{/* 지붕면 */} {/* 지붕면 */}
<td>{moduleInfo.roofSurface}</td> <td>{moduleInfo.roofSurface}</td>
@ -355,7 +353,6 @@ export default function Simulator() {
{/* 매수 */} {/* 매수 */}
<td>{convertNumberToPriceDecimal(moduleInfo.amount)}</td> <td>{convertNumberToPriceDecimal(moduleInfo.amount)}</td>
</tr> </tr>
</>
) )
}) })
) : ( ) : (
@ -388,7 +385,6 @@ export default function Simulator() {
{pcsInfoList.length > 0 ? ( {pcsInfoList.length > 0 ? (
pcsInfoList.map((pcsInfo) => { pcsInfoList.map((pcsInfo) => {
return ( return (
<>
<tr key={pcsInfo.itemId}> <tr key={pcsInfo.itemId}>
{/* 파워컨디셔너 */} {/* 파워컨디셔너 */}
<td className="al-l"> <td className="al-l">
@ -397,7 +393,6 @@ export default function Simulator() {
{/* 대 */} {/* 대 */}
<td>{convertNumberToPriceDecimal(pcsInfo.amount)}</td> <td>{convertNumberToPriceDecimal(pcsInfo.amount)}</td>
</tr> </tr>
</>
) )
}) })
) : ( ) : (

View File

@ -42,6 +42,7 @@ import { useEvent } from '@/hooks/useEvent'
import { logger } from '@/util/logger' import { logger } from '@/util/logger'
import { useText } from '@/hooks/useText' import { useText } from '@/hooks/useText'
import { usePolygon } from '@/hooks/usePolygon' import { usePolygon } from '@/hooks/usePolygon'
import { getDegreeByChon } from '@/util/canvas-util'
const defaultDotLineGridSetting = { const defaultDotLineGridSetting = {
INTERVAL: { INTERVAL: {
@ -177,8 +178,8 @@ export function useCanvasSetting(executeEffect = true) {
raft: item.raftBase && parseInt(item.raftBase), raft: item.raftBase && parseInt(item.raftBase),
layout: ['ROOF_ID_SLATE', 'ROOF_ID_SINGLE'].includes(item.roofMatlCd) ? ROOF_MATERIAL_LAYOUT.STAIRS : ROOF_MATERIAL_LAYOUT.PARALLEL, layout: ['ROOF_ID_SLATE', 'ROOF_ID_SINGLE'].includes(item.roofMatlCd) ? ROOF_MATERIAL_LAYOUT.STAIRS : ROOF_MATERIAL_LAYOUT.PARALLEL,
hajebichi: item.roofPchBase && parseInt(item.roofPchBase), hajebichi: item.roofPchBase && parseInt(item.roofPchBase),
pitch: item.pitch ? parseInt(item.pitch) : 4, pitch: item.inclBase ? parseInt(item.inclBase) : 4,
angle: item.angle ? parseInt(item.angle) : 21.8, angle: getDegreeByChon(item.inclBase ? parseInt(item.inclBase): 4) //item.angle ? parseInt(item.angle) : 21.8,
})) }))
setRoofMaterials(roofLists) setRoofMaterials(roofLists)
return roofLists return roofLists

View File

@ -114,15 +114,16 @@ export function useRoofAllocationSetting(id) {
*/ */
const fetchBasicSettings = async (planNo) => { const fetchBasicSettings = async (planNo) => {
try { try {
await get({ url: `/api/canvas-management/canvas-basic-settings/by-object/${correntObjectNo}/${planNo}` }).then((res) => { const response = await get({ url: `/api/canvas-management/canvas-basic-settings/by-object/${correntObjectNo}/${planNo}` });
let roofsArray = {}
if (res.length > 0) { let roofsArray = [];
roofsArray = res.map((item) => {
return { // API에서 데이터를 성공적으로 가져온 경우
if (response && response.length > 0) {
roofsArray = response.map((item, index) => ({
planNo: item.planNo, planNo: item.planNo,
roofApply: item.roofApply, roofApply: item.roofApply,
roofSeq: item.roofSeq, roofSeq: item.roofSeq || index,
roofMatlCd: item.roofMatlCd, roofMatlCd: item.roofMatlCd,
roofWidth: item.roofWidth, roofWidth: item.roofWidth,
roofHeight: item.roofHeight, roofHeight: item.roofHeight,
@ -131,12 +132,19 @@ export function useRoofAllocationSetting(id) {
roofLayout: item.roofLayout, roofLayout: item.roofLayout,
roofPitch: item.roofPitch, roofPitch: item.roofPitch,
roofAngle: item.roofAngle, roofAngle: item.roofAngle,
selected: index === 0, // 첫 번째 항목을 기본 선택으로 설정
index: index
}));
} }
}) // API에서 데이터가 없고 기존 roofList가 있는 경우
} else { else if (roofList && roofList.length > 0) {
if (roofList.length > 0) { roofsArray = roofList.map((roof, index) => ({
roofsArray = roofList ...roof,
} else { selected: index === 0 // 첫 번째 항목을 기본 선택으로 설정
}));
}
// 둘 다 없는 경우 기본값 설정
else {
roofsArray = [ roofsArray = [
{ {
planNo: planNo, planNo: planNo,
@ -153,7 +161,7 @@ export function useRoofAllocationSetting(id) {
}, },
] ]
} }
}
/** /**
* 데이터 설정 * 데이터 설정
@ -205,7 +213,7 @@ export function useRoofAllocationSetting(id) {
angle: roof.angle ?? '', angle: roof.angle ?? '',
})) }))
setCurrentRoofList(normalizedRoofs) setCurrentRoofList(normalizedRoofs)
})
} catch (error) { } catch (error) {
console.error('Data fetching error:', error) console.error('Data fetching error:', error)
} }
@ -374,11 +382,18 @@ export function useRoofAllocationSetting(id) {
setBasicSetting((prev) => { setBasicSetting((prev) => {
return { ...prev, selectedRoofMaterial: newRoofList.find((roof) => roof.selected) } return { ...prev, selectedRoofMaterial: newRoofList.find((roof) => roof.selected) }
}) })
const selectedRoofMaterial = newRoofList.find((roof) => roof.selected)
const roofs = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF && obj.roofMaterial?.index === selectedRoofMaterial.index)
roofs.forEach((roof) => {
setSurfaceShapePattern(roof, roofDisplay.column, false, { ...selectedRoofMaterial }, true)
drawDirectionArrow(roof)
})
setRoofList(newRoofList) setRoofList(newRoofList)
setRoofMaterials(newRoofList) setRoofMaterials(newRoofList)
setRoofsStore(newRoofList) setRoofsStore(newRoofList)
const selectedRoofMaterial = newRoofList.find((roof) => roof.selected)
setSurfaceShapePattern(currentObject, roofDisplay.column, false, selectedRoofMaterial, true) setSurfaceShapePattern(currentObject, roofDisplay.column, false, selectedRoofMaterial, true)
drawDirectionArrow(currentObject) drawDirectionArrow(currentObject)
modifyModuleSelectionData() modifyModuleSelectionData()

View File

@ -29,22 +29,39 @@ fabric.Rect.prototype.getCurrentPoints = function () {
/** /**
* fabric.Group에 getCurrentPoints 메서드를 추가 (도머 그룹용) * fabric.Group에 getCurrentPoints 메서드를 추가 (도머 그룹용)
* 그룹groupPoints를 다시 계산하여 반환 * 그룹 객체들의 점들을 수집하여 현재 월드 좌표를 반환
*/ */
fabric.Group.prototype.getCurrentPoints = function () { fabric.Group.prototype.getCurrentPoints = function () {
// groupPoints를 다시 계산 // 그룹 내 객체들로부터 실시간으로 점들을 계산
if (this._objects && this._objects.length > 0) {
let allPoints = []
// 그룹에 groupPoints가 있으면 해당 점들을 사용 (도머의 경우) // 그룹 내 모든 객체의 점들을 수집
if (this.groupPoints && Array.isArray(this.groupPoints)) { this._objects.forEach(function (obj) {
const matrix = this.calcTransformMatrix() if (obj.getCurrentPoints && typeof obj.getCurrentPoints === 'function') {
console.log('this.groupPoints', this.groupPoints) const objPoints = obj.getCurrentPoints()
return this.groupPoints.map(function (p) { allPoints = allPoints.concat(objPoints)
const point = new fabric.Point(p.x, p.y) } else if (obj.points && Array.isArray(obj.points)) {
return fabric.util.transformPoint(point, matrix) const pathOffset = obj.pathOffset || { x: 0, y: 0 }
const matrix = obj.calcTransformMatrix()
const transformedPoints = obj.points
.map(function (p) {
return new fabric.Point(p.x - pathOffset.x, p.y - pathOffset.y)
}) })
.map(function (p) {
return fabric.util.transformPoint(p, matrix)
})
allPoints = allPoints.concat(transformedPoints)
}
})
if (allPoints.length > 0) {
// Convex Hull 알고리즘을 사용하여 외곽 점들만 반환
return this.getConvexHull(allPoints)
}
} }
// groupPoints가 없으면 바운딩 박스를 사용 // 객체가 없으면 바운딩 박스를 사용
const bounds = this.getBoundingRect() const bounds = this.getBoundingRect()
const points = [ const points = [
{ x: bounds.left, y: bounds.top }, { x: bounds.left, y: bounds.top },