Compare commits

..

20 Commits

Author SHA1 Message Date
cb7950cc8c Merge remote-tracking branch 'origin/dev' into feature/skeleton_ysCha 2025-10-20 09:05:19 +09:00
a2cd08484d skeleton 2025-10-17 18:57:47 +09:00
f6a5e5db16 Merge remote-tracking branch 'origin/dev' into feature/skeleton_ysCha
# Conflicts:
#	src/components/fabric/QPolygon.js
#	src/util/skeleton-utils.js
2025-10-17 18:27:36 +09:00
3a3ff7c156 보조라인 길이 나오지 않음 => obj.name !== 'lengthText' && 추가 2025-10-16 18:04:09 +09:00
d548b0e1f4 360도 회전시 위치 수정 2025-10-15 16:15:21 +09:00
ddf326ca6b 동, 현이동 확인항목 추가 2025-10-15 09:54:13 +09:00
8b2cf6a9d3 시계방향 대응 추가 2025-10-14 14:16:02 +09:00
41de001986 순서에 따라 abs적용 추가 2025-10-14 13:58:15 +09:00
1385683bce hipSize 간혹 오류발생 - 기본값 0 설정 2025-10-14 11:05:50 +09:00
92fd17ed71 문의게시판 저장수정 글자변경 2025-10-14 10:59:10 +09:00
320080e0c1 문의게시판 상세 api 수정 2025-10-01 11:09:34 +09:00
d32553416c [1309] 문의사항 3 카테고리 문제 2025-10-01 10:14:15 +09:00
1aa6bc79a8 견적서 및 발전량에 페이지에서 canvas가 null임.. 2025-09-30 18:19:16 +09:00
b480345b24 [1222]견적의 제품 정보 화면에 대해서 - 그룹별 추가 2025-09-30 18:12:25 +09:00
5f726bf5db input 계산기추가(전각) 2025-09-30 14:25:31 +09:00
9a2c6adb96 [big.js] Division by zero - actualSize 가 0이 되어 나누기에 문제가 됨 2025-09-30 14:22:59 +09:00
75312f5ccf 스켈레톤.v0.1 2025-09-30 13:41:34 +09:00
ba94dd0579 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev_ysCha 2025-09-30 13:29:38 +09:00
384c68c1ef [big.js] Division by zero - actualSize 가 0이 되어 나누기에 문제가 됨 2025-09-30 13:29:09 +09:00
dfed51a758 각도 계산식 소수점 둘째자리까지 표시하도록 수정 2025-09-30 09:55:01 +09:00
15 changed files with 298 additions and 114 deletions

View File

