Merge branch 'dev' into dev-yj
This commit is contained in:
commit
2fea382aca
@ -1,12 +0,0 @@
|
||||
import Intro from '@/components/Intro'
|
||||
import { initCheck } from '@/util/session-util'
|
||||
|
||||
export default async function IntroPage() {
|
||||
return (
|
||||
<>
|
||||
<div className="container mx-auto p-4 m-4 border">
|
||||
<Intro />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -159,6 +159,8 @@ export const SAVE_KEY = [
|
||||
'groupName',
|
||||
'lineDirection',
|
||||
'groupId',
|
||||
'planeSize',
|
||||
'actualSize',
|
||||
]
|
||||
|
||||
export const OBJECT_PROTOTYPE = [fabric.Line.prototype, fabric.Polygon.prototype, fabric.Triangle.prototype]
|
||||
|
||||
@ -1,142 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
import Link from 'next/link'
|
||||
|
||||
import { useRecoilState } from 'recoil'
|
||||
import { modalContent, modalState } from '@/store/modalAtom'
|
||||
|
||||
import { useAxios } from '@/hooks/useAxios'
|
||||
|
||||
import { Button } from '@nextui-org/react'
|
||||
|
||||
import SingleDatePicker from './common/datepicker/SingleDatePicker'
|
||||
import RangeDatePicker from './common/datepicker/RangeDatePicker'
|
||||
import QGrid from './common/grid/QGrid'
|
||||
import { useSwal } from '@/hooks/useSwal'
|
||||
|
||||
export default function Intro() {
|
||||
const { get } = useAxios()
|
||||
const { swalFire } = useSwal()
|
||||
|
||||
// const [open, setOpen] = useState(false)
|
||||
const [startDate, setStartDate] = useState(new Date())
|
||||
const singleDatePickerProps = {
|
||||
startDate,
|
||||
setStartDate,
|
||||
}
|
||||
|
||||
const [dateRange, setDateRange] = useState([null, null])
|
||||
const [startRangeDate, endRangeDate] = dateRange
|
||||
const rangeDatePickerProps = {
|
||||
startRangeDate,
|
||||
endRangeDate,
|
||||
setDateRange,
|
||||
}
|
||||
|
||||
// const gridProps = {
|
||||
// isPageable: false,
|
||||
// }
|
||||
const [gridProps, setGridProps] = useState({
|
||||
gridData: [],
|
||||
isPageable: false,
|
||||
})
|
||||
|
||||
const [open, setOpen] = useRecoilState(modalState)
|
||||
const [contents, setContent] = useRecoilState(modalContent)
|
||||
|
||||
const modelProps = {
|
||||
open,
|
||||
setOpen,
|
||||
}
|
||||
|
||||
const ipsum = (
|
||||
<>
|
||||
<p className="text-2xl">title</p>
|
||||
<p>
|
||||
저작자·발명가·과학기술자와 예술가의 권리는 법률로써 보호한다. 이 헌법은 1988년 2월 25일부터 시행한다. 다만, 이 헌법을 시행하기 위하여 필요한
|
||||
법률의 제정·개정과 이 헌법에 의한 대통령 및 국회의원의 선거 기타 이 헌법시행에 관한 준비는 이 헌법시행 전에 할 수 있다.
|
||||
</p>
|
||||
<p>
|
||||
국가는 주택개발정책등을 통하여 모든 국민이 쾌적한 주거생활을 할 수 있도록 노력하여야 한다. 통신·방송의 시설기준과 신문의 기능을 보장하기
|
||||
위하여 필요한 사항은 법률로 정한다.
|
||||
</p>
|
||||
<p>
|
||||
국회에서 의결된 법률안은 정부에 이송되어 15일 이내에 대통령이 공포한다. 선거에 관한 경비는 법률이 정하는 경우를 제외하고는 정당 또는
|
||||
후보자에게 부담시킬 수 없다.
|
||||
</p>
|
||||
</>
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchData() {
|
||||
// const response = await fetch('https://www.ag-grid.com/example-assets/space-mission-data.json')
|
||||
// const data = await response.json()
|
||||
const data = await get({ url: 'https://www.ag-grid.com/example-assets/space-mission-data.json' })
|
||||
setGridProps({ ...gridProps, gridData: data })
|
||||
}
|
||||
fetchData()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="text-2xl">
|
||||
<Link href={'/login'}>
|
||||
<Button color="primary">로그인 페이지로 이동</Button>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="my-2">
|
||||
<div className="text-2xl">Single Date Picker</div>
|
||||
<div>
|
||||
<SingleDatePicker {...singleDatePickerProps} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="my-2">
|
||||
<div className="text-2xl">Range Date Picker</div>
|
||||
<div>
|
||||
<RangeDatePicker {...rangeDatePickerProps} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="my-2">
|
||||
<div className="text-2xl">QGrid</div>
|
||||
<div>
|
||||
<QGrid {...gridProps} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="my-2">
|
||||
<div className="text-2xl">QModal</div>
|
||||
<div>
|
||||
{/* <Button color="primary" onClick={() => setOpen(true)}>
|
||||
Open Modal
|
||||
</Button>
|
||||
<QModal {...modelProps}>{ipsum}</QModal> */}
|
||||
<Button
|
||||
color="primary"
|
||||
onClick={() => {
|
||||
setContent(ipsum)
|
||||
setOpen(true)
|
||||
}}
|
||||
>
|
||||
Open Modal
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="my-2">
|
||||
<div className="text-2xl">QToast</div>
|
||||
<div>
|
||||
<Button
|
||||
color="primary"
|
||||
onClick={() => {
|
||||
swalFire({
|
||||
text: 'This is a toast message',
|
||||
})
|
||||
}}
|
||||
>
|
||||
Open Toast
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -39,7 +39,7 @@ export default function Playground() {
|
||||
const [color, setColor] = useState('#ff0000')
|
||||
|
||||
const [textInput, setTextInput] = useState('')
|
||||
const [numberInput, setNumberInput] = useState(null)
|
||||
const [numberInput, setNumberInput] = useState('')
|
||||
const [radioInput, setRadioInput] = useState('')
|
||||
const [checkboxInput, setCheckboxInput] = useState([])
|
||||
const [selectedValue, setSelectedValue] = useState('')
|
||||
@ -171,7 +171,7 @@ export default function Playground() {
|
||||
<button
|
||||
className="btn-frame deepgray"
|
||||
onClick={() => {
|
||||
setNumberInput(null)
|
||||
setNumberInput('')
|
||||
}}
|
||||
>
|
||||
QInput NumberInput DATA RESET
|
||||
|
||||
@ -431,7 +431,7 @@ export default function Roof2(props) {
|
||||
{ x: 450, y: 850 },
|
||||
]
|
||||
|
||||
const polygon = new QPolygon(rectangleType2, {
|
||||
const polygon = new QPolygon(type2, {
|
||||
fill: 'transparent',
|
||||
stroke: 'green',
|
||||
strokeWidth: 1,
|
||||
|
||||
@ -52,7 +52,7 @@ export default function ColorPickerModal(props) {
|
||||
closePopup(id, isConfig)
|
||||
}}
|
||||
>
|
||||
{getMessage('common.message.save')}
|
||||
{getMessage('modal.common.save')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,16 +1,20 @@
|
||||
'use client'
|
||||
import { useEffect } from 'react'
|
||||
import '@/styles/contents.scss'
|
||||
import { useRecoilState } from 'recoil'
|
||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||
import { contextMenuListState, contextMenuState } from '@/store/contextMenu'
|
||||
import { useTempGrid } from '@/hooks/useTempGrid'
|
||||
import { useContextMenu } from '@/hooks/useContextMenu'
|
||||
import { useEvent } from '@/hooks/useEvent'
|
||||
|
||||
export default function QContextMenu(props) {
|
||||
const { contextRef, canvasProps, handleKeyup } = props
|
||||
const { contextRef, canvasProps } = props
|
||||
const [contextMenu, setContextMenu] = useRecoilState(contextMenuState)
|
||||
const [contextMenuList, setContextMenuList] = useRecoilState(contextMenuListState)
|
||||
const contextMenuList = useRecoilValue(contextMenuListState)
|
||||
const activeObject = canvasProps?.getActiveObject() //액티브된 객체를 가져옴
|
||||
const { tempGridMode, setTempGridMode } = useTempGrid()
|
||||
const { handleKeyup } = useContextMenu()
|
||||
const { addDocumentEventListener, removeDocumentEvent } = useEvent()
|
||||
|
||||
let contextType = ''
|
||||
|
||||
@ -22,13 +26,13 @@ export default function QContextMenu(props) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const getYPosition = (e) => {
|
||||
const contextLength = contextMenuList.reduce((acc, cur, index) => {
|
||||
return acc + cur.length
|
||||
}, 0)
|
||||
return e?.clientY - (contextLength * 25 + contextMenuList.length * 2 * 17)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!contextRef.current) return
|
||||
|
||||
@ -40,7 +44,7 @@ export default function QContextMenu(props) {
|
||||
y: window.innerHeight / 2 < e.pageY ? getYPosition(e) : e.pageY,
|
||||
}
|
||||
setContextMenu({ visible: true, ...position })
|
||||
document.addEventListener('keyup', (e) => handleKeyup(e))
|
||||
addDocumentEventListener('keyup', document, handleKeyup)
|
||||
canvasProps?.upperCanvasEl.removeEventListener('contextmenu', handleContextMenu) //한번 노출 후 이벤트 삭제
|
||||
}
|
||||
|
||||
@ -51,8 +55,9 @@ export default function QContextMenu(props) {
|
||||
|
||||
const handleOutsideClick = (e) => {
|
||||
// e.preventDefault()
|
||||
if (contextMenu.visible && !ref.current.contains(e.target)) {
|
||||
if (contextMenu.visible) {
|
||||
setContextMenu({ ...contextMenu, visible: false })
|
||||
removeDocumentEvent('keyup')
|
||||
}
|
||||
}
|
||||
|
||||
@ -61,10 +66,11 @@ export default function QContextMenu(props) {
|
||||
document.addEventListener('click', handleOutsideClick)
|
||||
|
||||
return () => {
|
||||
removeDocumentEvent('keyup')
|
||||
document.removeEventListener('click', handleClick)
|
||||
document.removeEventListener('click', handleOutsideClick)
|
||||
}
|
||||
}, [contextRef, contextMenu])
|
||||
}, [contextRef, contextMenuList])
|
||||
|
||||
const handleObjectMove = () => {
|
||||
activeObject.set({
|
||||
|
||||
@ -1,18 +1,18 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useState } from 'react'
|
||||
import Draggable from 'react-draggable'
|
||||
|
||||
export default function WithDraggable({ isShow, children, pos, handle = '' }) {
|
||||
const [position, setPosition] = useState({ x: 0, y: 0 })
|
||||
export default function WithDraggable({ isShow, children, pos = { x: 0, y: 0 }, handle = '' }) {
|
||||
const [position, setPosition] = useState(pos)
|
||||
|
||||
const handleOnDrag = (e, data) => {
|
||||
e.stopPropagation()
|
||||
setPosition({ x: data.x, y: data.y })
|
||||
}
|
||||
useEffect(() => {
|
||||
setPosition({ ...pos })
|
||||
}, [])
|
||||
// useEffect(() => {
|
||||
// setPosition({ ...pos })
|
||||
// }, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
'use client'
|
||||
|
||||
import { useCallback } from 'react'
|
||||
import { useCallback, useState } from 'react'
|
||||
|
||||
export default function QInput({ type, className, ref, id, readOnly = false, options = [], placeholder, value, onChange }) {
|
||||
const [prevNum, setPrevNum] = useState('')
|
||||
// options = options || [
|
||||
// {
|
||||
// id: 'one',
|
||||
@ -35,51 +36,59 @@ export default function QInput({ type, className, ref, id, readOnly = false, opt
|
||||
|
||||
const handleTextNumberChange = useCallback(
|
||||
(e) => {
|
||||
if (type === 'text') {
|
||||
onChange(e.target.value)
|
||||
} else if (type === 'number') {
|
||||
onChange(Number(e.target.value))
|
||||
}
|
||||
onChange(e.target.value)
|
||||
},
|
||||
[type, onChange],
|
||||
[onChange],
|
||||
)
|
||||
|
||||
// type=number 정수 부호, 소수점 검사, 일부 키 허용
|
||||
// const checkInputNumber = (e) => {
|
||||
// const value = e.target.value
|
||||
// const key = e.key
|
||||
// const allowKeys = ['Backspace', 'Delete', 'ArrowLeft', 'ArrowRight', 'Home', 'End', 'Tab', 'Enter', 'Control'] // 'ArrowUp', 'ArrowDown',
|
||||
// if (key >= '0' && key <= '9') {
|
||||
// return
|
||||
// }
|
||||
// if (key === '.' && !value.includes('.') && value.length > 0 && !isNaN(Number(value))) {
|
||||
// return
|
||||
// }
|
||||
// if (key === '-' && !value.includes('-') && value.length > 0) {
|
||||
// return
|
||||
// }
|
||||
// if (allowKeys.includes(key)) {
|
||||
// return
|
||||
// }
|
||||
// if (key === 'a' || key === 'c' || key === 'v' || key === 'x' || key === 'z') {
|
||||
// return
|
||||
// }
|
||||
// e.preventDefault()
|
||||
// }
|
||||
// type=number 정수 부호, 소수점 검사
|
||||
const checkInputNumber = (e) => {
|
||||
const value = e.target.value
|
||||
const key = e.key
|
||||
const allowKeys = ['Backspace', 'Delete', 'ArrowLeft', 'ArrowRight', 'Home', 'End', 'Tab', 'Enter', 'Control'] // 'ArrowUp', 'ArrowDown',
|
||||
if (key >= '0' && key <= '9') {
|
||||
return
|
||||
const value = e.target.defaultValue
|
||||
if (value === '') return
|
||||
|
||||
const regex = /^-?([1-9]\d*|\d)(\.\d*)?$/
|
||||
if (regex.test(value)) {
|
||||
setPrevNum(value)
|
||||
} else {
|
||||
onChange(prevNum)
|
||||
}
|
||||
if (key === '.' && !value.includes('.') && value.length > 0 && !isNaN(Number(value))) {
|
||||
return
|
||||
}
|
||||
if (key === '-' && !value.includes('-') && value.length > 0) {
|
||||
return
|
||||
}
|
||||
if (allowKeys.includes(key)) {
|
||||
return
|
||||
}
|
||||
if (key === 'a' || key === 'c' || key === 'v' || key === 'x' || key === 'z') {
|
||||
return
|
||||
}
|
||||
e.preventDefault()
|
||||
}
|
||||
|
||||
// input type : text, number
|
||||
const inputTextNumber = () => {
|
||||
return (
|
||||
<input
|
||||
type={type}
|
||||
type="text"
|
||||
className={`input-light ${className ? className : ''}`}
|
||||
ref={ref}
|
||||
id={id}
|
||||
readOnly={readOnly ? true : false}
|
||||
placeholder={placeholder}
|
||||
value={value === 0 ? 0 : value || ''}
|
||||
value={value}
|
||||
onChange={handleTextNumberChange}
|
||||
onKeyDown={type === 'number' ? checkInputNumber : undefined}
|
||||
onKeyUp={type === 'number' ? checkInputNumber : undefined}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
'use client'
|
||||
import { useRecoilState } from 'recoil'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { popupState } from '@/store/popupAtom'
|
||||
import { Fragment } from 'react'
|
||||
|
||||
export default function PopupManager() {
|
||||
const [popup, setPopup] = useRecoilState(popupState)
|
||||
const popup = useRecoilValue(popupState)
|
||||
|
||||
return [
|
||||
...popup?.config.map((child) => <Fragment key={child.id}>{child.component}</Fragment>),
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useState, useRef } from 'react'
|
||||
import { useEffect, useState, useContext } from 'react'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { floorPlanObjectState } from '@/store/floorPlanObjectAtom'
|
||||
import { sessionStore } from '@/store/commonAtom'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { useCanvasMenu } from '@/hooks/common/useCanvasMenu'
|
||||
import SingleDatePicker from '../common/datepicker/SingleDatePicker'
|
||||
@ -15,15 +14,15 @@ import dayjs from 'dayjs'
|
||||
import { useCommonCode } from '@/hooks/common/useCommonCode'
|
||||
import Select from 'react-select'
|
||||
import { useEstimateController } from '@/hooks/floorPlan/estimate/useEstimateController'
|
||||
import { SessionContext } from '@/app/SessionProvider'
|
||||
|
||||
export default function Estimate({ params }) {
|
||||
const { session } = useContext(SessionContext)
|
||||
const [objectNo, setObjectNo] = useState('') //물건번호
|
||||
const [planNo, setPlanNo] = useState('') //플랜번호
|
||||
const [files, setFiles] = useState([]) // 보내는 첨부파일
|
||||
|
||||
//체크박스
|
||||
const [checkItems, setCheckItems] = useState(new Set())
|
||||
const [checkedList, setCheckedList] = useState([])
|
||||
const [files, setFiles] = useState([]) // 보내는 첨부파일
|
||||
const [originFiles, setOriginFiles] = useState([]) //기존 첨부파일
|
||||
|
||||
const [showContentCode, setShowContentCode] = useState('ATTR001')
|
||||
|
||||
@ -40,7 +39,6 @@ export default function Estimate({ params }) {
|
||||
setStartDate,
|
||||
}
|
||||
|
||||
const sessionState = useRecoilValue(sessionStore)
|
||||
const objectRecoil = useRecoilValue(floorPlanObjectState)
|
||||
|
||||
//견적서 상세데이터
|
||||
@ -52,16 +50,14 @@ export default function Estimate({ params }) {
|
||||
const [specialNoteList, setSpecialNoteList] = useState([])
|
||||
|
||||
const globalLocaleState = useRecoilValue(globalLocaleStore)
|
||||
const { get, post } = useAxios(globalLocaleState)
|
||||
const { get, promisePost } = useAxios(globalLocaleState)
|
||||
|
||||
const { getMessage } = useMessage()
|
||||
|
||||
const { setMenuNumber } = useCanvasMenu()
|
||||
|
||||
//새로 추가한 첨부파일 props
|
||||
const fileUploadProps = {
|
||||
// objectNo: '',
|
||||
// planNo: params.pid,
|
||||
// category: '10',
|
||||
uploadFiles: files,
|
||||
setUploadFiles: setFiles,
|
||||
}
|
||||
@ -127,11 +123,42 @@ export default function Estimate({ params }) {
|
||||
event.stopPropagation()
|
||||
}
|
||||
|
||||
// 추가한 첨부파일 state에 넣기
|
||||
useEffect(() => {
|
||||
if (isNotEmptyArray(files)) {
|
||||
files.map((row) => {
|
||||
setState({ fileList: row.data })
|
||||
})
|
||||
} else {
|
||||
setState({ fileList: [] })
|
||||
}
|
||||
}, [files])
|
||||
|
||||
//상세에서 내려온 첨부파일 set 만들기
|
||||
useEffect(() => {
|
||||
if (isNotEmptyArray(state.fileList)) {
|
||||
setOriginFiles(state.fileList)
|
||||
}
|
||||
}, [state?.fileList])
|
||||
|
||||
// 기존첨부파일 삭제
|
||||
const deleteOriginFile = async (objectNo, no) => {
|
||||
const delParams = {
|
||||
userId: session.userId,
|
||||
objectNo: objectNo,
|
||||
no: no,
|
||||
}
|
||||
await promisePost({ url: 'api/file/fileDelete', data: delParams }).then((res) => {
|
||||
if (res.status === 204) {
|
||||
setOriginFiles(originFiles.filter((file) => file.objectNo === objectNo && file.no !== no))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="sub-content estimate">
|
||||
<div className="sub-content-inner">
|
||||
{/* 물건번호, 견적서번호, 등록일, 변경일시 시작 */}
|
||||
{/* <form onSubmit={handleSubmit(onValid)}> */}
|
||||
<div className="sub-content-box">
|
||||
<div className="sub-table-box">
|
||||
<div className="estimate-list-wrap one">
|
||||
@ -206,7 +233,6 @@ export default function Estimate({ params }) {
|
||||
defaultValue={state?.charger}
|
||||
onChange={(e) => {
|
||||
//담당자 charger
|
||||
// console.log('담당자:::::', e.target.value)
|
||||
setState({ charger: e.target.value })
|
||||
}}
|
||||
/>
|
||||
@ -227,7 +253,6 @@ export default function Estimate({ params }) {
|
||||
defaultValue={state?.objectName}
|
||||
onChange={(e) => {
|
||||
//안건명 objectName
|
||||
// console.log('안건명::::', e.target.value)
|
||||
setState({ objectName: e.target.value })
|
||||
}}
|
||||
/>
|
||||
@ -244,7 +269,6 @@ export default function Estimate({ params }) {
|
||||
if (isObjectNotEmpty(e)) {
|
||||
setState({ objectNameOmit: e.clCodeNm })
|
||||
} else {
|
||||
// console.log('XXX')
|
||||
setState({ objectNameOmit: '' })
|
||||
}
|
||||
}}
|
||||
@ -342,7 +366,6 @@ export default function Estimate({ params }) {
|
||||
defaultValue={state?.remarks}
|
||||
onChange={(e) => {
|
||||
//비고
|
||||
// console.log('비고:::::', e.target.value)
|
||||
setState({ remarks: e.target.value })
|
||||
}}
|
||||
/>
|
||||
@ -390,6 +413,23 @@ export default function Estimate({ params }) {
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>{getMessage('estimate.detail.header.fileList2')}</th>
|
||||
<td>
|
||||
<div className="drag-file-box">
|
||||
<ul className="file-list">
|
||||
{isNotEmptyArray(originFiles) &&
|
||||
originFiles.map((originFile) => {
|
||||
return (
|
||||
<li className="file-item">
|
||||
<span>
|
||||
{originFile.faileName}
|
||||
<button className="delete" onClick={() => deleteOriginFile(originFile.objectNo, originFile.no)}></button>
|
||||
</span>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@ -439,7 +479,7 @@ export default function Estimate({ params }) {
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
{/* 견적특이사항 선택한 내용?영역시작 */}
|
||||
{/* 견적특이사항 선택한 내용 영역시작 */}
|
||||
<div className="calculation-estimate">
|
||||
{specialNoteList.map((row) => {
|
||||
if (row.code === showContentCode) {
|
||||
@ -570,7 +610,6 @@ export default function Estimate({ params }) {
|
||||
</div>
|
||||
</div>
|
||||
{/* 기본정보끝 */}
|
||||
{/* </form> */}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -1,8 +1,15 @@
|
||||
import { fabric } from 'fabric'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { QLine } from '@/components/fabric/QLine'
|
||||
import { distanceBetweenPoints, findTopTwoIndexesByDistance, getDirectionByPoint, sortedPointLessEightPoint, sortedPoints } from '@/util/canvas-util'
|
||||
import { calculateAngle, drawRidgeRoof, drawHippedRoof, inPolygon, toGeoJSON } from '@/util/qpolygon-utils'
|
||||
import {
|
||||
distanceBetweenPoints,
|
||||
findTopTwoIndexesByDistance,
|
||||
getAllRelatedObjects,
|
||||
getDirectionByPoint,
|
||||
sortedPointLessEightPoint,
|
||||
sortedPoints,
|
||||
} from '@/util/canvas-util'
|
||||
import { calculateAngle, drawRidgeRoof, drawShedRoof, inPolygon, toGeoJSON } from '@/util/qpolygon-utils'
|
||||
import * as turf from '@turf/turf'
|
||||
import { LINE_TYPE } from '@/common/common'
|
||||
|
||||
@ -131,7 +138,8 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
||||
})
|
||||
|
||||
this.on('removed', () => {
|
||||
const children = this.canvas.getObjects().filter((obj) => obj.parentId === this.id)
|
||||
// const children = getAllRelatedObjects(this.id, this.canvas)
|
||||
const children = this.canvas.getObjects().filter((obj) => obj.parentId === this.id && obj.name === 'lengthText')
|
||||
children.forEach((child) => {
|
||||
this.canvas.remove(child)
|
||||
})
|
||||
@ -171,6 +179,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
||||
offset: 0,
|
||||
},
|
||||
parent: this,
|
||||
parentId: this.id,
|
||||
direction: getDirectionByPoint(point, nextPoint),
|
||||
idx: i + 1,
|
||||
})
|
||||
@ -189,7 +198,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
||||
const eavesType = [LINE_TYPE.WALLLINE.EAVES, LINE_TYPE.WALLLINE.HIPANDGABLE]
|
||||
const gableType = [LINE_TYPE.WALLLINE.GABLE, LINE_TYPE.WALLLINE.JERKINHEAD]
|
||||
|
||||
const isEaves = types.every((type) => eavesType.includes(type))
|
||||
// const isEaves = types.every((type) => eavesType.includes(type))
|
||||
const gableOdd = types.filter((type, i) => i % 2 === 0)
|
||||
const gableEven = types.filter((type, i) => i % 2 === 1)
|
||||
const hasShed = types.includes(LINE_TYPE.WALLLINE.SHED)
|
||||
@ -201,18 +210,40 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
||||
) {
|
||||
console.log('박공 지붕')
|
||||
} else if (hasShed) {
|
||||
//편류지붕
|
||||
let shedIndex = 0
|
||||
types.forEach((type, i) => {
|
||||
if (type === LINE_TYPE.WALLLINE.SHED) {
|
||||
shedIndex = i
|
||||
}
|
||||
const sheds = this.lines.filter((line) => line.attributes !== undefined && line.attributes.type === LINE_TYPE.WALLLINE.SHED)
|
||||
const areLinesParallel = function (line1, line2) {
|
||||
const angle1 = calculateAngle(line1.startPoint, line1.endPoint)
|
||||
const angle2 = calculateAngle(line2.startPoint, line2.endPoint)
|
||||
return angle1 === angle2
|
||||
}
|
||||
|
||||
let isShedRoof = true
|
||||
sheds.forEach((shed, i) => {
|
||||
isShedRoof = areLinesParallel(shed, sheds[(i + 1) % sheds.length])
|
||||
})
|
||||
const shedOdd = types.filter((type, i) => i % 2 === shedIndex % 2).filter((type) => type !== LINE_TYPE.WALLLINE.SHED)
|
||||
const shedEven = types.filter((type, i) => i % 2 !== shedIndex % 2)
|
||||
types.forEach((type, i) => console.log(type, i, i % 2, shedIndex % 2, i % 2 === shedIndex % 2))
|
||||
if (shedOdd.every((type) => type === LINE_TYPE.WALLLINE.EAVES) && shedEven.every((type) => type === LINE_TYPE.WALLLINE.GABLE)) {
|
||||
console.log('편류지붕')
|
||||
if (isShedRoof) {
|
||||
const eaves = this.lines
|
||||
.filter((line) => line.attributes !== undefined && line.attributes.type === LINE_TYPE.WALLLINE.EAVES)
|
||||
.filter((line) => {
|
||||
const angle1 = calculateAngle(sheds[0].startPoint, sheds[0].endPoint)
|
||||
const angle2 = calculateAngle(line.startPoint, line.endPoint)
|
||||
if (Math.abs(angle1 - angle2) === 180) {
|
||||
return line
|
||||
}
|
||||
})
|
||||
if (eaves.length > 0) {
|
||||
const gables = this.lines.filter((line) => sheds.includes(line) === false && eaves.includes(line) === false)
|
||||
const isGable = gables.every((line) => gableType.includes(line.attributes.type))
|
||||
if (isGable) {
|
||||
drawShedRoof(this.id, this.canvas)
|
||||
} else {
|
||||
drawRidgeRoof(this.id, this.canvas)
|
||||
}
|
||||
} else {
|
||||
drawRidgeRoof(this.id, this.canvas)
|
||||
}
|
||||
} else {
|
||||
drawRidgeRoof(this.id, this.canvas)
|
||||
}
|
||||
} else {
|
||||
drawRidgeRoof(this.id, this.canvas)
|
||||
|
||||
@ -19,7 +19,7 @@ export default function CanvasFrame() {
|
||||
const { canvas } = useCanvas('canvas')
|
||||
const { canvasLoadInit, gridInit } = useCanvasConfigInitialize()
|
||||
const currentMenu = useRecoilValue(currentMenuState)
|
||||
const { contextMenu, handleClick, handleKeyup } = useContextMenu()
|
||||
const { contextMenu, handleClick } = useContextMenu()
|
||||
const { selectedPlan, modifiedPlanFlag, checkCanvasObjectEvent, resetModifiedPlans } = usePlan()
|
||||
useEvent()
|
||||
|
||||
@ -57,7 +57,7 @@ export default function CanvasFrame() {
|
||||
<div className="canvas-frame">
|
||||
<canvas ref={canvasRef} id="canvas" style={{ position: 'relative' }}></canvas>
|
||||
|
||||
<QContextMenu contextRef={canvasRef} canvasProps={canvas} handleKeyup={handleKeyup}>
|
||||
<QContextMenu contextRef={canvasRef} canvasProps={canvas}>
|
||||
{contextMenu?.map((menus, index) => (
|
||||
<ul key={index}>
|
||||
{menus.map((menu) => (
|
||||
|
||||
@ -240,7 +240,7 @@ export default function PlacementSurfaceSetting({ id, pos = { x: 50, y: 230 } })
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<WithDraggable isShow={true} pos={{ x: 50, y: 230 }}>
|
||||
<WithDraggable isShow={true} pos={pos}>
|
||||
<div className={`modal-pop-wrap lr-2`}>
|
||||
<div className="modal-head">
|
||||
<h1 className="title">{getMessage('plan.menu.placement.surface.arrangement')} </h1>
|
||||
|
||||
@ -11,12 +11,12 @@ import { useRecoilValue } from 'recoil'
|
||||
import { usePopup } from '@/hooks/usePopup'
|
||||
|
||||
export default function SettingModal01(props) {
|
||||
const { setShowDotLineGridModal, setShowFontSettingModal, id, isConfig } = props
|
||||
console.log(props)
|
||||
const { id } = props
|
||||
const [buttonAct, setButtonAct] = useState(1)
|
||||
const { getMessage } = useMessage()
|
||||
const canGridOptionSeletorValue = useRecoilValue(canGridOptionSeletor)
|
||||
const { addPopup, closePopup } = usePopup()
|
||||
const { closePopup } = usePopup()
|
||||
|
||||
const handleBtnClick = (num) => {
|
||||
setButtonAct(num)
|
||||
}
|
||||
|
||||
@ -5,21 +5,19 @@ import { useRouter, usePathname } from 'next/navigation'
|
||||
import { useAxios } from '@/hooks/useAxios'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import StuffQGrid from './StuffQGrid'
|
||||
import { useRecoilValue, useRecoilState, useSetRecoilState } from 'recoil'
|
||||
import { useRecoilValue, useRecoilState, useSetRecoilState, useResetRecoilState } from 'recoil'
|
||||
import { stuffSearchState } from '@/store/stuffAtom'
|
||||
import { queryStringFormatter, isEmptyArray } from '@/util/common-utils'
|
||||
import dayjs from 'dayjs'
|
||||
import { isObjectNotEmpty } from '@/util/common-utils'
|
||||
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 { sessionStore } from '@/store/commonAtom'
|
||||
import { SessionContext } from '@/app/SessionProvider'
|
||||
|
||||
export default function Stuff() {
|
||||
const sessionState = useRecoilValue(sessionStore)
|
||||
const resetStuffRecoil = useResetRecoilState(stuffSearchState)
|
||||
const { session } = useContext(SessionContext)
|
||||
const setAppMessageState = useSetRecoilState(appMessageStore)
|
||||
const stuffSearchParams = useRecoilValue(stuffSearchState)
|
||||
@ -34,9 +32,6 @@ export default function Stuff() {
|
||||
const { get } = useAxios(globalLocaleState)
|
||||
const gridRef = useRef()
|
||||
|
||||
// const [selectedRowData, setSelectedRowData] = useState([])
|
||||
// const [selectedRowDataCount, setSelectedRowDataCount] = useState(0)
|
||||
|
||||
const router = useRouter()
|
||||
const pathname = usePathname()
|
||||
|
||||
@ -67,10 +62,6 @@ export default function Stuff() {
|
||||
field: 'lastEditDatetime',
|
||||
minWidth: 200,
|
||||
headerName: getMessage('stuff.gridHeader.lastEditDatetime'),
|
||||
// headerCheckboxSelection: true,
|
||||
// headerCheckboxSelectionCurrentPageOnly: true, //페이징시 현재 페이지만 체크되도록
|
||||
// checkboxSelection: true,
|
||||
// showDisabledCheckboxes: true,
|
||||
cellStyle: { justifyContent: 'center' },
|
||||
valueFormatter: function (params) {
|
||||
if (params.value) {
|
||||
@ -169,77 +160,69 @@ export default function Stuff() {
|
||||
|
||||
// 진입시 그리드 데이터 조회
|
||||
useEffect(() => {
|
||||
if (isObjectNotEmpty(session)) {
|
||||
if (stuffSearchParams?.code === 'S') {
|
||||
const params = {
|
||||
// saleStoreId: stuffSearchParams.schSelSaleStoreId,
|
||||
saleStoreId: session.storeId,
|
||||
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: (pageNo - 1) * pageSize + 1,
|
||||
endRow: pageNo * pageSize,
|
||||
schSelSaleStoreId: stuffSearchParams?.schOtherSelSaleStoreId
|
||||
? stuffSearchParams.schOtherSelSaleStoreId
|
||||
: stuffSearchParams.schSelSaleStoreId,
|
||||
schSortType: stuffSearchParams.schSortType,
|
||||
}
|
||||
async function fetchData() {
|
||||
const apiUrl = `/api/object/list?${queryStringFormatter(params)}`
|
||||
await get({
|
||||
url: apiUrl,
|
||||
}).then((res) => {
|
||||
if (!isEmptyArray(res)) {
|
||||
setGridProps({ ...gridProps, gridData: res, count: res[0].totCnt })
|
||||
setTotalCount(res[0].totCnt)
|
||||
}
|
||||
})
|
||||
}
|
||||
if (stuffSearchParams?.schSelSaleStoreId !== '') {
|
||||
fetchData()
|
||||
}
|
||||
} else if (stuffSearchParams?.code === 'M') {
|
||||
//메인화면에서 진입
|
||||
const params = {
|
||||
saleStoreId: session?.storeId,
|
||||
schObjectNo: stuffSearchParams.schObjectNo,
|
||||
schAddress: '',
|
||||
schObjectName: '',
|
||||
schSaleStoreName: '',
|
||||
schReceiveUser: '',
|
||||
schDispCompanyName: '',
|
||||
schDateType: 'U',
|
||||
schFromDt: dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD'),
|
||||
schToDt: dayjs(new Date()).format('YYYY-MM-DD'),
|
||||
startRow: (pageNo - 1) * pageSize + 1,
|
||||
endRow: pageNo * pageSize,
|
||||
schSelSaleStoreId: stuffSearchParams?.schOtherSelSaleStoreId
|
||||
? stuffSearchParams.schOtherSelSaleStoreId
|
||||
: stuffSearchParams.schSelSaleStoreId,
|
||||
schSortType: 'R',
|
||||
}
|
||||
setStuffSearch({
|
||||
...params,
|
||||
if (stuffSearchParams?.code === 'S') {
|
||||
const params = {
|
||||
saleStoreId: session.storeId,
|
||||
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: (pageNo - 1) * pageSize + 1,
|
||||
endRow: pageNo * pageSize,
|
||||
schSelSaleStoreId: stuffSearchParams?.schOtherSelSaleStoreId ? stuffSearchParams.schOtherSelSaleStoreId : stuffSearchParams.schSelSaleStoreId,
|
||||
schSortType: stuffSearchParams.schSortType,
|
||||
}
|
||||
|
||||
async function fetchData() {
|
||||
const apiUrl = `/api/object/list?${queryStringFormatter(params)}`
|
||||
await get({
|
||||
url: apiUrl,
|
||||
}).then((res) => {
|
||||
if (!isEmptyArray(res)) {
|
||||
setGridProps({ ...gridProps, gridData: res, count: res[0].totCnt })
|
||||
setTotalCount(res[0].totCnt)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}, [pageNo, session, stuffSearchParams])
|
||||
|
||||
useEffect(() => {
|
||||
if (stuffSearchParams?.code === 'E') {
|
||||
if (stuffSearch.schSelSaleStoreId !== '') {
|
||||
fetchData()
|
||||
}
|
||||
} else if (stuffSearchParams?.code === 'M') {
|
||||
const params = {
|
||||
saleStoreId: session?.storeId,
|
||||
schObjectNo: stuffSearchParams.schObjectNo,
|
||||
schAddress: '',
|
||||
schObjectName: '',
|
||||
schSaleStoreName: '',
|
||||
schReceiveUser: '',
|
||||
schDispCompanyName: '',
|
||||
schDateType: 'U',
|
||||
schFromDt: dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD'),
|
||||
schToDt: dayjs(new Date()).format('YYYY-MM-DD'),
|
||||
startRow: (pageNo - 1) * pageSize + 1,
|
||||
endRow: pageNo * pageSize,
|
||||
schSelSaleStoreId: stuffSearchParams?.schOtherSelSaleStoreId ? stuffSearchParams.schOtherSelSaleStoreId : stuffSearchParams.schSelSaleStoreId,
|
||||
schSortType: 'R',
|
||||
}
|
||||
setStuffSearch({
|
||||
...params,
|
||||
})
|
||||
} else if (stuffSearchParams?.code === 'E') {
|
||||
stuffSearchParams.startRow = 1
|
||||
stuffSearchParams.endRow = 1 * pageSize
|
||||
stuffSearchParams.schSortType = defaultSortType
|
||||
|
||||
setPageNo(1)
|
||||
setStuffSearch({
|
||||
...stuffSearch,
|
||||
code: 'FINISH',
|
||||
})
|
||||
|
||||
//조회를 눌렀을때
|
||||
async function fetchData() {
|
||||
const apiUrl = `/api/object/list?saleStoreId=${session?.storeId}&${queryStringFormatter(stuffSearchParams)}`
|
||||
await get({ url: apiUrl }).then((res) => {
|
||||
@ -252,7 +235,10 @@ export default function Stuff() {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fetchData()
|
||||
} else if (stuffSearchParams?.code === 'C') {
|
||||
resetStuffRecoil()
|
||||
}
|
||||
}, [stuffSearchParams])
|
||||
|
||||
|
||||
@ -11,7 +11,6 @@ import { isEmptyArray, isNotEmptyArray, isObjectNotEmpty } from '@/util/common-u
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { useForm } from 'react-hook-form'
|
||||
import { useRecoilValue, useSetRecoilState } from 'recoil'
|
||||
import { sessionStore } from '@/store/commonAtom'
|
||||
import { SessionContext } from '@/app/SessionProvider'
|
||||
import FindAddressPop from './popup/FindAddressPop'
|
||||
import PlanRequestPop from './popup/PlanRequestPop'
|
||||
@ -28,7 +27,6 @@ export default function StuffDetail() {
|
||||
const [selOptions, setSelOptions] = useState('') //선택한 1차점
|
||||
const [otherSelOptions, setOtherSelOptions] = useState('') //선택한 1차점외
|
||||
|
||||
const sessionState = useRecoilValue(sessionStore)
|
||||
const { session } = useContext(SessionContext)
|
||||
|
||||
const router = useRouter()
|
||||
@ -320,12 +318,11 @@ export default function StuffDetail() {
|
||||
let firstList
|
||||
let otherList
|
||||
let favList
|
||||
// if (sessionState?.storeId === 'T01') {
|
||||
if (session?.storeId === 'T01') {
|
||||
url = `/api/object/saleStore/${session?.storeId}/firstList?userId=${session?.userId}`
|
||||
} else {
|
||||
if (session.storeLvl === '1') {
|
||||
url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=1&userId=${sessionState?.userId}`
|
||||
url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=1&userId=${session?.userId}`
|
||||
} else {
|
||||
url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=1&userId=${session?.userId}`
|
||||
}
|
||||
@ -400,7 +397,6 @@ export default function StuffDetail() {
|
||||
}
|
||||
})
|
||||
}
|
||||
// }, [objectNo, sessionState])
|
||||
}, [objectNo, session])
|
||||
|
||||
useEffect(() => {
|
||||
@ -1279,9 +1275,7 @@ export default function StuffDetail() {
|
||||
|
||||
//1차점 or 2차점 안고르고 임시저장하면
|
||||
if (params.saleStoreId == '') {
|
||||
// params.saleStoreId = sessionState.storeId
|
||||
params.saleStoreId = session.storeId
|
||||
// params.saleStoreLevel = sessionState.storeLvl
|
||||
params.saleStoreLevel = session.storeLvl
|
||||
}
|
||||
|
||||
@ -1445,7 +1439,6 @@ export default function StuffDetail() {
|
||||
</th>
|
||||
<td>
|
||||
<div className="flx-box">
|
||||
{/* {sessionState?.storeId === 'T01' && ( */}
|
||||
{session?.storeId === 'T01' && (
|
||||
<>
|
||||
<div className="select-wrap mr5" style={{ width: '567px' }}>
|
||||
@ -1479,7 +1472,6 @@ export default function StuffDetail() {
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* {sessionState?.storeId !== 'T01' && sessionState?.storeLvl === '1' && ( */}
|
||||
{session?.storeId !== 'T01' && session?.storeLvl === '1' && (
|
||||
<>
|
||||
<div className="select-wrap mr5" style={{ width: '567px' }}>
|
||||
@ -1511,7 +1503,6 @@ export default function StuffDetail() {
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{/* {sessionState?.storeId !== 'T01' && sessionState?.storeLvl !== '1' && ( */}
|
||||
{session?.storeId !== 'T01' && session?.storeLvl !== '1' && (
|
||||
<>
|
||||
<div className="select-wrap mr5" style={{ width: '567px' }}>
|
||||
|
||||
@ -12,7 +12,6 @@ import { isEmptyArray } from '@/util/common-utils'
|
||||
import dayjs from 'dayjs'
|
||||
import Link from 'next/link'
|
||||
import SingleDatePicker from '../common/datepicker/SingleDatePicker'
|
||||
import { sessionStore } from '@/store/commonAtom'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { isObjectNotEmpty } from '@/util/common-utils'
|
||||
|
||||
@ -20,7 +19,6 @@ import { SessionContext } from '@/app/SessionProvider'
|
||||
|
||||
export default function StuffSearchCondition() {
|
||||
const { session } = useContext(SessionContext)
|
||||
const sessionState = useRecoilValue(sessionStore)
|
||||
const setAppMessageState = useSetRecoilState(appMessageStore)
|
||||
const globalLocaleState = useRecoilValue(globalLocaleStore)
|
||||
const { getMessage } = useMessage()
|
||||
@ -94,13 +92,13 @@ export default function StuffSearchCondition() {
|
||||
})
|
||||
} else {
|
||||
setStuffSearch({
|
||||
schObjectNo: objectNo ? objectNo : '',
|
||||
schSaleStoreName: saleStoreName ? saleStoreName : '',
|
||||
schAddress: address ? address : '',
|
||||
schObjectName: objectName ? objectName : '',
|
||||
schDispCompanyName: dispCompanyName ? dispCompanyName : '',
|
||||
schObjectNo: objectNo,
|
||||
schSaleStoreName: saleStoreName,
|
||||
schAddress: address,
|
||||
schObjectName: objectName,
|
||||
schDispCompanyName: dispCompanyName,
|
||||
schSelSaleStoreId: stuffSearch?.schOtherSelSaleStoreId ? stuffSearch.schOtherSelSaleStoreId : stuffSearch.schSelSaleStoreId,
|
||||
schReceiveUser: receiveUser ? receiveUser : '',
|
||||
schReceiveUser: receiveUser,
|
||||
schDateType: dateType,
|
||||
schFromDt: dayjs(startDate).format('YYYY-MM-DD'),
|
||||
schToDt: dayjs(endDate).format('YYYY-MM-DD'),
|
||||
@ -131,14 +129,12 @@ export default function StuffSearchCondition() {
|
||||
setDateType('U')
|
||||
setStartDate(dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD'))
|
||||
setEndDate(dayjs(new Date()).format('YYYY-MM-DD'))
|
||||
// if (sessionState?.storeId === 'T01') {
|
||||
if (session?.storeId === 'T01') {
|
||||
setSchSelSaleStoreId('')
|
||||
handleClear1() //판매대리점선택 자동완성 클리어
|
||||
resetStuffRecoil()
|
||||
setStuffSearch({
|
||||
...stuffSearch,
|
||||
code: 'C',
|
||||
schSelSaleStoreId: '',
|
||||
schOtherSelSaleStoreId: '',
|
||||
})
|
||||
@ -156,23 +152,17 @@ export default function StuffSearchCondition() {
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
// if (isObjectNotEmpty(sessionState)) {
|
||||
if (isObjectNotEmpty(session)) {
|
||||
// storeId가 T01 이거나 storeLvl이 1차점일때만 판매대리점 선택 활성화
|
||||
let url
|
||||
// if (sessionState?.storeId === 'T01') {
|
||||
if (session?.storeId === 'T01') {
|
||||
//T01일떄
|
||||
// url = `/api/object/saleStore/${sessionState?.storeId}/firstList?userId=${sessionState?.userId}`
|
||||
url = `/api/object/saleStore/${session?.storeId}/firstList?userId=${session?.userId}`
|
||||
} else {
|
||||
// if (sessionState.storeLvl === '1') {
|
||||
if (session.storeLvl === '1') {
|
||||
//T01아닌 1차점일때
|
||||
// url = `/api/object/saleStore/${sessionState?.storeId}/list?firstFlg=1&userId=${sessionState?.userId}`
|
||||
url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=1&userId=${sessionState?.userId}`
|
||||
url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=1&userId=${session?.userId}`
|
||||
} else {
|
||||
// url = `/api/object/saleStore/${sessionState?.storeId}/list?firstFlg=1&userId=${sessionState?.userId}`
|
||||
url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=1&userId=${session?.userId}`
|
||||
}
|
||||
}
|
||||
@ -187,7 +177,6 @@ export default function StuffSearchCondition() {
|
||||
let allList
|
||||
let favList
|
||||
let otherList
|
||||
// if (sessionState?.storeId === 'T01') {
|
||||
if (session?.storeId === 'T01') {
|
||||
allList = res
|
||||
allList.sort((a, b) => (a.saleStoreId !== 'T01') - (b.saleStoreId !== 'T01') || a.saleStoreId - b.saleStoreId)
|
||||
@ -195,17 +184,14 @@ export default function StuffSearchCondition() {
|
||||
setSchSelSaleStoreList(allList)
|
||||
setFavoriteStoreList(favList)
|
||||
setShowSaleStoreList(favList)
|
||||
// setSchSelSaleStoreId(sessionState?.storeId)
|
||||
setSchSelSaleStoreId(session?.storeId)
|
||||
setStuffSearch({
|
||||
...stuffSearch,
|
||||
code: 'S',
|
||||
// schSelSaleStoreId: sessionState?.storeId,
|
||||
schSelSaleStoreId: session?.storeId,
|
||||
})
|
||||
|
||||
//T01일때 2차 판매점 호출하기 디폴트로 1차점을 본인으로 셋팅해서 세션storeId사용
|
||||
// url = `/api/object/saleStore/${sessionState?.storeId}/list?firstFlg=0&userId=${sessionState?.userId}`
|
||||
url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=0&userId=${session?.userId}`
|
||||
|
||||
get({ url: url }).then((res) => {
|
||||
@ -222,7 +208,6 @@ export default function StuffSearchCondition() {
|
||||
}
|
||||
})
|
||||
} else {
|
||||
// if (sessionState?.storeLvl === '1') {
|
||||
if (session?.storeLvl === '1') {
|
||||
allList = res
|
||||
favList = res.filter((row) => row.priority !== 'B')
|
||||
@ -250,7 +235,6 @@ export default function StuffSearchCondition() {
|
||||
setOtherSaleStoreList(otherList)
|
||||
|
||||
//선택한 2차점 세션으로 자동셋팅
|
||||
// setOtherSaleStoreId(sessionState?.storeId)
|
||||
setOtherSaleStoreId(session?.storeId)
|
||||
setStuffSearch({
|
||||
...stuffSearch,
|
||||
@ -262,7 +246,6 @@ export default function StuffSearchCondition() {
|
||||
}
|
||||
})
|
||||
}
|
||||
// }, [sessionState])
|
||||
}, [session])
|
||||
|
||||
//초기화 눌렀을 때 1차판매점 자동완성도..
|
||||
@ -296,7 +279,6 @@ export default function StuffSearchCondition() {
|
||||
stuffSearch.schSelSaleStoreId = key.saleStoreId
|
||||
//T01아닌 1차점은 본인으로 디폴트셋팅이고 수정할수없어서 여기안옴
|
||||
//고른 1차점의 saleStoreId로 2차점 API호출하기
|
||||
// let url = `/api/object/saleStore/${key.saleStoreId}/list?firstFlg=0&userId=${sessionState?.userId}`
|
||||
let url = `/api/object/saleStore/${key.saleStoreId}/list?firstFlg=0&userId=${session?.userId}`
|
||||
let otherList
|
||||
get({ url: url }).then((res) => {
|
||||
@ -339,6 +321,12 @@ export default function StuffSearchCondition() {
|
||||
useEffect(() => {
|
||||
setStartDate(stuffSearch?.schFromDt ? stuffSearch.schFromDt : dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD'))
|
||||
setEndDate(stuffSearch?.schToDt ? stuffSearch.schToDt : dayjs(new Date()).format('YYYY-MM-DD'))
|
||||
setObjectNo(stuffSearch.schObjectNo ? stuffSearch.schObjectNo : objectNo)
|
||||
setSaleStoreName(stuffSearch.schSaleStoreName ? stuffSearch.schSaleStoreName : saleStoreName)
|
||||
setAddress(stuffSearch.schAddress ? stuffSearch.schAddress : address)
|
||||
setobjectName(stuffSearch.schObjectName ? stuffSearch.schObjectName : objectName)
|
||||
setDispCompanyName(stuffSearch.schDispCompanyName ? stuffSearch.schDispCompanyName : dispCompanyName)
|
||||
setReceiveUser(stuffSearch.schReceiveUser ? stuffSearch.schReceiveUser : receiveUser)
|
||||
}, [stuffSearch])
|
||||
|
||||
useEffect(() => {
|
||||
@ -401,7 +389,7 @@ export default function StuffSearchCondition() {
|
||||
ref={objectNoRef}
|
||||
className="input-light"
|
||||
defaultValue={stuffSearch?.schObjectNo ? stuffSearch.schObjectNo : objectNo}
|
||||
onChange={(e) => {
|
||||
onChange={() => {
|
||||
setObjectNo(objectNoRef.current.value)
|
||||
}}
|
||||
onKeyUp={handleByOnKeyUp}
|
||||
@ -416,7 +404,7 @@ export default function StuffSearchCondition() {
|
||||
ref={saleStoreNameRef}
|
||||
className="input-light"
|
||||
defaultValue={stuffSearch?.schSaleStoreName ? stuffSearch.schSaleStoreName : saleStoreName}
|
||||
onChange={(e) => {
|
||||
onChange={() => {
|
||||
setSaleStoreName(saleStoreNameRef.current.value)
|
||||
}}
|
||||
onKeyUp={handleByOnKeyUp}
|
||||
@ -431,7 +419,7 @@ export default function StuffSearchCondition() {
|
||||
ref={addressRef}
|
||||
className="input-light"
|
||||
defaultValue={stuffSearch?.schAddress ? stuffSearch.schAddress : address}
|
||||
onChange={(e) => {
|
||||
onChange={() => {
|
||||
setAddress(addressRef.current.value)
|
||||
}}
|
||||
onKeyUp={handleByOnKeyUp}
|
||||
@ -446,7 +434,7 @@ export default function StuffSearchCondition() {
|
||||
ref={dispCompanyNameRef}
|
||||
className="input-light"
|
||||
defaultValue={stuffSearch?.schDispCompanyName ? stuffSearch.schDispCompanyName : dispCompanyName}
|
||||
onChange={(e) => {
|
||||
onChange={() => {
|
||||
setDispCompanyName(dispCompanyNameRef.current.value)
|
||||
}}
|
||||
onKeyUp={handleByOnKeyUp}
|
||||
@ -463,7 +451,7 @@ export default function StuffSearchCondition() {
|
||||
ref={objectNameRef}
|
||||
className="input-light"
|
||||
defaultValue={stuffSearch?.schObjectName ? stuffSearch.schObjectName : objectName}
|
||||
onChange={(e) => {
|
||||
onChange={() => {
|
||||
setobjectName(objectNameRef.current.value)
|
||||
}}
|
||||
onKeyUp={handleByOnKeyUp}
|
||||
@ -478,7 +466,7 @@ export default function StuffSearchCondition() {
|
||||
className="input-light"
|
||||
ref={receiveUserRef}
|
||||
defaultValue={stuffSearch?.schReceiveUser ? stuffSearch.schReceiveUser : receiveUser}
|
||||
onChange={(e) => {
|
||||
onChange={() => {
|
||||
setReceiveUser(receiveUserRef.current.value)
|
||||
}}
|
||||
onKeyUp={handleByOnKeyUp}
|
||||
@ -620,7 +608,6 @@ export default function StuffSearchCondition() {
|
||||
value={'U'}
|
||||
onChange={(e) => {
|
||||
setDateType(e.target.value)
|
||||
//setStuffSearch({ ...stuffSearch, code: 'S', schDateType: e.target.value })
|
||||
}}
|
||||
/>
|
||||
<label htmlFor="radio_u">{getMessage('stuff.search.schDateTypeU')}</label>
|
||||
@ -634,7 +621,6 @@ export default function StuffSearchCondition() {
|
||||
value={'R'}
|
||||
onChange={(e) => {
|
||||
setDateType(e.target.value)
|
||||
//setStuffSearch({ ...stuffSearch, code: 'S', schDateType: e.target.value })
|
||||
}}
|
||||
/>
|
||||
<label htmlFor="radio_r">{getMessage('stuff.search.schDateTypeR')}</label>
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
import { useAxios } from '@/hooks/useAxios'
|
||||
import { useEffect, useReducer, useState } from 'react'
|
||||
import { useContext, useEffect, useReducer, useState } from 'react'
|
||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||
import { globalLocaleStore } from '@/store/localeAtom'
|
||||
import { estimateState, floorPlanObjectState } from '@/store/floorPlanObjectAtom'
|
||||
import { isObjectNotEmpty } from '@/util/common-utils'
|
||||
import { SessionContext } from '@/app/SessionProvider'
|
||||
|
||||
const reducer = (prevState, nextState) => {
|
||||
return { ...prevState, ...nextState }
|
||||
}
|
||||
@ -41,6 +43,7 @@ const defaultEstimateData = {
|
||||
unit: '',
|
||||
},
|
||||
],
|
||||
fileList: [],
|
||||
}
|
||||
|
||||
// Helper functions
|
||||
@ -51,14 +54,14 @@ const updateItemInList = (itemList, itemId, updates) => {
|
||||
}
|
||||
|
||||
export const useEstimateController = (planNo) => {
|
||||
const { session } = useContext(SessionContext)
|
||||
const globalLocaleState = useRecoilValue(globalLocaleStore)
|
||||
const objectRecoil = useRecoilValue(floorPlanObjectState)
|
||||
const [estimateData, setEstimateData] = useRecoilState(estimateState)
|
||||
|
||||
const { get, post } = useAxios(globalLocaleState)
|
||||
const { get, post, promisePost } = useAxios(globalLocaleState)
|
||||
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const { promisePost } = useAxios()
|
||||
const [state, setState] = useReducer(reducer, defaultEstimateData)
|
||||
|
||||
useEffect(() => {
|
||||
@ -123,12 +126,32 @@ export const useEstimateController = (planNo) => {
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setEstimateData({ ...state })
|
||||
setEstimateData({ ...state, userId: session.userId })
|
||||
//sapSalesStoreCd 추가예정 필수값
|
||||
// setEstimateData({ ...state, userId: session.userId, sapSalesStoreCd : session.sapSalesStoreCd })
|
||||
}, [state])
|
||||
|
||||
//견적서 저장
|
||||
const handleEstimateSubmit = async () => {
|
||||
console.log('::담긴 estimateData:::', estimateData)
|
||||
//1. 첨부파일 저장
|
||||
const formData = new FormData()
|
||||
formData.append('file', estimateData.fileList)
|
||||
formData.append('objectNo', estimateData.objectNo)
|
||||
formData.append('planNo', estimateData.planNo)
|
||||
formData.append('category', '10')
|
||||
formData.append('userId', estimateData.userId)
|
||||
for (const value of formData.values()) {
|
||||
console.log('formData::', value)
|
||||
}
|
||||
|
||||
await promisePost({ url: '/api/file/fileUpload', data: formData }).then((res) => {
|
||||
console.log('파일저장::::::::::', res)
|
||||
})
|
||||
|
||||
//2. 상세데이터 저장
|
||||
|
||||
console.log('상세저장시작!!')
|
||||
return
|
||||
try {
|
||||
const result = await promisePost({
|
||||
|
||||
@ -815,7 +815,7 @@ export function useAuxiliaryDrawing(id) {
|
||||
roofBase.lines.some((line) => isPointOnLine(line, { x: line.x2, y: line.y2 }))
|
||||
|
||||
if (inPolygon1 && inPolygon2) {
|
||||
line.attributes = { ...line.attributes, roofId: roofBase.id }
|
||||
line.attributes = { ...line.attributes, roofId: roofBase.id, actualSize: 0, planeSize: line.getLength() }
|
||||
return true
|
||||
}
|
||||
})
|
||||
|
||||
@ -2,7 +2,6 @@ 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 { useSwal } from '@/hooks/useSwal'
|
||||
import { usePolygon } from '@/hooks/usePolygon'
|
||||
import { roofDisplaySelector } from '@/store/settingAtom'
|
||||
@ -13,7 +12,7 @@ import { POLYGON_TYPE } from '@/common/common'
|
||||
export function useRoofAllocationSetting(id) {
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
const roofDisplay = useRecoilValue(roofDisplaySelector)
|
||||
const { drawDirectionArrow } = usePolygon()
|
||||
const { drawDirectionArrow, addLengthText, splitPolygonWithLines } = usePolygon()
|
||||
const { closePopup } = usePopup()
|
||||
|
||||
const { swalFire } = useSwal()
|
||||
|
||||
@ -170,6 +170,7 @@ export function useRoofShapeSetting(id) {
|
||||
//기존 wallLine 삭제
|
||||
|
||||
let outerLines
|
||||
let direction
|
||||
|
||||
switch (shapeNum) {
|
||||
case 1: {
|
||||
@ -196,6 +197,7 @@ export function useRoofShapeSetting(id) {
|
||||
// 서쪽
|
||||
initLineSetting()
|
||||
outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
|
||||
direction = 'west'
|
||||
|
||||
outerLines.forEach((line) => {
|
||||
setWestAndEastRoof(line)
|
||||
@ -240,6 +242,7 @@ export function useRoofShapeSetting(id) {
|
||||
case 6: {
|
||||
initLineSetting()
|
||||
outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
|
||||
direction = 'east'
|
||||
|
||||
outerLines.forEach((line) => {
|
||||
setWestAndEastRoof(line)
|
||||
@ -285,6 +288,7 @@ export function useRoofShapeSetting(id) {
|
||||
case 7: {
|
||||
initLineSetting()
|
||||
outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
|
||||
direction = 'south'
|
||||
|
||||
outerLines.forEach((line) => {
|
||||
setSouthAndNorthRoof(line)
|
||||
@ -329,6 +333,7 @@ export function useRoofShapeSetting(id) {
|
||||
case 8: {
|
||||
initLineSetting()
|
||||
outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
|
||||
direction = 'north'
|
||||
|
||||
outerLines.forEach((line) => {
|
||||
setSouthAndNorthRoof(line)
|
||||
@ -389,7 +394,7 @@ export function useRoofShapeSetting(id) {
|
||||
canvas.remove(obj)
|
||||
})
|
||||
|
||||
const polygon = addPolygonByLines(outerLines, { name: POLYGON_TYPE.WALL })
|
||||
const polygon = addPolygonByLines(outerLines, { name: POLYGON_TYPE.WALL, direction })
|
||||
polygon.lines = [...outerLines]
|
||||
|
||||
addPitchTextsByOuterLines()
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { canvasSizeState, canvasState, canvasZoomState, currentObjectState, fontFamilyState, fontSizeState } from '@/store/canvasAtom'
|
||||
import { canvasSizeState, canvasState, canvasZoomState, currentObjectState } from '@/store/canvasAtom'
|
||||
import { QPolygon } from '@/components/fabric/QPolygon'
|
||||
import { usePlan } from '@/hooks/usePlan'
|
||||
import { fontSelector } from '@/store/fontAtom'
|
||||
@ -12,8 +12,6 @@ export function useCanvasEvent() {
|
||||
const [canvasForEvent, setCanvasForEvent] = useState(null)
|
||||
const [currentObject, setCurrentObject] = useRecoilState(currentObjectState)
|
||||
const canvasSize = useRecoilValue(canvasSizeState)
|
||||
const fontSize = useRecoilValue(fontSizeState)
|
||||
const fontFamily = useRecoilValue(fontFamilyState)
|
||||
const [canvasZoom, setCanvasZoom] = useRecoilState(canvasZoomState)
|
||||
const lengthTextOption = useRecoilValue(fontSelector('lengthText'))
|
||||
const { modifiedPlanFlag, setModifiedPlanFlag } = usePlan()
|
||||
@ -209,13 +207,52 @@ export function useCanvasEvent() {
|
||||
created: (e) => {
|
||||
const target = e.selected[0]
|
||||
setCurrentObject(target)
|
||||
const { selected } = e
|
||||
|
||||
if (selected?.length > 0) {
|
||||
selected.forEach((obj) => {
|
||||
if (obj.type === 'QPolygon') {
|
||||
obj.set({ stroke: 'red' })
|
||||
}
|
||||
})
|
||||
canvas.renderAll()
|
||||
}
|
||||
},
|
||||
cleared: (e) => {
|
||||
setCurrentObject(null)
|
||||
const { deselected } = e
|
||||
|
||||
if (deselected?.length > 0) {
|
||||
deselected.forEach((obj) => {
|
||||
if (obj.type === 'QPolygon') {
|
||||
obj.set({ stroke: 'black' })
|
||||
}
|
||||
})
|
||||
}
|
||||
canvas.renderAll()
|
||||
},
|
||||
updated: (e) => {
|
||||
const target = e.selected[0]
|
||||
setCurrentObject(target)
|
||||
const { selected, deselected } = e
|
||||
|
||||
if (deselected?.length > 0) {
|
||||
deselected.forEach((obj) => {
|
||||
if (obj.type === 'QPolygon') {
|
||||
obj.set({ stroke: 'black' })
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (selected?.length > 0) {
|
||||
selected.forEach((obj) => {
|
||||
if (obj.type === 'QPolygon') {
|
||||
obj.set({ stroke: 'red' })
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
canvas.renderAll()
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@ -231,12 +231,12 @@ export function useContextMenu() {
|
||||
y: 180,
|
||||
})
|
||||
setCurrentContextMenu(menu)
|
||||
currentMenuSetting()
|
||||
setQContextMenu({ ...qContextMenu, visible: false })
|
||||
}
|
||||
|
||||
const handleKeyup = (e) => {
|
||||
let menu = null
|
||||
|
||||
for (let i = 0; i < contextMenu.length; i++) {
|
||||
const temp = contextMenu[i].filter((menu) => {
|
||||
return menu.shortcut?.includes(e.key)
|
||||
@ -259,7 +259,6 @@ export function useContextMenu() {
|
||||
useEffect(() => {
|
||||
console.log('currentObject', currentObject)
|
||||
if (currentObject?.name) {
|
||||
console.log(currentObject?.name)
|
||||
switch (currentObject.name) {
|
||||
case 'triangleDormer':
|
||||
case 'pentagonDormer':
|
||||
|
||||
@ -1,12 +1,11 @@
|
||||
import { useEffect, useRef } from 'react'
|
||||
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
|
||||
import { useRecoilValue, useSetRecoilState } from 'recoil'
|
||||
import { canvasState, canvasZoomState, currentMenuState, textModeState } from '@/store/canvasAtom'
|
||||
import { fabric } from 'fabric'
|
||||
import { calculateDistance, calculateIntersection, distanceBetweenPoints, findClosestPoint, polygonToTurfPolygon } from '@/util/canvas-util'
|
||||
import { calculateDistance, calculateIntersection, distanceBetweenPoints, findClosestPoint } from '@/util/canvas-util'
|
||||
import { useAdsorptionPoint } from '@/hooks/useAdsorptionPoint'
|
||||
import { useDotLineGrid } from '@/hooks/useDotLineGrid'
|
||||
import { useTempGrid } from '@/hooks/useTempGrid'
|
||||
import { gridDisplaySelector } from '@/store/settingAtom'
|
||||
|
||||
export function useEvent() {
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
@ -100,7 +99,13 @@ export function useEvent() {
|
||||
const distance = calculateDistance(pointer, closestLine)
|
||||
|
||||
if (distance < adsorptionRange) {
|
||||
arrivalPoint = closestLine.direction === 'vertical' ? { x: closestLine.x1, y: pointer.y } : { x: pointer.x, y: closestLine.y1 }
|
||||
arrivalPoint =
|
||||
closestLine.direction === 'vertical'
|
||||
? { x: closestLine.x1, y: pointer.y }
|
||||
: {
|
||||
x: pointer.x,
|
||||
y: closestLine.y1,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -261,6 +266,7 @@ export function useEvent() {
|
||||
addCanvasMouseEventListener,
|
||||
removeAllMouseEventListeners,
|
||||
removeAllDocumentEventListeners,
|
||||
removeDocumentEvent,
|
||||
removeMouseEvent,
|
||||
removeMouseLine,
|
||||
initEvent,
|
||||
|
||||
@ -1515,9 +1515,17 @@ export function useMode() {
|
||||
pitch: 4,
|
||||
sleeve: true,
|
||||
}
|
||||
/*if (index === 1 || index === 3) {
|
||||
/*if (index === 1) {
|
||||
line.attributes = {
|
||||
type: LINE_TYPE.WALLLINE.WALL,
|
||||
type: LINE_TYPE.WALLLINE.SHED,
|
||||
offset: 30, //출폭
|
||||
width: 30, //폭
|
||||
pitch: 4, //구배
|
||||
sleeve: true, //소매
|
||||
}
|
||||
} else if (index === 5 || index === 3) {
|
||||
line.attributes = {
|
||||
type: LINE_TYPE.WALLLINE.EAVES,
|
||||
offset: 50, //출폭
|
||||
width: 30, //폭
|
||||
pitch: 4, //구배
|
||||
@ -1525,8 +1533,8 @@ export function useMode() {
|
||||
}
|
||||
} else {
|
||||
line.attributes = {
|
||||
type: LINE_TYPE.WALLLINE.EAVES,
|
||||
offset: 40,
|
||||
type: LINE_TYPE.WALLLINE.GABLE,
|
||||
offset: 20,
|
||||
width: 50,
|
||||
pitch: 4,
|
||||
sleeve: true,
|
||||
@ -1746,12 +1754,20 @@ export function useMode() {
|
||||
return { x1: point.x, y1: point.y }
|
||||
}),
|
||||
)
|
||||
if (wall.direction) {
|
||||
roof.direction = wall.direction
|
||||
}
|
||||
roof.name = POLYGON_TYPE.ROOF
|
||||
roof.setWall(wall)
|
||||
|
||||
roof.lines.forEach((line, index) => {
|
||||
const lineLength = Math.sqrt(
|
||||
Math.pow(Math.round(Math.abs(line.x1 - line.x2) * 10), 2) + Math.pow(Math.round(Math.abs(line.y1 - line.y2) * 10), 2),
|
||||
)
|
||||
line.attributes = {
|
||||
roofId: roof.id,
|
||||
planeSize: lineLength,
|
||||
actualSize: lineLength,
|
||||
wallLine: wall.lines[index].id,
|
||||
type: wall.lines[index].attributes.type,
|
||||
offset: wall.lines[index].attributes.offset,
|
||||
@ -1770,9 +1786,6 @@ export function useMode() {
|
||||
line.attributes.currentRoof = roof.lines[index].id
|
||||
})
|
||||
|
||||
console.log('drawRoofPolygon roof : ', roof)
|
||||
console.log('drawRoofPolygon wall : ', wall)
|
||||
|
||||
setRoof(roof)
|
||||
setWall(wall)
|
||||
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
import { ANGLE_TYPE, canvasState, currentAngleTypeSelector, fontFamilyState, fontSizeState, pitchTextSelector } from '@/store/canvasAtom'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { fabric } from 'fabric'
|
||||
import { getDegreeByChon, getDirectionByPoint } from '@/util/canvas-util'
|
||||
import { getDegreeByChon, getDirectionByPoint, isPointOnLine } from '@/util/canvas-util'
|
||||
import { QPolygon } from '@/components/fabric/QPolygon'
|
||||
import { isSamePoint } from '@/util/qpolygon-utils'
|
||||
import { isSamePoint, removeDuplicatePolygons } from '@/util/qpolygon-utils'
|
||||
import { flowDisplaySelector } from '@/store/settingAtom'
|
||||
import { fontSelector } from '@/store/fontAtom'
|
||||
import { QLine } from '@/components/fabric/QLine'
|
||||
import { POLYGON_TYPE } from '@/common/common'
|
||||
|
||||
export const usePolygon = () => {
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
@ -25,6 +27,7 @@ export const usePolygon = () => {
|
||||
})
|
||||
|
||||
canvas?.add(polygon)
|
||||
addLengthText(polygon)
|
||||
|
||||
return polygon
|
||||
}
|
||||
@ -40,7 +43,64 @@ export const usePolygon = () => {
|
||||
}
|
||||
|
||||
const addLengthText = (polygon) => {
|
||||
const points = polygon.get('points')
|
||||
const lengthTexts = canvas.getObjects().filter((obj) => obj.name === 'lengthText' && obj.parentId === polygon.id)
|
||||
lengthTexts.forEach((text) => {
|
||||
canvas.remove(text)
|
||||
})
|
||||
const lines = polygon.lines
|
||||
|
||||
lines.forEach((line, i) => {
|
||||
const length = line.getLength()
|
||||
const { planeSize, actualSize } = line.attributes
|
||||
const scaleX = line.scaleX
|
||||
const scaleY = line.scaleY
|
||||
const x1 = line.left
|
||||
const y1 = line.top
|
||||
const x2 = line.left + line.width * scaleX
|
||||
const y2 = line.top + line.height * scaleY
|
||||
|
||||
let left, top
|
||||
|
||||
if (line.direction === 'left' || line.direction === 'right') {
|
||||
left = (x1 + x2) / 2
|
||||
top = (y1 + y2) / 2 + 10
|
||||
} else if (line.direction === 'top' || line.direction === 'bottom') {
|
||||
left = (x1 + x2) / 2 + 10
|
||||
top = (y1 + y2) / 2
|
||||
}
|
||||
|
||||
const minX = line.left
|
||||
const maxX = line.left + line.width
|
||||
const minY = line.top
|
||||
const maxY = line.top + line.length
|
||||
const degree = (Math.atan2(y2 - y1, x2 - x1) * 180) / Math.PI
|
||||
|
||||
const text = new fabric.Textbox(actualSize ? actualSize.toString() : planeSize ? planeSize.toString() : length.toString(), {
|
||||
left: left,
|
||||
top: top,
|
||||
fontSize: lengthTextFontOptions.fontSize.value,
|
||||
minX,
|
||||
maxX,
|
||||
minY,
|
||||
maxY,
|
||||
parentDirection: line.direction,
|
||||
parentDegree: degree,
|
||||
parentId: polygon.id,
|
||||
planeSize,
|
||||
actualSize,
|
||||
editable: false,
|
||||
selectable: true,
|
||||
lockRotation: true,
|
||||
lockScalingX: true,
|
||||
lockScalingY: true,
|
||||
parent: polygon,
|
||||
name: 'lengthText',
|
||||
})
|
||||
|
||||
canvas.add(text)
|
||||
})
|
||||
|
||||
/*const points = polygon.get('points')
|
||||
points.forEach((start, i) => {
|
||||
const end = points[(i + 1) % points.length]
|
||||
const dx = end.x - start.x
|
||||
@ -71,12 +131,12 @@ export const usePolygon = () => {
|
||||
lockScalingY: true,
|
||||
idx: i,
|
||||
name: 'lengthText',
|
||||
parent: this,
|
||||
parent: polygon,
|
||||
})
|
||||
|
||||
// this.texts.push(text)
|
||||
canvas.add(text)
|
||||
})
|
||||
})*/
|
||||
canvas.renderAll()
|
||||
}
|
||||
|
||||
@ -409,7 +469,8 @@ export const usePolygon = () => {
|
||||
|
||||
const addTextByArrows = (arrows, txt, canvas) => {
|
||||
arrows.forEach((arrow, index) => {
|
||||
const textStr = `${txt}${index + 1} (${currentAngleType === ANGLE_TYPE.SLOPE ? arrow.pitch : getDegreeByChon(arrow.pitch)}${pitchText})`
|
||||
// const textStr = `${txt}${index + 1} (${currentAngleType === ANGLE_TYPE.SLOPE ? arrow.pitch : getDegreeByChon(arrow.pitch)}${pitchText})`
|
||||
const textStr = `${txt} (${currentAngleType === ANGLE_TYPE.SLOPE ? arrow.pitch : getDegreeByChon(arrow.pitch)}${pitchText})`
|
||||
|
||||
const text = new fabric.Text(`${textStr}`, {
|
||||
fontSize: flowFontOptions.fontSize.value,
|
||||
@ -432,10 +493,272 @@ export const usePolygon = () => {
|
||||
})
|
||||
}
|
||||
|
||||
const splitPolygonWithLines = (polygon) => {
|
||||
polygon.set({ visible: false })
|
||||
let innerLines = [...polygon.innerLines]
|
||||
let polygonLines = [...polygon.lines]
|
||||
const roofs = []
|
||||
|
||||
let delIndexs = []
|
||||
let newLines = []
|
||||
|
||||
polygonLines.forEach((line, index) => {
|
||||
line.tempIndex = index
|
||||
innerLines.forEach((innerLine) => {
|
||||
let newLine1, newLine2
|
||||
if (isPointOnLine(line, innerLine.startPoint)) {
|
||||
// 해당 line을 startPoint로 나눈 line2개를 canvas에 추가 하고 기존 line을 제거한다.
|
||||
newLine1 = new QLine([line.x1, line.y1, innerLine.startPoint.x, innerLine.startPoint.y], {
|
||||
fontSize: polygon.fontSize,
|
||||
stroke: 'black',
|
||||
strokeWidth: 3,
|
||||
})
|
||||
|
||||
newLine2 = new QLine([innerLine.startPoint.x, innerLine.startPoint.y, line.x2, line.y2], {
|
||||
fontSize: polygon.fontSize,
|
||||
stroke: 'black',
|
||||
strokeWidth: 3,
|
||||
})
|
||||
delIndexs.push(polygonLines.indexOf(line))
|
||||
canvas.remove(polygonLines[polygonLines.indexOf(line)])
|
||||
if (newLine1.length / 10 > 10) {
|
||||
newLines.push(newLine1)
|
||||
}
|
||||
if (newLine2.length / 10 > 10) {
|
||||
newLines.push(newLine2)
|
||||
}
|
||||
}
|
||||
if (isPointOnLine(line, innerLine.endPoint)) {
|
||||
newLine1 = new QLine([line.x1, line.y1, innerLine.endPoint.x, innerLine.endPoint.y], {
|
||||
fontSize: polygon.fontSize,
|
||||
stroke: 'black',
|
||||
strokeWidth: 3,
|
||||
})
|
||||
|
||||
newLine2 = new QLine([innerLine.endPoint.x, innerLine.endPoint.y, line.x2, line.y2], {
|
||||
fontSize: polygon.fontSize,
|
||||
stroke: 'black',
|
||||
strokeWidth: 3,
|
||||
})
|
||||
delIndexs.push(polygonLines.indexOf(line))
|
||||
canvas.remove(polygonLines[polygonLines.indexOf(line)])
|
||||
if (newLine1.length / 10 > 10) {
|
||||
newLines.push(newLine1)
|
||||
}
|
||||
if (newLine2.length / 10 > 10) {
|
||||
newLines.push(newLine2)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
polygonLines = polygonLines.filter((line) => !delIndexs.includes(line.tempIndex))
|
||||
polygonLines = [...polygonLines, ...newLines]
|
||||
|
||||
const allLines = [...polygonLines, ...innerLines]
|
||||
|
||||
/**
|
||||
* 왼쪽 상단을 startPoint로 전부 변경
|
||||
*/
|
||||
allLines.forEach((line) => {
|
||||
let startPoint // 시작점
|
||||
let endPoint // 끝점
|
||||
if (line.x1 < line.x2) {
|
||||
startPoint = { x: line.x1, y: line.y1 }
|
||||
endPoint = { x: line.x2, y: line.y2 }
|
||||
} else if (line.x1 > line.x2) {
|
||||
startPoint = { x: line.x2, y: line.y2 }
|
||||
endPoint = { x: line.x1, y: line.y1 }
|
||||
} else {
|
||||
if (line.y1 < line.y2) {
|
||||
startPoint = { x: line.x1, y: line.y1 }
|
||||
endPoint = { x: line.x2, y: line.y2 }
|
||||
} else {
|
||||
startPoint = { x: line.x2, y: line.y2 }
|
||||
endPoint = { x: line.x1, y: line.y1 }
|
||||
}
|
||||
}
|
||||
|
||||
line.startPoint = startPoint
|
||||
line.endPoint = endPoint
|
||||
})
|
||||
|
||||
polygonLines.forEach((line) => {
|
||||
const startPoint = line.startPoint // 시작점
|
||||
let arrivalPoint = line.endPoint // 도착점
|
||||
|
||||
let currentPoint = startPoint
|
||||
const roofPoints = [startPoint]
|
||||
|
||||
const startLine = line
|
||||
const visitPoints = [startPoint]
|
||||
const visitLines = [startLine]
|
||||
let cnt = 0
|
||||
|
||||
while (!isSamePoint(currentPoint, arrivalPoint)) {
|
||||
let nextLines = allLines.filter(
|
||||
(line2) =>
|
||||
(isSamePoint(line2.startPoint, currentPoint) || isSamePoint(line2.endPoint, currentPoint)) &&
|
||||
line !== line2 &&
|
||||
innerLines.includes(line2) &&
|
||||
!visitLines.includes(line2),
|
||||
)
|
||||
|
||||
if (nextLines.length === 0) {
|
||||
nextLines = allLines.filter(
|
||||
(line2) =>
|
||||
(isSamePoint(line2.startPoint, currentPoint) || isSamePoint(line2.endPoint, currentPoint)) &&
|
||||
line !== line2 &&
|
||||
!visitLines.includes(line2),
|
||||
)
|
||||
}
|
||||
|
||||
if (!nextLines) {
|
||||
break
|
||||
}
|
||||
|
||||
let comparisonPoints = []
|
||||
|
||||
nextLines.forEach((nextLine) => {
|
||||
if (isSamePoint(nextLine.startPoint, currentPoint)) {
|
||||
comparisonPoints.push(nextLine.endPoint)
|
||||
} else {
|
||||
comparisonPoints.push(nextLine.startPoint)
|
||||
}
|
||||
})
|
||||
|
||||
comparisonPoints = comparisonPoints.filter((point) => !visitPoints.some((visitPoint) => isSamePoint(visitPoint, point)))
|
||||
comparisonPoints = comparisonPoints.filter((point) => !isSamePoint(point, currentPoint))
|
||||
|
||||
const minDistancePoint = comparisonPoints.reduce((prev, current) => {
|
||||
const prevDistance = Math.sqrt(Math.pow(prev.x - arrivalPoint.x, 2) + Math.pow(prev.y - arrivalPoint.y, 2))
|
||||
const currentDistance = Math.sqrt(Math.pow(current.x - arrivalPoint.x, 2) + Math.pow(current.y - arrivalPoint.y, 2))
|
||||
|
||||
return prevDistance < currentDistance ? prev : current
|
||||
}, comparisonPoints[0])
|
||||
|
||||
nextLines.forEach((nextLine) => {
|
||||
if (isSamePoint(nextLine.startPoint, minDistancePoint) || isSamePoint(nextLine.endPoint, minDistancePoint)) {
|
||||
visitLines.push(nextLine)
|
||||
}
|
||||
})
|
||||
|
||||
currentPoint = { ...minDistancePoint }
|
||||
roofPoints.push(currentPoint)
|
||||
cnt++
|
||||
if (cnt > 100) {
|
||||
throw new Error('무한루프')
|
||||
}
|
||||
}
|
||||
roofs.push(roofPoints)
|
||||
})
|
||||
|
||||
const newRoofs = removeDuplicatePolygons(roofs)
|
||||
|
||||
newRoofs.forEach((roofPoint, index) => {
|
||||
let defense, pitch
|
||||
const polygonLines = [...polygon.lines]
|
||||
|
||||
let representLines = []
|
||||
let representLine
|
||||
|
||||
// 지붕을 그리면서 기존 polygon의 line중 연결된 line을 찾는다.
|
||||
polygonLines.forEach((line) => {
|
||||
let startFlag = false
|
||||
let endFlag = false
|
||||
const startPoint = line.startPoint
|
||||
const endPoint = line.endPoint
|
||||
roofPoint.forEach((point, index) => {
|
||||
if (isSamePoint(point, startPoint)) {
|
||||
startFlag = true
|
||||
}
|
||||
if (isSamePoint(point, endPoint)) {
|
||||
endFlag = true
|
||||
}
|
||||
})
|
||||
|
||||
if (startFlag && endFlag) {
|
||||
if (!representLines.includes(line)) {
|
||||
representLines.push(line)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// representLines중 가장 긴 line을 찾는다.
|
||||
representLines.forEach((line) => {
|
||||
if (!representLine) {
|
||||
representLine = line
|
||||
} else {
|
||||
if (representLine.length < line.length) {
|
||||
representLine = line
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const direction = newRoofs.length === 1 ? polygon.direction : representLine.direction
|
||||
const polygonDirection = polygon.direction
|
||||
|
||||
switch (direction) {
|
||||
case 'top':
|
||||
defense = 'east'
|
||||
break
|
||||
case 'right':
|
||||
defense = 'south'
|
||||
break
|
||||
case 'bottom':
|
||||
defense = 'west'
|
||||
break
|
||||
case 'left':
|
||||
defense = 'north'
|
||||
break
|
||||
}
|
||||
pitch = polygon.lines[index].attributes?.pitch ?? 0
|
||||
|
||||
const roof = new QPolygon(roofPoint, {
|
||||
fontSize: polygon.fontSize,
|
||||
stroke: 'black',
|
||||
fill: 'transparent',
|
||||
strokeWidth: 3,
|
||||
name: POLYGON_TYPE.ROOF,
|
||||
originX: 'center',
|
||||
originY: 'center',
|
||||
selectable: true,
|
||||
defense: defense,
|
||||
direction: newRoofs.length === 1 ? polygonDirection : defense,
|
||||
pitch: pitch,
|
||||
})
|
||||
|
||||
//allLines중 생성된 roof와 관련있는 line을 찾는다.
|
||||
|
||||
roof.lines = [...polygon.lines, ...polygon.innerLines].filter((line) => {
|
||||
let startFlag = false
|
||||
let endFlag = false
|
||||
const startPoint = line.startPoint
|
||||
const endPoint = line.endPoint
|
||||
roofPoint.forEach((point, index) => {
|
||||
if (isSamePoint(point, startPoint)) {
|
||||
startFlag = true
|
||||
}
|
||||
if (isSamePoint(point, endPoint)) {
|
||||
endFlag = true
|
||||
}
|
||||
})
|
||||
|
||||
return startFlag && endFlag
|
||||
})
|
||||
|
||||
canvas.add(roof)
|
||||
addLengthText(roof)
|
||||
canvas.remove(polygon)
|
||||
canvas.renderAll()
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
addPolygon,
|
||||
addPolygonByLines,
|
||||
removePolygon,
|
||||
drawDirectionArrow,
|
||||
addLengthText,
|
||||
splitPolygonWithLines,
|
||||
}
|
||||
}
|
||||
|
||||
@ -925,3 +925,32 @@ export function checkLineOrientation(line) {
|
||||
return 'diagonal' // 대각선
|
||||
}
|
||||
}
|
||||
|
||||
// 최상위 parentId를 통해 모든 하위 객체를 찾는 함수
|
||||
export const getAllRelatedObjects = (id, canvas) => {
|
||||
const result = []
|
||||
const map = new Map()
|
||||
|
||||
// Create a map of objects by their id
|
||||
canvas.getObjects().forEach((obj) => {
|
||||
map.set(obj.id, obj)
|
||||
})
|
||||
|
||||
// Helper function to recursively find all related objects
|
||||
function findRelatedObjects(id) {
|
||||
const obj = map.get(id)
|
||||
if (obj) {
|
||||
result.push(obj)
|
||||
canvas.getObjects().forEach((o) => {
|
||||
if (o.parentId === id) {
|
||||
findRelatedObjects(o.id)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Start the search with the given parentId
|
||||
findRelatedObjects(id)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@ -1177,244 +1177,6 @@ export default function offsetPolygon(vertices, offset) {
|
||||
polygon.canvas.renderAll()
|
||||
})
|
||||
}*/
|
||||
export const splitPolygonWithLines = (polygon) => {
|
||||
const canvas = polygon.canvas
|
||||
polygon.set({ visible: false })
|
||||
let innerLines = [...polygon.innerLines]
|
||||
let polygonLines = [...polygon.lines]
|
||||
const roofs = []
|
||||
|
||||
let delIndexs = []
|
||||
let newLines = []
|
||||
|
||||
polygonLines.forEach((line, index) => {
|
||||
line.tempIndex = index
|
||||
innerLines.forEach((innerLine) => {
|
||||
let newLine1, newLine2
|
||||
if (isPointOnLine(line, innerLine.startPoint)) {
|
||||
// 해당 line을 startPoint로 나눈 line2개를 canvas에 추가 하고 기존 line을 제거한다.
|
||||
newLine1 = new QLine([line.x1, line.y1, innerLine.startPoint.x, innerLine.startPoint.y], {
|
||||
fontSize: polygon.fontSize,
|
||||
stroke: 'black',
|
||||
strokeWidth: 3,
|
||||
})
|
||||
|
||||
newLine2 = new QLine([innerLine.startPoint.x, innerLine.startPoint.y, line.x2, line.y2], {
|
||||
fontSize: polygon.fontSize,
|
||||
stroke: 'black',
|
||||
strokeWidth: 3,
|
||||
})
|
||||
delIndexs.push(polygonLines.indexOf(line))
|
||||
canvas.remove(polygonLines[polygonLines.indexOf(line)])
|
||||
if (newLine1.length / 10 > 10) {
|
||||
newLines.push(newLine1)
|
||||
}
|
||||
if (newLine2.length / 10 > 10) {
|
||||
newLines.push(newLine2)
|
||||
}
|
||||
}
|
||||
if (isPointOnLine(line, innerLine.endPoint)) {
|
||||
newLine1 = new QLine([line.x1, line.y1, innerLine.endPoint.x, innerLine.endPoint.y], {
|
||||
fontSize: polygon.fontSize,
|
||||
stroke: 'black',
|
||||
strokeWidth: 3,
|
||||
})
|
||||
|
||||
newLine2 = new QLine([innerLine.endPoint.x, innerLine.endPoint.y, line.x2, line.y2], {
|
||||
fontSize: polygon.fontSize,
|
||||
stroke: 'black',
|
||||
strokeWidth: 3,
|
||||
})
|
||||
delIndexs.push(polygonLines.indexOf(line))
|
||||
canvas.remove(polygonLines[polygonLines.indexOf(line)])
|
||||
if (newLine1.length / 10 > 10) {
|
||||
newLines.push(newLine1)
|
||||
}
|
||||
if (newLine2.length / 10 > 10) {
|
||||
newLines.push(newLine2)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
polygonLines = polygonLines.filter((line) => !delIndexs.includes(line.tempIndex))
|
||||
polygonLines = [...polygonLines, ...newLines]
|
||||
|
||||
const allLines = [...polygonLines, ...innerLines]
|
||||
|
||||
/**
|
||||
* 왼쪽 상단을 startPoint로 전부 변경
|
||||
*/
|
||||
allLines.forEach((line) => {
|
||||
let startPoint // 시작점
|
||||
let endPoint // 끝점
|
||||
if (line.x1 < line.x2) {
|
||||
startPoint = { x: line.x1, y: line.y1 }
|
||||
endPoint = { x: line.x2, y: line.y2 }
|
||||
} else if (line.x1 > line.x2) {
|
||||
startPoint = { x: line.x2, y: line.y2 }
|
||||
endPoint = { x: line.x1, y: line.y1 }
|
||||
} else {
|
||||
if (line.y1 < line.y2) {
|
||||
startPoint = { x: line.x1, y: line.y1 }
|
||||
endPoint = { x: line.x2, y: line.y2 }
|
||||
} else {
|
||||
startPoint = { x: line.x2, y: line.y2 }
|
||||
endPoint = { x: line.x1, y: line.y1 }
|
||||
}
|
||||
}
|
||||
|
||||
line.startPoint = startPoint
|
||||
line.endPoint = endPoint
|
||||
})
|
||||
|
||||
polygonLines.forEach((line) => {
|
||||
const startPoint = line.startPoint // 시작점
|
||||
let arrivalPoint = line.endPoint // 도착점
|
||||
|
||||
let currentPoint = startPoint
|
||||
const roofPoints = [startPoint]
|
||||
|
||||
const startLine = line
|
||||
const visitPoints = [startPoint]
|
||||
const visitLines = [startLine]
|
||||
let cnt = 0
|
||||
|
||||
while (!isSamePoint(currentPoint, arrivalPoint)) {
|
||||
line.set({ stroke: 'red' })
|
||||
canvas.renderAll()
|
||||
let nextLines = allLines.filter(
|
||||
(line2) =>
|
||||
(isSamePoint(line2.startPoint, currentPoint) || isSamePoint(line2.endPoint, currentPoint)) &&
|
||||
line !== line2 &&
|
||||
innerLines.includes(line2) &&
|
||||
!visitLines.includes(line2),
|
||||
)
|
||||
|
||||
if (nextLines.length === 0) {
|
||||
nextLines = allLines.filter(
|
||||
(line2) =>
|
||||
(isSamePoint(line2.startPoint, currentPoint) || isSamePoint(line2.endPoint, currentPoint)) &&
|
||||
line !== line2 &&
|
||||
!visitLines.includes(line2),
|
||||
)
|
||||
}
|
||||
|
||||
if (!nextLines) {
|
||||
break
|
||||
}
|
||||
|
||||
let comparisonPoints = []
|
||||
|
||||
nextLines.forEach((nextLine) => {
|
||||
if (isSamePoint(nextLine.startPoint, currentPoint)) {
|
||||
comparisonPoints.push(nextLine.endPoint)
|
||||
} else {
|
||||
comparisonPoints.push(nextLine.startPoint)
|
||||
}
|
||||
})
|
||||
|
||||
comparisonPoints = comparisonPoints.filter((point) => !visitPoints.some((visitPoint) => isSamePoint(visitPoint, point)))
|
||||
comparisonPoints = comparisonPoints.filter((point) => !isSamePoint(point, currentPoint))
|
||||
|
||||
const minDistancePoint = comparisonPoints.reduce((prev, current) => {
|
||||
const prevDistance = Math.sqrt(Math.pow(prev.x - arrivalPoint.x, 2) + Math.pow(prev.y - arrivalPoint.y, 2))
|
||||
const currentDistance = Math.sqrt(Math.pow(current.x - arrivalPoint.x, 2) + Math.pow(current.y - arrivalPoint.y, 2))
|
||||
|
||||
return prevDistance < currentDistance ? prev : current
|
||||
}, comparisonPoints[0])
|
||||
|
||||
nextLines.forEach((nextLine) => {
|
||||
if (isSamePoint(nextLine.startPoint, minDistancePoint) || isSamePoint(nextLine.endPoint, minDistancePoint)) {
|
||||
visitLines.push(nextLine)
|
||||
}
|
||||
})
|
||||
|
||||
currentPoint = { ...minDistancePoint }
|
||||
roofPoints.push(currentPoint)
|
||||
cnt++
|
||||
if (cnt > 100) {
|
||||
throw new Error('무한루프')
|
||||
}
|
||||
}
|
||||
roofs.push(roofPoints)
|
||||
})
|
||||
|
||||
const newRoofs = removeDuplicatePolygons(roofs)
|
||||
newRoofs.forEach((roofPoint, index) => {
|
||||
let defense, pitch
|
||||
const polygonLines = [...polygon.lines]
|
||||
|
||||
let representLines = []
|
||||
let representLine
|
||||
|
||||
// 지붕을 그리면서 기존 polygon의 line중 연결된 line을 찾는다.
|
||||
polygonLines.forEach((line) => {
|
||||
let startFlag = false
|
||||
let endFlag = false
|
||||
const startPoint = line.startPoint
|
||||
const endPoint = line.endPoint
|
||||
roofPoint.forEach((point, index) => {
|
||||
if (isSamePoint(point, startPoint)) {
|
||||
startFlag = true
|
||||
}
|
||||
if (isSamePoint(point, endPoint)) {
|
||||
endFlag = true
|
||||
}
|
||||
})
|
||||
|
||||
if (startFlag && endFlag) {
|
||||
representLines.push(line)
|
||||
}
|
||||
})
|
||||
|
||||
// representLines중 가장 긴 line을 찾는다.
|
||||
representLines.forEach((line) => {
|
||||
if (!representLine) {
|
||||
representLine = line
|
||||
} else {
|
||||
if (representLine.length < line.length) {
|
||||
representLine = line
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const direction = representLine.direction
|
||||
|
||||
switch (direction) {
|
||||
case 'top':
|
||||
defense = 'east'
|
||||
break
|
||||
case 'right':
|
||||
defense = 'south'
|
||||
break
|
||||
case 'bottom':
|
||||
defense = 'west'
|
||||
break
|
||||
case 'left':
|
||||
defense = 'north'
|
||||
break
|
||||
}
|
||||
pitch = polygon.lines[index].attributes?.pitch ?? 0
|
||||
|
||||
const roof = new QPolygon(roofPoint, {
|
||||
fontSize: polygon.fontSize,
|
||||
stroke: 'black',
|
||||
fill: 'transparent',
|
||||
strokeWidth: 3,
|
||||
name: POLYGON_TYPE.ROOF,
|
||||
originX: 'center',
|
||||
originY: 'center',
|
||||
selectable: true,
|
||||
defense: defense,
|
||||
direction: defense,
|
||||
pitch: pitch,
|
||||
})
|
||||
|
||||
polygon.canvas.add(roof)
|
||||
canvas.remove(polygon)
|
||||
polygon.canvas.renderAll()
|
||||
})
|
||||
}
|
||||
|
||||
function normalizePoint(point) {
|
||||
return {
|
||||
@ -1432,7 +1194,7 @@ function arePolygonsEqual(polygon1, polygon2) {
|
||||
return normalizedPolygon1.every((point, index) => arePointsEqual(point, normalizedPolygon2[index]))
|
||||
}
|
||||
|
||||
function removeDuplicatePolygons(polygons) {
|
||||
export function removeDuplicatePolygons(polygons) {
|
||||
const uniquePolygons = []
|
||||
|
||||
polygons.forEach((polygon) => {
|
||||
@ -1477,6 +1239,42 @@ function calculateAngleBetweenLines(line1, line2) {
|
||||
return (angleInRadians * 180) / Math.PI
|
||||
}
|
||||
|
||||
/**
|
||||
* 한쪽흐름 지붕
|
||||
* @param roofId
|
||||
* @param canvas
|
||||
*/
|
||||
export const drawShedRoof = (roofId, canvas) => {
|
||||
const roof = canvas?.getObjects().find((object) => object.id === roofId)
|
||||
const hasNonParallelLines = roof.lines.filter((line) => line.x1 !== line.x2 && line.y1 !== line.y2)
|
||||
if (hasNonParallelLines.length > 0) {
|
||||
alert('대각선이 존재합니다.')
|
||||
return
|
||||
}
|
||||
|
||||
const sheds = roof.lines.filter((line) => line.attributes !== undefined && line.attributes.type === LINE_TYPE.WALLLINE.SHED)
|
||||
const eaves = roof.lines.filter((line) => line.attributes !== undefined && line.attributes.type === LINE_TYPE.WALLLINE.EAVES)
|
||||
const gables = roof.lines.filter((line) => line.attributes !== undefined && line.attributes.type === LINE_TYPE.WALLLINE.GABLE)
|
||||
|
||||
console.log('gable', gables)
|
||||
|
||||
let shedDegree = sheds[0].attributes.degree || 0
|
||||
const shedChon = sheds[0].attributes.pitch || 0
|
||||
|
||||
if (shedDegree === 0) {
|
||||
shedDegree = getDegreeByChon(shedChon)
|
||||
}
|
||||
const getHeight = function (adjust, degree) {
|
||||
return Math.tan(degree * (Math.PI / 180)) * adjust
|
||||
}
|
||||
|
||||
gables.forEach((gable) => {
|
||||
const adjust = gable.attributes.planeSize
|
||||
const height = getHeight(adjust, shedDegree)
|
||||
gable.attributes.actualSize = Math.round(Math.sqrt(Math.pow(adjust, 2) + Math.pow(height, 2)))
|
||||
})
|
||||
}
|
||||
|
||||
export const drawRidgeRoof = (roofId, canvas) => {
|
||||
const roof = canvas?.getObjects().find((object) => object.id === roofId)
|
||||
const hasNonParallelLines = roof.lines.filter((line) => line.x1 !== line.x2 && line.y1 !== line.y2)
|
||||
@ -1769,6 +1567,9 @@ const drawRidge = (roof, canvas) => {
|
||||
attributes: { roofId: roof.id },
|
||||
},
|
||||
)
|
||||
ridge.attributes.planeSize = Math.round(Math.sqrt(Math.pow(ridge.x1 - ridge.x2, 2) + Math.pow(ridge.y1 - ridge.y2, 2)) * 10)
|
||||
ridge.attributes.actualSize = Math.round(Math.sqrt(Math.pow(ridge.x1 - ridge.x2, 2) + Math.pow(ridge.y1 - ridge.y2, 2)) * 10)
|
||||
|
||||
canvas.add(ridge)
|
||||
roof.ridges.push(ridge)
|
||||
roof.innerLines.push(ridge)
|
||||
@ -1809,6 +1610,8 @@ const drawRidge = (roof, canvas) => {
|
||||
roof.ridges = roof.ridges.filter((r) => !(ridge2.x1 === r.x1 && ridge2.y1 === r.y1 && ridge2.x2 === r.x2 && ridge2.y2 === r.y2))
|
||||
roof.innerLines = roof.innerLines.filter((r) => !(ridge.x1 === r.x1 && ridge.y1 === r.y1 && ridge.x2 === r.x2 && ridge.y2 === r.y2))
|
||||
roof.innerLines = roof.innerLines.filter((r) => !(ridge2.x1 === r.x1 && ridge2.y1 === r.y1 && ridge2.x2 === r.x2 && ridge2.y2 === r.y2))
|
||||
newRidge.attributes.planeSize = Math.round(Math.sqrt(Math.pow(ridge.x1 - ridge.x2, 2) + Math.pow(ridge.y1 - ridge.y2, 2)) * 10)
|
||||
newRidge.attributes.actualSize = Math.round(Math.sqrt(Math.pow(ridge.x1 - ridge.x2, 2) + Math.pow(ridge.y1 - ridge.y2, 2)) * 10)
|
||||
canvas.add(newRidge)
|
||||
roof.ridges.push(newRidge)
|
||||
roof.innerLines.push(newRidge)
|
||||
@ -1884,7 +1687,7 @@ const drawHips = (roof, canvas) => {
|
||||
stroke: 'red',
|
||||
strokeWidth: 1,
|
||||
name: LINE_TYPE.SUBLINE.HIP,
|
||||
attributes: { roofId: roof.id, currentRoof: currentRoof.id },
|
||||
attributes: { roofId: roof.id, currentRoof: currentRoof.id, actualSize: 0 },
|
||||
})
|
||||
canvas.add(hip1)
|
||||
const hip1Base = ((Math.abs(hip1.x1 - hip1.x2) + Math.abs(hip1.y1 - hip1.y2)) / 2) * 10
|
||||
@ -1901,12 +1704,7 @@ const drawHips = (roof, canvas) => {
|
||||
stroke: 'red',
|
||||
strokeWidth: 1,
|
||||
name: LINE_TYPE.SUBLINE.HIP,
|
||||
attributes: {
|
||||
roofId: roof.id,
|
||||
currentRoof: currentRoof.id,
|
||||
planeSize: currentRoof.length,
|
||||
actualSize: currentRoof.length,
|
||||
},
|
||||
attributes: { roofId: roof.id, currentRoof: currentRoof.id, actualSize: 0 },
|
||||
})
|
||||
canvas.add(hip2)
|
||||
const hip2Base = ((Math.abs(hip2.x1 - hip2.x2) + Math.abs(hip2.y1 - hip2.y2)) / 2) * 10
|
||||
@ -1977,12 +1775,7 @@ const drawHips = (roof, canvas) => {
|
||||
stroke: 'red',
|
||||
strokeWidth: 1,
|
||||
name: LINE_TYPE.SUBLINE.HIP,
|
||||
attributes: {
|
||||
roofId: roof.id,
|
||||
currentRoof: currentRoof.id,
|
||||
planeSize: currentRoof.length,
|
||||
actualSize: currentRoof.length,
|
||||
},
|
||||
attributes: { roofId: roof.id, currentRoof: currentRoof.id, actualSize: 0 },
|
||||
})
|
||||
canvas.add(hip)
|
||||
const hipBase = ((Math.abs(hip.x1 - hip.x2) + Math.abs(hip.y1 - hip.y2)) / 2) * 10
|
||||
@ -2040,92 +1833,6 @@ const checkValley = (polygon, line1, line2) => {
|
||||
return isValley
|
||||
}
|
||||
|
||||
const getPointInPolygon = (polygon, point, isInclude = false) => {
|
||||
let inside = false
|
||||
let minX = Math.min(polygon[0].x, polygon[1].x, polygon[2].x, polygon[3].x),
|
||||
maxX = Math.max(polygon[0].x, polygon[1].x, polygon[2].x, polygon[3].x),
|
||||
minY = Math.min(polygon[0].y, polygon[1].y, polygon[2].y, polygon[3].y),
|
||||
maxY = Math.max(polygon[0].y, polygon[1].y, polygon[2].y, polygon[3].y)
|
||||
if (!isInclude && minX < point.x && point.x < maxX && minY < point.y && point.y < maxY) {
|
||||
inside = true
|
||||
}
|
||||
if (isInclude && minX <= point.x && point.x <= maxX && minY <= point.y && point.y <= maxY) {
|
||||
inside = true
|
||||
}
|
||||
return inside
|
||||
}
|
||||
|
||||
/**
|
||||
* 라인과 마주하는 다른 라인과의 가장 가까운 거리를 구한다.
|
||||
* @param polygon
|
||||
* @param currentLine 현재 라인
|
||||
* @param dVector 현재 라인의 방향
|
||||
* @returns {*[]|null}
|
||||
*/
|
||||
const getAcrossLine = (polygon, currentLine, dVector) => {
|
||||
let acrossLine
|
||||
switch (dVector) {
|
||||
case 45:
|
||||
acrossLine = polygon.lines
|
||||
.filter((line) => line.x1 > currentLine.x1 && line.y1 <= currentLine.y1)
|
||||
.reduce((prev, current) => {
|
||||
if (prev.length > 0) {
|
||||
return Math.abs(currentLine.x1 - current.x1) < Math.abs(currentLine.x1 - prev.x1) ? current : prev
|
||||
} else {
|
||||
return current
|
||||
}
|
||||
}, [])
|
||||
break
|
||||
case 135:
|
||||
acrossLine = polygon.lines
|
||||
.filter((line) => line.x1 > currentLine.x1 && line.y1 >= currentLine.y1)
|
||||
.reduce((prev, current) => {
|
||||
if (prev.length > 0) {
|
||||
return Math.abs(currentLine.x1 - current.x1) < Math.abs(currentLine.x1 - prev.x1) ? current : prev
|
||||
} else {
|
||||
return current
|
||||
}
|
||||
}, [])
|
||||
break
|
||||
case 225:
|
||||
acrossLine = polygon.lines
|
||||
.filter((line) => line.x1 < currentLine.x1 && line.y1 >= currentLine.y1)
|
||||
.reduce((prev, current) => {
|
||||
if (prev.length > 0) {
|
||||
return Math.abs(currentLine.x1 - current.x1) < Math.abs(currentLine.x1 - prev.x1) ? current : prev
|
||||
} else {
|
||||
return current
|
||||
}
|
||||
}, [])
|
||||
break
|
||||
case 315:
|
||||
acrossLine = polygon.lines
|
||||
.filter((line) => line.x1 < currentLine.x1 && line.y1 <= currentLine.y1)
|
||||
.reduce((prev, current) => {
|
||||
if (prev.length > 0) {
|
||||
return Math.abs(currentLine.x1 - current.x1) < Math.abs(currentLine.x1 - prev.x1) ? current : prev
|
||||
} else {
|
||||
return current
|
||||
}
|
||||
}, [])
|
||||
break
|
||||
}
|
||||
return acrossLine
|
||||
}
|
||||
|
||||
/*
|
||||
추녀마루(hip) 중복방지를 위해 마루와 함께 그려진 추녀마루를 확인한다
|
||||
*/
|
||||
const isAlreadyHip = (polygon, line) => {
|
||||
let isAlreadyHip = false
|
||||
polygon.hips.forEach((hip) => {
|
||||
if (line.x1 === hip.x1 && line.y1 === hip.y1) {
|
||||
isAlreadyHip = true
|
||||
}
|
||||
})
|
||||
return isAlreadyHip
|
||||
}
|
||||
|
||||
/*
|
||||
3개 이상 이어지지 않은 라인 포인트 계산
|
||||
모임지붕에서 point는 3개 이상의 라인과 접해야 함.
|
||||
@ -2216,6 +1923,8 @@ const connectLinePoint = (polygon) => {
|
||||
stroke: 'purple',
|
||||
strokeWidth: 1,
|
||||
})
|
||||
line.attributes.planeSize = Math.round(Math.sqrt(Math.pow(line.x1 - line.x2, 2) + Math.pow(line.y1 - line.y2, 2)) * 10) / 10
|
||||
line.attributes.actualSize = Math.round(Math.sqrt(Math.pow(line.x1 - line.x2, 2) + Math.pow(line.y1 - line.y2, 2)) * 10) / 10
|
||||
polygon.canvas.add(line)
|
||||
polygon.innerLines.push(line)
|
||||
})
|
||||
@ -2272,6 +1981,8 @@ const connectLinePoint = (polygon) => {
|
||||
stroke: 'purple',
|
||||
strokeWidth: 1,
|
||||
})
|
||||
line.attributes.planeSize = Math.round(Math.sqrt(Math.pow(line.x1 - line.x2, 2) + Math.pow(line.y1 - line.y2, 2)) * 10)
|
||||
line.attributes.actualSize = Math.round(Math.sqrt(Math.pow(line.x1 - line.x2, 2) + Math.pow(line.y1 - line.y2, 2)) * 10)
|
||||
polygon.canvas.add(line)
|
||||
polygon.innerLines.push(line)
|
||||
})
|
||||
@ -2576,6 +2287,8 @@ const changeEavesRoof = (currentRoof, canvas) => {
|
||||
hipX2 = midX - addHipX2
|
||||
hipY2 = midY - addHipY2
|
||||
}
|
||||
ridge.attributes.planeSize = Math.round(Math.sqrt(Math.pow(ridge.x1 - ridge.x2, 2) + Math.pow(ridge.y1 - ridge.y2, 2)) * 10)
|
||||
ridge.attributes.actualSize = Math.round(Math.sqrt(Math.pow(ridge.x1 - ridge.x2, 2) + Math.pow(ridge.y1 - ridge.y2, 2)) * 10)
|
||||
}
|
||||
|
||||
hipLines.forEach((hip) => {
|
||||
@ -2753,6 +2466,8 @@ const changeGableRoof = (currentRoof, canvas) => {
|
||||
})
|
||||
currentRoof.attributes.ridgeCoordinate = { x1: ridge.x2, y1: ridge.y2 }
|
||||
}
|
||||
ridge.attributes.planeSize = Math.round(Math.sqrt(Math.pow(ridge.x1 - ridge.x2, 2) + Math.pow(ridge.y1 - ridge.y2, 2)) * 10)
|
||||
ridge.attributes.actualSize = Math.round(Math.sqrt(Math.pow(ridge.x1 - ridge.x2, 2) + Math.pow(ridge.y1 - ridge.y2, 2)) * 10)
|
||||
|
||||
let hip1 = new QLine([currentRoof.x1, currentRoof.y1, midX, midY], {
|
||||
fontSize: roof.fontSize,
|
||||
@ -2814,6 +2529,14 @@ const changeHipAndGableRoof = (currentRoof, canvas) => {
|
||||
if (wallLine.length > 0) {
|
||||
wallLine = wallLine[0]
|
||||
}
|
||||
let prevRoof, nextRoof
|
||||
roof.lines.forEach((r, index) => {
|
||||
if (r.id === currentRoof.id) {
|
||||
currentRoof = r
|
||||
prevRoof = roof.lines[index === 0 ? roof.lines.length - 1 : index - 1]
|
||||
nextRoof = roof.lines[index === roof.lines.length - 1 ? 0 : index + 1]
|
||||
}
|
||||
})
|
||||
|
||||
const midX = (currentRoof.x1 + currentRoof.x2) / 2 // 지붕의 X 중심
|
||||
const midY = (currentRoof.y1 + currentRoof.y2) / 2 // 지붕의 Y 중심
|
||||
@ -2927,6 +2650,8 @@ const changeHipAndGableRoof = (currentRoof, canvas) => {
|
||||
})
|
||||
currentRoof.attributes.ridgeCoordinate = { x1: ridge.x2, y1: ridge.y2 }
|
||||
}
|
||||
ridge.attributes.planeSize = Math.round(Math.sqrt(Math.pow(ridge.x1 - ridge.x2, 2) + Math.pow(ridge.y1 - ridge.y2, 2)) * 10)
|
||||
ridge.attributes.actualSize = Math.round(Math.sqrt(Math.pow(ridge.x1 - ridge.x2, 2) + Math.pow(ridge.y1 - ridge.y2, 2)) * 10)
|
||||
}
|
||||
|
||||
const hip1 = new QLine([currentRoof.x1, currentRoof.y1, midX + hipX2, midY + hipY2], {
|
||||
@ -2937,10 +2662,15 @@ const changeHipAndGableRoof = (currentRoof, canvas) => {
|
||||
attributes: {
|
||||
roofId: roof.id,
|
||||
currentRoof: currentRoof.id,
|
||||
planeSize: currentRoof.length,
|
||||
actualSize: currentRoof.length,
|
||||
},
|
||||
})
|
||||
const prevDegree = prevRoof.attributes.pitch > 0 ? getDegreeByChon(prevRoof.attributes.pitch) : prevRoof.attributes.degree
|
||||
const nextDegree = nextRoof.attributes.pitch > 0 ? getDegreeByChon(nextRoof.attributes.pitch) : nextRoof.attributes.degree
|
||||
|
||||
const hip1Base = ((Math.abs(hip1.x1 - hip1.x2) + Math.abs(hip1.y1 - hip1.y2)) / 2) * 10
|
||||
const hip1Height = Math.round(hip1Base / Math.tan(((90 - prevDegree) * Math.PI) / 180))
|
||||
hip1.attributes.planeSize = Math.round(Math.sqrt(Math.pow(hip1.x1 - hip1.x2, 2) + Math.pow(hip1.y1 - hip1.y2, 2))) * 10
|
||||
hip1.attributes.actualSize = Math.round(Math.sqrt(Math.pow(hip1.attributes.planeSize, 2) + Math.pow(hip1Height, 2)))
|
||||
canvas?.add(hip1)
|
||||
roof.innerLines.push(hip1)
|
||||
|
||||
@ -2956,6 +2686,10 @@ const changeHipAndGableRoof = (currentRoof, canvas) => {
|
||||
actualSize: currentRoof.length,
|
||||
},
|
||||
})
|
||||
const hip2Base = ((Math.abs(hip2.x1 - hip2.x2) + Math.abs(hip2.y1 - hip2.y2)) / 2) * 10
|
||||
const hip2Height = Math.round(hip2Base / Math.tan(((90 - nextDegree) * Math.PI) / 180))
|
||||
hip2.attributes.planeSize = Math.round(Math.sqrt(Math.pow(hip2.x1 - hip2.x2, 2) + Math.pow(hip2.y1 - hip2.y2, 2))) * 10
|
||||
hip2.attributes.actualSize = Math.round(Math.sqrt(Math.pow(hip2.attributes.planeSize, 2) + Math.pow(hip2Height, 2)))
|
||||
canvas?.add(hip2)
|
||||
roof.innerLines.push(hip2)
|
||||
|
||||
@ -2974,7 +2708,7 @@ const changeHipAndGableRoof = (currentRoof, canvas) => {
|
||||
})
|
||||
})
|
||||
|
||||
hipLines.forEach((hip) => {
|
||||
hipLines.forEach((hip, i) => {
|
||||
const gableLine = new QLine([hip.x2, hip.y2, currentRoof.attributes.ridgeCoordinate.x1, currentRoof.attributes.ridgeCoordinate.y1], {
|
||||
fontSize: roof.fontSize,
|
||||
stroke: 'red',
|
||||
@ -2983,10 +2717,15 @@ const changeHipAndGableRoof = (currentRoof, canvas) => {
|
||||
attributes: {
|
||||
roofId: roof.id,
|
||||
currentRoof: currentRoof.id,
|
||||
planeSize: currentRoof.length,
|
||||
actualSize: currentRoof.length,
|
||||
actualSize: 0,
|
||||
},
|
||||
})
|
||||
const gableDegree = i === 0 ? prevDegree : nextDegree
|
||||
const gableBase = ((Math.abs(gableLine.x1 - gableLine.x2) + Math.abs(gableLine.y1 - gableLine.y2)) / 2) * 10
|
||||
const gableHeight = Math.round(gableBase / Math.tan(((90 - gableDegree) * Math.PI) / 180))
|
||||
gableLine.attributes.planeSize =
|
||||
Math.round(Math.sqrt(Math.pow(gableLine.x1 - gableLine.x2, 2) + Math.pow(gableLine.y1 - gableLine.y2, 2))) * 10
|
||||
gableLine.attributes.actualSize = Math.round(Math.sqrt(Math.pow(gableLine.attributes.planeSize, 2) + Math.pow(gableHeight, 2)))
|
||||
canvas?.add(gableLine)
|
||||
roof.innerLines.push(gableLine)
|
||||
})
|
||||
@ -3016,6 +2755,18 @@ const changeJerkInHeadRoof = (currentRoof, canvas) => {
|
||||
wallLine = wallLine[0]
|
||||
}
|
||||
|
||||
let prevRoof, nextRoof
|
||||
roof.lines.forEach((r, index) => {
|
||||
if (r.id === currentRoof.id) {
|
||||
currentRoof = r
|
||||
prevRoof = roof.lines[index === 0 ? roof.lines.length - 1 : index - 1]
|
||||
nextRoof = roof.lines[index === roof.lines.length - 1 ? 0 : index + 1]
|
||||
}
|
||||
})
|
||||
|
||||
const prevDegree = prevRoof.attributes.pitch > 0 ? getDegreeByChon(prevRoof.attributes.pitch) : prevRoof.attributes.degree
|
||||
const nextDegree = nextRoof.attributes.pitch > 0 ? getDegreeByChon(nextRoof.attributes.pitch) : nextRoof.attributes.degree
|
||||
|
||||
const midX = (currentRoof.x1 + currentRoof.x2) / 2 // 지붕의 X 중심
|
||||
const midY = (currentRoof.y1 + currentRoof.y2) / 2 // 지붕의 Y 중심
|
||||
const midWallX = (wallLine.x1 + wallLine.x2) / 2 // 벽의 X 중심
|
||||
@ -3137,6 +2888,8 @@ const changeJerkInHeadRoof = (currentRoof, canvas) => {
|
||||
hipX2 = midX - xWidth
|
||||
hipY2 = midY - yWidth
|
||||
}
|
||||
ridge.attributes.planeSize = Math.round(Math.sqrt(Math.pow(ridge.x1 - ridge.x2, 2) + Math.pow(ridge.y1 - ridge.y2, 2)) * 10)
|
||||
ridge.attributes.actualSize = Math.round(Math.sqrt(Math.pow(ridge.x1 - ridge.x2, 2) + Math.pow(ridge.y1 - ridge.y2, 2)) * 10)
|
||||
}
|
||||
|
||||
let hipX1 = (Math.sign(currentRoof.x1 - midX) * currentRoof.attributes.width) / 2
|
||||
@ -3150,10 +2903,14 @@ const changeJerkInHeadRoof = (currentRoof, canvas) => {
|
||||
attributes: {
|
||||
roofId: roof.id,
|
||||
currentRoofId: currentRoof.id,
|
||||
planeSize: currentRoof.length,
|
||||
actualSize: currentRoof.length,
|
||||
actualSize: 0,
|
||||
},
|
||||
})
|
||||
const gableDegree = currentRoof.attributes.degree > 0 ? currentRoof.attributes.degree : getDegreeByChon(currentRoof.attributes.pitch)
|
||||
const gable1Base = ((Math.abs(gable1.x1 - gable1.x2) + Math.abs(gable1.y1 - gable1.y2)) / 2) * 10
|
||||
const gable1Height = Math.round(gable1Base / Math.tan(((90 - gableDegree) * Math.PI) / 180))
|
||||
gable1.attributes.planeSize = Math.round(Math.sqrt(Math.pow(gable1.x1 - gable1.x2, 2) + Math.pow(gable1.y1 - gable1.y2, 2))) * 10
|
||||
gable1.attributes.actualSize = Math.round(Math.sqrt(Math.pow(gable1.attributes.planeSize, 2) + Math.pow(gable1Height, 2)))
|
||||
canvas?.add(gable1)
|
||||
roof.innerLines.push(gable1)
|
||||
|
||||
@ -3168,10 +2925,13 @@ const changeJerkInHeadRoof = (currentRoof, canvas) => {
|
||||
attributes: {
|
||||
roofId: roof.id,
|
||||
currentRoofId: currentRoof.id,
|
||||
planeSize: currentRoof.length,
|
||||
actualSize: currentRoof.length,
|
||||
actualSize: 0,
|
||||
},
|
||||
})
|
||||
const gable2Base = ((Math.abs(gable2.x1 - gable2.x2) + Math.abs(gable2.y1 - gable2.y2)) / 2) * 10
|
||||
const gable2Height = Math.round(gable2Base / Math.tan(((90 - gableDegree) * Math.PI) / 180))
|
||||
gable2.attributes.planeSize = Math.round(Math.sqrt(Math.pow(gable2.x1 - gable2.x2, 2) + Math.pow(gable2.y1 - gable2.y2, 2))) * 10
|
||||
gable2.attributes.actualSize = Math.round(Math.sqrt(Math.pow(gable2.attributes.planeSize, 2) + Math.pow(gable2Height, 2)))
|
||||
canvas?.add(gable2)
|
||||
roof.innerLines.push(gable2)
|
||||
|
||||
@ -3183,10 +2943,11 @@ const changeJerkInHeadRoof = (currentRoof, canvas) => {
|
||||
attributes: {
|
||||
roofId: roof.id,
|
||||
currentRoofId: currentRoof.id,
|
||||
planeSize: currentRoof.length,
|
||||
actualSize: currentRoof.length,
|
||||
actualSize: 0,
|
||||
},
|
||||
})
|
||||
gable3.attributes.planeSize = Math.round(Math.sqrt(Math.pow(gable3.x1 - gable3.x2, 2) + Math.pow(gable3.y1 - gable3.y2, 2))) * 10
|
||||
gable3.attributes.actualSize = Math.round(Math.sqrt(Math.pow(gable3.x1 - gable3.x2, 2) + Math.pow(gable3.y1 - gable3.y2, 2))) * 10
|
||||
canvas?.add(gable3)
|
||||
roof.innerLines.push(gable3)
|
||||
|
||||
@ -3198,10 +2959,13 @@ const changeJerkInHeadRoof = (currentRoof, canvas) => {
|
||||
attributes: {
|
||||
roofId: roof.id,
|
||||
currentRoofId: currentRoof.id,
|
||||
planeSize: currentRoof.length,
|
||||
actualSize: currentRoof.length,
|
||||
actualSize: 0,
|
||||
},
|
||||
})
|
||||
const hip1Base = ((Math.abs(hip1.x1 - hip1.x2) + Math.abs(hip1.y1 - hip1.y2)) / 2) * 10
|
||||
const hip1Height = Math.round(hip1Base / Math.tan(((90 - prevDegree) * Math.PI) / 180))
|
||||
hip1.attributes.planeSize = Math.round(Math.sqrt(Math.pow(hip1.x1 - hip1.x2, 2) + Math.pow(hip1.y1 - hip1.y2, 2))) * 10
|
||||
hip1.attributes.actualSize = Math.round(Math.sqrt(Math.pow(hip1.attributes.planeSize, 2) + Math.pow(hip1Height, 2)))
|
||||
canvas?.add(hip1)
|
||||
roof.innerLines.push(hip1)
|
||||
|
||||
@ -3213,10 +2977,13 @@ const changeJerkInHeadRoof = (currentRoof, canvas) => {
|
||||
attributes: {
|
||||
roofId: roof.id,
|
||||
currentRoofId: currentRoof.id,
|
||||
planeSize: currentRoof.length,
|
||||
actualSize: currentRoof.length,
|
||||
actualSize: 0,
|
||||
},
|
||||
})
|
||||
const hip2Base = ((Math.abs(hip2.x1 - hip2.x2) + Math.abs(hip2.y1 - hip2.y2)) / 2) * 10
|
||||
const hip2Height = Math.round(hip2Base / Math.tan(((90 - nextDegree) * Math.PI) / 180))
|
||||
hip2.attributes.planeSize = Math.round(Math.sqrt(Math.pow(hip2.x1 - hip2.x2, 2) + Math.pow(hip2.y1 - hip2.y2, 2))) * 10
|
||||
hip2.attributes.actualSize = Math.round(Math.sqrt(Math.pow(hip2.attributes.planeSize, 2) + Math.pow(hip2Height, 2)))
|
||||
canvas?.add(hip2)
|
||||
roof.innerLines.push(hip2)
|
||||
}
|
||||
@ -3312,6 +3079,9 @@ const changeWallRoof = (currentRoof, canvas) => {
|
||||
canvas?.remove(line)
|
||||
})
|
||||
|
||||
const prevDegree = prevRoof.attributes.pitch > 0 ? getDegreeByChon(prevRoof.attributes.pitch) : prevRoof.attributes.degree
|
||||
const nextDegree = nextRoof.attributes.pitch > 0 ? getDegreeByChon(nextRoof.attributes.pitch) : nextRoof.attributes.degree
|
||||
|
||||
if (currentRoof.attributes.sleeve && currentRoof.attributes.width > 0 && prevRoof.attributes.offset > 0 && nextRoof.attributes.offset > 0) {
|
||||
const prevSignX = Math.sign(prevRoof.x1 - prevRoof.x2)
|
||||
const prevSignY = Math.sign(prevRoof.y1 - prevRoof.y2)
|
||||
@ -3417,6 +3187,8 @@ const changeWallRoof = (currentRoof, canvas) => {
|
||||
y2: ridge.y2 - diffY,
|
||||
})
|
||||
}
|
||||
ridge.attributes.planeSize = Math.round(Math.sqrt(Math.pow(ridge.x1 - ridge.x2, 2) + Math.pow(ridge.y1 - ridge.y2, 2)) * 10)
|
||||
ridge.attributes.actualSize = Math.round(Math.sqrt(Math.pow(ridge.x1 - ridge.x2, 2) + Math.pow(ridge.y1 - ridge.y2, 2)) * 10)
|
||||
|
||||
let hip1 = new QLine([currentRoof.x1, currentRoof.y1, wallMidX, wallMidY], {
|
||||
fontSize: roof.fontSize,
|
||||
@ -3426,10 +3198,14 @@ const changeWallRoof = (currentRoof, canvas) => {
|
||||
attributes: {
|
||||
roofId: roof.id,
|
||||
currentRoofId: currentRoof.id,
|
||||
planeSize: currentRoof.length,
|
||||
actualSize: currentRoof.length,
|
||||
actualSize: 0,
|
||||
},
|
||||
})
|
||||
const hip1Base = ((Math.abs(hip1.x1 - hip1.x2) + Math.abs(hip1.y1 - hip1.y2)) / 2) * 10
|
||||
const hip1Height = Math.round(hip1Base / Math.tan(((90 - prevDegree) * Math.PI) / 180))
|
||||
hip1.attributes.planeSize = Math.round(Math.sqrt(Math.pow(hip1.x1 - hip1.x2, 2) + Math.pow(hip1.y1 - hip1.y2, 2))) * 10
|
||||
hip1.attributes.actualSize = Math.round(Math.sqrt(Math.pow(hip1.attributes.planeSize, 2) + Math.pow(hip1Height, 2)))
|
||||
|
||||
let hip2 = new QLine([currentRoof.x2, currentRoof.y2, wallMidX, wallMidY], {
|
||||
fontSize: roof.fontSize,
|
||||
stroke: 'red',
|
||||
@ -3442,6 +3218,10 @@ const changeWallRoof = (currentRoof, canvas) => {
|
||||
actualSize: currentRoof.length,
|
||||
},
|
||||
})
|
||||
const hip2Base = ((Math.abs(hip2.x1 - hip2.x2) + Math.abs(hip2.y1 - hip2.y2)) / 2) * 10
|
||||
const hip2Height = Math.round(hip2Base / Math.tan(((90 - nextDegree) * Math.PI) / 180))
|
||||
hip2.attributes.planeSize = Math.round(Math.sqrt(Math.pow(hip2.x1 - hip2.x2, 2) + Math.pow(hip2.y1 - hip2.y2, 2))) * 10
|
||||
hip2.attributes.actualSize = Math.round(Math.sqrt(Math.pow(hip2.attributes.planeSize, 2) + Math.pow(hip2Height, 2)))
|
||||
canvas?.add(hip1)
|
||||
canvas?.add(hip2)
|
||||
roof.innerLines.push(hip1)
|
||||
@ -3505,8 +3285,13 @@ export const changeCurrentRoof = (currentRoof, canvas) => {
|
||||
newRoof.setWall(wall)
|
||||
|
||||
newRoof.lines.forEach((line, index) => {
|
||||
const lineLength = Math.sqrt(
|
||||
Math.pow(Math.round(Math.abs(line.x1 - line.x2) * 10), 2) + Math.pow(Math.round(Math.abs(line.y1 - line.y2) * 10), 2),
|
||||
)
|
||||
line.attributes = {
|
||||
roofId: newRoof.id,
|
||||
planeSize: lineLength,
|
||||
actualSize: lineLength,
|
||||
wallLine: wall.lines[index].id,
|
||||
type: wall.lines[index].attributes.type,
|
||||
offset: wall.lines[index].attributes.offset,
|
||||
@ -3561,6 +3346,19 @@ const reDrawPolygon = (polygon, canvas) => {
|
||||
line.attributes = l.attributes
|
||||
}
|
||||
})
|
||||
const lineLength = Math.sqrt(
|
||||
Math.pow(Math.round(Math.abs(line.x1 - line.x2) * 10), 2) + Math.pow(Math.round(Math.abs(line.y1 - line.y2) * 10), 2),
|
||||
)
|
||||
if (line.attributes !== undefined) {
|
||||
line.attributes.planeSize = lineLength
|
||||
line.attributes.actualSize = line
|
||||
} else {
|
||||
line.attributes = {
|
||||
roofId: newPolygon.id,
|
||||
planeSize: lineLength,
|
||||
actualSize: lineLength,
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
canvas?.add(newPolygon)
|
||||
|
||||
@ -1,12 +0,0 @@
|
||||
import { checkSession } from '@/lib/user'
|
||||
import { redirect } from 'next/navigation'
|
||||
|
||||
export const initCheck = async () => {
|
||||
const { session } = await checkSession()
|
||||
|
||||
if (!session.isLoggedIn) {
|
||||
redirect('/login')
|
||||
}
|
||||
|
||||
return session
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user