@ -3,7 +3,7 @@ import { createCalculator } from '@/util/calc-utils'
import '@/styles/calc.scss' import '@/styles/calc.scss'
export const CalculatorInput = forwardRef( export const CalculatorInput = forwardRef(
({ value, onChange, label, options = {}, id, className = 'calculator-input', readOnly = false, placeholder }, ref) => { ({ value, onChange, label, options = {}, id, className = 'calculator-input', readOnly = false, placeholder, name='', disabled = false }, ref) => {
const [showKeypad, setShowKeypad] = useState(false) const [showKeypad, setShowKeypad] = useState(false)
const [displayValue, setDisplayValue] = useState(value || '0') const [displayValue, setDisplayValue] = useState(value || '0')
const [hasOperation, setHasOperation] = useState(false) const [hasOperation, setHasOperation] = useState(false)
@ -353,6 +353,7 @@ export const CalculatorInput = forwardRef(
ref={inputRef} ref={inputRef}
type="text" type="text"
id={id} id={id}
name={name}
value={displayValue} value={displayValue}
readOnly={readOnly} readOnly={readOnly}
className={className} className={className}
@ -363,6 +364,7 @@ export const CalculatorInput = forwardRef(
tabIndex={readOnly ? -1 : 0} tabIndex={readOnly ? -1 : 0}
placeholder={placeholder} placeholder={placeholder}
autoComplete={'off'} autoComplete={'off'}
disabled={disabled}
/> />
{showKeypad && !readOnly && ( {showKeypad && !readOnly && (

View File

@ -25,6 +25,7 @@ export default function QnaDetailModal({ qnaNo, setOpen, qnaType }) {
compCd : 5200, compCd : 5200,
loginId : sessionState.userId, loginId : sessionState.userId,
langCd : 'JA', langCd : 'JA',
siteTpCd : 'QC',
}) })
const apiUrl = `${url}?${params.toString()}` const apiUrl = `${url}?${params.toString()}`

View File

@ -22,7 +22,8 @@ export default function QnaRegModal({ setOpen, setReload, searchValue, selectPag
const [sessionState, setSessionState] = useRecoilState(sessionStore) const [sessionState, setSessionState] = useRecoilState(sessionStore)
const globalLocaleState = useRecoilValue(globalLocaleStore) const globalLocaleState = useRecoilValue(globalLocaleStore)
const [files, setFiles] = useState([]) const [files, setFiles] = useState([])
const [qnaData, setQnaData] = useState([]) //const [qnaData, setQnaData] = useState([])
const [qnaData, setQnaData] = useState({})
const [closeMdFlg, setCloseMdFlg] = useState(true) const [closeMdFlg, setCloseMdFlg] = useState(true)
const [closeSmFlg, setCloseSmFlg] = useState(true) const [closeSmFlg, setCloseSmFlg] = useState(true)
const [hideSmFlg, setHideSmFlg] = useState(false) const [hideSmFlg, setHideSmFlg] = useState(false)
@ -44,6 +45,10 @@ export default function QnaRegModal({ setOpen, setReload, searchValue, selectPag
const [isBtnDisable, setIsBtnDisable] = useState(false); const [isBtnDisable, setIsBtnDisable] = useState(false);
const { promiseGet, post, promisePost } = useAxios(globalLocaleState) const { promiseGet, post, promisePost } = useAxios(globalLocaleState)
useEffect(() => {
console.log('qnaData updated:', qnaData);
}, [qnaData]);
let fileCheck = false; let fileCheck = false;
const regPhoneNumber = (e) => { const regPhoneNumber = (e) => {
const result = e.target.value const result = e.target.value
@ -80,14 +85,16 @@ let fileCheck = false;
//setQnaData([]) //setQnaData([])
setQnaData({ setQnaData({
...qnaData,
compCd: "5200", compCd: "5200",
siteTpCd: "QC", siteTpCd: "QC",
schNoticeClsCd: "QNA", schNoticeClsCd: "QNA",
regId: sessionState.userId, regId: sessionState?.userId || '',
storeId: sessionState.userId, storeId: sessionState?.userId || '',
qstMail : sessionState.email qstMail: sessionState?.email || '',
}) qnaClsLrgCd: '',
qnaClsMidCd: '',
qnaClsSmlCd: ''
});
const codeL = findCommonCode(204200) const codeL = findCommonCode(204200)
if (codeL != null) { if (codeL != null) {
@ -119,41 +126,42 @@ let fileCheck = false;
} }
const onChangeQnaTypeM = (e) => { const onChangeQnaTypeM = (e) => {
if (!e?.clCode) return;
if(e === undefined || e === null) return; //
const codeS = findCommonCode(204400) setQnaData(prevState => ({
if (codeS != null) { ...prevState,
qnaClsMidCd: e.clCode,
// ( )
qnaClsSmlCd: ''
}));
let codeList = [] //
const codeS = findCommonCode(204400);
if (codeS) {
const filteredCodeList = codeS.filter(item => item.clRefChr1 === e.clCode);
setQnaTypeSmCodeList(filteredCodeList);
codeS.map((item) => { // ,
const hasSubCategories = filteredCodeList.length > 0;
if (item.clRefChr1 === e.clCode) { setCloseSmFlg(!hasSubCategories);
codeList.push(item); setHideSmFlg(!hasSubCategories);
} else {
}
})
setQnaData({ ...qnaData, qnaClsMidCd: e.clCode })
setCloseSmFlg(false)
setQnaTypeSmCodeList(codeList)
qnaTypeSmCodeRef.current?.setValue();
if(codeList.length > 0) {
setHideSmFlg(false)
}else{
setHideSmFlg(true) setHideSmFlg(true)
} }
//
qnaTypeSmCodeRef.current?.setValue();
};
}
}
const onChangeQnaTypeS = (e) => { const onChangeQnaTypeS = (e) => {
if(e === undefined || e === null) return; if (!e?.clCode) return;
setQnaData({ ...qnaData, qnaClsSmlCd:e.clCode})
setQnaData(prevState => ({
...prevState,
qnaClsSmlCd: e.clCode
}));
} }
const onFileSave = () => { const onFileSave = () => {

View File

@ -138,7 +138,27 @@ export default function Estimate({}) {
updatedRes = [...res] updatedRes = [...res]
} }
setOriginDisplayItemList(res) const groupByItemGroup = (items) => {
const grouped = items.reduce((acc, item) => {
const group = item.itemGroup || '기타';
if (!acc[group]) {
acc[group] = {
label: group,
options: []
};
}
acc[group].options.push({
value: item.itemId,
label: `${item.itemNo} - ${item.itemName}`,
...item
});
return acc;
}, {});
return Object.values(grouped);
};
const groupedItems = groupByItemGroup(res);
setOriginDisplayItemList(groupedItems)
setDisplayItemList(updatedRes) setDisplayItemList(updatedRes)
} }
}) })
@ -153,6 +173,19 @@ export default function Estimate({}) {
}) })
} }
const groupStyles = {
groupHeading: (provided) => ({
...provided,
fontSize: '14px',
fontWeight: 'bold',
color: '#333',
backgroundColor: '#f5f5f5',
padding: '8px 12px',
marginBottom: '4px',
borderBottom: '2px solid #ddd'
})
};
useEffect(() => { useEffect(() => {
// console.log('🚀 ~ Estimate ~ selectedPlan:', selectedPlan) // console.log('🚀 ~ Estimate ~ selectedPlan:', selectedPlan)
if (selectedPlan) initEstimate(selectedPlan?.planNo?? currentPid) if (selectedPlan) initEstimate(selectedPlan?.planNo?? currentPid)
@ -1998,6 +2031,7 @@ export default function Estimate({}) {
classNamePrefix="custom" classNamePrefix="custom"
placeholder="Select" placeholder="Select"
options={originDisplayItemList} options={originDisplayItemList}
styles={groupStyles}
onChange={(e) => { onChange={(e) => {
if (isObjectNotEmpty(e)) { if (isObjectNotEmpty(e)) {
onChangeDisplayItem(e.itemId, item.dispOrder, index, false) onChangeDisplayItem(e.itemId, item.dispOrder, index, false)

View File

@ -336,8 +336,8 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
if (types.every((type) => type === LINE_TYPE.WALLLINE.EAVES)) { if (types.every((type) => type === LINE_TYPE.WALLLINE.EAVES)) {
// 용마루 -- straight-skeleton // 용마루 -- straight-skeleton
console.log('용마루 지붕') console.log('용마루 지붕')
//drawRidgeRoof(this.id, this.canvas, textMode) drawRidgeRoof(this.id, this.canvas, textMode)
drawSkeletonRidgeRoof(this.id, this.canvas, textMode); //drawSkeletonRidgeRoof(this.id, this.canvas, textMode);
} else if (isGableRoof(types)) { } else if (isGableRoof(types)) {
// A형, B형 박공 지붕 // A형, B형 박공 지붕
console.log('패턴 지붕') console.log('패턴 지붕')

View File

@ -346,6 +346,7 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla
/> */} /> */}
<CalculatorInput <CalculatorInput
id="" id=""
name=""
label="" label=""
className="input-origin block" className="input-origin block"
readOnly={currentRoof?.roofAngleSet !== item.value} readOnly={currentRoof?.roofAngleSet !== item.value}
@ -412,15 +413,33 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla
<div className="flex-ment"> <div className="flex-ment">
<span>W</span> <span>W</span>
<div className="input-grid" style={{ width: '84px' }}> <div className="input-grid" style={{ width: '84px' }}>
<input {/*<input*/}
type="text" {/* type="text"*/}
{/* className="input-origin block"*/}
{/* name={`width`}*/}
{/* ref={roofRef.width}*/}
{/* value={parseInt(currentRoof?.width)}*/}
{/* onChange={(e) => changeInput(normalizeDigits(e.target.value), e)}*/}
{/* readOnly={currentRoof?.widAuth === 'R'}*/}
{/* disabled={currentRoof?.roofSizeSet === '3'}*/}
{/*/>*/}
<CalculatorInput
id=""
name={'width'}
label=""
className="input-origin block" className="input-origin block"
name={`width`}
ref={roofRef.width} ref={roofRef.width}
value={parseInt(currentRoof?.width)} value={currentRoof?.width||0}
onChange={(e) => changeInput(normalizeDigits(e.target.value), e)} onChange={(value) => {
setCurrentRoof({ ...currentRoof, value })
}}
readOnly={currentRoof?.widAuth === 'R'} readOnly={currentRoof?.widAuth === 'R'}
disabled={currentRoof?.roofSizeSet === '3'} disabled={currentRoof?.roofSizeSet === '3'}
options={{
allowNegative: false,
allowDecimal: false //(index !== 0),
}}
/> />
</div> </div>
</div> </div>
@ -429,15 +448,33 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla
<div className="flex-ment"> <div className="flex-ment">
<span>L</span> <span>L</span>
<div className="input-grid" style={{ width: '84px' }}> <div className="input-grid" style={{ width: '84px' }}>
<input {/*<input*/}
type="text" {/* type="text"*/}
{/* className="input-origin block"*/}
{/* name={`length`}*/}
{/* ref={roofRef.length}*/}
{/* value={parseInt(currentRoof?.length)}*/}
{/* onChange={(e) => changeInput(normalizeDigits(e.target.value), e)}*/}
{/* readOnly={currentRoof?.lenAuth === 'R'}*/}
{/* disabled={currentRoof?.roofSizeSet === '3'}*/}
{/*/>*/}
<CalculatorInput
id=""
name={'length'}
label=""
className="input-origin block" className="input-origin block"
name={`length`}
ref={roofRef.length} ref={roofRef.length}
value={parseInt(currentRoof?.length)} value={currentRoof?.length||0}
onChange={(e) => changeInput(normalizeDigits(e.target.value), e)} onChange={(value) => {
setCurrentRoof({ ...currentRoof, value })
}}
readOnly={currentRoof?.lenAuth === 'R'} readOnly={currentRoof?.lenAuth === 'R'}
disabled={currentRoof?.roofSizeSet === '3'} disabled={currentRoof?.roofSizeSet === '3'}
options={{
allowNegative: false,
allowDecimal: false //(index !== 0),
}}
/> />
</div> </div>
</div> </div>
@ -465,16 +502,34 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla
<div className="flex-ment"> <div className="flex-ment">
<span>{getMessage('hajebichi')}</span> <span>{getMessage('hajebichi')}</span>
<div className="input-grid" style={{ width: '84px' }}> <div className="input-grid" style={{ width: '84px' }}>
<input {/*<input*/}
type="text" {/* type="text"*/}
{/* className="input-origin block"*/}
{/* name={`hajebichi`}*/}
{/* ref={roofRef.hajebichi}*/}
{/* value={parseInt(currentRoof?.hajebichi)}*/}
{/* onChange={(e) => changeInput(normalizeDigits(e.target.value), e)}*/}
{/* readOnly={currentRoof?.roofPchAuth === 'R'}*/}
{/* disabled={currentRoof?.roofSizeSet === '3'}*/}
{/*/>*/}
<CalculatorInput
id=""
name={'hajebichi'}
label=""
className="input-origin block" className="input-origin block"
name={`hajebichi`}
ref={roofRef.hajebichi} ref={roofRef.hajebichi}
value={parseInt(currentRoof?.hajebichi)} value={currentRoof?.hajebichi||0}
onChange={(e) => changeInput(normalizeDigits(e.target.value), e)} onChange={(value) => {
setCurrentRoof({ ...currentRoof, value })
}}
readOnly={currentRoof?.roofPchAuth === 'R'} readOnly={currentRoof?.roofPchAuth === 'R'}
disabled={currentRoof?.roofSizeSet === '3'} disabled={currentRoof?.roofSizeSet === '3'}
options={{
allowNegative: false,
allowDecimal: false //(index !== 0),
}}
/> />
</div> </div>
</div> </div>
)} )}

View File

@ -632,9 +632,12 @@ export function useCanvasSetting(executeEffect = true) {
originHorizon: res.originHorizon, originHorizon: res.originHorizon,
originVertical: res.originVertical, originVertical: res.originVertical,
}) })
if (canvas) {
canvas.setWidth(res.originHorizon) canvas.setWidth(res.originHorizon)
canvas.setHeight(res.originVertical) canvas.setHeight(res.originVertical)
canvas.renderAll() canvas.renderAll()
}
/** 데이터 설정 */ /** 데이터 설정 */
setSettingModalFirstOptions({ setSettingModalFirstOptions({

View File

@ -319,6 +319,14 @@ export function useMovementSetting(id) {
const roofId = target.attributes.roofId const roofId = target.attributes.roofId
const roof = canvas.getObjects().find((obj) => obj.id === roofId) const roof = canvas.getObjects().find((obj) => obj.id === roofId)
// 현이동, 동이동 추가
const moveFlowLine = typeRef.current === TYPE.FLOW_LINE ? FLOW_LINE_REF.POINTER_INPUT_REF.current.value : 0
const moveUpDown = typeRef.current === TYPE.UP_DOWN ? UP_DOWN_REF.POINTER_INPUT_REF.current.value : 0
roof.moveFlowLine = parseInt(moveFlowLine, 10) || 0;
roof.moveUpDown = parseInt(moveUpDown, 10) || 0;
roof.moveDirect = "";
roof.moveSelectLine = target;
const wall = canvas.getObjects().find((obj) => obj.name === POLYGON_TYPE.WALL && obj.attributes.roofId === roofId) const wall = canvas.getObjects().find((obj) => obj.name === POLYGON_TYPE.WALL && obj.attributes.roofId === roofId)
const baseLines = wall.baseLines const baseLines = wall.baseLines
let targetBaseLines = [] let targetBaseLines = []
@ -348,6 +356,7 @@ export function useMovementSetting(id) {
? 'right' ? 'right'
: 'left' : 'left'
let checkBaseLines, currentBaseLines let checkBaseLines, currentBaseLines
roof.moveDirect = lineVector
switch (lineVector) { switch (lineVector) {
case 'up': case 'up':
checkBaseLines = baseLines.filter((line) => line.y1 === line.y2 && line.y1 < target.y1) checkBaseLines = baseLines.filter((line) => line.y1 === line.y2 && line.y1 < target.y1)
@ -442,10 +451,17 @@ export function useMovementSetting(id) {
let value let value
if (typeRef.current === TYPE.FLOW_LINE) { if (typeRef.current === TYPE.FLOW_LINE) {
value = value = (() => {
FLOW_LINE_REF.FILLED_INPUT_REF.current.value !== '' const filledValue = FLOW_LINE_REF.FILLED_INPUT_REF.current?.value;
? Big(FLOW_LINE_REF.FILLED_INPUT_REF.current.value).times(2) const pointerValue = FLOW_LINE_REF.POINTER_INPUT_REF.current?.value;
: Big(FLOW_LINE_REF.POINTER_INPUT_REF.current.value).times(2)
if (filledValue && !isNaN(filledValue) && filledValue.trim() !== '') {
return Big(filledValue).times(2);
} else if (pointerValue && !isNaN(pointerValue) && pointerValue.trim() !== '') {
return Big(pointerValue).times(2);
}
return Big(0); // 기본값으로 0 반환 또는 다른 적절한 기본값
})();
if (target.y1 === target.y2) { if (target.y1 === target.y2) {
value = value.neg() value = value.neg()
} }

View File

@ -252,6 +252,7 @@ export function useOuterLineWall(id, propertiesId) {
canvas?.renderAll() canvas?.renderAll()
setOuterLineFix(true) setOuterLineFix(true)
closePopup(id) closePopup(id)
ccwCheck()
addPopup(propertiesId, 1, <RoofShapeSetting id={propertiesId} pos={{ x: 50, y: 230 }} />) addPopup(propertiesId, 1, <RoofShapeSetting id={propertiesId} pos={{ x: 50, y: 230 }} />)
} }
@ -905,6 +906,51 @@ export function useOuterLineWall(id, propertiesId) {
} }
} }
// 시계방향으로 그려진 경우 반시게방향으로 변경
const ccwCheck = () => {
let outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
if (outerLines.length < 2) {
swalFire({ text: getMessage('wall.line.not.found') })
return
}
/**
* 외벽선이 시계방향인지 시계반대 방향인지 확인
*/
const outerLinePoints = outerLines.map((line) => ({ x: line.x1, y: line.y1 }))
let counterClockwise = true
let signedArea = 0
outerLinePoints.forEach((point, index) => {
const nextPoint = outerLinePoints[(index + 1) % outerLinePoints.length]
signedArea += point.x * nextPoint.y - point.y * nextPoint.x
})
if (signedArea > 0) {
counterClockwise = false
}
/** 시계 방향일 경우 외벽선 reverse*/
if (!counterClockwise) {
outerLines.reverse().forEach((line, index) => {
addLine([line.x2, line.y2, line.x1, line.y1], {
stroke: line.stroke,
strokeWidth: line.strokeWidth,
idx: index,
selectable: line.selectable,
name: 'outerLine',
x1: line.x2,
y1: line.y2,
x2: line.x1,
y2: line.y1,
visible: line.visible,
})
canvas.remove(line)
})
canvas.renderAll()
}
}
return { return {
points, points,
setPoints, setPoints,

View File

@ -179,46 +179,6 @@ export function useRoofShapeSetting(id) {
let outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') let outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
let direction let direction
if (outerLines.length < 2) {
swalFire({ text: getMessage('wall.line.not.found') })
return
}
/**
* 외벽선이 시계방향인지 시계반대 방향인지 확인
*/
const outerLinePoints = outerLines.map((line) => ({ x: line.x1, y: line.y1 }))
let counterClockwise = true
let signedArea = 0
outerLinePoints.forEach((point, index) => {
const nextPoint = outerLinePoints[(index + 1) % outerLinePoints.length]
signedArea += point.x * nextPoint.y - point.y * nextPoint.x
})
if (signedArea > 0) {
counterClockwise = false
}
/** 시계 방향일 경우 외벽선 reverse*/
if (!counterClockwise) {
outerLines.reverse().forEach((line, index) => {
addLine([line.x2, line.y2, line.x1, line.y1], {
stroke: line.stroke,
strokeWidth: line.strokeWidth,
idx: index,
selectable: line.selectable,
name: 'outerLine',
x1: line.x2,
y1: line.y2,
x2: line.x1,
y2: line.y1,
visible: line.visible,
})
canvas.remove(line)
})
canvas.renderAll()
}
if ([1, 2, 3, 5, 6, 7, 8].includes(shapeNum)) { if ([1, 2, 3, 5, 6, 7, 8].includes(shapeNum)) {
// 변별로 설정이 아닌 경우 경사를 지붕재에 적용해주어야함 // 변별로 설정이 아닌 경우 경사를 지붕재에 적용해주어야함
setRoofPitch() setRoofPitch()
@ -507,7 +467,7 @@ export function useRoofShapeSetting(id) {
originX: 'center', originX: 'center',
originY: 'center', originY: 'center',
}) })
polygon.setViewLengthText(false) // polygon.setViewLengthText(false)
polygon.lines = [...outerLines] polygon.lines = [...outerLines]
addPitchTextsByOuterLines() addPitchTextsByOuterLines()

View File

@ -1451,6 +1451,50 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) {
// 그룹화할 객체들 배열 (currentObject + relatedObjects) // 그룹화할 객체들 배열 (currentObject + relatedObjects)
const objectsToGroup = [currentObject, ...relatedObjects] const objectsToGroup = [currentObject, ...relatedObjects]
// 회전 카운트 초기화 및 최초 상태 저장
if (!currentObject.rotationCount) {
currentObject.rotationCount = 0
}
// 최초 회전일 때 (rotationCount === 0) 원본 상태 저장
if (currentObject.rotationCount === 0) {
objectsToGroup.forEach((obj) => {
if (!obj.originalState) {
obj.originalState = {
left: obj.left,
top: obj.top,
angle: obj.angle || 0,
points: obj.type === 'QPolygon' ? JSON.parse(JSON.stringify(obj.points)) : null,
scaleX: obj.scaleX || 1,
scaleY: obj.scaleY || 1,
}
}
})
}
// 회전 카운트 증가 (먼저 증가시켜서 목표 각도 계산)
currentObject.rotationCount = (currentObject.rotationCount + 1) % 4
// 목표 회전 각도 계산 (원본 기준)
const targetAngle = currentObject.rotationCount * 90
// 원본 상태로 먼저 복원한 후 목표 각도만큼 회전
objectsToGroup.forEach((obj) => {
if (obj.originalState) {
// 원본 상태로 복원
obj.set({
left: obj.originalState.left,
top: obj.originalState.top,
angle: obj.originalState.angle,
scaleX: obj.originalState.scaleX,
scaleY: obj.originalState.scaleY,
})
if (obj.originalState.points && obj.type === 'QPolygon') {
obj.set({ points: JSON.parse(JSON.stringify(obj.originalState.points)) })
}
}
})
// 기존 객체들을 캔버스에서 제거 // 기존 객체들을 캔버스에서 제거
objectsToGroup.forEach((obj) => canvas.remove(obj)) objectsToGroup.forEach((obj) => canvas.remove(obj))
@ -1463,12 +1507,8 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) {
// 그룹을 캔버스에 추가 // 그룹을 캔버스에 추가
canvas.add(group) canvas.add(group)
// 현재 회전값에 90도 추가 // 목표 각도로 회전 (원본 기준)
const currentAngle = group.angle || 0 group.rotate(targetAngle)
const newAngle = (currentAngle + 90) % 360
// 그룹 전체를 회전
group.rotate(newAngle)
group.setCoords() group.setCoords()
// 그룹을 해제하고 개별 객체로 복원 // 그룹을 해제하고 개별 객체로 복원

View File

@ -1820,7 +1820,13 @@ export function useMode() {
x: xDiff.eq(0) ? offsetCurrentPoint.x : nextWall.x1, x: xDiff.eq(0) ? offsetCurrentPoint.x : nextWall.x1,
y: yDiff.eq(0) ? offsetCurrentPoint.y : nextWall.y1, y: yDiff.eq(0) ? offsetCurrentPoint.y : nextWall.y1,
} }
const diffOffset = Big(nextWall.attributes.offset).minus(Big(currentWall.attributes.offset)) let diffOffset
if (nextWall.index > currentWall.index) {
diffOffset = Big(nextWall.attributes.offset).minus(Big(currentWall.attributes.offset)).abs()
} else {
diffOffset = Big(currentWall.attributes.offset).minus(Big(nextWall.attributes.offset))
}
const offsetPoint2 = { const offsetPoint2 = {
x: yDiff.eq(0) ? offsetPoint1.x : Big(offsetPoint1.x).plus(diffOffset).toNumber(), x: yDiff.eq(0) ? offsetPoint1.x : Big(offsetPoint1.x).plus(diffOffset).toNumber(),
y: xDiff.eq(0) ? offsetPoint1.y : Big(offsetPoint1.y).plus(diffOffset).toNumber(), y: xDiff.eq(0) ? offsetPoint1.y : Big(offsetPoint1.y).plus(diffOffset).toNumber(),

View File

@ -614,7 +614,7 @@
"qna.sub.title": "お問合せリスト", "qna.sub.title": "お問合せリスト",
"qna.reg.header.regDt": "お問い合わせ登録日", "qna.reg.header.regDt": "お問い合わせ登録日",
"qna.reg.header.regUserNm": "名前", "qna.reg.header.regUserNm": "名前",
"qna.reg.header.regUserTelNo": "お問い合わせ", "qna.reg.header.regUserTelNo": "電話番号",
"qna.reg.header.type": "お問い合わせ区分", "qna.reg.header.type": "お問い合わせ区分",
"qna.reg.header.title": "お問い合わせタイトル", "qna.reg.header.title": "お問い合わせタイトル",
"qna.reg.header.contents": "お問い合わせ内容", "qna.reg.header.contents": "お問い合わせ内容",

View File

@ -260,7 +260,7 @@ export const getDegreeByChon = (chon) => {
// tan(theta) = height / base // tan(theta) = height / base
const radians = Math.atan(chon / 10) const radians = Math.atan(chon / 10)
// 라디안을 도 단위로 변환 // 라디안을 도 단위로 변환
return Number((radians * (180 / Math.PI)).toFixed(1)) return Number((radians * (180 / Math.PI)).toFixed(2))
} }
/** /**

View File

@ -7579,7 +7579,12 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => {
hipBasePoint = { x1: line.x1, y1: line.y1, x2: line.x2, y2: line.y2 } hipBasePoint = { x1: line.x1, y1: line.y1, x2: line.x2, y2: line.y2 }
point = [mergePoint[0].x, mergePoint[0].y, mergePoint[3].x, mergePoint[3].y] point = [mergePoint[0].x, mergePoint[0].y, mergePoint[3].x, mergePoint[3].y]
const theta = Big(Math.acos(Big(line.line.attributes.planeSize).div(line.line.attributes.actualSize))) const theta = Big(Math.acos(Big(line.line.attributes.planeSize).div(
line.line.attributes.actualSize === 0 ||
line.line.attributes.actualSize === '' ||
line.line.attributes.actualSize === undefined ?
line.line.attributes.planeSize : line.line.attributes.actualSize
)))
.times(180) .times(180)
.div(Math.PI) .div(Math.PI)
.round(1) .round(1)
@ -7660,7 +7665,11 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => {
.filter((line) => (line.x2 === ridge.x1 && line.y2 === ridge.y1) || (line.x2 === ridge.x2 && line.y2 === ridge.y2)) .filter((line) => (line.x2 === ridge.x1 && line.y2 === ridge.y1) || (line.x2 === ridge.x2 && line.y2 === ridge.y2))
.filter((line) => baseLines.filter((baseLine) => baseLine.x1 === line.x1 && baseLine.y1 === line.y1).length > 0) .filter((line) => baseLines.filter((baseLine) => baseLine.x1 === line.x1 && baseLine.y1 === line.y1).length > 0)
basePoints.sort((a, b) => a.line.attributes.planeSize - b.line.attributes.planeSize) basePoints.sort((a, b) => a.line.attributes.planeSize - b.line.attributes.planeSize)
if (basePoints.length > 0 && basePoints[0].line) {
hipSize = Big(basePoints[0].line.attributes.planeSize) hipSize = Big(basePoints[0].line.attributes.planeSize)
} else {
hipSize = Big(0) // 또는 기본값 설정
}
} }
hipSize = hipSize.pow(2).div(2).sqrt().round().div(10).toNumber() hipSize = hipSize.pow(2).div(2).sqrt().round().div(10).toNumber()
@ -9223,7 +9232,11 @@ const getSortedPoint = (points, lines) => {
const reCalculateSize = (line) => { const reCalculateSize = (line) => {
const oldPlaneSize = line.attributes.planeSize const oldPlaneSize = line.attributes.planeSize
const oldActualSize = line.attributes.actualSize const oldActualSize = line.attributes.actualSize
const theta = Big(Math.acos(Big(oldPlaneSize).div(oldActualSize))) const theta = Big(Math.acos(Big(oldPlaneSize).div(
oldActualSize === 0 || oldActualSize === '' || oldActualSize === undefined ?
oldPlaneSize :
oldActualSize
)))
.times(180) .times(180)
.div(Math.PI) .div(Math.PI)
const planeSize = calcLinePlaneSize({ const planeSize = calcLinePlaneSize({