Merge branch 'dev'
This commit is contained in:
commit
f5c7c7cb1b
@ -12,6 +12,7 @@
|
|||||||
"@nextui-org/react": "^2.4.2",
|
"@nextui-org/react": "^2.4.2",
|
||||||
"ag-grid-react": "^32.0.2",
|
"ag-grid-react": "^32.0.2",
|
||||||
"axios": "^1.7.3",
|
"axios": "^1.7.3",
|
||||||
|
"chart.js": "^4.4.6",
|
||||||
"fabric": "^5.3.0",
|
"fabric": "^5.3.0",
|
||||||
"framer-motion": "^11.2.13",
|
"framer-motion": "^11.2.13",
|
||||||
"fs": "^0.0.1-security",
|
"fs": "^0.0.1-security",
|
||||||
@ -22,6 +23,7 @@
|
|||||||
"next": "14.2.3",
|
"next": "14.2.3",
|
||||||
"next-international": "^1.2.4",
|
"next-international": "^1.2.4",
|
||||||
"react": "^18",
|
"react": "^18",
|
||||||
|
"react-chartjs-2": "^5.2.0",
|
||||||
"react-colorful": "^5.6.1",
|
"react-colorful": "^5.6.1",
|
||||||
"react-datepicker": "^7.3.0",
|
"react-datepicker": "^7.3.0",
|
||||||
"react-dom": "^18",
|
"react-dom": "^18",
|
||||||
|
|||||||
@ -13,7 +13,6 @@ export async function GET(req) {
|
|||||||
const decodeUrl = decodeURIComponent(targetUrl)
|
const decodeUrl = decodeURIComponent(targetUrl)
|
||||||
|
|
||||||
const response = await fetch(decodeUrl)
|
const response = await fetch(decodeUrl)
|
||||||
|
|
||||||
const data = await response.arrayBuffer()
|
const data = await response.arrayBuffer()
|
||||||
const buffer = Buffer.from(data)
|
const buffer = Buffer.from(data)
|
||||||
|
|
||||||
|
|||||||
9
src/app/floor-plan/simulator/[mid]/page.jsx
Normal file
9
src/app/floor-plan/simulator/[mid]/page.jsx
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import Simulator from '@/components/simulator/Simulator'
|
||||||
|
|
||||||
|
export default function SimulatorPage() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Simulator />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -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,9 @@ export const SAVE_KEY = [
|
|||||||
'groupName',
|
'groupName',
|
||||||
'lineDirection',
|
'lineDirection',
|
||||||
'groupId',
|
'groupId',
|
||||||
|
'planeSize',
|
||||||
|
'actualSize',
|
||||||
|
'surfaceId',
|
||||||
]
|
]
|
||||||
|
|
||||||
export const OBJECT_PROTOTYPE = [fabric.Line.prototype, fabric.Polygon.prototype, fabric.Triangle.prototype]
|
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>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@ -431,7 +431,25 @@ export default function Roof2(props) {
|
|||||||
{ x: 450, y: 850 },
|
{ x: 450, y: 850 },
|
||||||
]
|
]
|
||||||
|
|
||||||
const polygon = new QPolygon(rectangleType2, {
|
const test1 = [
|
||||||
|
{ x: 381, y: 178 },
|
||||||
|
{ x: 381, y: 659.3 },
|
||||||
|
{ x: 773.3, y: 659.3 },
|
||||||
|
{ x: 773.3, y: 497.9 },
|
||||||
|
{ x: 1457, y: 497.9 },
|
||||||
|
{ x: 1457, y: 178 },
|
||||||
|
]
|
||||||
|
|
||||||
|
const test2 = [
|
||||||
|
{ x: 113, y: 114.9 },
|
||||||
|
{ x: 113, y: 371.9 },
|
||||||
|
{ x: 762, y: 371.9 },
|
||||||
|
{ x: 762, y: 818.7 },
|
||||||
|
{ x: 1468.6, y: 818.7 },
|
||||||
|
{ x: 1468.6, y: 114.9 },
|
||||||
|
]
|
||||||
|
|
||||||
|
const polygon = new QPolygon(type2, {
|
||||||
fill: 'transparent',
|
fill: 'transparent',
|
||||||
stroke: 'green',
|
stroke: 'green',
|
||||||
strokeWidth: 1,
|
strokeWidth: 1,
|
||||||
|
|||||||
@ -3,61 +3,73 @@
|
|||||||
import { useEffect, useState, useContext } from 'react'
|
import { useEffect, useState, useContext } from 'react'
|
||||||
import { useRecoilValue } from 'recoil'
|
import { useRecoilValue } from 'recoil'
|
||||||
import { floorPlanObjectState } from '@/store/floorPlanObjectAtom'
|
import { floorPlanObjectState } from '@/store/floorPlanObjectAtom'
|
||||||
import { SessionContext } from '@/app/SessionProvider'
|
|
||||||
import { useMessage } from '@/hooks/useMessage'
|
import { useMessage } from '@/hooks/useMessage'
|
||||||
import { useCanvasMenu } from '@/hooks/common/useCanvasMenu'
|
import { useCanvasMenu } from '@/hooks/common/useCanvasMenu'
|
||||||
import SingleDatePicker from '../common/datepicker/SingleDatePicker'
|
import SingleDatePicker from '../common/datepicker/SingleDatePicker'
|
||||||
import EstimateFileUploader from './EstimateFileUploader'
|
import EstimateFileUploader from './EstimateFileUploader'
|
||||||
import { useAxios } from '@/hooks/useAxios'
|
import { useAxios } from '@/hooks/useAxios'
|
||||||
import { globalLocaleStore } from '@/store/localeAtom'
|
import { globalLocaleStore } from '@/store/localeAtom'
|
||||||
import { isNotEmptyArray, isObjectNotEmpty } from '@/util/common-utils'
|
import { isNotEmptyArray, isObjectNotEmpty, queryStringFormatter } from '@/util/common-utils'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import { useCommonCode } from '@/hooks/common/useCommonCode'
|
import { useCommonCode } from '@/hooks/common/useCommonCode'
|
||||||
import Select from 'react-select'
|
|
||||||
import { useEstimateController } from '@/hooks/floorPlan/estimate/useEstimateController'
|
import { useEstimateController } from '@/hooks/floorPlan/estimate/useEstimateController'
|
||||||
|
import { SessionContext } from '@/app/SessionProvider'
|
||||||
|
import Select, { components } from 'react-select'
|
||||||
|
import { convertNumberToPriceDecimal } from '@/util/common-utils'
|
||||||
|
import ProductFeaturesPop from './popup/ProductFeaturesPop'
|
||||||
|
|
||||||
export default function Estimate({ params }) {
|
export default function Estimate({ params }) {
|
||||||
|
const { session } = useContext(SessionContext)
|
||||||
const [objectNo, setObjectNo] = useState('') //물건번호
|
const [objectNo, setObjectNo] = useState('') //물건번호
|
||||||
const [planNo, setPlanNo] = useState('') //플랜번호
|
const [planNo, setPlanNo] = useState('') //플랜번호
|
||||||
|
|
||||||
const [files, setFiles] = useState([]) // 보내는 첨부파일
|
const [files, setFiles] = useState([]) // 보내는 첨부파일
|
||||||
|
const [originFiles, setOriginFiles] = useState([]) //기존 첨부파일
|
||||||
|
|
||||||
const [showContentCode, setShowContentCode] = useState('ATTR001')
|
const [showContentCode, setShowContentCode] = useState('ATTR001')
|
||||||
|
|
||||||
|
const [productFeaturesPopupOpen, setProductFeaturesPopupOpen] = useState(false) //견적특이사항 팝업
|
||||||
|
const [showProductFeatureData, setShowProductFeatureData] = useState([]) //팝업에 보여줄 견적특이사항 데이터
|
||||||
|
|
||||||
//견적특이사항 접고 펼치기
|
//견적특이사항 접고 펼치기
|
||||||
const [hidden, setHidden] = useState(false)
|
const [hidden, setHidden] = useState(false)
|
||||||
|
|
||||||
|
//아이템 자동완성 리스트
|
||||||
|
const [displayItemList, setDisplayItemList] = useState([])
|
||||||
|
|
||||||
//공통코드
|
//공통코드
|
||||||
const { findCommonCode } = useCommonCode()
|
const { findCommonCode } = useCommonCode()
|
||||||
const [honorificCodeList, setHonorificCodeList] = useState([]) //경칭 공통코드
|
const [honorificCodeList, setHonorificCodeList] = useState([]) //경칭 공통코드
|
||||||
|
|
||||||
|
const [storePriceList, setStorePriceList] = useState([]) //가격표시 option
|
||||||
|
|
||||||
|
const [tempPriceCd, setTempPriceCd] = useState('')
|
||||||
|
|
||||||
const [startDate, setStartDate] = useState(new Date())
|
const [startDate, setStartDate] = useState(new Date())
|
||||||
const singleDatePickerProps = {
|
const singleDatePickerProps = {
|
||||||
startDate,
|
startDate,
|
||||||
setStartDate,
|
setStartDate,
|
||||||
}
|
}
|
||||||
|
|
||||||
const { session } = useContext(SessionContext)
|
|
||||||
const objectRecoil = useRecoilValue(floorPlanObjectState)
|
const objectRecoil = useRecoilValue(floorPlanObjectState)
|
||||||
|
|
||||||
//견적서 상세데이터
|
//견적서 상세데이터
|
||||||
const { state, setState } = useEstimateController(params.pid)
|
const { state, setState } = useEstimateController(params.pid)
|
||||||
|
|
||||||
//견적특이사항 상세 데이터 LIST
|
const [itemList, setItemList] = useState([]) //기존 아이템 리스트
|
||||||
|
|
||||||
//견적특이사항 List
|
//견적특이사항 List
|
||||||
const [specialNoteList, setSpecialNoteList] = useState([])
|
const [specialNoteList, setSpecialNoteList] = useState([])
|
||||||
|
|
||||||
const globalLocaleState = useRecoilValue(globalLocaleStore)
|
const globalLocaleState = useRecoilValue(globalLocaleStore)
|
||||||
const { get, post } = useAxios(globalLocaleState)
|
const { get, promisePost } = useAxios(globalLocaleState)
|
||||||
|
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
|
|
||||||
const { setMenuNumber } = useCanvasMenu()
|
const { setMenuNumber } = useCanvasMenu()
|
||||||
|
|
||||||
|
//새로 추가한 첨부파일 props
|
||||||
const fileUploadProps = {
|
const fileUploadProps = {
|
||||||
// objectNo: '',
|
|
||||||
// planNo: params.pid,
|
|
||||||
// category: '10',
|
|
||||||
uploadFiles: files,
|
uploadFiles: files,
|
||||||
setUploadFiles: setFiles,
|
setUploadFiles: setFiles,
|
||||||
}
|
}
|
||||||
@ -72,6 +84,17 @@ export default function Estimate({ params }) {
|
|||||||
if (code1 != null) {
|
if (code1 != null) {
|
||||||
setHonorificCodeList(code1)
|
setHonorificCodeList(code1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//아이템 자동완성 목록 가져오기
|
||||||
|
const param = {
|
||||||
|
saleStoreId: session.storeId,
|
||||||
|
}
|
||||||
|
const apiUrl = `/api/display-item/item-list?${queryStringFormatter(param)}`
|
||||||
|
get({ url: apiUrl }).then((res) => {
|
||||||
|
if (res.length > 0) {
|
||||||
|
setDisplayItemList(res)
|
||||||
|
}
|
||||||
|
})
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -123,24 +146,130 @@ export default function Estimate({ params }) {
|
|||||||
event.stopPropagation()
|
event.stopPropagation()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 첨부파일 state에 넣기
|
// 추가한 첨부파일 state에 넣기
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// console.log(files)
|
if (isNotEmptyArray(files)) {
|
||||||
if (files.length > 0) {
|
|
||||||
files.map((row) => {
|
files.map((row) => {
|
||||||
setState({ fileList: row.data })
|
setState({ fileList: row.data })
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
console.log('첨부파일 없음')
|
|
||||||
setState({ fileList: [] })
|
setState({ fileList: [] })
|
||||||
}
|
}
|
||||||
}, [files])
|
}, [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))
|
||||||
|
setState({
|
||||||
|
fileList: originFiles.filter((file) => file.objectNo === objectNo && file.no !== no),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
//아이템 목록
|
||||||
|
useEffect(() => {
|
||||||
|
if (isNotEmptyArray(state.itemList)) {
|
||||||
|
setItemList(state.itemList)
|
||||||
|
}
|
||||||
|
}, [state?.itemList])
|
||||||
|
|
||||||
|
//가격표시 option 최초세팅
|
||||||
|
useEffect(() => {
|
||||||
|
const param = {
|
||||||
|
saleStoreId: session.storeId,
|
||||||
|
sapSalesStoreCd: session.custCd,
|
||||||
|
docTpCd: state?.estimateType,
|
||||||
|
}
|
||||||
|
|
||||||
|
const apiUrl = `/api/estimate/price/store-price-list?${queryStringFormatter(param)}`
|
||||||
|
get({ url: apiUrl }).then((res) => {
|
||||||
|
if (isNotEmptyArray(res?.data)) {
|
||||||
|
setStorePriceList(res.data)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, [state?.estimateType])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (state.priceCd) {
|
||||||
|
setTempPriceCd(state.priceCd)
|
||||||
|
}
|
||||||
|
}, [state?.priceCd])
|
||||||
|
|
||||||
|
//가격표시 option 변경시
|
||||||
|
useEffect(() => {
|
||||||
|
if (tempPriceCd !== '') {
|
||||||
|
const param = {
|
||||||
|
saleStoreId: session.storeId,
|
||||||
|
sapSalesStoreCd: session.custCd,
|
||||||
|
docTpCd: tempPriceCd,
|
||||||
|
}
|
||||||
|
|
||||||
|
const apiUrl = `/api/estimate/price/store-price-list?${queryStringFormatter(param)}`
|
||||||
|
get({ url: apiUrl }).then((res) => {
|
||||||
|
if (isNotEmptyArray(res?.data)) {
|
||||||
|
setStorePriceList(res.data)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, [tempPriceCd])
|
||||||
|
|
||||||
|
//Pricing 버튼
|
||||||
|
const handlePricing = async (priceCd) => {
|
||||||
|
const param = {
|
||||||
|
saleStoreId: session.storeId,
|
||||||
|
sapSalesStoreCd: session.custCd,
|
||||||
|
docTpCd: state.estimateType,
|
||||||
|
priceCd: session.storeLvl === '1' ? tempPriceCd : priceCd,
|
||||||
|
itemIdList: state.itemList, //아이템 최초정보로 호출
|
||||||
|
}
|
||||||
|
|
||||||
|
// console.log('프라이싱파람::', param)
|
||||||
|
await promisePost({ url: '/api/estimate/price/item-price-list', data: param }).then((res) => {
|
||||||
|
if (res) {
|
||||||
|
if (res.status === 200) {
|
||||||
|
const data = res.data
|
||||||
|
if (data.result.code === 200) {
|
||||||
|
if (isNotEmptyArray(data.data2)) {
|
||||||
|
//아이템쪽 다 새로고침............
|
||||||
|
//성공후..
|
||||||
|
//기존itemList랑 프라이싱결과랑 비교해서 단가만 업뎃 서로 갯수가 안맞을 수 있음 없는 itemId면 unitPrice 0으로
|
||||||
|
//itemId로 비교해서 단가정보만 업데이트
|
||||||
|
setState({
|
||||||
|
priceCd: session.storeLvl === '1' ? tempPriceCd : priceCd,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 제품 추가 테스트
|
||||||
|
const addItemTest = () => {
|
||||||
|
const newItemDispOrder = (Math.max(...state.itemList.map((item) => item.dispOrder)) + 1) * 100
|
||||||
|
console.log('newItemDispOrder::', newItemDispOrder)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="sub-content estimate">
|
<div className="sub-content estimate">
|
||||||
<div className="sub-content-inner">
|
<div className="sub-content-inner">
|
||||||
{/* 물건번호, 견적서번호, 등록일, 변경일시 시작 */}
|
{/* 물건번호, 견적서번호, 등록일, 변경일시 시작 */}
|
||||||
{/* <form onSubmit={handleSubmit(onValid)}> */}
|
|
||||||
<div className="sub-content-box">
|
<div className="sub-content-box">
|
||||||
<div className="sub-table-box">
|
<div className="sub-table-box">
|
||||||
<div className="estimate-list-wrap one">
|
<div className="estimate-list-wrap one">
|
||||||
@ -188,7 +317,7 @@ export default function Estimate({ params }) {
|
|||||||
<tr>
|
<tr>
|
||||||
{/* 1차 판매점명 */}
|
{/* 1차 판매점명 */}
|
||||||
<th>{getMessage('estimate.detail.saleStoreId')}</th>
|
<th>{getMessage('estimate.detail.saleStoreId')}</th>
|
||||||
<td></td>
|
<td>{state?.firstSaleStoreName}</td>
|
||||||
{/* 견적일 */}
|
{/* 견적일 */}
|
||||||
<th>
|
<th>
|
||||||
{getMessage('estimate.detail.estimateDate')} <span className="important">*</span>
|
{getMessage('estimate.detail.estimateDate')} <span className="important">*</span>
|
||||||
@ -202,7 +331,7 @@ export default function Estimate({ params }) {
|
|||||||
<tr>
|
<tr>
|
||||||
{/* 2차 판매점명 */}
|
{/* 2차 판매점명 */}
|
||||||
<th>{getMessage('estimate.detail.otherSaleStoreId')}</th>
|
<th>{getMessage('estimate.detail.otherSaleStoreId')}</th>
|
||||||
<td></td>
|
<td>{state?.agencySaleStoreName}</td>
|
||||||
{/* 담당자 */}
|
{/* 담당자 */}
|
||||||
<th>
|
<th>
|
||||||
{getMessage('estimate.detail.receiveUser')} <span className="important">*</span>
|
{getMessage('estimate.detail.receiveUser')} <span className="important">*</span>
|
||||||
@ -286,6 +415,7 @@ export default function Estimate({ params }) {
|
|||||||
value={'YJSS'}
|
value={'YJSS'}
|
||||||
checked={state?.estimateType === 'YJSS' ? true : false}
|
checked={state?.estimateType === 'YJSS' ? true : false}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
|
//주문분류
|
||||||
setState({ estimateType: e.target.value })
|
setState({ estimateType: e.target.value })
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@ -362,9 +492,18 @@ export default function Estimate({ params }) {
|
|||||||
<div className="title-wrap">
|
<div className="title-wrap">
|
||||||
<h3>{getMessage('estimate.detail.header.fileList1')}</h3>
|
<h3>{getMessage('estimate.detail.header.fileList1')}</h3>
|
||||||
<div className="d-check-box light mr5">
|
<div className="d-check-box light mr5">
|
||||||
<input type="checkbox" id="next" />
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id="next"
|
||||||
|
checked={state?.fileFlg === '0' ? false : true}
|
||||||
|
onChange={(e) => {
|
||||||
|
setState({
|
||||||
|
fileFlg: e.target.checked ? '1' : '0',
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<label htmlFor="next" style={{ color: '#101010' }}>
|
<label htmlFor="next" style={{ color: '#101010' }}>
|
||||||
{getMessage('estimate.detail.nextSubmit')}
|
{getMessage('estimate.detail.fileFlg')}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -395,6 +534,23 @@ export default function Estimate({ params }) {
|
|||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{getMessage('estimate.detail.header.fileList2')}</th>
|
<th>{getMessage('estimate.detail.header.fileList2')}</th>
|
||||||
|
<td>
|
||||||
|
<div className="drag-file-box">
|
||||||
|
<ul className="file-list">
|
||||||
|
{originFiles.length > 0 &&
|
||||||
|
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>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
@ -417,47 +573,48 @@ export default function Estimate({ params }) {
|
|||||||
<div className="estimate-check-inner">
|
<div className="estimate-check-inner">
|
||||||
<div className="special-note-check-wrap">
|
<div className="special-note-check-wrap">
|
||||||
{/* SpecialNoteList반복문 */}
|
{/* SpecialNoteList반복문 */}
|
||||||
{specialNoteList.map((row) => {
|
{specialNoteList.length > 0 &&
|
||||||
return (
|
specialNoteList.map((row) => {
|
||||||
<div
|
return (
|
||||||
className="special-note-check-item"
|
<div
|
||||||
onClick={(event) => {
|
className="special-note-check-item"
|
||||||
settingShowContent(row.code, event)
|
onClick={(event) => {
|
||||||
}}
|
settingShowContent(row.code, event)
|
||||||
>
|
}}
|
||||||
<div className="d-check-box light">
|
>
|
||||||
<input
|
<div className="d-check-box light">
|
||||||
type="checkbox"
|
<input
|
||||||
id={row.code}
|
type="checkbox"
|
||||||
checked={!!row.text}
|
id={row.code}
|
||||||
disabled={row.code === 'ATTR001' ? true : false}
|
checked={!!row.text}
|
||||||
onChange={(event) => {
|
disabled={row.code === 'ATTR001' ? true : false}
|
||||||
setSpecialNoteList((specialNote) =>
|
onChange={(event) => {
|
||||||
specialNote.map((temp) => (temp.code === row.code ? { ...temp, text: !temp.text } : temp)),
|
setSpecialNoteList((specialNote) =>
|
||||||
)
|
specialNote.map((temp) => (temp.code === row.code ? { ...temp, text: !temp.text } : temp)),
|
||||||
settingShowContent(row.code, event)
|
)
|
||||||
}}
|
settingShowContent(row.code, event)
|
||||||
/>
|
}}
|
||||||
<label htmlFor={row.code}>{row.codeNm}</label>
|
/>
|
||||||
|
<label htmlFor={row.code}>{row.codeNm}</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)
|
||||||
)
|
})}
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
{/* 견적특이사항 선택한 내용?영역시작 */}
|
{/* 견적특이사항 선택한 내용 영역시작 */}
|
||||||
<div className="calculation-estimate">
|
<div className="calculation-estimate">
|
||||||
{specialNoteList.map((row) => {
|
{specialNoteList.map((row) => {
|
||||||
if (row.code === showContentCode) {
|
if (row.code === showContentCode) {
|
||||||
return (
|
return (
|
||||||
<dl>
|
<dl>
|
||||||
<dt>{row.codeNm}</dt>
|
<dt>{row.codeNm}</dt>
|
||||||
<dd>{row.remarks}</dd>
|
<dd dangerouslySetInnerHTML={{ __html: row.remarks }}></dd>
|
||||||
</dl>
|
</dl>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
{/* 견적특이사항 선택한 내용?영역끝 */}
|
{/* 견적특이사항 선택한 내용 영역끝 */}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -530,12 +687,29 @@ export default function Estimate({ params }) {
|
|||||||
<div className="product-price-wrap">
|
<div className="product-price-wrap">
|
||||||
<div className="product-price-tit">{getMessage('estimate.detail.header.showPrice')}</div>
|
<div className="product-price-tit">{getMessage('estimate.detail.header.showPrice')}</div>
|
||||||
<div className="select-wrap">
|
<div className="select-wrap">
|
||||||
<select className="select-light" name="" id="">
|
{session?.storeLvl === '1' ? (
|
||||||
<option value="">111</option>
|
<select
|
||||||
<option value="">222</option>
|
className="select-light"
|
||||||
</select>
|
onChange={(e) => {
|
||||||
|
setTempPriceCd(e.target.value)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{storePriceList.length > 0 && storePriceList.map((row) => <option value={row.priceCd}>{row.priceNm}</option>)}
|
||||||
|
</select>
|
||||||
|
) : (
|
||||||
|
<select className="select-light">
|
||||||
|
<option value="UNIT_PRICE">{getMessage('estimate.detail.header.unitPrice')}</option>
|
||||||
|
</select>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<button className="btn-origin grey ml5">{getMessage('estimate.detail.showPrice.btn1')}</button>
|
<button
|
||||||
|
className="btn-origin grey ml5"
|
||||||
|
onClick={() => {
|
||||||
|
handlePricing(state.priceCd)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{getMessage('estimate.detail.showPrice.pricingBtn')}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="product-edit-wrap">
|
<div className="product-edit-wrap">
|
||||||
<ul className="product-edit-explane">
|
<ul className="product-edit-explane">
|
||||||
@ -557,26 +731,140 @@ export default function Estimate({ params }) {
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div className="product-edit-btn">
|
<div className="product-edit-btn">
|
||||||
<button className="btn-origin navy mr5" type="submit">
|
<button className="btn-origin navy mr5" type="button" onClick={() => addItemTest()}>
|
||||||
<span className="plus"></span>
|
<span className="plus"></span>
|
||||||
{getMessage('estimate.detail.showPrice.btn2')}
|
{getMessage('estimate.detail.showPrice.addItem')}
|
||||||
</button>
|
</button>
|
||||||
<button className="btn-origin grey">
|
<button className="btn-origin grey">
|
||||||
<span className="minus"></span>
|
<span className="minus"></span>
|
||||||
{getMessage('estimate.detail.showPrice.btn3')}
|
{getMessage('estimate.detail.showPrice.delItem')}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/* 가격표시영역끝 */}
|
{/* 가격표시영역끝 */}
|
||||||
{/* html테이블시작 */}
|
{/* html테이블시작 */}
|
||||||
<div className="q-grid no-cols"></div>
|
<div className="esimate-table">
|
||||||
|
<table>
|
||||||
|
<colgroup>
|
||||||
|
<col width={50} />
|
||||||
|
<col width={100} />
|
||||||
|
<col />
|
||||||
|
<col width={200} />
|
||||||
|
<col width={100} />
|
||||||
|
<col width={100} />
|
||||||
|
<col width={200} />
|
||||||
|
<col width={240} />
|
||||||
|
</colgroup>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
<div className="d-check-box pop no-text" style={{ display: 'none' }}>
|
||||||
|
<input type="checkbox" id="ch97" />
|
||||||
|
<label htmlFor="ch97"></label>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
<th>{getMessage('estimate.detail.itemTableHeader.dispOrder')}</th>
|
||||||
|
<th>{getMessage('estimate.detail.itemTableHeader.itemId')}</th>
|
||||||
|
<th>{getMessage('estimate.detail.itemTableHeader.itemNo')}</th>
|
||||||
|
<th>{getMessage('estimate.detail.itemTableHeader.amount')}</th>
|
||||||
|
<th>{getMessage('estimate.detail.itemTableHeader.unit')}</th>
|
||||||
|
<th>{getMessage('estimate.detail.itemTableHeader.salePrice')}</th>
|
||||||
|
<th>{getMessage('estimate.detail.itemTableHeader.saleTotPrice')}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{itemList.length > 0 &&
|
||||||
|
itemList.map((item, index) => {
|
||||||
|
return (
|
||||||
|
<tr key={index}>
|
||||||
|
<td className="al-c">
|
||||||
|
<div className="d-check-box light no-text">
|
||||||
|
<input type="checkbox" id={item?.dispOrder * 100} />
|
||||||
|
<label htmlFor={item?.dispOrder * 100}></label>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td className="al-r">{item?.dispOrder * 100}</td>
|
||||||
|
<td>
|
||||||
|
<div className="form-flex-wrap">
|
||||||
|
<div className="select-wrap mr5">
|
||||||
|
<Select
|
||||||
|
id="long-value-select1"
|
||||||
|
instanceId="long-value-select1"
|
||||||
|
className="react-select-custom"
|
||||||
|
classNamePrefix="custom"
|
||||||
|
placeholder="Select"
|
||||||
|
options={displayItemList}
|
||||||
|
getOptionLabel={(x) => x.itemName}
|
||||||
|
getOptionValue={(x) => x.itemId}
|
||||||
|
isClearable={true}
|
||||||
|
isDisabled={false}
|
||||||
|
value={displayItemList.filter(function (option) {
|
||||||
|
return option.itemId === item.itemId
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{item?.itemChangeFlg === '1' && (
|
||||||
|
<div className="btn-area">
|
||||||
|
<span className="tb_ico change_check"></span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div className="form-flex-wrap">
|
||||||
|
<div className="name">{item?.itemNo}</div>
|
||||||
|
<div className="icon-wrap">
|
||||||
|
{item?.fileUploadFlg === '1' && <span className="tb_ico file_check"></span>}
|
||||||
|
{item?.specialNoteCd && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="grid-tip"
|
||||||
|
onClick={() => {
|
||||||
|
setProductFeaturesPopupOpen(true)
|
||||||
|
setShowProductFeatureData(item?.specialNoteCd)
|
||||||
|
}}
|
||||||
|
></button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div className="input-wrap" style={{ width: '100%' }}>
|
||||||
|
<input type="text" className="input-light al-r" defaultValue={item?.amount} />
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>{item.unit}</td>
|
||||||
|
<td>
|
||||||
|
<div className="form-flex-wrap">
|
||||||
|
<div className="input-wrap mr5">
|
||||||
|
<input type="text" className="input-light al-r" defaultValue={convertNumberToPriceDecimal(item?.salePrice)} />
|
||||||
|
</div>
|
||||||
|
{/* <div className="btn-area">
|
||||||
|
<span className="tb_ico open_check">OPEN아이콘 처리</span>
|
||||||
|
</div> */}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td className="al-r">{convertNumberToPriceDecimal(item?.saleTotPrice)}</td>
|
||||||
|
</tr>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* html테이블끝 */}
|
{/* html테이블끝 */}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/* 기본정보끝 */}
|
{/* 기본정보끝 */}
|
||||||
{/* </form> */}
|
|
||||||
</div>
|
</div>
|
||||||
|
{productFeaturesPopupOpen && (
|
||||||
|
<ProductFeaturesPop
|
||||||
|
specialNoteList={specialNoteList}
|
||||||
|
showProductFeatureData={showProductFeatureData}
|
||||||
|
setProductFeaturesPopupOpen={setProductFeaturesPopupOpen}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,6 +27,7 @@ export default function EstimateFileUploader({ uploadFiles, setUploadFiles }) {
|
|||||||
// if (res.data > 0) setUploadFiles([...files, { name: e.target.files[0].name, id: uuidv4() }])
|
// if (res.data > 0) setUploadFiles([...files, { name: e.target.files[0].name, id: uuidv4() }])
|
||||||
// })
|
// })
|
||||||
setUploadFiles([...uploadFiles, { data: e.target.files[0], id: uuidv4() }])
|
setUploadFiles([...uploadFiles, { data: e.target.files[0], id: uuidv4() }])
|
||||||
|
e.target.value = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
const deleteFile = (id) => {
|
const deleteFile = (id) => {
|
||||||
|
|||||||
255
src/components/estimate/popup/DocDownOptionPop.jsx
Normal file
255
src/components/estimate/popup/DocDownOptionPop.jsx
Normal file
@ -0,0 +1,255 @@
|
|||||||
|
'use client'
|
||||||
|
import { useState } from 'react'
|
||||||
|
import { useMessage } from '@/hooks/useMessage'
|
||||||
|
import { useAxios } from '@/hooks/useAxios'
|
||||||
|
import { useRecoilValue } from 'recoil'
|
||||||
|
import { floorPlanObjectState } from '@/store/floorPlanObjectAtom'
|
||||||
|
|
||||||
|
export default function DocDownOptionPop({ planNo, setEstimatePopupOpen }) {
|
||||||
|
// console.log('플랜번호::::::::::::', planNo)
|
||||||
|
const { getMessage } = useMessage()
|
||||||
|
const { promiseGet } = useAxios()
|
||||||
|
|
||||||
|
//다운로드 파일 EXCEL
|
||||||
|
const [schUnitPriceFlg, setSchUnitPriceFlg] = useState('0')
|
||||||
|
|
||||||
|
//견적제출서 표시명
|
||||||
|
const [schDisplayFlg, setSchSchDisplayFlg] = useState('0')
|
||||||
|
//가대 중량표 포함
|
||||||
|
const [schWeightFlg, setSchWeightFlg] = useState('0')
|
||||||
|
//도면/시뮬레이션 파일 포함
|
||||||
|
const [schDrawingFlg, setSchDrawingFlg] = useState('0')
|
||||||
|
|
||||||
|
// recoil 물건번호
|
||||||
|
const objectRecoil = useRecoilValue(floorPlanObjectState)
|
||||||
|
|
||||||
|
//문서 다운로드
|
||||||
|
const handleFileDown = async () => {
|
||||||
|
// console.log('물건번호:::', objectRecoil.floorPlanObjectNo)
|
||||||
|
// console.log('planNo::', planNo)
|
||||||
|
// 고른 옵션값들
|
||||||
|
//0 : 견적가 Excel 1 : 정가용Excel 2: 견적가 PDF 3 :정가용PDF
|
||||||
|
// console.log(schUnitPriceFlg)
|
||||||
|
// console.log(schDisplayFlg)
|
||||||
|
// console.log(schWeightFlg)
|
||||||
|
// console.log(schDrawingFlg)
|
||||||
|
const url = '/api/estimate/excel-download'
|
||||||
|
const params = {}
|
||||||
|
const options = { responseType: 'blob' }
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="modal-popup">
|
||||||
|
<div className="modal-dialog middle">
|
||||||
|
<div className="modal-content">
|
||||||
|
<div className="modal-header">
|
||||||
|
<h1 className="title">{getMessage('estimate.detail.docPopup.title')}</h1>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="modal-close"
|
||||||
|
onClick={() => {
|
||||||
|
setEstimatePopupOpen(false)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{getMessage('estimate.detail.docPopup.close')}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="modal-body">
|
||||||
|
<div className="modal-body-inner">
|
||||||
|
<div className="explane">{getMessage('estimate.detail.docPopup.explane')}</div>
|
||||||
|
<div className="common-table">
|
||||||
|
<table>
|
||||||
|
<colgroup>
|
||||||
|
<col style={{ width: '260px' }} />
|
||||||
|
<col />
|
||||||
|
</colgroup>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
{getMessage('estimate.detail.docPopup.schUnitPriceFlg')}
|
||||||
|
<span className="red">*</span>
|
||||||
|
</th>
|
||||||
|
<td>
|
||||||
|
<div className="form-flex-wrap">
|
||||||
|
<div className="d-check-radio light mr10">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
id="schUnitPriceFlg0"
|
||||||
|
name="schUnitPriceFlg"
|
||||||
|
value={'0'}
|
||||||
|
checked={schUnitPriceFlg === '0'}
|
||||||
|
onChange={(e) => {
|
||||||
|
setSchUnitPriceFlg(e.target.value)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<label htmlFor="schUnitPriceFlg0">{getMessage('estimate.detail.docPopup.schUnitPriceFlg.schUnitPriceFlg0')}</label>
|
||||||
|
</div>
|
||||||
|
<div className="d-check-radio light mr10">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
id="schUnitPriceFlg1"
|
||||||
|
name="schUnitPriceFlg"
|
||||||
|
value={'1'}
|
||||||
|
checked={schUnitPriceFlg === '1'}
|
||||||
|
onChange={(e) => {
|
||||||
|
setSchUnitPriceFlg(e.target.value)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<label htmlFor="schUnitPriceFlg1">{getMessage('estimate.detail.docPopup.schUnitPriceFlg.schUnitPriceFlg1')}</label>
|
||||||
|
</div>
|
||||||
|
<div className="d-check-radio light mr10">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
id="schUnitPricePdfFlg0"
|
||||||
|
name="schUnitPriceFlg"
|
||||||
|
value={'2'}
|
||||||
|
checked={schUnitPriceFlg === '2'}
|
||||||
|
onChange={(e) => {
|
||||||
|
setSchUnitPriceFlg(e.target.value)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<label htmlFor="schUnitPricePdfFlg0">{getMessage('estimate.detail.docPopup.schUnitPriceFlg.schUnitPriceFlg2')}</label>
|
||||||
|
</div>
|
||||||
|
<div className="d-check-radio light ">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
id="schUnitPricePdfFlg1"
|
||||||
|
name="schUnitPriceFlg"
|
||||||
|
value={'3'}
|
||||||
|
checked={schUnitPriceFlg === '3'}
|
||||||
|
onChange={(e) => {
|
||||||
|
setSchUnitPriceFlg(e.target.value)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<label htmlFor="schUnitPricePdfFlg1">{getMessage('estimate.detail.docPopup.schUnitPriceFlg.schUnitPriceFlg3')}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
{getMessage('estimate.detail.docPopup.schDisplayFlg')} <span className="red">*</span>
|
||||||
|
</th>
|
||||||
|
<td>
|
||||||
|
<div className="form-flex-wrap">
|
||||||
|
<div className="d-check-radio light mr10">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="schDisplayFlg"
|
||||||
|
id="schDisplayFlg0"
|
||||||
|
value={'0'}
|
||||||
|
checked={schDisplayFlg === '0'}
|
||||||
|
onChange={(e) => {
|
||||||
|
setSchSchDisplayFlg(e.target.value)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<label htmlFor="schDisplayFlg0">{getMessage('estimate.detail.docPopup.schDisplayFlg.schDisplayFlg0')}</label>
|
||||||
|
</div>
|
||||||
|
<div className="d-check-radio light">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="schDisplayFlg"
|
||||||
|
id="schDisplayFlg1"
|
||||||
|
value={'1'}
|
||||||
|
checked={schDisplayFlg === '1'}
|
||||||
|
onChange={(e) => {
|
||||||
|
setSchSchDisplayFlg(e.target.value)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<label htmlFor="schDisplayFlg1">{getMessage('estimate.detail.docPopup.schDisplayFlg.schDisplayFlg1')}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
{getMessage('estimate.detail.docPopup.schWeightFlg')} <span className="red">*</span>
|
||||||
|
</th>
|
||||||
|
<td>
|
||||||
|
<div className="form-flex-wrap">
|
||||||
|
<div className="d-check-radio light mr10">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="schWeightFlg"
|
||||||
|
id="schWeightFlg0"
|
||||||
|
value={'0'}
|
||||||
|
checked={schWeightFlg === '0'}
|
||||||
|
onChange={(e) => {
|
||||||
|
setSchWeightFlg(e.target.value)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<label htmlFor="schWeightFlg0">{getMessage('estimate.detail.docPopup.schWeightFlg.schWeightFlg0')}</label>
|
||||||
|
</div>
|
||||||
|
<div className="d-check-radio light">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="schWeightFlg"
|
||||||
|
id="schWeightFlg1"
|
||||||
|
value={'1'}
|
||||||
|
checked={schWeightFlg === '1'}
|
||||||
|
onChange={(e) => {
|
||||||
|
setSchWeightFlg(e.target.value)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<label htmlFor="schWeightFlg1">{getMessage('estimate.detail.docPopup.schWeightFlg.schWeightFlg1')}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>{getMessage('estimate.detail.docPopup.schDrawingFlg')}</th>
|
||||||
|
<td>
|
||||||
|
<div className="form-flex-wrap">
|
||||||
|
<div className="d-check-radio light mr10">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="schDrawingFlg"
|
||||||
|
id="schDrawingFlg0"
|
||||||
|
value={'0'}
|
||||||
|
checked={schDrawingFlg === '0'}
|
||||||
|
onChange={(e) => {
|
||||||
|
setSchDrawingFlg(e.target.value)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<label htmlFor="schDrawingFlg0">{getMessage('estimate.detail.docPopup.schDrawingFlg.schDrawingFlg0')}</label>
|
||||||
|
</div>
|
||||||
|
<div className="d-check-radio light">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="schDrawingFlg"
|
||||||
|
id="schDrawingFlg01"
|
||||||
|
value={'1'}
|
||||||
|
checked={schDrawingFlg === '1'}
|
||||||
|
onChange={(e) => {
|
||||||
|
setSchDrawingFlg(e.target.value)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<label htmlFor="schDrawingFlg01">{getMessage('estimate.detail.docPopup.schDrawingFlg.schDrawingFlg1')}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="footer-btn-wrap">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn-origin grey mr5"
|
||||||
|
onClick={() => {
|
||||||
|
setEstimatePopupOpen(false)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{getMessage('estimate.detail.docPopup.close')}
|
||||||
|
</button>
|
||||||
|
<button type="button" className="btn-origin navy" onClick={() => handleFileDown()}>
|
||||||
|
{getMessage('estimate.detail.docPopup.docDownload')}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
67
src/components/estimate/popup/ProductFeaturesPop.jsx
Normal file
67
src/components/estimate/popup/ProductFeaturesPop.jsx
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
'use client'
|
||||||
|
import { useEffect, useState } from 'react'
|
||||||
|
import { useMessage } from '@/hooks/useMessage'
|
||||||
|
export default function ProductFeaturesPop({ specialNoteList, showProductFeatureData, setProductFeaturesPopupOpen }) {
|
||||||
|
const [showSpecialNoteList, setShowSpecialNoteList] = useState([])
|
||||||
|
const { getMessage } = useMessage()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let pushData = []
|
||||||
|
specialNoteList.map((row) => {
|
||||||
|
let option = showProductFeatureData.split('、')
|
||||||
|
option.map((row2) => {
|
||||||
|
if (row.code === row2) {
|
||||||
|
pushData.push(row)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
setShowSpecialNoteList(pushData)
|
||||||
|
}, [specialNoteList])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="modal-popup">
|
||||||
|
<div className="modal-dialog">
|
||||||
|
<div className="modal-content">
|
||||||
|
<div className="modal-header">
|
||||||
|
<h1 className="title">{getMessage('estimate.detail.productFeaturesPopup.title')}</h1>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="modal-close"
|
||||||
|
onClick={() => {
|
||||||
|
setProductFeaturesPopupOpen(false)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{getMessage('estimate.detail.productFeaturesPopup.close')}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="modal-body">
|
||||||
|
<div className="modal-body-inner border">
|
||||||
|
<div className="calculation-estimate usemodal">
|
||||||
|
{showSpecialNoteList.length > 0 &&
|
||||||
|
showSpecialNoteList.map((row) => {
|
||||||
|
return (
|
||||||
|
<dl>
|
||||||
|
<dt>{row.codeNm}</dt>
|
||||||
|
<dd dangerouslySetInnerHTML={{ __html: row.remarks }}></dd>
|
||||||
|
</dl>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="footer-btn-wrap">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn-origin navy"
|
||||||
|
onClick={() => {
|
||||||
|
setProductFeaturesPopupOpen(false)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{getMessage('estimate.detail.productFeaturesPopup.close')}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -1,8 +1,15 @@
|
|||||||
import { fabric } from 'fabric'
|
import { fabric } from 'fabric'
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
import { QLine } from '@/components/fabric/QLine'
|
import { QLine } from '@/components/fabric/QLine'
|
||||||
import { distanceBetweenPoints, findTopTwoIndexesByDistance, getDirectionByPoint, sortedPointLessEightPoint, sortedPoints } from '@/util/canvas-util'
|
import {
|
||||||
import { calculateAngle, drawRidgeRoof, drawHippedRoof, inPolygon, toGeoJSON } from '@/util/qpolygon-utils'
|
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 * as turf from '@turf/turf'
|
||||||
import { LINE_TYPE } from '@/common/common'
|
import { LINE_TYPE } from '@/common/common'
|
||||||
|
|
||||||
@ -131,12 +138,14 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
|||||||
})
|
})
|
||||||
|
|
||||||
this.on('removed', () => {
|
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) => {
|
children.forEach((child) => {
|
||||||
this.canvas.remove(child)
|
this.canvas.remove(child)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
//QPolygon 좌표 이동시 좌표 재계산
|
||||||
this.on('polygonMoved', () => {
|
this.on('polygonMoved', () => {
|
||||||
//폴리곤일때만 사용
|
//폴리곤일때만 사용
|
||||||
let matrix = this.calcTransformMatrix()
|
let matrix = this.calcTransformMatrix()
|
||||||
@ -148,8 +157,9 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
|||||||
.map((p) => {
|
.map((p) => {
|
||||||
return fabric.util.transformPoint(p, matrix)
|
return fabric.util.transformPoint(p, matrix)
|
||||||
})
|
})
|
||||||
this.set('points', transformedPoints)
|
this.points = transformedPoints
|
||||||
this.set('pathOffset', { x: this.left, y: this.top })
|
const { left, top } = this.calcOriginCoords()
|
||||||
|
this.set('pathOffset', { x: left, y: top })
|
||||||
this.setCoords()
|
this.setCoords()
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -169,6 +179,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
|||||||
offset: 0,
|
offset: 0,
|
||||||
},
|
},
|
||||||
parent: this,
|
parent: this,
|
||||||
|
parentId: this.id,
|
||||||
direction: getDirectionByPoint(point, nextPoint),
|
direction: getDirectionByPoint(point, nextPoint),
|
||||||
idx: i + 1,
|
idx: i + 1,
|
||||||
})
|
})
|
||||||
@ -187,7 +198,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
|||||||
const eavesType = [LINE_TYPE.WALLLINE.EAVES, LINE_TYPE.WALLLINE.HIPANDGABLE]
|
const eavesType = [LINE_TYPE.WALLLINE.EAVES, LINE_TYPE.WALLLINE.HIPANDGABLE]
|
||||||
const gableType = [LINE_TYPE.WALLLINE.GABLE, LINE_TYPE.WALLLINE.JERKINHEAD]
|
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 gableOdd = types.filter((type, i) => i % 2 === 0)
|
||||||
const gableEven = types.filter((type, i) => i % 2 === 1)
|
const gableEven = types.filter((type, i) => i % 2 === 1)
|
||||||
const hasShed = types.includes(LINE_TYPE.WALLLINE.SHED)
|
const hasShed = types.includes(LINE_TYPE.WALLLINE.SHED)
|
||||||
@ -199,18 +210,40 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
|||||||
) {
|
) {
|
||||||
console.log('박공 지붕')
|
console.log('박공 지붕')
|
||||||
} else if (hasShed) {
|
} else if (hasShed) {
|
||||||
//편류지붕
|
const sheds = this.lines.filter((line) => line.attributes !== undefined && line.attributes.type === LINE_TYPE.WALLLINE.SHED)
|
||||||
let shedIndex = 0
|
const areLinesParallel = function (line1, line2) {
|
||||||
types.forEach((type, i) => {
|
const angle1 = calculateAngle(line1.startPoint, line1.endPoint)
|
||||||
if (type === LINE_TYPE.WALLLINE.SHED) {
|
const angle2 = calculateAngle(line2.startPoint, line2.endPoint)
|
||||||
shedIndex = i
|
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)
|
if (isShedRoof) {
|
||||||
const shedEven = types.filter((type, i) => i % 2 !== shedIndex % 2)
|
const eaves = this.lines
|
||||||
types.forEach((type, i) => console.log(type, i, i % 2, shedIndex % 2, i % 2 === shedIndex % 2))
|
.filter((line) => line.attributes !== undefined && line.attributes.type === LINE_TYPE.WALLLINE.EAVES)
|
||||||
if (shedOdd.every((type) => type === LINE_TYPE.WALLLINE.EAVES) && shedEven.every((type) => type === LINE_TYPE.WALLLINE.GABLE)) {
|
.filter((line) => {
|
||||||
console.log('편류지붕')
|
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 {
|
} else {
|
||||||
drawRidgeRoof(this.id, this.canvas)
|
drawRidgeRoof(this.id, this.canvas)
|
||||||
@ -757,7 +790,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
|||||||
setViewLengthText(isView) {
|
setViewLengthText(isView) {
|
||||||
this.canvas
|
this.canvas
|
||||||
?.getObjects()
|
?.getObjects()
|
||||||
.filter((obj) => obj.name === 'lengthText' && obj.parent === this)
|
.filter((obj) => obj.name === 'lengthText' && obj.parentId === this.id)
|
||||||
.forEach((text) => {
|
.forEach((text) => {
|
||||||
text.set({ visible: isView })
|
text.set({ visible: isView })
|
||||||
})
|
})
|
||||||
@ -770,7 +803,33 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
|||||||
this.scaleY = scale
|
this.scaleY = scale
|
||||||
this.addLengthText()
|
this.addLengthText()
|
||||||
},
|
},
|
||||||
divideLine() {
|
|
||||||
// splitPolygonWithLines(this)
|
calcOriginCoords() {
|
||||||
|
const points = this.points
|
||||||
|
const minX = Math.min(...points.map((p) => p.x))
|
||||||
|
const maxX = Math.max(...points.map((p) => p.x))
|
||||||
|
const minY = Math.min(...points.map((p) => p.y))
|
||||||
|
const maxY = Math.max(...points.map((p) => p.y))
|
||||||
|
|
||||||
|
let left = 0
|
||||||
|
let top = 0
|
||||||
|
|
||||||
|
if (this.originX === 'center') {
|
||||||
|
left = (minX + maxX) / 2
|
||||||
|
} else if (this.originX === 'left') {
|
||||||
|
left = minX
|
||||||
|
} else if (this.originX === 'right') {
|
||||||
|
left = maxX
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.originY === 'center') {
|
||||||
|
top = (minY + maxY) / 2
|
||||||
|
} else if (this.originY === 'top') {
|
||||||
|
top = minY
|
||||||
|
} else if (this.originY === 'bottom') {
|
||||||
|
top = maxY
|
||||||
|
}
|
||||||
|
|
||||||
|
return { left, top }
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@ -16,11 +16,11 @@ import PanelBatchStatistics from '@/components/floor-plan/modal/panelBatch/Panel
|
|||||||
|
|
||||||
export default function CanvasFrame() {
|
export default function CanvasFrame() {
|
||||||
const canvasRef = useRef(null)
|
const canvasRef = useRef(null)
|
||||||
const { canvas } = useCanvas('canvas')
|
const { canvas, handleBackImageLoadToCanvas } = useCanvas('canvas')
|
||||||
const { canvasLoadInit, gridInit } = useCanvasConfigInitialize()
|
const { canvasLoadInit, gridInit } = useCanvasConfigInitialize()
|
||||||
const currentMenu = useRecoilValue(currentMenuState)
|
const currentMenu = useRecoilValue(currentMenuState)
|
||||||
const { contextMenu, handleClick } = useContextMenu()
|
const { contextMenu, handleClick } = useContextMenu()
|
||||||
const { selectedPlan, modifiedPlanFlag, checkCanvasObjectEvent, resetModifiedPlans } = usePlan()
|
const { selectedPlan, modifiedPlanFlag, checkCanvasObjectEvent, resetModifiedPlans, currentCanvasPlan } = usePlan()
|
||||||
useEvent()
|
useEvent()
|
||||||
|
|
||||||
const loadCanvas = () => {
|
const loadCanvas = () => {
|
||||||
|
|||||||
@ -33,6 +33,8 @@ import useMenu from '@/hooks/common/useMenu'
|
|||||||
import { MENU } from '@/common/common'
|
import { MENU } from '@/common/common'
|
||||||
|
|
||||||
import { useEstimateController } from '@/hooks/floorPlan/estimate/useEstimateController'
|
import { useEstimateController } from '@/hooks/floorPlan/estimate/useEstimateController'
|
||||||
|
import { estimateState } from '@/store/floorPlanObjectAtom'
|
||||||
|
import DocDownOptionPop from '../estimate/popup/DocDownOptionPop'
|
||||||
|
|
||||||
export default function CanvasMenu(props) {
|
export default function CanvasMenu(props) {
|
||||||
const { menuNumber, setMenuNumber } = props
|
const { menuNumber, setMenuNumber } = props
|
||||||
@ -53,7 +55,10 @@ export default function CanvasMenu(props) {
|
|||||||
const canvas = useRecoilValue(canvasState)
|
const canvas = useRecoilValue(canvasState)
|
||||||
const { handleZoomClear, handleZoom } = useCanvasEvent()
|
const { handleZoomClear, handleZoom } = useCanvasEvent()
|
||||||
const { handleMenu } = useMenu()
|
const { handleMenu } = useMenu()
|
||||||
|
|
||||||
const { handleEstimateSubmit } = useEstimateController()
|
const { handleEstimateSubmit } = useEstimateController()
|
||||||
|
const estimateRecoilState = useRecoilValue(estimateState)
|
||||||
|
const [estimatePopupOpen, setEstimatePopupOpen] = useState(false)
|
||||||
|
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
const { currentCanvasPlan, saveCanvas } = usePlan()
|
const { currentCanvasPlan, saveCanvas } = usePlan()
|
||||||
@ -81,6 +86,9 @@ export default function CanvasMenu(props) {
|
|||||||
case 4:
|
case 4:
|
||||||
setType('module')
|
setType('module')
|
||||||
break
|
break
|
||||||
|
case 6:
|
||||||
|
router.push(`/floor-plan/simulator/${menu.index}`)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pathname !== '/floor-plan') router.push('/floor-plan')
|
if (pathname !== '/floor-plan') router.push('/floor-plan')
|
||||||
@ -135,6 +143,38 @@ export default function CanvasMenu(props) {
|
|||||||
addPopup(id, 1, <SettingModal01 id={id} />, true)
|
addPopup(id, 1, <SettingModal01 id={id} />, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 견적서 초기화 버튼
|
||||||
|
const handleEstimateReset = () => {
|
||||||
|
// console.log('estimateRecoilState::', estimateRecoilState)
|
||||||
|
//objectNo, planNo
|
||||||
|
swalFire({
|
||||||
|
//저장된 견적서 정보가 초기화되고, 도면정보가 반영됩니다. 정말로 초기화 하시겠습니까?
|
||||||
|
//물건정보
|
||||||
|
text: getMessage('estimate.detail.reset.confirmMsg'),
|
||||||
|
type: 'confirm',
|
||||||
|
confirmFn: () => {
|
||||||
|
console.log('내용초기화 및 변경일시 갱신')
|
||||||
|
},
|
||||||
|
denyFn: () => {
|
||||||
|
console.log('초기화하지 않음. 변경일시 갱신안함')
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 견적서 복사버튼
|
||||||
|
* (견적서 번호(estimateRecoilState.docNo)가 생성된 이후 버튼 활성화 )
|
||||||
|
* T01관리자 계정 및 1차판매점에게만 제공
|
||||||
|
*/
|
||||||
|
|
||||||
|
const handleEstimateCopy = () => {
|
||||||
|
// console.log('estimateRecoilState::', estimateRecoilState)
|
||||||
|
//objectNo, planNo
|
||||||
|
console.log('복사')
|
||||||
|
console.log('물건정보+도면+견적서를 모두 복사')
|
||||||
|
console.log('견적서 가격은 정가를 표시')
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (globalLocale === 'ko') {
|
if (globalLocale === 'ko') {
|
||||||
setAppMessageState(KO)
|
setAppMessageState(KO)
|
||||||
@ -225,7 +265,7 @@ export default function CanvasMenu(props) {
|
|||||||
{menuNumber === 5 && (
|
{menuNumber === 5 && (
|
||||||
<>
|
<>
|
||||||
<div className="ico-btn-from">
|
<div className="ico-btn-from">
|
||||||
<button className="btn-frame gray ico-flx act">
|
<button className="btn-frame gray ico-flx" onClick={() => setEstimatePopupOpen(true)}>
|
||||||
<span className="ico ico01"></span>
|
<span className="ico ico01"></span>
|
||||||
<span>{getMessage('plan.menu.estimate.docDown')}</span>
|
<span>{getMessage('plan.menu.estimate.docDown')}</span>
|
||||||
</button>
|
</button>
|
||||||
@ -233,11 +273,24 @@ export default function CanvasMenu(props) {
|
|||||||
<span className="ico ico02"></span>
|
<span className="ico ico02"></span>
|
||||||
<span>{getMessage('plan.menu.estimate.save')}</span>
|
<span>{getMessage('plan.menu.estimate.save')}</span>
|
||||||
</button>
|
</button>
|
||||||
<button className="btn-frame gray ico-flx">
|
{/* {estimateRecoilState?.docNo != null && ( */}
|
||||||
|
<button
|
||||||
|
className="btn-frame gray ico-flx"
|
||||||
|
onClick={() => {
|
||||||
|
handleEstimateReset()
|
||||||
|
}}
|
||||||
|
>
|
||||||
<span className="ico ico03"></span>
|
<span className="ico ico03"></span>
|
||||||
<span>{getMessage('plan.menu.estimate.reset')}</span>
|
<span>{getMessage('plan.menu.estimate.reset')}</span>
|
||||||
</button>
|
</button>
|
||||||
<button className="btn-frame gray ico-flx">
|
{/* )} */}
|
||||||
|
|
||||||
|
<button
|
||||||
|
className="btn-frame gray ico-flx"
|
||||||
|
onClick={() => {
|
||||||
|
handleEstimateCopy()
|
||||||
|
}}
|
||||||
|
>
|
||||||
<span className="ico ico04"></span>
|
<span className="ico ico04"></span>
|
||||||
<span>{getMessage('plan.menu.estimate.copy')}</span>
|
<span>{getMessage('plan.menu.estimate.copy')}</span>
|
||||||
</button>
|
</button>
|
||||||
@ -263,6 +316,8 @@ export default function CanvasMenu(props) {
|
|||||||
<div className={`canvas-depth2-wrap ${menuNumber === 2 || menuNumber === 3 || menuNumber === 4 ? 'active' : ''}`}>
|
<div className={`canvas-depth2-wrap ${menuNumber === 2 || menuNumber === 3 || menuNumber === 4 ? 'active' : ''}`}>
|
||||||
{(menuNumber === 2 || menuNumber === 3 || menuNumber === 4) && <MenuDepth01 />}
|
{(menuNumber === 2 || menuNumber === 3 || menuNumber === 4) && <MenuDepth01 />}
|
||||||
</div>
|
</div>
|
||||||
|
{/* 견적서(menuNumber=== 5) 상세화면인경우 문서다운로드 팝업 */}
|
||||||
|
{estimatePopupOpen && <DocDownOptionPop planNo={estimateRecoilState?.planNo} setEstimatePopupOpen={setEstimatePopupOpen} />}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,14 +5,18 @@ import { useRecoilValue } from 'recoil'
|
|||||||
import { contextPopupPositionState } from '@/store/popupAtom'
|
import { contextPopupPositionState } from '@/store/popupAtom'
|
||||||
import { useMessage } from '@/hooks/useMessage'
|
import { useMessage } from '@/hooks/useMessage'
|
||||||
import { usePopup } from '@/hooks/usePopup'
|
import { usePopup } from '@/hooks/usePopup'
|
||||||
|
import { useSurfaceShapeBatch } from '@/hooks/surface/useSurfaceShapeBatch'
|
||||||
export default function FlowDirectionSetting(props) {
|
export default function FlowDirectionSetting(props) {
|
||||||
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
||||||
const { id, pos = contextPopupPosition, target } = props
|
const { id, pos = contextPopupPosition, target } = props
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
const { closePopup } = usePopup()
|
const { closePopup } = usePopup()
|
||||||
const [compasDeg, setCompasDeg] = useState(360)
|
const [compasDeg, setCompasDeg] = useState(360)
|
||||||
|
const [flowDirection, setFlowDirection] = useState(target.direction)
|
||||||
|
const { changeSurfaceFlowDirection } = useSurfaceShapeBatch()
|
||||||
|
|
||||||
const orientations = [
|
const orientations = [
|
||||||
|
// { name: `${getMessage('commons.none')}`, value: 0 },
|
||||||
{ name: `${getMessage('commons.south')}`, value: 360 },
|
{ name: `${getMessage('commons.south')}`, value: 360 },
|
||||||
{ name: `${getMessage('commons.south')}${getMessage('commons.east')}`, value: 315 },
|
{ name: `${getMessage('commons.south')}${getMessage('commons.east')}`, value: 315 },
|
||||||
{ name: `${getMessage('commons.south')}${getMessage('commons.west')}`, value: 45 },
|
{ name: `${getMessage('commons.south')}${getMessage('commons.west')}`, value: 45 },
|
||||||
@ -75,13 +79,13 @@ export default function FlowDirectionSetting(props) {
|
|||||||
<div className="object-direction-wrap">
|
<div className="object-direction-wrap">
|
||||||
<div className="plane-direction">
|
<div className="plane-direction">
|
||||||
<span className="top">{getMessage('commons.north')}</span>
|
<span className="top">{getMessage('commons.north')}</span>
|
||||||
<button className="plane-btn up"></button>
|
<button className={`plane-btn up ${flowDirection === 'north' ? 'act' : ''}`} onClick={() => setFlowDirection('north')}></button>
|
||||||
<span className="right">{getMessage('commons.east')}</span>
|
<span className="right">{getMessage('commons.east')}</span>
|
||||||
<button className="plane-btn right"></button>
|
<button className={`plane-btn right ${flowDirection === 'east' ? 'act' : ''}`} onClick={() => setFlowDirection('east')}></button>
|
||||||
<span className="bottom">{getMessage('commons.south')}</span>
|
<span className="bottom">{getMessage('commons.south')}</span>
|
||||||
<button className="plane-btn down act"></button>
|
<button className={`plane-btn down ${flowDirection === 'south' ? 'act' : ''}`} onClick={() => setFlowDirection('south')}></button>
|
||||||
<span className="left">{getMessage('commons.west')}</span>
|
<span className="left">{getMessage('commons.west')}</span>
|
||||||
<button className="plane-btn left"></button>
|
<button className={`plane-btn left ${flowDirection === 'west' ? 'act' : ''}`} onClick={() => setFlowDirection('west')}></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -111,18 +115,14 @@ export default function FlowDirectionSetting(props) {
|
|||||||
key={index}
|
key={index}
|
||||||
className={`circle ${compasDeg === 15 * (12 + index) ? 'act' : ''}`}
|
className={`circle ${compasDeg === 15 * (12 + index) ? 'act' : ''}`}
|
||||||
onClick={() => setCompasDeg(15 * (12 + index))}
|
onClick={() => setCompasDeg(15 * (12 + index))}
|
||||||
>
|
></div>
|
||||||
<i>{13 - index}</i>
|
|
||||||
</div>
|
|
||||||
))}
|
))}
|
||||||
{Array.from({ length: 180 / 15 - 1 }).map((dot, index) => (
|
{Array.from({ length: 180 / 15 - 1 }).map((dot, index) => (
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={index}
|
||||||
className={`circle ${compasDeg === 15 * (index + 1) ? 'act' : ''}`}
|
className={`circle ${compasDeg === 15 * (index + 1) ? 'act' : ''}`}
|
||||||
onClick={() => setCompasDeg(15 * (index + 1))}
|
onClick={() => setCompasDeg(15 * (index + 1))}
|
||||||
>
|
></div>
|
||||||
<i>{24 - index}</i>
|
|
||||||
</div>
|
|
||||||
))}
|
))}
|
||||||
<div className="compas">
|
<div className="compas">
|
||||||
<div className="compas-arr" style={{ transform: `rotate(${compasDeg}deg)` }}></div>
|
<div className="compas-arr" style={{ transform: `rotate(${compasDeg}deg)` }}></div>
|
||||||
@ -133,7 +133,9 @@ export default function FlowDirectionSetting(props) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid-btn-wrap">
|
<div className="grid-btn-wrap">
|
||||||
<button className="btn-frame modal act">{getMessage('modal.common.save')}</button>
|
<button className="btn-frame modal act" onClick={() => changeSurfaceFlowDirection(target, flowDirection, selectedOrientation)}>
|
||||||
|
{getMessage('modal.common.save')}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -3,13 +3,18 @@ import { useRecoilValue } from 'recoil'
|
|||||||
import { contextPopupPositionState } from '@/store/popupAtom'
|
import { contextPopupPositionState } from '@/store/popupAtom'
|
||||||
import { useMessage } from '@/hooks/useMessage'
|
import { useMessage } from '@/hooks/useMessage'
|
||||||
import { usePopup } from '@/hooks/usePopup'
|
import { usePopup } from '@/hooks/usePopup'
|
||||||
import { useState } from 'react'
|
import { useState, useEffect } from 'react'
|
||||||
|
import { useSurfaceShapeBatch } from '@/hooks/surface/useSurfaceShapeBatch'
|
||||||
|
import { useEvent } from '@/hooks/useEvent'
|
||||||
|
|
||||||
export default function LinePropertySetting(props) {
|
export default function LinePropertySetting(props) {
|
||||||
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
||||||
const { id, pos = contextPopupPosition } = props
|
const { id, pos = contextPopupPosition, target } = props
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
const { closePopup } = usePopup()
|
const { closePopup } = usePopup()
|
||||||
|
const { changeSurfaceLinePropertyEvent, changeSurfaceLineProperty } = useSurfaceShapeBatch()
|
||||||
|
const { initEvent } = useEvent()
|
||||||
|
|
||||||
const properties = [
|
const properties = [
|
||||||
{ name: getMessage('eaves.line'), value: 'eaves' },
|
{ name: getMessage('eaves.line'), value: 'eaves' },
|
||||||
{ name: getMessage('ridge'), value: 'ridge' },
|
{ name: getMessage('ridge'), value: 'ridge' },
|
||||||
@ -29,6 +34,14 @@ export default function LinePropertySetting(props) {
|
|||||||
{ name: getMessage('no.setting'), value: 'noSetting' },
|
{ name: getMessage('no.setting'), value: 'noSetting' },
|
||||||
]
|
]
|
||||||
const [selectedProperty, setSelectedProperty] = useState(null)
|
const [selectedProperty, setSelectedProperty] = useState(null)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
changeSurfaceLinePropertyEvent(target)
|
||||||
|
return () => {
|
||||||
|
initEvent()
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<WithDraggable isShow={true} pos={pos}>
|
<WithDraggable isShow={true} pos={pos}>
|
||||||
<div className={`modal-pop-wrap r mount`}>
|
<div className={`modal-pop-wrap r mount`}>
|
||||||
@ -66,7 +79,9 @@ export default function LinePropertySetting(props) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid-btn-wrap">
|
<div className="grid-btn-wrap">
|
||||||
<button className="btn-frame modal act">{getMessage('modal.common.save')}</button>
|
<button className="btn-frame modal act" onClick={() => changeSurfaceLineProperty(selectedProperty)}>
|
||||||
|
{getMessage('modal.common.save')}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -18,7 +18,7 @@ export default function SizeSetting(props) {
|
|||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
const { closePopup } = usePopup()
|
const { closePopup } = usePopup()
|
||||||
const { resizeObjectBatch } = useObjectBatch({})
|
const { resizeObjectBatch } = useObjectBatch({})
|
||||||
const { reSizePolygon } = useSurfaceShapeBatch()
|
const { resizeSurfaceShapeBatch } = useSurfaceShapeBatch()
|
||||||
const widthRef = useRef(null)
|
const widthRef = useRef(null)
|
||||||
const heightRef = useRef(null)
|
const heightRef = useRef(null)
|
||||||
|
|
||||||
@ -36,10 +36,11 @@ export default function SizeSetting(props) {
|
|||||||
target.name === BATCH_TYPE.OPENING ||
|
target.name === BATCH_TYPE.OPENING ||
|
||||||
target.name === BATCH_TYPE.SHADOW ||
|
target.name === BATCH_TYPE.SHADOW ||
|
||||||
target.name === BATCH_TYPE.TRIANGLE_DORMER ||
|
target.name === BATCH_TYPE.TRIANGLE_DORMER ||
|
||||||
target.name === BATCH_TYPE.PENTAGON_DORMER ||
|
target.name === BATCH_TYPE.PENTAGON_DORMER
|
||||||
target.name === POLYGON_TYPE.ROOF
|
|
||||||
) {
|
) {
|
||||||
resizeObjectBatch(settingTarget, target, width, height)
|
resizeObjectBatch(settingTarget, target, width, height)
|
||||||
|
} else if (target.name === POLYGON_TYPE.ROOF) {
|
||||||
|
resizeSurfaceShapeBatch(settingTarget, target, width, height)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,14 +1,20 @@
|
|||||||
import SizeGuide from '@/components/floor-plan/modal/placementShape/SizeGuide'
|
import { useEffect, useState } from 'react'
|
||||||
import MaterialGuide from '@/components/floor-plan/modal/placementShape/MaterialGuide'
|
|
||||||
import WithDraggable from '@/components/common/draggable/WithDraggable'
|
|
||||||
import { useRecoilState } from 'recoil'
|
import { useRecoilState } from 'recoil'
|
||||||
import { Fragment, useEffect, useState } from 'react'
|
|
||||||
import { canvasSettingState } from '@/store/canvasAtom'
|
import { canvasSettingState } from '@/store/canvasAtom'
|
||||||
|
import { basicSettingState } from '@/store/settingAtom'
|
||||||
|
|
||||||
import { useMessage } from '@/hooks/useMessage'
|
import { useMessage } from '@/hooks/useMessage'
|
||||||
import { useAxios } from '@/hooks/useAxios'
|
import { useAxios } from '@/hooks/useAxios'
|
||||||
import { useSwal } from '@/hooks/useSwal'
|
import { useSwal } from '@/hooks/useSwal'
|
||||||
import { usePopup } from '@/hooks/usePopup'
|
import { usePopup } from '@/hooks/usePopup'
|
||||||
import { basicSettingState } from '@/store/settingAtom'
|
import useRefFiles from '@/hooks/common/useRefFiles'
|
||||||
|
import { usePlan } from '@/hooks/usePlan'
|
||||||
|
|
||||||
|
import SizeGuide from '@/components/floor-plan/modal/placementShape/SizeGuide'
|
||||||
|
import MaterialGuide from '@/components/floor-plan/modal/placementShape/MaterialGuide'
|
||||||
|
import WithDraggable from '@/components/common/draggable/WithDraggable'
|
||||||
|
import { SessionContext } from '@/app/SessionProvider'
|
||||||
|
|
||||||
export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, setShowPlaceShapeModal }) {
|
export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, setShowPlaceShapeModal }) {
|
||||||
const [objectNo, setObjectNo] = useState('test123241008001') // 후에 삭제 필요
|
const [objectNo, setObjectNo] = useState('test123241008001') // 후에 삭제 필요
|
||||||
@ -18,7 +24,20 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, set
|
|||||||
const [canvasSetting, setCanvasSetting] = useRecoilState(canvasSettingState)
|
const [canvasSetting, setCanvasSetting] = useRecoilState(canvasSettingState)
|
||||||
const { closePopup } = usePopup()
|
const { closePopup } = usePopup()
|
||||||
const [basicSetting, setBasicSettings] = useRecoilState(basicSettingState)
|
const [basicSetting, setBasicSettings] = useRecoilState(basicSettingState)
|
||||||
const [image, setImage] = useState(null)
|
const {
|
||||||
|
refImage,
|
||||||
|
queryRef,
|
||||||
|
setRefImage,
|
||||||
|
handleRefFile,
|
||||||
|
refFileMethod,
|
||||||
|
setRefFileMethod,
|
||||||
|
handleRefFileMethod,
|
||||||
|
mapPositionAddress,
|
||||||
|
setMapPositionAddress,
|
||||||
|
handleFileDelete,
|
||||||
|
handleMapImageDown,
|
||||||
|
} = useRefFiles()
|
||||||
|
const { currentCanvasPlan } = usePlan()
|
||||||
|
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
const { get, post } = useAxios()
|
const { get, post } = useAxios()
|
||||||
@ -487,19 +506,85 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, set
|
|||||||
<tr>
|
<tr>
|
||||||
<th>{getMessage('common.input.file')}</th>
|
<th>{getMessage('common.input.file')}</th>
|
||||||
<td>
|
<td>
|
||||||
<div className="flex-box">
|
<div className="pop-form-radio mb10">
|
||||||
|
<div className="d-check-radio pop">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="radio03"
|
||||||
|
id="ra06"
|
||||||
|
value={'1'}
|
||||||
|
onChange={(e) => handleRefFileMethod(e)}
|
||||||
|
checked={refFileMethod === '1'}
|
||||||
|
/>
|
||||||
|
<label htmlFor="ra06">ファイルを読み込む</label>
|
||||||
|
</div>
|
||||||
|
<div className="d-check-radio pop">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="radio03"
|
||||||
|
id="ra07"
|
||||||
|
value={'2'}
|
||||||
|
onChange={(e) => handleRefFileMethod(e)}
|
||||||
|
checked={refFileMethod === '2'}
|
||||||
|
/>
|
||||||
|
<label htmlFor="ra07">アドレスを読み込む</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 파일 불러오기 */}
|
||||||
|
{refFileMethod === '1' && (
|
||||||
|
<div className="flex-box">
|
||||||
|
<div className="img-edit-wrap">
|
||||||
|
<label className="img-edit-btn" htmlFor="img_file">
|
||||||
|
<span className="img-edit"></span>
|
||||||
|
{getMessage('common.input.file.load')}
|
||||||
|
</label>
|
||||||
|
<input type="file" id="img_file" style={{ display: 'none' }} onChange={(e) => handleRefFile(e.target.files[0])} />
|
||||||
|
</div>
|
||||||
|
<div className="img-name-wrap">
|
||||||
|
{currentCanvasPlan?.bgImageName === null ? (
|
||||||
|
<input type="text" className="input-origin al-l" defaultValue={''} value={refImage ? refImage.name : ''} readOnly />
|
||||||
|
) : (
|
||||||
|
<input type="text" className="input-origin al-l" value={currentCanvasPlan?.bgImageName} readOnly />
|
||||||
|
)}
|
||||||
|
{(refImage || currentCanvasPlan?.bgImageName) && <button className="img-check" onClick={handleFileDelete}></button>}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 주소 불러오기 */}
|
||||||
|
{refFileMethod === '2' && (
|
||||||
|
<div className="flex-box for-address">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className="input-origin al-l mr10"
|
||||||
|
placeholder={'住所入力'}
|
||||||
|
ref={queryRef}
|
||||||
|
value={mapPositionAddress}
|
||||||
|
onChange={(e) => setMapPositionAddress(e.target.value)}
|
||||||
|
/>
|
||||||
|
<div className="img-edit-wrap mr5">
|
||||||
|
<button className="img-edit-btn" onClick={handleMapImageDown}>
|
||||||
|
完了
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{mapPositionAddress && <span className="check-address fail"></span>}
|
||||||
|
{/* <span className="check-address success"></span> */}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{/* <div className="flex-box">
|
||||||
<div className="img-edit-wrap">
|
<div className="img-edit-wrap">
|
||||||
<label className="img-edit-btn" htmlFor="img_file">
|
<label className="img-edit-btn" htmlFor="img_file">
|
||||||
<span className="img-edit"></span>
|
<span className="img-edit"></span>
|
||||||
{getMessage('common.input.file.load')}
|
{getMessage('common.input.file.load')}
|
||||||
</label>
|
</label>
|
||||||
<input type="file" id="img_file" style={{ display: 'none' }} onChange={(e) => setImage(e.target.files[0])} />
|
<input type="file" id="img_file" style={{ display: 'none' }} onChange={(e) => handleRefFile(e.target.files[0])} />
|
||||||
</div>
|
</div>
|
||||||
<div className="img-name-wrap">
|
<div className="img-name-wrap">
|
||||||
<input type="text" className="input-origin al-l" defaultValue={''} value={image ? image.name : ''} readOnly />
|
<input type="text" className="input-origin al-l" defaultValue={''} value={refImage ? refImage.name : ''} readOnly />
|
||||||
{image && <button className="img-check" onClick={() => setImage(null)}></button>}
|
{refImage && <button className="img-check" onClick={() => setRefImage(null)}></button>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div> */}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
@ -0,0 +1,122 @@
|
|||||||
|
import WithDraggable from '@/components/common/draggable/WithDraggable'
|
||||||
|
import { useMessage } from '@/hooks/useMessage'
|
||||||
|
import { usePopup } from '@/hooks/usePopup'
|
||||||
|
import { useRecoilValue } from 'recoil'
|
||||||
|
import { contextPopupPositionState } from '@/store/popupAtom'
|
||||||
|
import { useEffect, useState } from 'react'
|
||||||
|
import { currentObjectState } from '@/store/canvasAtom'
|
||||||
|
import { useRoofAllocationSetting } from '@/hooks/roofcover/useRoofAllocationSetting'
|
||||||
|
import { useSwal } from '@/hooks/useSwal'
|
||||||
|
|
||||||
|
export default function ActualSizeSetting(props) {
|
||||||
|
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
||||||
|
const { id, pos = contextPopupPosition } = props
|
||||||
|
const currentObject = useRecoilValue(currentObjectState)
|
||||||
|
const [planeSize, setPlaneSize] = useState(0)
|
||||||
|
const [actualSize, setActualSize] = useState(0)
|
||||||
|
const { setLineSize, handleAlloc } = useRoofAllocationSetting()
|
||||||
|
const { closePopup } = usePopup()
|
||||||
|
const { getMessage } = useMessage()
|
||||||
|
const { swalFire } = useSwal()
|
||||||
|
useEffect(() => {
|
||||||
|
if (currentObject && currentObject.attributes) {
|
||||||
|
setPlaneSize(currentObject.attributes.planeSize ?? 0)
|
||||||
|
setActualSize(currentObject.attributes.actualSize ?? 0)
|
||||||
|
}
|
||||||
|
}, [currentObject])
|
||||||
|
|
||||||
|
const handleFinish = () => {
|
||||||
|
swalFire({
|
||||||
|
text: '완료 하시겠습니까?',
|
||||||
|
type: 'confirm',
|
||||||
|
confirmFn: () => {
|
||||||
|
handleAlloc()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
swalFire({
|
||||||
|
text: '완료 하시겠습니까?',
|
||||||
|
type: 'confirm',
|
||||||
|
confirmFn: () => {
|
||||||
|
handleAlloc()
|
||||||
|
},
|
||||||
|
// denyFn: () => {
|
||||||
|
// swalFire({ text: '취소되었습니다.', icon: 'error' })
|
||||||
|
// },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleApply = () => {
|
||||||
|
if (!currentObject) {
|
||||||
|
swalFire({ type: 'alert', icon: 'error', text: getMessage('modal.actual.size.setting.not.exist.auxiliary.line') })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (actualSize !== 0) {
|
||||||
|
setLineSize(currentObject?.id, actualSize)
|
||||||
|
} else {
|
||||||
|
swalFire({ type: 'alert', icon: 'error', text: getMessage('modal.actual.size.setting.not.exist.size') })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<WithDraggable isShow={true} pos={pos}>
|
||||||
|
<div className={`modal-pop-wrap ssm mount`}>
|
||||||
|
<div className="modal-head">
|
||||||
|
<h1 className="title">{getMessage('modal.actual.size.setting')}</h1>
|
||||||
|
<button className="modal-close" onClick={() => closePopup(id)}>
|
||||||
|
닫기
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="modal-body">
|
||||||
|
<div className="guide">
|
||||||
|
<span>{getMessage('modal.actual.size.setting.info')}</span>
|
||||||
|
</div>
|
||||||
|
<div className="properties-setting-wrap outer">
|
||||||
|
<div className="setting-tit">{getMessage('setting')}</div>
|
||||||
|
<div className="outline-wrap">
|
||||||
|
<div className="eaves-keraba-table">
|
||||||
|
<div className="eaves-keraba-item">
|
||||||
|
<div className="eaves-keraba-th">{getMessage('modal.actual.size.setting.plane.size.length')}</div>
|
||||||
|
<div className="eaves-keraba-td">
|
||||||
|
<div className="outline-form">
|
||||||
|
<div className="input-grid mr5">
|
||||||
|
<input type="text" className="input-origin block" value={planeSize} readOnly={true} />
|
||||||
|
</div>
|
||||||
|
<span className="thin">mm</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="eaves-keraba-item">
|
||||||
|
<div className="eaves-keraba-th">{getMessage('modal.actual.size.setting.actual.size.length')}</div>
|
||||||
|
<div className="eaves-keraba-td">
|
||||||
|
<div className="outline-form">
|
||||||
|
<div className="input-grid mr5">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className="input-origin block"
|
||||||
|
value={actualSize}
|
||||||
|
onChange={(e) => setActualSize(Number(e.target.value))}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<span className="thin">mm</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="grid-btn-wrap">
|
||||||
|
<button className="btn-frame modal mr5" onClick={handleFinish}>
|
||||||
|
{getMessage('common.setting.finish')}
|
||||||
|
</button>
|
||||||
|
<button className="btn-frame modal act" onClick={handleApply}>
|
||||||
|
{getMessage('apply')}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</WithDraggable>
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -9,13 +9,11 @@ import { useRecoilValue, useRecoilState, useSetRecoilState, useResetRecoilState
|
|||||||
import { stuffSearchState } from '@/store/stuffAtom'
|
import { stuffSearchState } from '@/store/stuffAtom'
|
||||||
import { queryStringFormatter, isEmptyArray } from '@/util/common-utils'
|
import { queryStringFormatter, isEmptyArray } from '@/util/common-utils'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import { isObjectNotEmpty } from '@/util/common-utils'
|
|
||||||
import { convertNumberToPriceDecimal } from '@/util/common-utils'
|
import { convertNumberToPriceDecimal } from '@/util/common-utils'
|
||||||
import { appMessageStore, globalLocaleStore } from '@/store/localeAtom'
|
import { appMessageStore, globalLocaleStore } from '@/store/localeAtom'
|
||||||
import KO from '@/locales/ko.json'
|
import KO from '@/locales/ko.json'
|
||||||
import JA from '@/locales/ja.json'
|
import JA from '@/locales/ja.json'
|
||||||
import QPagination from '../common/pagination/QPagination'
|
import QPagination from '../common/pagination/QPagination'
|
||||||
import { sessionStore } from '@/store/commonAtom'
|
|
||||||
import { SessionContext } from '@/app/SessionProvider'
|
import { SessionContext } from '@/app/SessionProvider'
|
||||||
|
|
||||||
export default function Stuff() {
|
export default function Stuff() {
|
||||||
@ -192,7 +190,11 @@ export default function Stuff() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//if (session.storeId === 'T01') {
|
||||||
fetchData()
|
fetchData()
|
||||||
|
//} else if (stuffSearch.schSelSaleStoreId !== '') {
|
||||||
|
//fetchData()
|
||||||
|
//}
|
||||||
} else if (stuffSearchParams?.code === 'M') {
|
} else if (stuffSearchParams?.code === 'M') {
|
||||||
const params = {
|
const params = {
|
||||||
saleStoreId: session?.storeId,
|
saleStoreId: session?.storeId,
|
||||||
|
|||||||
@ -59,7 +59,7 @@ export default function StuffDetail() {
|
|||||||
standardWindSpeedId: '', //기준풍속
|
standardWindSpeedId: '', //기준풍속
|
||||||
verticalSnowCover: '', //수직적설량
|
verticalSnowCover: '', //수직적설량
|
||||||
coldRegionFlg: false, //한랭지대책시행(true : 1 / false : 0)
|
coldRegionFlg: false, //한랭지대책시행(true : 1 / false : 0)
|
||||||
surfaceType: 'III・IV', //면조도구분(III・IV / Ⅱ)
|
surfaceType: 'Ⅲ・Ⅳ', //면조도구분(Ⅲ・Ⅳ / Ⅱ)
|
||||||
saltAreaFlg: false, //염해지역용아이템사용 (true : 1 / false : 0)
|
saltAreaFlg: false, //염해지역용아이템사용 (true : 1 / false : 0)
|
||||||
installHeight: '', //설치높이
|
installHeight: '', //설치높이
|
||||||
conType: '0', //계약조건(잉여 / 전량)
|
conType: '0', //계약조건(잉여 / 전량)
|
||||||
@ -550,7 +550,7 @@ export default function StuffDetail() {
|
|||||||
|
|
||||||
//면조도구분 surfaceType null로 내려오면 셋팅 안하고 저장할때 필수값 체크하도록
|
//면조도구분 surfaceType null로 내려오면 셋팅 안하고 저장할때 필수값 체크하도록
|
||||||
// form.setValue('surfaceType', 'Ⅱ')
|
// form.setValue('surfaceType', 'Ⅱ')
|
||||||
// form.setValue('surfaceType', 'III・IV')
|
// form.setValue('surfaceType', 'Ⅲ・Ⅳ')
|
||||||
form.setValue('surfaceType', detailData.surfaceType)
|
form.setValue('surfaceType', detailData.surfaceType)
|
||||||
//염해지역용아이템사용 saltAreaFlg 1이면 true
|
//염해지역용아이템사용 saltAreaFlg 1이면 true
|
||||||
form.setValue('saltAreaFlg', detailData.saltAreaFlg === '1' ? true : false)
|
form.setValue('saltAreaFlg', detailData.saltAreaFlg === '1' ? true : false)
|
||||||
@ -872,6 +872,9 @@ export default function StuffDetail() {
|
|||||||
form.setValue('standardWindSpeedId', `WL_${info.windSpeed}`)
|
form.setValue('standardWindSpeedId', `WL_${info.windSpeed}`)
|
||||||
form.setValue('verticalSnowCover', info.verticalSnowCover)
|
form.setValue('verticalSnowCover', info.verticalSnowCover)
|
||||||
form.setValue('surfaceType', info.surfaceType)
|
form.setValue('surfaceType', info.surfaceType)
|
||||||
|
if (info.surfaceType === 'Ⅱ') {
|
||||||
|
form.setValue('saltAreaFlg', true)
|
||||||
|
}
|
||||||
form.setValue('installHeight', info.installHeight)
|
form.setValue('installHeight', info.installHeight)
|
||||||
form.setValue('remarks', info.remarks)
|
form.setValue('remarks', info.remarks)
|
||||||
|
|
||||||
@ -893,6 +896,15 @@ export default function StuffDetail() {
|
|||||||
form.setValue('standardWindSpeedId', info.windSpeed)
|
form.setValue('standardWindSpeedId', info.windSpeed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//면조도구분surfaceType & 염해지역용아이템사용saltAreaFlg 컨트롤
|
||||||
|
const handleRadioChange = (e) => {
|
||||||
|
if (e.target.value === 'Ⅱ') {
|
||||||
|
form.setValue('saltAreaFlg', true)
|
||||||
|
} else {
|
||||||
|
form.setValue('saltAreaFlg', false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//receiveUser: '', //담당자
|
//receiveUser: '', //담당자
|
||||||
const _receiveUser = watch('receiveUser')
|
const _receiveUser = watch('receiveUser')
|
||||||
//objectName: '', //물건명
|
//objectName: '', //물건명
|
||||||
@ -1561,7 +1573,7 @@ export default function StuffDetail() {
|
|||||||
onChange={onSelectionChange2}
|
onChange={onSelectionChange2}
|
||||||
getOptionLabel={(x) => x.saleStoreName}
|
getOptionLabel={(x) => x.saleStoreName}
|
||||||
getOptionValue={(x) => x.saleStoreId}
|
getOptionValue={(x) => x.saleStoreId}
|
||||||
isDisabled={otherSaleStoreList.length > 1 ? false : true}
|
isDisabled={otherSaleStoreList.length > 0 ? false : true}
|
||||||
isClearable={true}
|
isClearable={true}
|
||||||
value={otherSaleStoreList.filter(function (option) {
|
value={otherSaleStoreList.filter(function (option) {
|
||||||
return option.saleStoreId === otherSelOptions
|
return option.saleStoreId === otherSelOptions
|
||||||
@ -1715,11 +1727,29 @@ export default function StuffDetail() {
|
|||||||
<td>
|
<td>
|
||||||
<div className="flx-box">
|
<div className="flx-box">
|
||||||
<div className="d-check-radio light mr10">
|
<div className="d-check-radio light mr10">
|
||||||
<input type="radio" name="surfaceType" value="III・IV" id="surfaceType0" {...form.register('surfaceType')} />
|
<input
|
||||||
<label htmlFor="surfaceType0">III・IV</label>
|
type="radio"
|
||||||
|
name="surfaceType"
|
||||||
|
value="Ⅲ・Ⅳ"
|
||||||
|
id="surfaceType0"
|
||||||
|
{...form.register('surfaceType')}
|
||||||
|
onChange={(e) => {
|
||||||
|
handleRadioChange(e)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<label htmlFor="surfaceType0">Ⅲ・Ⅳ</label>
|
||||||
</div>
|
</div>
|
||||||
<div className="d-check-radio light mr10">
|
<div className="d-check-radio light mr10">
|
||||||
<input type="radio" name="surfaceType" value="Ⅱ" id="surfaceType1" {...form.register('surfaceType')} />
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="surfaceType"
|
||||||
|
value="Ⅱ"
|
||||||
|
id="surfaceType1"
|
||||||
|
{...form.register('surfaceType')}
|
||||||
|
onChange={(e) => {
|
||||||
|
handleRadioChange(e)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<label htmlFor="surfaceType1">Ⅱ</label>
|
<label htmlFor="surfaceType1">Ⅱ</label>
|
||||||
</div>
|
</div>
|
||||||
<div className="d-check-box light mr5">
|
<div className="d-check-box light mr5">
|
||||||
@ -2211,11 +2241,29 @@ export default function StuffDetail() {
|
|||||||
<td>
|
<td>
|
||||||
<div className="flx-box">
|
<div className="flx-box">
|
||||||
<div className="d-check-radio light mr10">
|
<div className="d-check-radio light mr10">
|
||||||
<input type="radio" name="surfaceType" value="III・IV" id="surfaceType0" {...form.register('surfaceType')} />
|
<input
|
||||||
<label htmlFor="surfaceType0">III・IV</label>
|
type="radio"
|
||||||
|
name="surfaceType"
|
||||||
|
value="Ⅲ・Ⅳ"
|
||||||
|
id="surfaceType0"
|
||||||
|
{...form.register('surfaceType')}
|
||||||
|
onChange={(e) => {
|
||||||
|
handleRadioChange(e)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<label htmlFor="surfaceType0">Ⅲ・Ⅳ</label>
|
||||||
</div>
|
</div>
|
||||||
<div className="d-check-radio light mr10">
|
<div className="d-check-radio light mr10">
|
||||||
<input type="radio" name="surfaceType" value="Ⅱ" id="surfaceType1" {...form.register('surfaceType')} />
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="surfaceType"
|
||||||
|
value="Ⅱ"
|
||||||
|
id="surfaceType1"
|
||||||
|
{...form.register('surfaceType')}
|
||||||
|
onChange={(e) => {
|
||||||
|
handleRadioChange(e)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<label htmlFor="surfaceType1">Ⅱ</label>
|
<label htmlFor="surfaceType1">Ⅱ</label>
|
||||||
</div>
|
</div>
|
||||||
<div className="d-check-box light mr5">
|
<div className="d-check-box light mr5">
|
||||||
|
|||||||
@ -12,7 +12,6 @@ import { isEmptyArray } from '@/util/common-utils'
|
|||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import SingleDatePicker from '../common/datepicker/SingleDatePicker'
|
import SingleDatePicker from '../common/datepicker/SingleDatePicker'
|
||||||
import { sessionStore } from '@/store/commonAtom'
|
|
||||||
import { useMessage } from '@/hooks/useMessage'
|
import { useMessage } from '@/hooks/useMessage'
|
||||||
import { isObjectNotEmpty } from '@/util/common-utils'
|
import { isObjectNotEmpty } from '@/util/common-utils'
|
||||||
|
|
||||||
@ -20,7 +19,6 @@ import { SessionContext } from '@/app/SessionProvider'
|
|||||||
|
|
||||||
export default function StuffSearchCondition() {
|
export default function StuffSearchCondition() {
|
||||||
const { session } = useContext(SessionContext)
|
const { session } = useContext(SessionContext)
|
||||||
const sessionState = useRecoilValue(sessionStore)
|
|
||||||
const setAppMessageState = useSetRecoilState(appMessageStore)
|
const setAppMessageState = useSetRecoilState(appMessageStore)
|
||||||
const globalLocaleState = useRecoilValue(globalLocaleStore)
|
const globalLocaleState = useRecoilValue(globalLocaleStore)
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
@ -163,7 +161,7 @@ export default function StuffSearchCondition() {
|
|||||||
} else {
|
} else {
|
||||||
if (session.storeLvl === '1') {
|
if (session.storeLvl === '1') {
|
||||||
//T01아닌 1차점일때
|
//T01아닌 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 {
|
} else {
|
||||||
url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=1&userId=${session?.userId}`
|
url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=1&userId=${session?.userId}`
|
||||||
}
|
}
|
||||||
@ -186,34 +184,36 @@ export default function StuffSearchCondition() {
|
|||||||
setSchSelSaleStoreList(allList)
|
setSchSelSaleStoreList(allList)
|
||||||
setFavoriteStoreList(favList)
|
setFavoriteStoreList(favList)
|
||||||
setShowSaleStoreList(favList)
|
setShowSaleStoreList(favList)
|
||||||
setSchSelSaleStoreId(session?.storeId)
|
// setSchSelSaleStoreId(session?.storeId)
|
||||||
setStuffSearch({
|
setStuffSearch({
|
||||||
...stuffSearch,
|
...stuffSearch,
|
||||||
code: 'S',
|
code: 'S',
|
||||||
schSelSaleStoreId: session?.storeId,
|
// schSelSaleStoreId: session?.storeId,
|
||||||
})
|
})
|
||||||
|
|
||||||
//T01일때 2차 판매점 호출하기 디폴트로 1차점을 본인으로 셋팅해서 세션storeId사용
|
//T01일때 2차 판매점 호출하기 디폴트로 1차점을 본인으로 셋팅해서 세션storeId사용
|
||||||
url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=0&userId=${session?.userId}`
|
// 디폴트 셋팅 안하기로
|
||||||
|
// url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=0&userId=${session?.userId}`
|
||||||
|
|
||||||
get({ url: url }).then((res) => {
|
// get({ url: url }).then((res) => {
|
||||||
if (!isEmptyArray(res)) {
|
// if (!isEmptyArray(res)) {
|
||||||
res.map((row) => {
|
// res.map((row) => {
|
||||||
row.value = row.saleStoreId
|
// row.value = row.saleStoreId
|
||||||
row.label = row.saleStoreName
|
// row.label = row.saleStoreName
|
||||||
})
|
// })
|
||||||
|
|
||||||
otherList = res
|
// otherList = res
|
||||||
setOtherSaleStoreList(otherList)
|
// setOtherSaleStoreList(otherList)
|
||||||
} else {
|
// } else {
|
||||||
setOtherSaleStoreList([])
|
// setOtherSaleStoreList([])
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
} else {
|
} else {
|
||||||
if (session?.storeLvl === '1') {
|
if (session?.storeLvl === '1') {
|
||||||
allList = res
|
allList = res
|
||||||
favList = res.filter((row) => row.priority !== 'B')
|
favList = res.filter((row) => row.priority !== 'B')
|
||||||
otherList = res.filter((row) => row.firstAgentYn === 'N')
|
otherList = res.filter((row) => row.firstAgentYn === 'N')
|
||||||
|
|
||||||
setSchSelSaleStoreList(allList)
|
setSchSelSaleStoreList(allList)
|
||||||
setFavoriteStoreList(allList)
|
setFavoriteStoreList(allList)
|
||||||
setShowSaleStoreList(allList)
|
setShowSaleStoreList(allList)
|
||||||
@ -323,6 +323,12 @@ export default function StuffSearchCondition() {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setStartDate(stuffSearch?.schFromDt ? stuffSearch.schFromDt : dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD'))
|
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'))
|
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])
|
}, [stuffSearch])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -385,7 +391,7 @@ export default function StuffSearchCondition() {
|
|||||||
ref={objectNoRef}
|
ref={objectNoRef}
|
||||||
className="input-light"
|
className="input-light"
|
||||||
defaultValue={stuffSearch?.schObjectNo ? stuffSearch.schObjectNo : objectNo}
|
defaultValue={stuffSearch?.schObjectNo ? stuffSearch.schObjectNo : objectNo}
|
||||||
onChange={(e) => {
|
onChange={() => {
|
||||||
setObjectNo(objectNoRef.current.value)
|
setObjectNo(objectNoRef.current.value)
|
||||||
}}
|
}}
|
||||||
onKeyUp={handleByOnKeyUp}
|
onKeyUp={handleByOnKeyUp}
|
||||||
@ -400,7 +406,7 @@ export default function StuffSearchCondition() {
|
|||||||
ref={saleStoreNameRef}
|
ref={saleStoreNameRef}
|
||||||
className="input-light"
|
className="input-light"
|
||||||
defaultValue={stuffSearch?.schSaleStoreName ? stuffSearch.schSaleStoreName : saleStoreName}
|
defaultValue={stuffSearch?.schSaleStoreName ? stuffSearch.schSaleStoreName : saleStoreName}
|
||||||
onChange={(e) => {
|
onChange={() => {
|
||||||
setSaleStoreName(saleStoreNameRef.current.value)
|
setSaleStoreName(saleStoreNameRef.current.value)
|
||||||
}}
|
}}
|
||||||
onKeyUp={handleByOnKeyUp}
|
onKeyUp={handleByOnKeyUp}
|
||||||
@ -415,7 +421,7 @@ export default function StuffSearchCondition() {
|
|||||||
ref={addressRef}
|
ref={addressRef}
|
||||||
className="input-light"
|
className="input-light"
|
||||||
defaultValue={stuffSearch?.schAddress ? stuffSearch.schAddress : address}
|
defaultValue={stuffSearch?.schAddress ? stuffSearch.schAddress : address}
|
||||||
onChange={(e) => {
|
onChange={() => {
|
||||||
setAddress(addressRef.current.value)
|
setAddress(addressRef.current.value)
|
||||||
}}
|
}}
|
||||||
onKeyUp={handleByOnKeyUp}
|
onKeyUp={handleByOnKeyUp}
|
||||||
@ -430,7 +436,7 @@ export default function StuffSearchCondition() {
|
|||||||
ref={dispCompanyNameRef}
|
ref={dispCompanyNameRef}
|
||||||
className="input-light"
|
className="input-light"
|
||||||
defaultValue={stuffSearch?.schDispCompanyName ? stuffSearch.schDispCompanyName : dispCompanyName}
|
defaultValue={stuffSearch?.schDispCompanyName ? stuffSearch.schDispCompanyName : dispCompanyName}
|
||||||
onChange={(e) => {
|
onChange={() => {
|
||||||
setDispCompanyName(dispCompanyNameRef.current.value)
|
setDispCompanyName(dispCompanyNameRef.current.value)
|
||||||
}}
|
}}
|
||||||
onKeyUp={handleByOnKeyUp}
|
onKeyUp={handleByOnKeyUp}
|
||||||
@ -447,7 +453,7 @@ export default function StuffSearchCondition() {
|
|||||||
ref={objectNameRef}
|
ref={objectNameRef}
|
||||||
className="input-light"
|
className="input-light"
|
||||||
defaultValue={stuffSearch?.schObjectName ? stuffSearch.schObjectName : objectName}
|
defaultValue={stuffSearch?.schObjectName ? stuffSearch.schObjectName : objectName}
|
||||||
onChange={(e) => {
|
onChange={() => {
|
||||||
setobjectName(objectNameRef.current.value)
|
setobjectName(objectNameRef.current.value)
|
||||||
}}
|
}}
|
||||||
onKeyUp={handleByOnKeyUp}
|
onKeyUp={handleByOnKeyUp}
|
||||||
@ -462,7 +468,7 @@ export default function StuffSearchCondition() {
|
|||||||
className="input-light"
|
className="input-light"
|
||||||
ref={receiveUserRef}
|
ref={receiveUserRef}
|
||||||
defaultValue={stuffSearch?.schReceiveUser ? stuffSearch.schReceiveUser : receiveUser}
|
defaultValue={stuffSearch?.schReceiveUser ? stuffSearch.schReceiveUser : receiveUser}
|
||||||
onChange={(e) => {
|
onChange={() => {
|
||||||
setReceiveUser(receiveUserRef.current.value)
|
setReceiveUser(receiveUserRef.current.value)
|
||||||
}}
|
}}
|
||||||
onKeyUp={handleByOnKeyUp}
|
onKeyUp={handleByOnKeyUp}
|
||||||
@ -580,7 +586,7 @@ export default function StuffSearchCondition() {
|
|||||||
onChange={onSelectionChange2}
|
onChange={onSelectionChange2}
|
||||||
getOptionLabel={(x) => x.saleStoreName}
|
getOptionLabel={(x) => x.saleStoreName}
|
||||||
getOptionValue={(x) => x.saleStoreId}
|
getOptionValue={(x) => x.saleStoreId}
|
||||||
isDisabled={otherSaleStoreList.length > 1 ? false : true}
|
isDisabled={otherSaleStoreList.length > 0 ? false : true}
|
||||||
isClearable={true}
|
isClearable={true}
|
||||||
value={otherSaleStoreList.filter(function (option) {
|
value={otherSaleStoreList.filter(function (option) {
|
||||||
return option.saleStoreId === otherSaleStoreId
|
return option.saleStoreId === otherSaleStoreId
|
||||||
|
|||||||
365
src/components/simulator/Simulator.jsx
Normal file
365
src/components/simulator/Simulator.jsx
Normal file
@ -0,0 +1,365 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import 'chart.js/auto'
|
||||||
|
import { Bar } from 'react-chartjs-2'
|
||||||
|
import dayjs from 'dayjs'
|
||||||
|
|
||||||
|
import { useEffect, useState, useRef } from 'react'
|
||||||
|
import { useRecoilValue } from 'recoil'
|
||||||
|
import { floorPlanObjectState } from '@/store/floorPlanObjectAtom'
|
||||||
|
|
||||||
|
import { useAxios } from '@/hooks/useAxios'
|
||||||
|
import { useMessage } from '@/hooks/useMessage'
|
||||||
|
import { usePlan } from '@/hooks/usePlan'
|
||||||
|
import { useCanvasMenu } from '@/hooks/common/useCanvasMenu'
|
||||||
|
|
||||||
|
import { convertNumberToPriceDecimal } from '@/util/common-utils'
|
||||||
|
|
||||||
|
export default function Simulator() {
|
||||||
|
const { plans } = usePlan()
|
||||||
|
const plan = plans.find((plan) => plan.isCurrent === true)
|
||||||
|
|
||||||
|
const chartRef = useRef(null)
|
||||||
|
|
||||||
|
// recoil 물건번호
|
||||||
|
const objectRecoil = useRecoilValue(floorPlanObjectState)
|
||||||
|
const [objectNo, setObjectNo] = useState('')
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setObjectNo(objectRecoil.floorPlanObjectNo)
|
||||||
|
}, [objectRecoil])
|
||||||
|
|
||||||
|
// 캔버스 메뉴 넘버 셋팅
|
||||||
|
const { setMenuNumber } = useCanvasMenu()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setMenuNumber(6)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const { get } = useAxios()
|
||||||
|
const { getMessage } = useMessage()
|
||||||
|
|
||||||
|
// 차트 관련
|
||||||
|
const [chartData, setChartData] = useState([])
|
||||||
|
const data = {
|
||||||
|
labels: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: 'kWh',
|
||||||
|
data: chartData.slice(0, 12),
|
||||||
|
|
||||||
|
backgroundColor: [
|
||||||
|
'rgba(255, 99, 132, 0.2)',
|
||||||
|
'rgba(54, 162, 235, 0.2)',
|
||||||
|
'rgba(255, 206, 86, 0.2)',
|
||||||
|
'rgba(75, 192, 192, 0.2)',
|
||||||
|
'rgba(153, 102, 255, 0.2)',
|
||||||
|
'rgba(255, 159, 64, 0.2)',
|
||||||
|
'rgba(0, 99, 132, 0.2)',
|
||||||
|
'rgba(0, 162, 235, 0.2)',
|
||||||
|
'rgba(0, 206, 86, 0.2)',
|
||||||
|
'rgba(0, 192, 192, 0.2)',
|
||||||
|
'rgba(0, 102, 255, 0.2)',
|
||||||
|
'rgba(0, 159, 64, 0.2)',
|
||||||
|
],
|
||||||
|
borderColor: [
|
||||||
|
'rgba(255, 99, 132, 0.2)',
|
||||||
|
'rgba(54, 162, 235, 0.2)',
|
||||||
|
'rgba(255, 206, 86, 0.2)',
|
||||||
|
'rgba(75, 192, 192, 0.2)',
|
||||||
|
'rgba(153, 102, 255, 0.2)',
|
||||||
|
'rgba(255, 159, 64, 0.2)',
|
||||||
|
'rgba(0, 99, 132, 0.2)',
|
||||||
|
'rgba(0, 162, 235, 0.2)',
|
||||||
|
'rgba(0, 206, 86, 0.2)',
|
||||||
|
'rgba(0, 192, 192, 0.2)',
|
||||||
|
'rgba(0, 102, 255, 0.2)',
|
||||||
|
'rgba(0, 159, 64, 0.2)',
|
||||||
|
],
|
||||||
|
borderWidth: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
position: 'top',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
scales: {
|
||||||
|
x: {
|
||||||
|
grid: {
|
||||||
|
display: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
beginAtZero: true,
|
||||||
|
grid: {
|
||||||
|
display: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (objectNo) {
|
||||||
|
fetchObjectDetail(objectNo)
|
||||||
|
}
|
||||||
|
fetchSimulatorNotice()
|
||||||
|
}, [objectNo, plan])
|
||||||
|
|
||||||
|
// 물건 상세 정보 조회
|
||||||
|
const [objectDetail, setObjectDetail] = useState({})
|
||||||
|
|
||||||
|
// 모듈배치정보 조회
|
||||||
|
const [moduleInfoList, setModuleInfoList] = useState([])
|
||||||
|
|
||||||
|
// 파워컨디셔너 조회
|
||||||
|
const [pcsInfoList, setPcsInfoList] = useState([])
|
||||||
|
|
||||||
|
const fetchObjectDetail = async (objectNo) => {
|
||||||
|
const apiUrl = `/api/pwrGnrSimulation/calculations?objectNo=${objectNo}&planNo=${plan?.id}`
|
||||||
|
const resultData = await get({ url: apiUrl })
|
||||||
|
if (resultData) {
|
||||||
|
setObjectDetail(resultData)
|
||||||
|
if (resultData.frcPwrGnrList) {
|
||||||
|
setChartData(resultData.frcPwrGnrList)
|
||||||
|
}
|
||||||
|
if (resultData.pcsList) {
|
||||||
|
setPcsInfoList(resultData.pcsList)
|
||||||
|
}
|
||||||
|
if (resultData.roofModuleList) {
|
||||||
|
setModuleInfoList(resultData.roofModuleList)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 시뮬레이션 안내사항 조회
|
||||||
|
const [content, setContent] = useState('')
|
||||||
|
|
||||||
|
const fetchSimulatorNotice = async () => {
|
||||||
|
get({ url: '/api/pwrGnrSimulation/guideInfo' }).then((res) => {
|
||||||
|
if (res.data) {
|
||||||
|
setContent(res.data.replaceAll('\n', '<br/>'))
|
||||||
|
} else {
|
||||||
|
setContent(getMessage('common.message.no.data'))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="sub-content estimate">
|
||||||
|
<div className="sub-content-inner">
|
||||||
|
<div className="sub-content-box">
|
||||||
|
<div className="sub-table-box">
|
||||||
|
<div className="estimate-list-wrap">
|
||||||
|
{/* 물건번호 */}
|
||||||
|
<div className="estimate-box">
|
||||||
|
<div className="estimate-tit">{getMessage('simulator.title.sub1')}</div>
|
||||||
|
<div className="estimate-name">
|
||||||
|
{objectDetail.objectNo} (Plan No: {objectDetail.planNo})
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/* 작성일 */}
|
||||||
|
<div className="estimate-box">
|
||||||
|
<div className="estimate-tit">{getMessage('simulator.title.sub2')}</div>
|
||||||
|
<div className="estimate-name">{`${dayjs(objectDetail.drawingEstimateCreateDate).format('YYYY.MM.DD')}`}</div>
|
||||||
|
</div>
|
||||||
|
{/* 시스템용량 */}
|
||||||
|
<div className="estimate-box">
|
||||||
|
<div className="estimate-tit">{getMessage('simulator.title.sub3')}</div>
|
||||||
|
<div className="estimate-name">{convertNumberToPriceDecimal(objectDetail.capacity)}kW</div>
|
||||||
|
</div>
|
||||||
|
{/* 연간예측발전량 */}
|
||||||
|
<div className="estimate-box">
|
||||||
|
<div className="estimate-tit">{getMessage('simulator.title.sub4')}</div>
|
||||||
|
<div className="estimate-name">{convertNumberToPriceDecimal(objectDetail.anlFrcsGnrt)}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="estimate-list-wrap">
|
||||||
|
{/* 도도부현 */}
|
||||||
|
<div className="estimate-box">
|
||||||
|
<div className="estimate-tit">{getMessage('simulator.title.sub5')}</div>
|
||||||
|
<div className="estimate-name">{objectDetail.prefName}</div>
|
||||||
|
</div>
|
||||||
|
{/* 일사량 관측지점 */}
|
||||||
|
<div className="estimate-box">
|
||||||
|
<div className="estimate-tit">{getMessage('simulator.title.sub6')}</div>
|
||||||
|
<div className="estimate-name">{objectDetail.areaName}</div>
|
||||||
|
</div>
|
||||||
|
{/* 적설조건 */}
|
||||||
|
<div className="estimate-box">
|
||||||
|
<div className="estimate-tit">{getMessage('simulator.title.sub7')}</div>
|
||||||
|
<div className="estimate-name">{objectDetail.snowfall}</div>
|
||||||
|
</div>
|
||||||
|
{/* 풍속조건 */}
|
||||||
|
<div className="estimate-box">
|
||||||
|
<div className="estimate-tit">{getMessage('simulator.title.sub8')}</div>
|
||||||
|
<div className="estimate-name">{objectDetail.standardWindSpeedId}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="sub-content-box">
|
||||||
|
<div className="chart-wrap">
|
||||||
|
<div className="chart-inner">
|
||||||
|
<div className="sub-table-box">
|
||||||
|
<div className="chart-box">
|
||||||
|
<Bar ref={chartRef} data={data} options={options} />
|
||||||
|
</div>
|
||||||
|
<div className="table-box-title-wrap">
|
||||||
|
<div className="title-wrap">
|
||||||
|
<h3>{getMessage('simulator.table.sub9')} </h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/* 예측발전량 */}
|
||||||
|
<div className="chart-month-table">
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>1月</th>
|
||||||
|
<th>2月</th>
|
||||||
|
<th>3月</th>
|
||||||
|
<th>4月</th>
|
||||||
|
<th>5月</th>
|
||||||
|
<th>6月</th>
|
||||||
|
<th>7月</th>
|
||||||
|
<th>8月</th>
|
||||||
|
<th>9月</th>
|
||||||
|
<th>10月</th>
|
||||||
|
<th>11月</th>
|
||||||
|
<th>12月</th>
|
||||||
|
<th>{getMessage('simulator.table.sub6')}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{chartData.length > 0 ? (
|
||||||
|
<tr>
|
||||||
|
{chartData.map((data) => (
|
||||||
|
<td key={data}>{convertNumberToPriceDecimal(data)}</td>
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
|
) : (
|
||||||
|
<tr>
|
||||||
|
<td colSpan={13}>{getMessage('common.message.no.data')}</td>
|
||||||
|
</tr>
|
||||||
|
)}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="chart-table-wrap">
|
||||||
|
<div className="sub-table-box">
|
||||||
|
<div className="module-table ">
|
||||||
|
<table className="small">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{getMessage('simulator.table.sub1')}</th>
|
||||||
|
<th>{getMessage('simulator.table.sub2')}</th>
|
||||||
|
<th>{getMessage('simulator.table.sub3')}</th>
|
||||||
|
<th>{getMessage('simulator.table.sub4')}</th>
|
||||||
|
<th>{getMessage('simulator.table.sub5')}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{moduleInfoList.length > 0 ? (
|
||||||
|
moduleInfoList.map((moduleInfo) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<tr key={moduleInfo.itemId}>
|
||||||
|
{/* 지붕면 */}
|
||||||
|
<td>{moduleInfo.roofSurface}</td>
|
||||||
|
{/* 경사각 */}
|
||||||
|
<td>{convertNumberToPriceDecimal(moduleInfo.slope)}寸</td>
|
||||||
|
{/* 방위각(도) */}
|
||||||
|
<td>{convertNumberToPriceDecimal(moduleInfo.angle)}</td>
|
||||||
|
{/* 태양전지모듈 */}
|
||||||
|
<td>
|
||||||
|
<div className="overflow-lab">{moduleInfo.itemNo}</div>
|
||||||
|
</td>
|
||||||
|
{/* 매수 */}
|
||||||
|
<td>{moduleInfo.amount}</td>
|
||||||
|
</tr>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
) : (
|
||||||
|
<tr>
|
||||||
|
<td colSpan={5}>{getMessage('common.message.no.data')}</td>
|
||||||
|
</tr>
|
||||||
|
)}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{moduleInfoList.length > 0 && (
|
||||||
|
<div className="module-total">
|
||||||
|
<div className="total-title">{getMessage('simulator.table.sub6')}</div>
|
||||||
|
<div className="total-num">
|
||||||
|
{moduleInfoList.reduce((acc, moduleInfo) => convertNumberToPriceDecimal(Number(acc) + Number(moduleInfo.amount)), 0)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="sub-table-box">
|
||||||
|
<div className="module-table ">
|
||||||
|
<table className="big">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{getMessage('simulator.table.sub7')}</th>
|
||||||
|
<th>{getMessage('simulator.table.sub8')}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{pcsInfoList.length > 0 ? (
|
||||||
|
pcsInfoList.map((pcsInfo) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<tr key={pcsInfo.itemId}>
|
||||||
|
{/* 파워컨디셔너 */}
|
||||||
|
<td className="al-l">
|
||||||
|
<div className="overflow-lab">{pcsInfo.itemNo}</div>
|
||||||
|
</td>
|
||||||
|
{/* 대 */}
|
||||||
|
<td>{pcsInfo.amount}</td>
|
||||||
|
</tr>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
) : (
|
||||||
|
<tr>
|
||||||
|
<td colSpan={2}>{getMessage('common.message.no.data')}</td>
|
||||||
|
</tr>
|
||||||
|
)}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="sub-content-box">
|
||||||
|
<div className="sub-table-box">
|
||||||
|
<div className="simulation-guide-wrap">
|
||||||
|
<div className="simulation-tit-wrap">
|
||||||
|
<span>
|
||||||
|
{getMessage('simulator.notice.sub1')}
|
||||||
|
<br />
|
||||||
|
{getMessage('simulator.notice.sub2')}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{/* 시뮬레이션 안내사항 */}
|
||||||
|
<div
|
||||||
|
className="simulation-guide-box"
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: content,
|
||||||
|
}}
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -7,6 +7,7 @@ import { useFont } from '@/hooks/common/useFont'
|
|||||||
import { useGrid } from '@/hooks/common/useGrid'
|
import { useGrid } from '@/hooks/common/useGrid'
|
||||||
import { globalFontAtom } from '@/store/fontAtom'
|
import { globalFontAtom } from '@/store/fontAtom'
|
||||||
import { useRoof } from '@/hooks/common/useRoof'
|
import { useRoof } from '@/hooks/common/useRoof'
|
||||||
|
import { usePolygon } from '@/hooks/usePolygon'
|
||||||
|
|
||||||
export function useCanvasConfigInitialize() {
|
export function useCanvasConfigInitialize() {
|
||||||
const canvas = useRecoilValue(canvasState)
|
const canvas = useRecoilValue(canvasState)
|
||||||
@ -20,6 +21,7 @@ export function useCanvasConfigInitialize() {
|
|||||||
const {} = useFont()
|
const {} = useFont()
|
||||||
const {} = useGrid()
|
const {} = useGrid()
|
||||||
const {} = useRoof()
|
const {} = useRoof()
|
||||||
|
const { drawDirectionArrow } = usePolygon()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!canvas) return
|
if (!canvas) return
|
||||||
@ -173,6 +175,7 @@ export function useCanvasConfigInitialize() {
|
|||||||
|
|
||||||
//그룹 객체로 다시 만든다 (좌표때문에)
|
//그룹 객체로 다시 만든다 (좌표때문에)
|
||||||
const group = new fabric.Group(objectArray, {
|
const group = new fabric.Group(objectArray, {
|
||||||
|
...objectArray,
|
||||||
groupId: id,
|
groupId: id,
|
||||||
name: objectsName,
|
name: objectsName,
|
||||||
selectable: true,
|
selectable: true,
|
||||||
|
|||||||
112
src/hooks/common/useRefFiles.js
Normal file
112
src/hooks/common/useRefFiles.js
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
import { useRef, useState } from 'react'
|
||||||
|
import { useRecoilState } from 'recoil'
|
||||||
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
|
|
||||||
|
import { useSwal } from '@/hooks/useSwal'
|
||||||
|
import { convertDwgToPng } from '@/lib/cadAction'
|
||||||
|
import { useAxios } from '../useAxios'
|
||||||
|
import { currentCanvasPlanState } from '@/store/canvasAtom'
|
||||||
|
|
||||||
|
export default function useRefFiles() {
|
||||||
|
const converterUrl = process.env.NEXT_PUBLIC_CONVERTER_API_URL
|
||||||
|
const [refImage, setRefImage] = useState(null)
|
||||||
|
const [refFileMethod, setRefFileMethod] = useState('1')
|
||||||
|
const [mapPositionAddress, setMapPositionAddress] = useState('')
|
||||||
|
const [currentCanvasPlan, setCurrentCanvasPlan] = useRecoilState(currentCanvasPlanState)
|
||||||
|
const queryRef = useRef(null)
|
||||||
|
|
||||||
|
const { swalFire } = useSwal()
|
||||||
|
const { get, promisePut } = useAxios()
|
||||||
|
// const { currentCanvasPlan, setCurrentCanvasPlan } = usePlan()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 파일 불러오기 버튼 컨트롤
|
||||||
|
* @param {*} file
|
||||||
|
*/
|
||||||
|
const handleRefFile = (file) => {
|
||||||
|
setRefImage(file)
|
||||||
|
file.name.split('.').pop() === 'dwg' ? handleUploadRefFile(file) : () => {}
|
||||||
|
// handleUploadRefFile(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 파일 삭제
|
||||||
|
*/
|
||||||
|
const handleFileDelete = () => {
|
||||||
|
setRefImage(null)
|
||||||
|
setCurrentCanvasPlan((prev) => ({ ...prev, bgFileName: null }))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 주소 삭제
|
||||||
|
*/
|
||||||
|
const handleAddressDelete = () => {
|
||||||
|
setCurrentCanvasPlan((prev) => ({ ...prev, mapPositionAddress: null }))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 주소로 구글 맵 이미지 다운로드
|
||||||
|
*/
|
||||||
|
const handleMapImageDown = async () => {
|
||||||
|
if (queryRef.current.value === '' || queryRef.current.value === null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await get({ url: `http://localhost:3000/api/html2canvas?q=${queryRef.current.value}&fileNm=${uuidv4()}&zoom=20` })
|
||||||
|
console.log('🚀 ~ handleMapImageDown ~ res:', res)
|
||||||
|
setCurrentCanvasPlan((prev) => ({ ...prev, bgImageName: res.fileNm, mapPositionAddress: queryRef.current.value }))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 현재 플랜이 변경되면 플랜 상태 저장
|
||||||
|
*/
|
||||||
|
// useEffect(() => {
|
||||||
|
// const handleCurrentPlan = async () => {
|
||||||
|
// await promisePut({ url: '/api/canvas-management/canvas-statuses', data: currentCanvasPlan }).then((res) => {
|
||||||
|
// console.log('🚀 ~ awaitpromisePut ~ res:', res)
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// handleCurrentPlan()
|
||||||
|
// }, [currentCanvasPlan])
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RefFile이 캐드 도면 파일일 경우 변환하여 이미지로 저장
|
||||||
|
* @param {*} file
|
||||||
|
*/
|
||||||
|
const handleUploadRefFile = async (file) => {
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append('file', file)
|
||||||
|
|
||||||
|
await promisePost({ url: converterUrl, data: formData })
|
||||||
|
.then((res) => {
|
||||||
|
convertDwgToPng(res.data.Files[0].FileName, res.data.Files[0].FileData)
|
||||||
|
swalFire({ text: '파일 변환 성공' })
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
swalFire({ text: '파일 변환 실패', icon: 'error' })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 라디오 버튼 컨트롤
|
||||||
|
* @param {*} e
|
||||||
|
*/
|
||||||
|
const handleRefFileMethod = (e) => {
|
||||||
|
setRefFileMethod(e.target.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
refImage,
|
||||||
|
queryRef,
|
||||||
|
setRefImage,
|
||||||
|
handleRefFile,
|
||||||
|
refFileMethod,
|
||||||
|
setRefFileMethod,
|
||||||
|
mapPositionAddress,
|
||||||
|
setMapPositionAddress,
|
||||||
|
handleRefFileMethod,
|
||||||
|
handleFileDelete,
|
||||||
|
handleAddressDelete,
|
||||||
|
handleMapImageDown,
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -5,6 +5,7 @@ import { globalLocaleStore } from '@/store/localeAtom'
|
|||||||
import { estimateState, floorPlanObjectState } from '@/store/floorPlanObjectAtom'
|
import { estimateState, floorPlanObjectState } from '@/store/floorPlanObjectAtom'
|
||||||
import { isObjectNotEmpty } from '@/util/common-utils'
|
import { isObjectNotEmpty } from '@/util/common-utils'
|
||||||
import { SessionContext } from '@/app/SessionProvider'
|
import { SessionContext } from '@/app/SessionProvider'
|
||||||
|
import { useMessage } from '@/hooks/useMessage'
|
||||||
|
|
||||||
const reducer = (prevState, nextState) => {
|
const reducer = (prevState, nextState) => {
|
||||||
return { ...prevState, ...nextState }
|
return { ...prevState, ...nextState }
|
||||||
@ -21,35 +22,14 @@ const defaultEstimateData = {
|
|||||||
estimateType: 'YJOD', //주문분류
|
estimateType: 'YJOD', //주문분류
|
||||||
remarks: '', //비고
|
remarks: '', //비고
|
||||||
estimateOption: '', //견적특이사항
|
estimateOption: '', //견적특이사항
|
||||||
// itemList: [{ id: 1, name: '' }],
|
itemList: [],
|
||||||
//아이템에 필요없는거 빼기
|
|
||||||
itemList: [
|
|
||||||
{
|
|
||||||
amount: '',
|
|
||||||
fileUploadFlg: '',
|
|
||||||
itemChangeFlg: '',
|
|
||||||
itemGroup: '',
|
|
||||||
itemId: '', //키값??
|
|
||||||
itemName: '',
|
|
||||||
itemNo: '',
|
|
||||||
moduleFlg: '',
|
|
||||||
objectNo: '',
|
|
||||||
pkgMaterialFlg: '',
|
|
||||||
planNo: '',
|
|
||||||
pnowW: '',
|
|
||||||
salePrice: '',
|
|
||||||
saleTotPrice: '',
|
|
||||||
specification: '',
|
|
||||||
unit: '',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
fileList: [],
|
fileList: [],
|
||||||
|
fileFlg: '0', //후일 자료 제출 (체크 1 노체크 0)
|
||||||
|
priceCd: '',
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper functions
|
// Helper functions
|
||||||
// const updateItemInList = (itemList, id, updates) => {
|
|
||||||
const updateItemInList = (itemList, itemId, updates) => {
|
const updateItemInList = (itemList, itemId, updates) => {
|
||||||
// return itemList.map((item) => (item.id === id ? { ...item, ...updates } : item))
|
|
||||||
return itemList.map((item) => (item.itemId === itemId ? { ...item, ...updates } : item))
|
return itemList.map((item) => (item.itemId === itemId ? { ...item, ...updates } : item))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,6 +39,8 @@ export const useEstimateController = (planNo) => {
|
|||||||
const objectRecoil = useRecoilValue(floorPlanObjectState)
|
const objectRecoil = useRecoilValue(floorPlanObjectState)
|
||||||
const [estimateData, setEstimateData] = useRecoilState(estimateState)
|
const [estimateData, setEstimateData] = useRecoilState(estimateState)
|
||||||
|
|
||||||
|
const { getMessage } = useMessage()
|
||||||
|
|
||||||
const { get, post, promisePost } = useAxios(globalLocaleState)
|
const { get, post, promisePost } = useAxios(globalLocaleState)
|
||||||
|
|
||||||
const [isLoading, setIsLoading] = useState(false)
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
@ -87,81 +69,86 @@ export const useEstimateController = (planNo) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// const updateItem = (id, updates) => {
|
|
||||||
const updateItem = (itemId, updates) => {
|
const updateItem = (itemId, updates) => {
|
||||||
setState({
|
setState({
|
||||||
// itemList: updateItemInList(state.itemList, id, updates),
|
|
||||||
itemList: updateItemInList(state.itemList, itemId, updates),
|
itemList: updateItemInList(state.itemList, itemId, updates),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const addItem = () => {
|
const addItem = () => {
|
||||||
// const newId = Math.max(...state.itemList.map((item) => item.id)) + 1
|
const newItemDispOrder = (Math.max(...state.itemList.map((item) => item.dispOrder)) + 1) * 100
|
||||||
const newItemId = Math.max(...state.itemList.map((item) => item.itemId)) + 1
|
|
||||||
setState({
|
setState({
|
||||||
// itemList: [...state.itemList, { id: newId, name: '' }],
|
|
||||||
//셋팅할필요없는거 빼기
|
|
||||||
itemList: [
|
itemList: [
|
||||||
...state.itemList,
|
...state.itemList,
|
||||||
{
|
{
|
||||||
itemId: newItemId,
|
dispOrder: newItemDispOrder,
|
||||||
amount: '',
|
itemId: '', //제품번호
|
||||||
fileUploadFlg: '',
|
itemNo: '', //형명
|
||||||
itemChangeFlg: '',
|
|
||||||
itemGroup: '',
|
|
||||||
itemName: '',
|
itemName: '',
|
||||||
itemNo: '',
|
amount: '', //수량
|
||||||
moduleFlg: '',
|
unitPrice: '0',
|
||||||
objectNo: '',
|
unit: '', //단위
|
||||||
pkgMaterialFlg: '',
|
salePrice: '0', //단가
|
||||||
planNo: '',
|
saleTotPrice: '0', //금액(부가세별도)
|
||||||
pnowW: '',
|
|
||||||
salePrice: '',
|
|
||||||
saleTotPrice: '',
|
|
||||||
specification: '',
|
|
||||||
unit: '',
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setEstimateData({ ...state, userId: session.userId })
|
setEstimateData({ ...state, userId: session.userId, sapSalesStoreCd: session.custCd })
|
||||||
//sapSalesStoreCd 추가예정 필수값
|
|
||||||
// setEstimateData({ ...state, userId: session.userId, sapSalesStoreCd : session.sapSalesStoreCd })
|
|
||||||
}, [state])
|
}, [state])
|
||||||
|
|
||||||
//견적서 저장
|
//견적서 저장
|
||||||
const handleEstimateSubmit = async () => {
|
const handleEstimateSubmit = async () => {
|
||||||
|
//0. 필수체크
|
||||||
|
let flag = true
|
||||||
console.log('::담긴 estimateData:::', estimateData)
|
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) => {
|
//아이템 fileUploadFlg가1(첨부파일 필수)이 1개라도 있는데 후일 자료 제출(fileFlg) 체크안했으면(0) alert 저장안돼
|
||||||
console.log('파일저장::::::::::', res)
|
if (estimateData.itemList.length > 1) {
|
||||||
})
|
estimateData.itemList.map((row) => {
|
||||||
|
if (row.fileUploadFlg === '1') {
|
||||||
//2. 상세데이터 저장
|
if (estimateData.fileFlg === '0') {
|
||||||
|
alert(getMessage('estimate.detail.save.requiredMsg'))
|
||||||
console.log('상세저장시작!!')
|
flag = false
|
||||||
return
|
}
|
||||||
try {
|
}
|
||||||
const result = await promisePost({
|
|
||||||
url: ESTIMATE_API_ENDPOINT,
|
|
||||||
data: estimateData,
|
|
||||||
})
|
})
|
||||||
return result
|
}
|
||||||
} catch (error) {
|
if (flag) {
|
||||||
console.error('Failed to submit estimate:', error)
|
//1. 첨부파일 저장
|
||||||
throw error
|
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 post({ url: '/api/file/fileUpload', data: formData })
|
||||||
|
|
||||||
|
//2. 상세데이터 저장
|
||||||
|
return
|
||||||
|
await promisePost({ url: `${ESTIMATE_API_ENDPOINT}/save-estimate`, data: estimateData }).then((res) => {
|
||||||
|
if (res) {
|
||||||
|
alert(getMessage('estimate.detail.save.alertMsg'))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// try {
|
||||||
|
// const result = await promisePost({
|
||||||
|
// url: ESTIMATE_API_ENDPOINT,
|
||||||
|
// data: estimateData,
|
||||||
|
// })
|
||||||
|
// alert(getMessage('estimate.detail.save.alertMsg'))
|
||||||
|
// return result
|
||||||
|
// } catch (error) {
|
||||||
|
// console.error('Failed to submit estimate:', error)
|
||||||
|
// throw error
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -374,6 +374,9 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
|
|||||||
const trianglePolygon = pointsToTurfPolygon(triangleToPolygon(dormer))
|
const trianglePolygon = pointsToTurfPolygon(triangleToPolygon(dormer))
|
||||||
const selectedSurfacePolygon = polygonToTurfPolygon(selectedSurface)
|
const selectedSurfacePolygon = polygonToTurfPolygon(selectedSurface)
|
||||||
|
|
||||||
|
console.log('trianglePolygon', trianglePolygon)
|
||||||
|
console.log('selectedSurfacePolygon', selectedSurfacePolygon)
|
||||||
|
|
||||||
//지붕 밖으로 그렸을때
|
//지붕 밖으로 그렸을때
|
||||||
if (!turf.booleanWithin(trianglePolygon, selectedSurfacePolygon)) {
|
if (!turf.booleanWithin(trianglePolygon, selectedSurfacePolygon)) {
|
||||||
swalFire({ text: '도머를 배치할 수 없습니다.', icon: 'error' })
|
swalFire({ text: '도머를 배치할 수 없습니다.', icon: 'error' })
|
||||||
@ -496,6 +499,10 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
|
|||||||
})
|
})
|
||||||
canvas?.add(objectGroup)
|
canvas?.add(objectGroup)
|
||||||
|
|
||||||
|
objectGroup.getObjects().forEach((obj, index) => {
|
||||||
|
console.log(`최초 pathOffset ${index}`, obj.get('pathOffset'))
|
||||||
|
})
|
||||||
|
|
||||||
isDown = false
|
isDown = false
|
||||||
initEvent()
|
initEvent()
|
||||||
dbClickEvent()
|
dbClickEvent()
|
||||||
@ -917,20 +924,17 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
|
|||||||
originY: 'center',
|
originY: 'center',
|
||||||
left: changedCoords.x,
|
left: changedCoords.x,
|
||||||
top: changedCoords.y,
|
top: changedCoords.y,
|
||||||
|
width: width / 10,
|
||||||
|
height: height / 10,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (target.name === 'roof') {
|
//얘는 일단 도머에 적용함
|
||||||
//얘는 일단 도머에 적용함
|
if (target.type === 'group') {
|
||||||
if (target.type === 'group') {
|
target._objects.forEach((obj) => setSurfaceShapePattern(obj))
|
||||||
target._objects.forEach((obj) => setSurfaceShapePattern(obj))
|
|
||||||
} else {
|
|
||||||
setSurfaceShapePattern(target)
|
|
||||||
target.fire('modified')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// target.setCoords()
|
// target.setCoords()
|
||||||
canvas.renderAll()
|
canvas.renderAll()
|
||||||
|
|
||||||
if (target.type === 'group') reGroupObject(target)
|
if (target.type === 'group') reGroupObject(target)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import { globalLocaleStore } from '@/store/localeAtom'
|
|||||||
import { useMessage } from '@/hooks/useMessage'
|
import { useMessage } from '@/hooks/useMessage'
|
||||||
import { useAxios } from '@/hooks/useAxios'
|
import { useAxios } from '@/hooks/useAxios'
|
||||||
import { useSwal } from '@/hooks/useSwal'
|
import { useSwal } from '@/hooks/useSwal'
|
||||||
import { settingModalFirstOptionsState, settingModalSecondOptionsState } from '@/store/settingAtom'
|
import { corridorDimensionSelector, settingModalFirstOptionsState, settingModalSecondOptionsState } from '@/store/settingAtom'
|
||||||
import { setSurfaceShapePattern } from '@/util/canvas-util'
|
import { setSurfaceShapePattern } from '@/util/canvas-util'
|
||||||
import { POLYGON_TYPE } from '@/common/common'
|
import { POLYGON_TYPE } from '@/common/common'
|
||||||
|
|
||||||
@ -17,6 +17,8 @@ export function useCanvasSetting() {
|
|||||||
const { option1, option2, dimensionDisplay } = settingModalFirstOptions
|
const { option1, option2, dimensionDisplay } = settingModalFirstOptions
|
||||||
const { option3, option4 } = settingModalSecondOptions
|
const { option3, option4 } = settingModalSecondOptions
|
||||||
|
|
||||||
|
const corridorDimension = useRecoilValue(corridorDimensionSelector)
|
||||||
|
|
||||||
const globalLocaleState = useRecoilValue(globalLocaleStore)
|
const globalLocaleState = useRecoilValue(globalLocaleStore)
|
||||||
const { get, post } = useAxios(globalLocaleState)
|
const { get, post } = useAxios(globalLocaleState)
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
@ -27,6 +29,36 @@ export function useCanvasSetting() {
|
|||||||
|
|
||||||
const [objectNo, setObjectNo] = useState('test123240912001') // 이후 삭제 필요
|
const [objectNo, setObjectNo] = useState('test123240912001') // 이후 삭제 필요
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!canvas) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const { column } = corridorDimension
|
||||||
|
const lengthTexts = canvas.getObjects().filter((obj) => obj.name === 'lengthText')
|
||||||
|
switch (column) {
|
||||||
|
case 'corridorDimension':
|
||||||
|
lengthTexts.forEach((obj) => {
|
||||||
|
if (obj.planeSize) {
|
||||||
|
obj.set({ text: obj.planeSize.toString() })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
break
|
||||||
|
case 'realDimension':
|
||||||
|
lengthTexts.forEach((obj) => {
|
||||||
|
if (obj.actualSize) {
|
||||||
|
obj.set({ text: obj.actualSize.toString() })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
break
|
||||||
|
case 'noneDimension':
|
||||||
|
lengthTexts.forEach((obj) => {
|
||||||
|
obj.set({ text: '' })
|
||||||
|
})
|
||||||
|
break
|
||||||
|
}
|
||||||
|
canvas.renderAll()
|
||||||
|
}, [corridorDimension])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log('useCanvasSetting useEffect 실행1')
|
console.log('useCanvasSetting useEffect 실행1')
|
||||||
fetchSettings()
|
fetchSettings()
|
||||||
@ -257,7 +289,7 @@ export function useCanvasSetting() {
|
|||||||
optionName = ['7']
|
optionName = ['7']
|
||||||
break
|
break
|
||||||
case 'flowDisplay': //흐름방향 표시
|
case 'flowDisplay': //흐름방향 표시
|
||||||
optionName = ['arrow']
|
optionName = ['arrow', 'flowText']
|
||||||
break
|
break
|
||||||
case 'trestleDisplay': //가대 표시
|
case 'trestleDisplay': //가대 표시
|
||||||
optionName = ['8']
|
optionName = ['8']
|
||||||
|
|||||||
@ -815,7 +815,7 @@ export function useAuxiliaryDrawing(id) {
|
|||||||
roofBase.lines.some((line) => isPointOnLine(line, { x: line.x2, y: line.y2 }))
|
roofBase.lines.some((line) => isPointOnLine(line, { x: line.x2, y: line.y2 }))
|
||||||
|
|
||||||
if (inPolygon1 && inPolygon2) {
|
if (inPolygon1 && inPolygon2) {
|
||||||
line.attributes = { ...line.attributes, roofId: roofBase.id }
|
line.attributes = { ...line.attributes, roofId: roofBase.id, actualSize: 0, planeSize: line.getLength() }
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,23 +1,26 @@
|
|||||||
import { useRecoilValue } from 'recoil'
|
import { useRecoilValue } from 'recoil'
|
||||||
import { canvasState } from '@/store/canvasAtom'
|
import { canvasState, currentObjectState } from '@/store/canvasAtom'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { setSurfaceShapePattern } from '@/util/canvas-util'
|
import { setSurfaceShapePattern } from '@/util/canvas-util'
|
||||||
import { splitPolygonWithLines } from '@/util/qpolygon-utils'
|
|
||||||
import { useSwal } from '@/hooks/useSwal'
|
import { useSwal } from '@/hooks/useSwal'
|
||||||
import { usePolygon } from '@/hooks/usePolygon'
|
import { usePolygon } from '@/hooks/usePolygon'
|
||||||
import { roofDisplaySelector } from '@/store/settingAtom'
|
import { roofDisplaySelector } from '@/store/settingAtom'
|
||||||
import { usePopup } from '@/hooks/usePopup'
|
import { usePopup } from '@/hooks/usePopup'
|
||||||
import { POLYGON_TYPE } from '@/common/common'
|
import { POLYGON_TYPE } from '@/common/common'
|
||||||
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
|
import ActualSizeSetting from '@/components/floor-plan/modal/roofAllocation/ActualSizeSetting'
|
||||||
|
import { useMessage } from '@/hooks/useMessage'
|
||||||
|
|
||||||
// 지붕면 할당
|
// 지붕면 할당
|
||||||
export function useRoofAllocationSetting(id) {
|
export function useRoofAllocationSetting(id) {
|
||||||
const canvas = useRecoilValue(canvasState)
|
const canvas = useRecoilValue(canvasState)
|
||||||
const roofDisplay = useRecoilValue(roofDisplaySelector)
|
const roofDisplay = useRecoilValue(roofDisplaySelector)
|
||||||
const { drawDirectionArrow } = usePolygon()
|
const { drawDirectionArrow, addLengthText, splitPolygonWithLines } = usePolygon()
|
||||||
const { closePopup } = usePopup()
|
const [popupId, setPopupId] = useState(uuidv4())
|
||||||
|
const { addPopup, closePopup, closeAll } = usePopup()
|
||||||
|
const { getMessage } = useMessage()
|
||||||
|
const currentObject = useRecoilValue(currentObjectState)
|
||||||
const { swalFire } = useSwal()
|
const { swalFire } = useSwal()
|
||||||
|
|
||||||
const roofMaterials = [
|
const roofMaterials = [
|
||||||
{
|
{
|
||||||
id: 'A',
|
id: 'A',
|
||||||
@ -78,21 +81,47 @@ export function useRoofAllocationSetting(id) {
|
|||||||
])
|
])
|
||||||
|
|
||||||
const [radioValue, setRadioValue] = useState('A')
|
const [radioValue, setRadioValue] = useState('A')
|
||||||
|
const [editingLines, setEditingLines] = useState([])
|
||||||
|
|
||||||
const [selectedRoofMaterial, setSelectedRoofMaterial] = useState(roofMaterials[0])
|
const [selectedRoofMaterial, setSelectedRoofMaterial] = useState(roofMaterials[0])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF) // roofPolygon.innerLines
|
||||||
|
|
||||||
|
roofBases.forEach((roof) => {
|
||||||
|
roof.innerLines.forEach((line) => {
|
||||||
|
if (!line.attributes.actualSize || line.attributes?.actualSize === 0) {
|
||||||
|
line.set({
|
||||||
|
strokeWidth: 4,
|
||||||
|
stroke: 'black',
|
||||||
|
selectable: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (editingLines.includes(line)) {
|
||||||
|
line.set({
|
||||||
|
strokeWidth: 2,
|
||||||
|
stroke: 'black',
|
||||||
|
selectable: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
if (currentObject && currentObject.name && !currentObject.name.toLowerCase().includes('text')) {
|
||||||
|
currentObject.set({
|
||||||
|
strokeWidth: 4,
|
||||||
|
stroke: '#EA10AC',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, [currentObject])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// canvas.getObjects().filter((obj) => obj.type === 'QLine')
|
||||||
const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
|
const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
|
||||||
if (roofBases.length === 0) {
|
if (roofBases.length === 0) {
|
||||||
swalFire({ text: '할당할 지붕이 없습니다.' })
|
swalFire({ text: '할당할 지붕이 없습니다.' })
|
||||||
closePopup(id)
|
closePopup(id)
|
||||||
}
|
}
|
||||||
// if (type === POLYGON_TYPE.ROOF) {
|
|
||||||
// // 지붕면 할당
|
|
||||||
//
|
|
||||||
// } else if ('roof') {
|
|
||||||
// // 지붕재 변경
|
|
||||||
// }
|
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const onAddRoofMaterial = () => {
|
const onAddRoofMaterial = () => {
|
||||||
@ -105,6 +134,48 @@ export function useRoofAllocationSetting(id) {
|
|||||||
|
|
||||||
// 선택한 지붕재로 할당
|
// 선택한 지붕재로 할당
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
|
// 모두 actualSize 있으면 바로 적용 없으면 actualSize 설정
|
||||||
|
if (checkInnerLines()) {
|
||||||
|
addPopup(popupId, 1, <ActualSizeSetting id={popupId} />)
|
||||||
|
} else {
|
||||||
|
apply()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleAlloc = () => {
|
||||||
|
if (!checkInnerLines()) {
|
||||||
|
apply()
|
||||||
|
} else {
|
||||||
|
swalFire({
|
||||||
|
type: 'alert',
|
||||||
|
icon: 'error',
|
||||||
|
text: getMessage('실제치수를 입력해 주세요.'),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkInnerLines = () => {
|
||||||
|
const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF) // roofPolygon.innerLines
|
||||||
|
let result = false
|
||||||
|
|
||||||
|
roofBases.forEach((roof) => {
|
||||||
|
roof.innerLines.forEach((line) => {
|
||||||
|
if (!line.attributes.actualSize || line.attributes?.actualSize === 0) {
|
||||||
|
line.set({
|
||||||
|
strokeWidth: 4,
|
||||||
|
stroke: 'black',
|
||||||
|
selectable: true,
|
||||||
|
})
|
||||||
|
result = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
if (result) canvas?.renderAll()
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
const apply = () => {
|
||||||
const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
|
const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
|
||||||
const wallLines = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.WALL)
|
const wallLines = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.WALL)
|
||||||
roofBases.forEach((roofBase) => {
|
roofBases.forEach((roofBase) => {
|
||||||
@ -136,8 +207,26 @@ export function useRoofAllocationSetting(id) {
|
|||||||
removeTargets.forEach((obj) => {
|
removeTargets.forEach((obj) => {
|
||||||
canvas.remove(obj)
|
canvas.remove(obj)
|
||||||
})
|
})
|
||||||
|
setEditingLines([])
|
||||||
|
closeAll()
|
||||||
|
}
|
||||||
|
|
||||||
closePopup(id)
|
const setLineSize = (id, size) => {
|
||||||
|
const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
|
||||||
|
roofBases.forEach((roof) => {
|
||||||
|
roof.innerLines.forEach((line) => {
|
||||||
|
if (id === line.id) {
|
||||||
|
setEditingLines([...editingLines.filter((editLine) => editLine.id !== line.id), line])
|
||||||
|
line.attributes.actualSize = size
|
||||||
|
line.set({
|
||||||
|
strokeWidth: 2,
|
||||||
|
stroke: 'black',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
canvas?.renderAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleRadioOnChange = (e) => {
|
const handleRadioOnChange = (e) => {
|
||||||
@ -149,6 +238,8 @@ export function useRoofAllocationSetting(id) {
|
|||||||
onAddRoofMaterial,
|
onAddRoofMaterial,
|
||||||
onDeleteRoofMaterial,
|
onDeleteRoofMaterial,
|
||||||
handleRadioOnChange,
|
handleRadioOnChange,
|
||||||
|
handleAlloc,
|
||||||
|
setLineSize,
|
||||||
widths,
|
widths,
|
||||||
lengths,
|
lengths,
|
||||||
rafters,
|
rafters,
|
||||||
|
|||||||
@ -167,9 +167,8 @@ export function useRoofShapeSetting(id) {
|
|||||||
]
|
]
|
||||||
|
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
//기존 wallLine 삭제
|
|
||||||
|
|
||||||
let outerLines
|
let outerLines
|
||||||
|
let direction
|
||||||
|
|
||||||
switch (shapeNum) {
|
switch (shapeNum) {
|
||||||
case 1: {
|
case 1: {
|
||||||
@ -196,6 +195,7 @@ export function useRoofShapeSetting(id) {
|
|||||||
// 서쪽
|
// 서쪽
|
||||||
initLineSetting()
|
initLineSetting()
|
||||||
outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
|
outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
|
||||||
|
direction = 'west'
|
||||||
|
|
||||||
outerLines.forEach((line) => {
|
outerLines.forEach((line) => {
|
||||||
setWestAndEastRoof(line)
|
setWestAndEastRoof(line)
|
||||||
@ -240,6 +240,7 @@ export function useRoofShapeSetting(id) {
|
|||||||
case 6: {
|
case 6: {
|
||||||
initLineSetting()
|
initLineSetting()
|
||||||
outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
|
outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
|
||||||
|
direction = 'east'
|
||||||
|
|
||||||
outerLines.forEach((line) => {
|
outerLines.forEach((line) => {
|
||||||
setWestAndEastRoof(line)
|
setWestAndEastRoof(line)
|
||||||
@ -285,6 +286,7 @@ export function useRoofShapeSetting(id) {
|
|||||||
case 7: {
|
case 7: {
|
||||||
initLineSetting()
|
initLineSetting()
|
||||||
outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
|
outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
|
||||||
|
direction = 'south'
|
||||||
|
|
||||||
outerLines.forEach((line) => {
|
outerLines.forEach((line) => {
|
||||||
setSouthAndNorthRoof(line)
|
setSouthAndNorthRoof(line)
|
||||||
@ -329,6 +331,7 @@ export function useRoofShapeSetting(id) {
|
|||||||
case 8: {
|
case 8: {
|
||||||
initLineSetting()
|
initLineSetting()
|
||||||
outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
|
outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
|
||||||
|
direction = 'north'
|
||||||
|
|
||||||
outerLines.forEach((line) => {
|
outerLines.forEach((line) => {
|
||||||
setSouthAndNorthRoof(line)
|
setSouthAndNorthRoof(line)
|
||||||
@ -374,22 +377,22 @@ export function useRoofShapeSetting(id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 기존 wallLine, roofBase 제거
|
// 기존 wallLine, roofBase 제거
|
||||||
canvas
|
/*canvas
|
||||||
.getObjects()
|
.getObjects()
|
||||||
.filter((obj) => obj.name === POLYGON_TYPE.WALL)
|
.filter((obj) => obj.name === POLYGON_TYPE.WALL)
|
||||||
.forEach((line) => {
|
.forEach((line) => {
|
||||||
canvas.remove(line)
|
canvas.remove(line)
|
||||||
})
|
})*/
|
||||||
|
|
||||||
canvas
|
/*canvas
|
||||||
.getObjects()
|
.getObjects()
|
||||||
.filter((obj) => obj.name === POLYGON_TYPE.ROOF)
|
.filter((obj) => obj.name === POLYGON_TYPE.ROOF)
|
||||||
.forEach((obj) => {
|
.forEach((obj) => {
|
||||||
canvas.remove(...obj.innerLines)
|
canvas.remove(...obj.innerLines)
|
||||||
canvas.remove(obj)
|
canvas.remove(obj)
|
||||||
})
|
})*/
|
||||||
|
|
||||||
const polygon = addPolygonByLines(outerLines, { name: POLYGON_TYPE.WALL })
|
const polygon = addPolygonByLines(outerLines, { name: POLYGON_TYPE.WALL, direction })
|
||||||
polygon.lines = [...outerLines]
|
polygon.lines = [...outerLines]
|
||||||
|
|
||||||
addPitchTextsByOuterLines()
|
addPitchTextsByOuterLines()
|
||||||
@ -397,7 +400,6 @@ export function useRoofShapeSetting(id) {
|
|||||||
|
|
||||||
canvas?.renderAll()
|
canvas?.renderAll()
|
||||||
roof.drawHelpLine()
|
roof.drawHelpLine()
|
||||||
// setShowRoofShapeSettingModal(false)
|
|
||||||
isFixRef.current = true
|
isFixRef.current = true
|
||||||
closePopup(id)
|
closePopup(id)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,6 +13,8 @@ import { usePopup } from '@/hooks/usePopup'
|
|||||||
import { roofDisplaySelector } from '@/store/settingAtom'
|
import { roofDisplaySelector } from '@/store/settingAtom'
|
||||||
import { usePolygon } from '@/hooks/usePolygon'
|
import { usePolygon } from '@/hooks/usePolygon'
|
||||||
import { fontSelector } from '@/store/fontAtom'
|
import { fontSelector } from '@/store/fontAtom'
|
||||||
|
import { slopeSelector } from '@/store/commonAtom'
|
||||||
|
import { QLine } from '@/components/fabric/QLine'
|
||||||
|
|
||||||
export function useSurfaceShapeBatch() {
|
export function useSurfaceShapeBatch() {
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
@ -22,6 +24,7 @@ export function useSurfaceShapeBatch() {
|
|||||||
const canvas = useRecoilValue(canvasState)
|
const canvas = useRecoilValue(canvasState)
|
||||||
const globalPitch = useRecoilValue(globalPitchState)
|
const globalPitch = useRecoilValue(globalPitchState)
|
||||||
const roofDisplay = useRecoilValue(roofDisplaySelector)
|
const roofDisplay = useRecoilValue(roofDisplaySelector)
|
||||||
|
const slope = useRecoilValue(slopeSelector(globalPitch))
|
||||||
const { swalFire } = useSwal()
|
const { swalFire } = useSwal()
|
||||||
const { addCanvasMouseEventListener, initEvent } = useEvent()
|
const { addCanvasMouseEventListener, initEvent } = useEvent()
|
||||||
const { closePopup } = usePopup()
|
const { closePopup } = usePopup()
|
||||||
@ -126,6 +129,7 @@ export function useSurfaceShapeBatch() {
|
|||||||
addCanvasMouseEventListener('mouse:down', (e) => {
|
addCanvasMouseEventListener('mouse:down', (e) => {
|
||||||
isDrawing = false
|
isDrawing = false
|
||||||
obj.set('name', POLYGON_TYPE.ROOF)
|
obj.set('name', POLYGON_TYPE.ROOF)
|
||||||
|
obj.set('surfaceId', surfaceId)
|
||||||
initEvent()
|
initEvent()
|
||||||
setSurfaceShapePattern(obj, roofDisplay.column)
|
setSurfaceShapePattern(obj, roofDisplay.column)
|
||||||
closePopup(id)
|
closePopup(id)
|
||||||
@ -580,18 +584,19 @@ export function useSurfaceShapeBatch() {
|
|||||||
text: '배치면 내용을 전부 삭제하시겠습니까?',
|
text: '배치면 내용을 전부 삭제하시겠습니까?',
|
||||||
type: 'confirm',
|
type: 'confirm',
|
||||||
confirmFn: () => {
|
confirmFn: () => {
|
||||||
canvas?.getObjects().forEach((obj) => {
|
// canvas?.getObjects().forEach((obj) => {
|
||||||
if (
|
// if (
|
||||||
obj.name === POLYGON_TYPE.ROOF ||
|
// obj.name === POLYGON_TYPE.ROOF ||
|
||||||
obj.name === BATCH_TYPE.OPENING ||
|
// obj.name === BATCH_TYPE.OPENING ||
|
||||||
obj.name === BATCH_TYPE.SHADOW ||
|
// obj.name === BATCH_TYPE.SHADOW ||
|
||||||
obj.name === BATCH_TYPE.TRIANGLE_DORMER ||
|
// obj.name === BATCH_TYPE.TRIANGLE_DORMER ||
|
||||||
obj.name === BATCH_TYPE.PENTAGON_DORMER ||
|
// obj.name === BATCH_TYPE.PENTAGON_DORMER ||
|
||||||
obj.name === 'lengthText'
|
// obj.name === 'lengthText'
|
||||||
) {
|
// ) {
|
||||||
canvas?.remove(obj)
|
// canvas?.remove(obj)
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
|
canvas.clear()
|
||||||
swalFire({ text: '삭제 완료 되었습니다.' })
|
swalFire({ text: '삭제 완료 되었습니다.' })
|
||||||
},
|
},
|
||||||
// denyFn: () => {
|
// denyFn: () => {
|
||||||
@ -657,7 +662,7 @@ export function useSurfaceShapeBatch() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getAllRelatedObjects(id) {
|
function getAllRelatedObjects(id) {
|
||||||
const result = []
|
const ult = []
|
||||||
const map = new Map()
|
const map = new Map()
|
||||||
|
|
||||||
// Create a map of objects by their id
|
// Create a map of objects by their id
|
||||||
@ -688,52 +693,8 @@ export function useSurfaceShapeBatch() {
|
|||||||
const roof = canvas.getActiveObject()
|
const roof = canvas.getActiveObject()
|
||||||
|
|
||||||
if (roof) {
|
if (roof) {
|
||||||
let isDragging = false
|
|
||||||
|
|
||||||
const childrenObjects = canvas.getObjects().filter((obj) => obj.parentId === roof.id)
|
const childrenObjects = canvas.getObjects().filter((obj) => obj.parentId === roof.id)
|
||||||
|
|
||||||
console.log('childrenObjects', childrenObjects)
|
|
||||||
|
|
||||||
// const groupObjects = canvas.getObjects().filter((obj) => obj.parentId === roof.id && obj.type === 'group')
|
|
||||||
|
|
||||||
// const ungroupObjects = [] // 그룹 해제된 객체들
|
|
||||||
// const groupChildObjects = []
|
|
||||||
|
|
||||||
// groupObjects.forEach((obj) => {
|
|
||||||
// obj._restoreObjectsState()
|
|
||||||
// obj.getObjects().forEach((o) => {
|
|
||||||
// o.set({
|
|
||||||
// ungroupYn: true,
|
|
||||||
// })
|
|
||||||
// canvas.add(o)
|
|
||||||
// ungroupObjects.push(o)
|
|
||||||
// })
|
|
||||||
// canvas.remove(obj)
|
|
||||||
// })
|
|
||||||
|
|
||||||
// const childObjects = findAllChildren(roof.id)
|
|
||||||
// groupObjects.forEach((obj) => {
|
|
||||||
// groupChildObjects.push(...obj.getObjects())
|
|
||||||
// })
|
|
||||||
|
|
||||||
// console.log('ungroupObjects', ungroupObjects)
|
|
||||||
// console.log('childObjects', childObjects)
|
|
||||||
// console.log('groupChildObjects', groupChildObjects)
|
|
||||||
|
|
||||||
// const children = canvas.getObjects().filter((obj) => obj.parentId === roof.id)
|
|
||||||
// let grandChildren = []
|
|
||||||
|
|
||||||
// children.forEach((child) => {
|
|
||||||
// if (child.type === 'group') {
|
|
||||||
// child.getObjects().forEach((grandChild) => {
|
|
||||||
// const groupObjects = canvas.getObjects().filter((obj) => obj.parentId === grandChild.id)
|
|
||||||
// grandChildren.push(...groupObjects)
|
|
||||||
// })
|
|
||||||
// } else {
|
|
||||||
// grandChildren.push(...canvas.getObjects().filter((obj) => obj.parentId === child.id))
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
|
|
||||||
const selectionArray = [roof, ...childrenObjects]
|
const selectionArray = [roof, ...childrenObjects]
|
||||||
|
|
||||||
const selection = new fabric.ActiveSelection(selectionArray, {
|
const selection = new fabric.ActiveSelection(selectionArray, {
|
||||||
@ -748,7 +709,6 @@ export function useSurfaceShapeBatch() {
|
|||||||
canvas.setActiveObject(selection)
|
canvas.setActiveObject(selection)
|
||||||
|
|
||||||
addCanvasMouseEventListener('mouse:up', (e) => {
|
addCanvasMouseEventListener('mouse:up', (e) => {
|
||||||
isDragging = false
|
|
||||||
canvas.selection = true
|
canvas.selection = true
|
||||||
|
|
||||||
canvas.discardActiveObject() // 모든 선택 해제
|
canvas.discardActiveObject() // 모든 선택 해제
|
||||||
@ -768,7 +728,6 @@ export function useSurfaceShapeBatch() {
|
|||||||
|
|
||||||
canvas.renderAll()
|
canvas.renderAll()
|
||||||
roof.fire('polygonMoved')
|
roof.fire('polygonMoved')
|
||||||
if (roof.type === 'group') reGroupObject(obj)
|
|
||||||
drawDirectionArrow(roof)
|
drawDirectionArrow(roof)
|
||||||
initEvent()
|
initEvent()
|
||||||
})
|
})
|
||||||
@ -805,9 +764,223 @@ export function useSurfaceShapeBatch() {
|
|||||||
canvas?.remove(groupObj)
|
canvas?.remove(groupObj)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const resizeSurfaceShapeBatch = (side, target, width, height) => {
|
||||||
|
const objectWidth = target.width
|
||||||
|
const objectHeight = target.height
|
||||||
|
const changeWidth = width / 10 / objectWidth
|
||||||
|
const changeHeight = height / 10 / objectHeight
|
||||||
|
let sideX = 'left'
|
||||||
|
let sideY = 'top'
|
||||||
|
|
||||||
|
//그룹 중심점 변경
|
||||||
|
if (side === 2) {
|
||||||
|
sideX = 'right'
|
||||||
|
sideY = 'top'
|
||||||
|
} else if (side === 3) {
|
||||||
|
sideX = 'left'
|
||||||
|
sideY = 'bottom'
|
||||||
|
} else if (side === 4) {
|
||||||
|
sideX = 'right'
|
||||||
|
sideY = 'bottom'
|
||||||
|
}
|
||||||
|
|
||||||
|
//변경 전 좌표
|
||||||
|
const newCoords = target.getPointByOrigin(sideX, sideY)
|
||||||
|
|
||||||
|
target.set({
|
||||||
|
originX: sideX,
|
||||||
|
originY: sideY,
|
||||||
|
left: newCoords.x,
|
||||||
|
top: newCoords.y,
|
||||||
|
})
|
||||||
|
|
||||||
|
target.scaleX = changeWidth
|
||||||
|
target.scaleY = changeHeight
|
||||||
|
|
||||||
|
const currentPoints = target.getCurrentPoints()
|
||||||
|
|
||||||
|
target.set({
|
||||||
|
scaleX: 1,
|
||||||
|
scaleY: 1,
|
||||||
|
width: parseInt((width / 10).toFixed(0)),
|
||||||
|
height: parseInt((height / 10).toFixed(0)),
|
||||||
|
})
|
||||||
|
//크기 변경후 좌표를 재 적용
|
||||||
|
const changedCoords = target.getPointByOrigin('center', 'center')
|
||||||
|
|
||||||
|
target.set({
|
||||||
|
originX: 'center',
|
||||||
|
originY: 'center',
|
||||||
|
left: changedCoords.x,
|
||||||
|
top: changedCoords.y,
|
||||||
|
})
|
||||||
|
|
||||||
|
//면형상 리사이즈시에만
|
||||||
|
target.fire('polygonMoved')
|
||||||
|
target.points = currentPoints
|
||||||
|
target.fire('modified')
|
||||||
|
|
||||||
|
setSurfaceShapePattern(target, roofDisplay.column)
|
||||||
|
if (target.direction) {
|
||||||
|
drawDirectionArrow(target)
|
||||||
|
}
|
||||||
|
target.setCoords()
|
||||||
|
canvas.renderAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
const surfaceShapeActualSize = () => {
|
||||||
|
const roofs = canvas.getObjects().filter((obj) => obj.name === 'roof')
|
||||||
|
let notParallelLines = []
|
||||||
|
|
||||||
|
const roofArray = []
|
||||||
|
|
||||||
|
roofs.forEach((obj) => {
|
||||||
|
const roof = {
|
||||||
|
id: obj.id,
|
||||||
|
surfaceId: obj.surfaceId,
|
||||||
|
targetLines: [],
|
||||||
|
baseLines: [],
|
||||||
|
direction: obj.direction,
|
||||||
|
}
|
||||||
|
obj.lines.forEach((line) => {
|
||||||
|
if (obj.direction === 'north' || obj.direction === 'south') {
|
||||||
|
if (line.y1 !== line.y2) {
|
||||||
|
roof.targetLines.push(line)
|
||||||
|
} else {
|
||||||
|
roof.baseLines.push(line)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (line.x1 !== line.x2) {
|
||||||
|
roof.targetLines.push(line)
|
||||||
|
} else {
|
||||||
|
roof.baseLines.push(line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
roofArray.push(roof)
|
||||||
|
})
|
||||||
|
|
||||||
|
const slopeRadians = slope.angleValue * (Math.PI / 180)
|
||||||
|
const tanSlope = Math.tan(slopeRadians)
|
||||||
|
|
||||||
|
roofArray.forEach((roof) => {
|
||||||
|
roof.targetLines.forEach((line) => {
|
||||||
|
let calcLength = line.length
|
||||||
|
|
||||||
|
if (roof.surfaceId === 1) {
|
||||||
|
calcLength = roof.direction === 'north' || roof.direction === 'south' ? Math.abs(line.y1 - line.y2) : Math.abs(line.x1 - line.x2)
|
||||||
|
}
|
||||||
|
|
||||||
|
const h = calcLength * tanSlope
|
||||||
|
const resultLength = Math.sqrt(Math.pow(calcLength, 2) + Math.pow(h, 2))
|
||||||
|
|
||||||
|
line.set({
|
||||||
|
attributes: {
|
||||||
|
...line.attributes,
|
||||||
|
actualSize: parseInt((Math.floor(resultLength) * 10).toFixed(0)),
|
||||||
|
planeSize: parseInt(line.length * 10),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
roof.baseLines.forEach((line) => {
|
||||||
|
line.set({
|
||||||
|
attributes: {
|
||||||
|
...line.attributes,
|
||||||
|
actualSize: parseInt((Math.floor(line.length) * 10).toFixed(0)),
|
||||||
|
planeSize: parseInt(line.length * 10),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const changeSurfaceLinePropertyEvent = (roof) => {
|
||||||
|
let tmpLines = []
|
||||||
|
roof.set({
|
||||||
|
selectable: false,
|
||||||
|
})
|
||||||
|
canvas.discardActiveObject()
|
||||||
|
roof.lines.forEach((obj, index) => {
|
||||||
|
const tmpLine = new QLine([obj.x1, obj.y1, obj.x2, obj.y2], {
|
||||||
|
...obj,
|
||||||
|
stroke: 'rgb(3, 255, 0)',
|
||||||
|
strokeWidth: 8,
|
||||||
|
selectable: true,
|
||||||
|
name: 'lineProperty',
|
||||||
|
index: index,
|
||||||
|
})
|
||||||
|
|
||||||
|
tmpLines.push(tmpLine)
|
||||||
|
canvas.add(tmpLine)
|
||||||
|
})
|
||||||
|
|
||||||
|
addCanvasMouseEventListener('mouse:down', (e) => {
|
||||||
|
const selectedLine = e.target
|
||||||
|
if (selectedLine) {
|
||||||
|
selectedLine.set({
|
||||||
|
stroke: 'red',
|
||||||
|
name: 'selectedLineProperty',
|
||||||
|
})
|
||||||
|
tmpLines.forEach((line) => {
|
||||||
|
if (line.index !== selectedLine.index) {
|
||||||
|
line.set({
|
||||||
|
stroke: 'rgb(3, 255, 0)',
|
||||||
|
name: 'lineProperty',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
tmpLines.forEach((line) => {
|
||||||
|
line.set({
|
||||||
|
stroke: 'rgb(3, 255, 0)',
|
||||||
|
name: 'lineProperty',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
canvas.renderAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
const changeSurfaceLineProperty = (property) => {
|
||||||
|
console.log(property)
|
||||||
|
if (!property) {
|
||||||
|
swalFire({ text: getMessage('modal.line.property.change'), icon: 'error' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectedLine = canvas.getActiveObjects()
|
||||||
|
if (selectedLine && selectedLine[0].name === 'selectedLineProperty') {
|
||||||
|
swalFire({
|
||||||
|
text: getMessage('modal.line.property.change.confirm'),
|
||||||
|
type: 'confirm',
|
||||||
|
confirmFn: () => {
|
||||||
|
selectedLine.set({
|
||||||
|
type: property.value,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
swalFire({ text: getMessage('modal.line.property.change.unselect'), icon: 'error' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const changeSurfaceFlowDirection = (roof, direction, orientation) => {
|
||||||
|
roof.set({
|
||||||
|
direction: direction,
|
||||||
|
})
|
||||||
|
drawDirectionArrow(roof)
|
||||||
|
canvas?.renderAll()
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
applySurfaceShape,
|
applySurfaceShape,
|
||||||
deleteAllSurfacesAndObjects,
|
deleteAllSurfacesAndObjects,
|
||||||
moveSurfaceShapeBatch,
|
moveSurfaceShapeBatch,
|
||||||
|
resizeSurfaceShapeBatch,
|
||||||
|
surfaceShapeActualSize,
|
||||||
|
changeSurfaceFlowDirection,
|
||||||
|
changeSurfaceLinePropertyEvent,
|
||||||
|
changeSurfaceLineProperty,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
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 { QPolygon } from '@/components/fabric/QPolygon'
|
||||||
import { usePlan } from '@/hooks/usePlan'
|
import { usePlan } from '@/hooks/usePlan'
|
||||||
import { fontSelector } from '@/store/fontAtom'
|
import { fontSelector } from '@/store/fontAtom'
|
||||||
@ -12,8 +12,6 @@ export function useCanvasEvent() {
|
|||||||
const [canvasForEvent, setCanvasForEvent] = useState(null)
|
const [canvasForEvent, setCanvasForEvent] = useState(null)
|
||||||
const [currentObject, setCurrentObject] = useRecoilState(currentObjectState)
|
const [currentObject, setCurrentObject] = useRecoilState(currentObjectState)
|
||||||
const canvasSize = useRecoilValue(canvasSizeState)
|
const canvasSize = useRecoilValue(canvasSizeState)
|
||||||
const fontSize = useRecoilValue(fontSizeState)
|
|
||||||
const fontFamily = useRecoilValue(fontFamilyState)
|
|
||||||
const [canvasZoom, setCanvasZoom] = useRecoilState(canvasZoomState)
|
const [canvasZoom, setCanvasZoom] = useRecoilState(canvasZoomState)
|
||||||
const lengthTextOption = useRecoilValue(fontSelector('lengthText'))
|
const lengthTextOption = useRecoilValue(fontSelector('lengthText'))
|
||||||
const { modifiedPlanFlag, setModifiedPlanFlag } = usePlan()
|
const { modifiedPlanFlag, setModifiedPlanFlag } = usePlan()
|
||||||
@ -211,7 +209,7 @@ export function useCanvasEvent() {
|
|||||||
setCurrentObject(target)
|
setCurrentObject(target)
|
||||||
const { selected } = e
|
const { selected } = e
|
||||||
|
|
||||||
if (selected.length > 0) {
|
if (selected?.length > 0) {
|
||||||
selected.forEach((obj) => {
|
selected.forEach((obj) => {
|
||||||
if (obj.type === 'QPolygon') {
|
if (obj.type === 'QPolygon') {
|
||||||
obj.set({ stroke: 'red' })
|
obj.set({ stroke: 'red' })
|
||||||
@ -224,7 +222,7 @@ export function useCanvasEvent() {
|
|||||||
setCurrentObject(null)
|
setCurrentObject(null)
|
||||||
const { deselected } = e
|
const { deselected } = e
|
||||||
|
|
||||||
if (deselected.length > 0) {
|
if (deselected?.length > 0) {
|
||||||
deselected.forEach((obj) => {
|
deselected.forEach((obj) => {
|
||||||
if (obj.type === 'QPolygon') {
|
if (obj.type === 'QPolygon') {
|
||||||
obj.set({ stroke: 'black' })
|
obj.set({ stroke: 'black' })
|
||||||
@ -238,7 +236,7 @@ export function useCanvasEvent() {
|
|||||||
setCurrentObject(target)
|
setCurrentObject(target)
|
||||||
const { selected, deselected } = e
|
const { selected, deselected } = e
|
||||||
|
|
||||||
if (deselected.length > 0) {
|
if (deselected?.length > 0) {
|
||||||
deselected.forEach((obj) => {
|
deselected.forEach((obj) => {
|
||||||
if (obj.type === 'QPolygon') {
|
if (obj.type === 'QPolygon') {
|
||||||
obj.set({ stroke: 'black' })
|
obj.set({ stroke: 'black' })
|
||||||
@ -246,7 +244,7 @@ export function useCanvasEvent() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selected.length > 0) {
|
if (selected?.length > 0) {
|
||||||
selected.forEach((obj) => {
|
selected.forEach((obj) => {
|
||||||
if (obj.type === 'QPolygon') {
|
if (obj.type === 'QPolygon') {
|
||||||
obj.set({ stroke: 'red' })
|
obj.set({ stroke: 'red' })
|
||||||
|
|||||||
@ -51,7 +51,7 @@ export function useContextMenu() {
|
|||||||
const [column, setColumn] = useState(null)
|
const [column, setColumn] = useState(null)
|
||||||
const { handleZoomClear } = useCanvasEvent()
|
const { handleZoomClear } = useCanvasEvent()
|
||||||
const { moveObjectBatch } = useObjectBatch({})
|
const { moveObjectBatch } = useObjectBatch({})
|
||||||
const { moveSurfaceShapeBatch } = useSurfaceShapeBatch()
|
const { moveSurfaceShapeBatch, surfaceShapeActualSize } = useSurfaceShapeBatch()
|
||||||
const currentMenuSetting = () => {
|
const currentMenuSetting = () => {
|
||||||
switch (currentMenu) {
|
switch (currentMenu) {
|
||||||
case MENU.PLAN_DRAWING:
|
case MENU.PLAN_DRAWING:
|
||||||
@ -176,6 +176,7 @@ export function useContextMenu() {
|
|||||||
{
|
{
|
||||||
id: 'sizeEdit',
|
id: 'sizeEdit',
|
||||||
name: getMessage('contextmenu.size.edit'),
|
name: getMessage('contextmenu.size.edit'),
|
||||||
|
component: <SizeSetting id={popupId} target={currentObject} />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'remove',
|
id: 'remove',
|
||||||
@ -211,6 +212,7 @@ export function useContextMenu() {
|
|||||||
{
|
{
|
||||||
id: 'flowDirectionEdit',
|
id: 'flowDirectionEdit',
|
||||||
name: getMessage('contextmenu.flow.direction.edit'),
|
name: getMessage('contextmenu.flow.direction.edit'),
|
||||||
|
component: <FlowDirectionSetting id={popupId} target={currentObject} />,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
])
|
])
|
||||||
@ -257,7 +259,6 @@ export function useContextMenu() {
|
|||||||
}, [currentContextMenu])
|
}, [currentContextMenu])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log('currentObject', currentObject)
|
|
||||||
if (currentObject?.name) {
|
if (currentObject?.name) {
|
||||||
switch (currentObject.name) {
|
switch (currentObject.name) {
|
||||||
case 'triangleDormer':
|
case 'triangleDormer':
|
||||||
@ -303,6 +304,11 @@ export function useContextMenu() {
|
|||||||
case 'roof':
|
case 'roof':
|
||||||
setContextMenu([
|
setContextMenu([
|
||||||
[
|
[
|
||||||
|
{
|
||||||
|
id: 'surfaceShapeActualSize',
|
||||||
|
name: '면형상 실측',
|
||||||
|
fn: () => surfaceShapeActualSize(),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'sizeEdit',
|
id: 'sizeEdit',
|
||||||
name: '사이즈 변경',
|
name: '사이즈 변경',
|
||||||
@ -336,7 +342,7 @@ export function useContextMenu() {
|
|||||||
{
|
{
|
||||||
id: 'linePropertyEdit',
|
id: 'linePropertyEdit',
|
||||||
name: getMessage('contextmenu.line.property.edit'),
|
name: getMessage('contextmenu.line.property.edit'),
|
||||||
component: <LinePropertySetting id={popupId} />,
|
component: <LinePropertySetting id={popupId} target={currentObject} />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'flowDirectionEdit',
|
id: 'flowDirectionEdit',
|
||||||
|
|||||||
@ -1515,9 +1515,17 @@ export function useMode() {
|
|||||||
pitch: 4,
|
pitch: 4,
|
||||||
sleeve: true,
|
sleeve: true,
|
||||||
}
|
}
|
||||||
/*if (index === 1 || index === 3) {
|
/*if (index === 1) {
|
||||||
line.attributes = {
|
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, //출폭
|
offset: 50, //출폭
|
||||||
width: 30, //폭
|
width: 30, //폭
|
||||||
pitch: 4, //구배
|
pitch: 4, //구배
|
||||||
@ -1525,8 +1533,8 @@ export function useMode() {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
line.attributes = {
|
line.attributes = {
|
||||||
type: LINE_TYPE.WALLLINE.EAVES,
|
type: LINE_TYPE.WALLLINE.GABLE,
|
||||||
offset: 40,
|
offset: 20,
|
||||||
width: 50,
|
width: 50,
|
||||||
pitch: 4,
|
pitch: 4,
|
||||||
sleeve: true,
|
sleeve: true,
|
||||||
@ -1746,12 +1754,20 @@ export function useMode() {
|
|||||||
return { x1: point.x, y1: point.y }
|
return { x1: point.x, y1: point.y }
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
if (wall.direction) {
|
||||||
|
roof.direction = wall.direction
|
||||||
|
}
|
||||||
roof.name = POLYGON_TYPE.ROOF
|
roof.name = POLYGON_TYPE.ROOF
|
||||||
roof.setWall(wall)
|
roof.setWall(wall)
|
||||||
|
|
||||||
roof.lines.forEach((line, index) => {
|
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 = {
|
line.attributes = {
|
||||||
roofId: roof.id,
|
roofId: roof.id,
|
||||||
|
planeSize: lineLength,
|
||||||
|
actualSize: lineLength,
|
||||||
wallLine: wall.lines[index].id,
|
wallLine: wall.lines[index].id,
|
||||||
type: wall.lines[index].attributes.type,
|
type: wall.lines[index].attributes.type,
|
||||||
offset: wall.lines[index].attributes.offset,
|
offset: wall.lines[index].attributes.offset,
|
||||||
@ -1766,13 +1782,15 @@ export function useMode() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
wall.lines.forEach((line, index) => {
|
wall.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
|
line.attributes.roofId = roof.id
|
||||||
line.attributes.currentRoof = roof.lines[index].id
|
line.attributes.currentRoof = roof.lines[index].id
|
||||||
|
line.attributes.planeSize = lineLength
|
||||||
|
line.attributes.actualSize = lineLength
|
||||||
})
|
})
|
||||||
|
|
||||||
console.log('drawRoofPolygon roof : ', roof)
|
|
||||||
console.log('drawRoofPolygon wall : ', wall)
|
|
||||||
|
|
||||||
setRoof(roof)
|
setRoof(roof)
|
||||||
setWall(wall)
|
setWall(wall)
|
||||||
|
|
||||||
|
|||||||
@ -187,6 +187,8 @@ export function usePlan() {
|
|||||||
userId: item.userId,
|
userId: item.userId,
|
||||||
canvasStatus: dbToCanvasFormat(item.canvasStatus),
|
canvasStatus: dbToCanvasFormat(item.canvasStatus),
|
||||||
isCurrent: false,
|
isCurrent: false,
|
||||||
|
bgImageName: item.bgImageName,
|
||||||
|
mapPositionAddress: item.mapPositionAddress,
|
||||||
})),
|
})),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,13 @@
|
|||||||
import { ANGLE_TYPE, canvasState, currentAngleTypeSelector, fontFamilyState, fontSizeState, pitchTextSelector } from '@/store/canvasAtom'
|
import { ANGLE_TYPE, canvasState, currentAngleTypeSelector, fontFamilyState, fontSizeState, pitchTextSelector } from '@/store/canvasAtom'
|
||||||
import { useRecoilValue } from 'recoil'
|
import { useRecoilValue } from 'recoil'
|
||||||
import { fabric } from 'fabric'
|
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 { QPolygon } from '@/components/fabric/QPolygon'
|
||||||
import { isSamePoint } from '@/util/qpolygon-utils'
|
import { isSamePoint, removeDuplicatePolygons } from '@/util/qpolygon-utils'
|
||||||
import { flowDisplaySelector } from '@/store/settingAtom'
|
import { flowDisplaySelector } from '@/store/settingAtom'
|
||||||
import { fontSelector } from '@/store/fontAtom'
|
import { fontSelector } from '@/store/fontAtom'
|
||||||
|
import { QLine } from '@/components/fabric/QLine'
|
||||||
|
import { POLYGON_TYPE } from '@/common/common'
|
||||||
|
|
||||||
export const usePolygon = () => {
|
export const usePolygon = () => {
|
||||||
const canvas = useRecoilValue(canvasState)
|
const canvas = useRecoilValue(canvasState)
|
||||||
@ -25,6 +27,7 @@ export const usePolygon = () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
canvas?.add(polygon)
|
canvas?.add(polygon)
|
||||||
|
addLengthText(polygon)
|
||||||
|
|
||||||
return polygon
|
return polygon
|
||||||
}
|
}
|
||||||
@ -40,7 +43,64 @@ export const usePolygon = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const addLengthText = (polygon) => {
|
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(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) => {
|
points.forEach((start, i) => {
|
||||||
const end = points[(i + 1) % points.length]
|
const end = points[(i + 1) % points.length]
|
||||||
const dx = end.x - start.x
|
const dx = end.x - start.x
|
||||||
@ -71,12 +131,12 @@ export const usePolygon = () => {
|
|||||||
lockScalingY: true,
|
lockScalingY: true,
|
||||||
idx: i,
|
idx: i,
|
||||||
name: 'lengthText',
|
name: 'lengthText',
|
||||||
parent: this,
|
parent: polygon,
|
||||||
})
|
})
|
||||||
|
|
||||||
// this.texts.push(text)
|
// this.texts.push(text)
|
||||||
canvas.add(text)
|
canvas.add(text)
|
||||||
})
|
})*/
|
||||||
canvas.renderAll()
|
canvas.renderAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -409,7 +469,8 @@ export const usePolygon = () => {
|
|||||||
|
|
||||||
const addTextByArrows = (arrows, txt, canvas) => {
|
const addTextByArrows = (arrows, txt, canvas) => {
|
||||||
arrows.forEach((arrow, index) => {
|
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}`, {
|
const text = new fabric.Text(`${textStr}`, {
|
||||||
fontSize: flowFontOptions.fontSize.value,
|
fontSize: flowFontOptions.fontSize.value,
|
||||||
@ -432,10 +493,271 @@ 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 = 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: 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 {
|
return {
|
||||||
addPolygon,
|
addPolygon,
|
||||||
addPolygonByLines,
|
addPolygonByLines,
|
||||||
removePolygon,
|
removePolygon,
|
||||||
drawDirectionArrow,
|
drawDirectionArrow,
|
||||||
|
addLengthText,
|
||||||
|
splitPolygonWithLines,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -285,6 +285,12 @@
|
|||||||
"modal.flow.direction.setting": "流れ方向の設定",
|
"modal.flow.direction.setting": "流れ方向の設定",
|
||||||
"modal.flow.direction.setting.info": "流れ方向を選択してください。",
|
"modal.flow.direction.setting.info": "流れ方向を選択してください。",
|
||||||
"modal.image.size.setting": "画像のサイズ変更",
|
"modal.image.size.setting": "画像のサイズ変更",
|
||||||
|
"modal.actual.size.setting": "実測値設定",
|
||||||
|
"modal.actual.size.setting.info": "※隅棟・谷・棟の実際の寸法を入力してください。",
|
||||||
|
"modal.actual.size.setting.not.exist.auxiliary.line": "실측치 입력할 보조선을 선택해 주세요(JA)",
|
||||||
|
"modal.actual.size.setting.not.exist.size": "실제치수 길이를 입력해 주세요.(JA)",
|
||||||
|
"modal.actual.size.setting.plane.size.length": "廊下の寸法の長さ",
|
||||||
|
"modal.actual.size.setting.actual.size.length": "実寸長",
|
||||||
"plan.message.confirm.save": "PLAN을 저장하시겠습니까?",
|
"plan.message.confirm.save": "PLAN을 저장하시겠습니까?",
|
||||||
"plan.message.confirm.copy": "PLAN을 복사하시겠습니까?",
|
"plan.message.confirm.copy": "PLAN을 복사하시겠습니까?",
|
||||||
"plan.message.confirm.delete": "PLAN을 삭제하시겠습니까?",
|
"plan.message.confirm.delete": "PLAN을 삭제하시겠습니까?",
|
||||||
@ -818,7 +824,7 @@
|
|||||||
"estimate.detail.estimateType": "注文分類",
|
"estimate.detail.estimateType": "注文分類",
|
||||||
"estimate.detail.roofCns": "屋根材・仕様施工",
|
"estimate.detail.roofCns": "屋根材・仕様施工",
|
||||||
"estimate.detail.remarks": "備考",
|
"estimate.detail.remarks": "備考",
|
||||||
"estimate.detail.nextSubmit": "後日資料提出",
|
"estimate.detail.fileFlg": "後日資料提出",
|
||||||
"estimate.detail.header.fileList1": "ファイル添付",
|
"estimate.detail.header.fileList1": "ファイル添付",
|
||||||
"estimate.detail.fileList.btn": "ファイル選択",
|
"estimate.detail.fileList.btn": "ファイル選択",
|
||||||
"estimate.detail.header.fileList2": "添付ファイル一覧",
|
"estimate.detail.header.fileList2": "添付ファイル一覧",
|
||||||
@ -835,11 +841,62 @@
|
|||||||
"estimate.detail.sepcialEstimateProductInfo.calcFormula1": "(モジュール容量 × 数量)÷1000",
|
"estimate.detail.sepcialEstimateProductInfo.calcFormula1": "(モジュール容量 × 数量)÷1000",
|
||||||
"estimate.detail.sepcialEstimateProductInfo.calcFormula2": "PKG単価 (W)×PKG容量(W)",
|
"estimate.detail.sepcialEstimateProductInfo.calcFormula2": "PKG単価 (W)×PKG容量(W)",
|
||||||
"estimate.detail.header.showPrice": "価格表示",
|
"estimate.detail.header.showPrice": "価格表示",
|
||||||
"estimate.detail.showPrice.btn1": "Pricing",
|
"estimate.detail.header.unitPrice": "定価",
|
||||||
|
"estimate.detail.showPrice.pricingBtn": "Pricing",
|
||||||
"estimate.detail.showPrice.description1": "製品価格 OPEN",
|
"estimate.detail.showPrice.description1": "製品価格 OPEN",
|
||||||
"estimate.detail.showPrice.description2": "追加, 変更資材",
|
"estimate.detail.showPrice.description2": "追加, 変更資材",
|
||||||
"estimate.detail.showPrice.description3": "添付必須",
|
"estimate.detail.showPrice.description3": "添付必須",
|
||||||
"estimate.detail.showPrice.description4": "クリックして製品の特異性を確認する",
|
"estimate.detail.showPrice.description4": "クリックして製品の特異性を確認する",
|
||||||
"estimate.detail.showPrice.btn2": "製品を追加",
|
"estimate.detail.showPrice.addItem": "製品を追加",
|
||||||
"estimate.detail.showPrice.btn3": "製品削除"
|
"estimate.detail.showPrice.delItem": "製品削除",
|
||||||
|
"estimate.detail.itemTableHeader.dispOrder": "アイテム",
|
||||||
|
"estimate.detail.itemTableHeader.itemId": "品番",
|
||||||
|
"estimate.detail.itemTableHeader.itemNo": "型板",
|
||||||
|
"estimate.detail.itemTableHeader.amount": "数量",
|
||||||
|
"estimate.detail.itemTableHeader.unit": "単位",
|
||||||
|
"estimate.detail.itemTableHeader.salePrice": "単価",
|
||||||
|
"estimate.detail.itemTableHeader.saleTotPrice": "金額 (税別別)",
|
||||||
|
"estimate.detail.docPopup.title": "ドキュメントダウンロードオプションの設定",
|
||||||
|
"estimate.detail.docPopup.explane": "ダウンロードする文書のオプションを選択したら、 [文書のダウンロード]ボタンをクリックします.",
|
||||||
|
"estimate.detail.docPopup.schUnitPriceFlg": "ダウンロードファイル",
|
||||||
|
"estimate.detail.docPopup.schUnitPriceFlg.schUnitPriceFlg0": "見積もり Excel",
|
||||||
|
"estimate.detail.docPopup.schUnitPriceFlg.schUnitPriceFlg1": "定価用 Excel",
|
||||||
|
"estimate.detail.docPopup.schUnitPriceFlg.schUnitPriceFlg2": "見積もり PDF",
|
||||||
|
"estimate.detail.docPopup.schUnitPriceFlg.schUnitPriceFlg3": "定価用 PDF",
|
||||||
|
"estimate.detail.docPopup.schDisplayFlg": "見積提出先表示名",
|
||||||
|
"estimate.detail.docPopup.schDisplayFlg.schDisplayFlg0": "販売店名",
|
||||||
|
"estimate.detail.docPopup.schDisplayFlg.schDisplayFlg1": "案件名",
|
||||||
|
"estimate.detail.docPopup.schWeightFlg": "架台重量表を含む",
|
||||||
|
"estimate.detail.docPopup.schWeightFlg.schWeightFlg0": "含む",
|
||||||
|
"estimate.detail.docPopup.schWeightFlg.schWeightFlg1": "含まない",
|
||||||
|
"estimate.detail.docPopup.schDrawingFlg": "図面/シミュレーションファイルを含む",
|
||||||
|
"estimate.detail.docPopup.schDrawingFlg.schDrawingFlg0": "含む",
|
||||||
|
"estimate.detail.docPopup.schDrawingFlg.schDrawingFlg1": "含まない",
|
||||||
|
"estimate.detail.docPopup.close": "閉じる",
|
||||||
|
"estimate.detail.docPopup.docDownload": "文書のダウンロード",
|
||||||
|
"estimate.detail.productFeaturesPopup.title": "製品特異事項",
|
||||||
|
"estimate.detail.productFeaturesPopup.close": "閉じる",
|
||||||
|
"estimate.detail.save.alertMsg": "保存されている見積書で製品を変更した場合、図面や回路には反映されません.",
|
||||||
|
"estimate.detail.save.requiredMsg": "ファイル添付が必須のアイテムがあります。ファイルを添付するか、後日添付をチェックしてください.",
|
||||||
|
"estimate.detail.reset.confirmMsg": "保存した見積書情報が初期化され、図面情報が反映されます。本当に初期化しますか?",
|
||||||
|
"simulator.title.sub1": "物件番号",
|
||||||
|
"simulator.title.sub2": "作成日",
|
||||||
|
"simulator.title.sub3": "システム容量",
|
||||||
|
"simulator.title.sub4": "年間予測発電量",
|
||||||
|
"simulator.title.sub5": "都道府県",
|
||||||
|
"simulator.title.sub6": "日射量観測地点",
|
||||||
|
"simulator.title.sub7": "積雪条件",
|
||||||
|
"simulator.title.sub8": "風速条件",
|
||||||
|
"simulator.title.sub9": "以下",
|
||||||
|
"simulator.table.sub1": "屋根面",
|
||||||
|
"simulator.table.sub2": "傾斜角",
|
||||||
|
"simulator.table.sub3": "方位角(度)",
|
||||||
|
"simulator.table.sub4": "太陽電池モジュール",
|
||||||
|
"simulator.table.sub5": "枚数",
|
||||||
|
"simulator.table.sub6": "合計",
|
||||||
|
"simulator.table.sub7": "パワーコンディショナー",
|
||||||
|
"simulator.table.sub8": "台",
|
||||||
|
"simulator.table.sub9": "予測発電量 (kWh)",
|
||||||
|
"simulator.notice.sub1": "Hanwha Japan 年間発電量",
|
||||||
|
"simulator.notice.sub2": "シミュレーション案内事項"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -290,6 +290,12 @@
|
|||||||
"modal.flow.direction.setting": "흐름 방향 설정",
|
"modal.flow.direction.setting": "흐름 방향 설정",
|
||||||
"modal.flow.direction.setting.info": "흐름방향을 선택하세요.",
|
"modal.flow.direction.setting.info": "흐름방향을 선택하세요.",
|
||||||
"modal.image.size.setting": "이미지 크기 조절",
|
"modal.image.size.setting": "이미지 크기 조절",
|
||||||
|
"modal.actual.size.setting": "실측치 설정",
|
||||||
|
"modal.actual.size.setting.info": "※隅棟・谷・棟의 실제 치수를 입력해주세요.",
|
||||||
|
"modal.actual.size.setting.not.exist.auxiliary.line": "실측치 입력할 보조선을 선택해 주세요",
|
||||||
|
"modal.actual.size.setting.not.exist.size": "실제치수 길이를 입력해 주세요",
|
||||||
|
"modal.actual.size.setting.plane.size.length": "복도치수 길이",
|
||||||
|
"modal.actual.size.setting.actual.size.length": "실제치수 길이",
|
||||||
"plan.message.confirm.save": "PLAN을 저장하시겠습니까?",
|
"plan.message.confirm.save": "PLAN을 저장하시겠습니까?",
|
||||||
"plan.message.confirm.copy": "PLAN을 복사하시겠습니까?",
|
"plan.message.confirm.copy": "PLAN을 복사하시겠습니까?",
|
||||||
"plan.message.confirm.delete": "PLAN을 삭제하시겠습니까?",
|
"plan.message.confirm.delete": "PLAN을 삭제하시겠습니까?",
|
||||||
@ -394,6 +400,9 @@
|
|||||||
"modal.module.circuit.number.edit": "모듈 일괄 회로 번호 변경",
|
"modal.module.circuit.number.edit": "모듈 일괄 회로 번호 변경",
|
||||||
"modal.module.circuit.number.edit.info": "회로 번호를 입력해주세요.",
|
"modal.module.circuit.number.edit.info": "회로 번호를 입력해주세요.",
|
||||||
"modal.module.circuit.number": "회로 번호",
|
"modal.module.circuit.number": "회로 번호",
|
||||||
|
"modal.line.property.change": "변경할 속성을 선택해 주세요.",
|
||||||
|
"modal.line.property.change.unselect": "변경할 라인을 선택해 주세요.",
|
||||||
|
"modal.line.property.change.confirm": "속성을 변경하시겠습니까?",
|
||||||
"common.message.no.data": "No data",
|
"common.message.no.data": "No data",
|
||||||
"common.message.no.dataDown": "No data to download",
|
"common.message.no.dataDown": "No data to download",
|
||||||
"common.message.noData": "No data to display",
|
"common.message.noData": "No data to display",
|
||||||
@ -489,6 +498,7 @@
|
|||||||
"commons.east": "동",
|
"commons.east": "동",
|
||||||
"commons.south": "남",
|
"commons.south": "남",
|
||||||
"commons.north": "북",
|
"commons.north": "북",
|
||||||
|
"commons.none": "선택안함",
|
||||||
"font.style.normal": "보통",
|
"font.style.normal": "보통",
|
||||||
"font.style.italic": "기울임꼴",
|
"font.style.italic": "기울임꼴",
|
||||||
"font.style.bold": "굵게",
|
"font.style.bold": "굵게",
|
||||||
@ -824,7 +834,7 @@
|
|||||||
"estimate.detail.estimateType": "주문분류",
|
"estimate.detail.estimateType": "주문분류",
|
||||||
"estimate.detail.roofCns": "지붕재・사양시공",
|
"estimate.detail.roofCns": "지붕재・사양시공",
|
||||||
"estimate.detail.remarks": "비고",
|
"estimate.detail.remarks": "비고",
|
||||||
"estimate.detail.nextSubmit": "후일자료제출",
|
"estimate.detail.fileFlg": "후일자료제출",
|
||||||
"estimate.detail.header.fileList1": "파일첨부",
|
"estimate.detail.header.fileList1": "파일첨부",
|
||||||
"estimate.detail.fileList.btn": "파일선택",
|
"estimate.detail.fileList.btn": "파일선택",
|
||||||
"estimate.detail.header.fileList2": "첨부파일 목록",
|
"estimate.detail.header.fileList2": "첨부파일 목록",
|
||||||
@ -841,11 +851,62 @@
|
|||||||
"estimate.detail.sepcialEstimateProductInfo.calcFormula1": "(모듈수량 * 수량)÷100",
|
"estimate.detail.sepcialEstimateProductInfo.calcFormula1": "(모듈수량 * 수량)÷100",
|
||||||
"estimate.detail.sepcialEstimateProductInfo.calcFormula2": "PKG단가(W) * PKG용량(W)",
|
"estimate.detail.sepcialEstimateProductInfo.calcFormula2": "PKG단가(W) * PKG용량(W)",
|
||||||
"estimate.detail.header.showPrice": "가격표시",
|
"estimate.detail.header.showPrice": "가격표시",
|
||||||
"estimate.detail.showPrice.btn1": "Pricing",
|
"estimate.detail.header.unitPrice": "정가",
|
||||||
|
"estimate.detail.showPrice.pricingBtn": "Pricing",
|
||||||
"estimate.detail.showPrice.description1": "제품 가격 OPEN",
|
"estimate.detail.showPrice.description1": "제품 가격 OPEN",
|
||||||
"estimate.detail.showPrice.description2": "추가, 변경 자재",
|
"estimate.detail.showPrice.description2": "추가, 변경 자재",
|
||||||
"estimate.detail.showPrice.description3": "첨부필수",
|
"estimate.detail.showPrice.description3": "첨부필수",
|
||||||
"estimate.detail.showPrice.description4": "클릭하여 제품 특이사항 확인",
|
"estimate.detail.showPrice.description4": "클릭하여 제품 특이사항 확인",
|
||||||
"estimate.detail.showPrice.btn2": "제품추가",
|
"estimate.detail.showPrice.addItem": "제품추가",
|
||||||
"estimate.detail.showPrice.btn3": "제품삭제"
|
"estimate.detail.showPrice.delItem": "제품삭제",
|
||||||
|
"estimate.detail.itemTableHeader.dispOrder": "Item",
|
||||||
|
"estimate.detail.itemTableHeader.itemId": "품번",
|
||||||
|
"estimate.detail.itemTableHeader.itemNo": "형명",
|
||||||
|
"estimate.detail.itemTableHeader.amount": "수량",
|
||||||
|
"estimate.detail.itemTableHeader.unit": "단위",
|
||||||
|
"estimate.detail.itemTableHeader.salePrice": "단가",
|
||||||
|
"estimate.detail.itemTableHeader.saleTotPrice": "금액(부가세별도)",
|
||||||
|
"estimate.detail.docPopup.title": "문서다운로드 옵션설정",
|
||||||
|
"estimate.detail.docPopup.explane": "다운로드할 문서 옵션을 선택한 후 문서 다운로드 버튼을 클릭합니다.",
|
||||||
|
"estimate.detail.docPopup.schUnitPriceFlg": "다운로드 파일",
|
||||||
|
"estimate.detail.docPopup.schUnitPriceFlg.schUnitPriceFlg0": "견적가 Excel",
|
||||||
|
"estimate.detail.docPopup.schUnitPriceFlg.schUnitPriceFlg1": "정가용 Excel",
|
||||||
|
"estimate.detail.docPopup.schUnitPriceFlg.schUnitPriceFlg2": "견적가 PDF",
|
||||||
|
"estimate.detail.docPopup.schUnitPriceFlg.schUnitPriceFlg3": "정가용 PDF",
|
||||||
|
"estimate.detail.docPopup.schDisplayFlg": "견적제출서 표시명",
|
||||||
|
"estimate.detail.docPopup.schDisplayFlg.schDisplayFlg0": "판매점명",
|
||||||
|
"estimate.detail.docPopup.schDisplayFlg.schDisplayFlg1": "안건명",
|
||||||
|
"estimate.detail.docPopup.schWeightFlg": "가대 중량표 포함",
|
||||||
|
"estimate.detail.docPopup.schWeightFlg.schWeightFlg0": "포함",
|
||||||
|
"estimate.detail.docPopup.schWeightFlg.schWeightFlg1": "미포함",
|
||||||
|
"estimate.detail.docPopup.schDrawingFlg": "도면/시뮬레이션 파일 포함",
|
||||||
|
"estimate.detail.docPopup.schDrawingFlg.schDrawingFlg0": "포함",
|
||||||
|
"estimate.detail.docPopup.schDrawingFlg.schDrawingFlg1": "미포함",
|
||||||
|
"estimate.detail.docPopup.close": "닫기",
|
||||||
|
"estimate.detail.docPopup.docDownload": "문서 다운로드",
|
||||||
|
"estimate.detail.productFeaturesPopup.title": "제품특이사항",
|
||||||
|
"estimate.detail.productFeaturesPopup.close": "닫기",
|
||||||
|
"estimate.detail.save.alertMsg": "저장되었습니다. 견적서에서 제품을 변경할 경우, 도면 및 회로에 반영되지 않습니다.",
|
||||||
|
"estimate.detail.save.requiredMsg": "파일첨부가 필수인 아이템이 있습니다. 파일을 첨부하거나 후일첨부를 체크해주십시오.",
|
||||||
|
"estimate.detail.reset.confirmMsg": "저장된 견적서 정보가 초기화되고, 도면정보가 반영됩니다. 정말로 초기화 하시겠습니까?",
|
||||||
|
"simulator.title.sub1": "물건번호",
|
||||||
|
"simulator.title.sub2": "작성일",
|
||||||
|
"simulator.title.sub3": "시스템 용량",
|
||||||
|
"simulator.title.sub4": "연간예측발전량",
|
||||||
|
"simulator.title.sub5": "도도부현",
|
||||||
|
"simulator.title.sub6": "일사량 관측지점",
|
||||||
|
"simulator.title.sub7": "적설조건",
|
||||||
|
"simulator.title.sub8": "풍속조건",
|
||||||
|
"simulator.title.sub9": "이하",
|
||||||
|
"simulator.table.sub1": "지붕면",
|
||||||
|
"simulator.table.sub2": "경사각",
|
||||||
|
"simulator.table.sub3": "방위각(도)",
|
||||||
|
"simulator.table.sub4": "태양전지모듈",
|
||||||
|
"simulator.table.sub5": "매수",
|
||||||
|
"simulator.table.sub6": "합계",
|
||||||
|
"simulator.table.sub7": "파워 컨디셔너",
|
||||||
|
"simulator.table.sub8": "대",
|
||||||
|
"simulator.table.sub9": "예측발전량 (kWh)",
|
||||||
|
"simulator.notice.sub1": "Hanwha Japan 연간 발전량",
|
||||||
|
"simulator.notice.sub2": "시뮬레이션 안내사항"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -185,6 +185,7 @@ export const corridorDimensionSelector = selector({
|
|||||||
const settingModalFirstOptions = get(settingModalFirstOptionsState)
|
const settingModalFirstOptions = get(settingModalFirstOptionsState)
|
||||||
return settingModalFirstOptions.dimensionDisplay.find((option) => option.selected)
|
return settingModalFirstOptions.dimensionDisplay.find((option) => option.selected)
|
||||||
},
|
},
|
||||||
|
dangerouslyAllowMutability: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
// 디스플레이 설정 - 화면 표시
|
// 디스플레이 설정 - 화면 표시
|
||||||
|
|||||||
@ -925,3 +925,32 @@ export function checkLineOrientation(line) {
|
|||||||
return 'diagonal' // 대각선
|
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
|
||||||
|
}
|
||||||
|
|||||||
@ -1,13 +1,6 @@
|
|||||||
import { fabric } from 'fabric'
|
import { fabric } from 'fabric'
|
||||||
import { QLine } from '@/components/fabric/QLine'
|
import { QLine } from '@/components/fabric/QLine'
|
||||||
import {
|
import { calculateIntersection, distanceBetweenPoints, findClosestPoint, getDegreeByChon, getDirectionByPoint } from '@/util/canvas-util'
|
||||||
calculateIntersection,
|
|
||||||
distanceBetweenPoints,
|
|
||||||
findClosestPoint,
|
|
||||||
getDegreeByChon,
|
|
||||||
getDirectionByPoint,
|
|
||||||
isPointOnLine,
|
|
||||||
} from '@/util/canvas-util'
|
|
||||||
|
|
||||||
import { QPolygon } from '@/components/fabric/QPolygon'
|
import { QPolygon } from '@/components/fabric/QPolygon'
|
||||||
import * as turf from '@turf/turf'
|
import * as turf from '@turf/turf'
|
||||||
@ -1177,244 +1170,6 @@ export default function offsetPolygon(vertices, offset) {
|
|||||||
polygon.canvas.renderAll()
|
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) {
|
function normalizePoint(point) {
|
||||||
return {
|
return {
|
||||||
@ -1432,7 +1187,7 @@ function arePolygonsEqual(polygon1, polygon2) {
|
|||||||
return normalizedPolygon1.every((point, index) => arePointsEqual(point, normalizedPolygon2[index]))
|
return normalizedPolygon1.every((point, index) => arePointsEqual(point, normalizedPolygon2[index]))
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeDuplicatePolygons(polygons) {
|
export function removeDuplicatePolygons(polygons) {
|
||||||
const uniquePolygons = []
|
const uniquePolygons = []
|
||||||
|
|
||||||
polygons.forEach((polygon) => {
|
polygons.forEach((polygon) => {
|
||||||
@ -1477,6 +1232,42 @@ function calculateAngleBetweenLines(line1, line2) {
|
|||||||
return (angleInRadians * 180) / Math.PI
|
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) => {
|
export const drawRidgeRoof = (roofId, canvas) => {
|
||||||
const roof = canvas?.getObjects().find((object) => object.id === roofId)
|
const roof = canvas?.getObjects().find((object) => object.id === roofId)
|
||||||
const hasNonParallelLines = roof.lines.filter((line) => line.x1 !== line.x2 && line.y1 !== line.y2)
|
const hasNonParallelLines = roof.lines.filter((line) => line.x1 !== line.x2 && line.y1 !== line.y2)
|
||||||
@ -1509,14 +1300,19 @@ const drawRidge = (roof, canvas) => {
|
|||||||
prevRoof = index === 0 ? wallLines[wallLines.length - 1] : wallLines[index - 1]
|
prevRoof = index === 0 ? wallLines[wallLines.length - 1] : wallLines[index - 1]
|
||||||
nextRoof = index === wallLines.length - 1 ? wallLines[0] : index === wallLines.length ? wallLines[1] : wallLines[index + 1]
|
nextRoof = index === wallLines.length - 1 ? wallLines[0] : index === wallLines.length ? wallLines[1] : wallLines[index + 1]
|
||||||
|
|
||||||
if (prevRoof.direction !== nextRoof.direction && currentWall.length <= currentRoof.length) {
|
const angle1 = calculateAngle(prevRoof.startPoint, prevRoof.endPoint)
|
||||||
ridgeRoof.push({ index: index, roof: currentRoof, length: currentRoof.length })
|
const angle2 = calculateAngle(nextRoof.startPoint, nextRoof.endPoint)
|
||||||
|
|
||||||
|
if (Math.abs(angle1 - angle2) === 180 && currentWall.attributes.planeSize <= currentRoof.attributes.planeSize) {
|
||||||
|
ridgeRoof.push({ index: index, roof: currentRoof, length: currentRoof.attributes.planeSize })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// 지붕의 길이가 짧은 순으로 정렬
|
// 지붕의 길이가 짧은 순으로 정렬
|
||||||
ridgeRoof.sort((a, b) => a.length - b.length)
|
ridgeRoof.sort((a, b) => a.length - b.length)
|
||||||
|
|
||||||
|
console.log('ridgeRoof', ridgeRoof)
|
||||||
|
|
||||||
ridgeRoof.forEach((item) => {
|
ridgeRoof.forEach((item) => {
|
||||||
if (getMaxRidge(roofLines.length) > roof.ridges.length) {
|
if (getMaxRidge(roofLines.length) > roof.ridges.length) {
|
||||||
let index = item.index,
|
let index = item.index,
|
||||||
@ -1538,28 +1334,24 @@ const drawRidge = (roof, canvas) => {
|
|||||||
let xEqualInnerLines = anotherRoof.filter((roof) => roof.x1 === roof.x2 && isInnerLine(prevRoof, currentRoof, nextRoof, roof)), //x가 같은 내부선
|
let xEqualInnerLines = anotherRoof.filter((roof) => roof.x1 === roof.x2 && isInnerLine(prevRoof, currentRoof, nextRoof, roof)), //x가 같은 내부선
|
||||||
yEqualInnerLines = anotherRoof.filter((roof) => roof.y1 === roof.y2 && isInnerLine(prevRoof, currentRoof, nextRoof, roof)) //y가 같은 내부선
|
yEqualInnerLines = anotherRoof.filter((roof) => roof.y1 === roof.y2 && isInnerLine(prevRoof, currentRoof, nextRoof, roof)) //y가 같은 내부선
|
||||||
|
|
||||||
let ridgeBaseLength = Math.round((currentRoof.length / 2) * 10) / 10, // 지붕의 기반 길이
|
let ridgeBaseLength = Math.round(currentRoof.attributes.planeSize / 2), // 지붕의 기반 길이
|
||||||
ridgeMaxLength = Math.min(prevRoof.length, nextRoof.length), // 지붕의 최대 길이. 이전, 다음 벽 중 짧은 길이
|
ridgeMaxLength = Math.min(prevRoof.attributes.planeSize, nextRoof.attributes.planeSize), // 지붕의 최대 길이. 이전, 다음 벽 중 짧은 길이
|
||||||
ridgeAcrossLength = Math.round((ridgeMaxLength - currentRoof.length) * 10) / 10 // 맞은편 벽까지의 길이 - 지붕의 기반 길이
|
ridgeAcrossLength = Math.abs(Math.max(prevRoof.attributes.planeSize, nextRoof.attributes.planeSize) - currentRoof.attributes.planeSize) // 맞은편 벽까지의 길이 - 지붕의 기반 길이
|
||||||
|
|
||||||
|
console.log('ridgeBaseLength', ridgeBaseLength, 'ridgeMaxLength', ridgeMaxLength, 'ridgeAcrossLength', ridgeAcrossLength)
|
||||||
let acrossRoof = anotherRoof
|
let acrossRoof = anotherRoof
|
||||||
.filter((roof) => {
|
.filter((roof) => {
|
||||||
if (roof.x1 === roof.x2) {
|
const angle1 = calculateAngle(currentRoof.startPoint, currentRoof.endPoint)
|
||||||
if ((nextRoof.direction === 'right' && roof.x1 > currentRoof.x1) || (nextRoof.direction === 'left' && roof.x1 < currentRoof.x1)) {
|
const angle2 = calculateAngle(roof.startPoint, roof.endPoint)
|
||||||
return roof
|
if (Math.abs(angle1 - angle2) === 180) {
|
||||||
}
|
return roof
|
||||||
}
|
|
||||||
if (roof.y1 === roof.y2) {
|
|
||||||
if ((nextRoof.direction === 'top' && roof.y1 < currentRoof.y1) || (nextRoof.direction === 'bottom' && roof.y1 > currentRoof.y1)) {
|
|
||||||
return roof
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.reduce((prev, current) => {
|
.reduce((prev, current) => {
|
||||||
let hasBetweenRoof = false
|
let hasBetweenRoof = false
|
||||||
if (current.x1 === current.x2) {
|
if (current.x1 === current.x2) {
|
||||||
hasBetweenRoof = roofLines
|
hasBetweenRoof = roofLines
|
||||||
.filter((roof) => roof !== current && roof !== currentRoof)
|
.filter((roof) => roof !== current)
|
||||||
.some((line) => {
|
.some((line) => {
|
||||||
let currentY2 = currentRoof.y2
|
let currentY2 = currentRoof.y2
|
||||||
if (yEqualInnerLines.length > 0) {
|
if (yEqualInnerLines.length > 0) {
|
||||||
@ -1571,12 +1363,13 @@ const drawRidge = (roof, canvas) => {
|
|||||||
const isY2Between = (line.y2 > currentRoof.y1 && line.y2 < currentY2) || (line.y2 > currentY2 && line.y2 < currentRoof.y1)
|
const isY2Between = (line.y2 > currentRoof.y1 && line.y2 < currentY2) || (line.y2 > currentY2 && line.y2 < currentRoof.y1)
|
||||||
const isX1Between = (line.x1 > currentRoof.x1 && line.x1 < current.x1) || (line.x1 > currentRoof.x1 && line.x1 < current.x1)
|
const isX1Between = (line.x1 > currentRoof.x1 && line.x1 < current.x1) || (line.x1 > currentRoof.x1 && line.x1 < current.x1)
|
||||||
const isX2Between = (line.x2 > currentRoof.x1 && line.x2 < current.x1) || (line.x2 > currentRoof.x1 && line.x2 < current.x1)
|
const isX2Between = (line.x2 > currentRoof.x1 && line.x2 < current.x1) || (line.x2 > currentRoof.x1 && line.x2 < current.x1)
|
||||||
|
|
||||||
return isY1Between && isY2Between && isX1Between && isX2Between
|
return isY1Between && isY2Between && isX1Between && isX2Between
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (current.y1 === current.y2) {
|
if (current.y1 === current.y2) {
|
||||||
hasBetweenRoof = wallLines
|
hasBetweenRoof = roofLines
|
||||||
.filter((roof) => roof !== current && roof !== currentRoof)
|
.filter((roof) => roof !== current)
|
||||||
.some((line) => {
|
.some((line) => {
|
||||||
let currentX2 = currentRoof.x2
|
let currentX2 = currentRoof.x2
|
||||||
if (xEqualInnerLines.length > 0) {
|
if (xEqualInnerLines.length > 0) {
|
||||||
@ -1614,20 +1407,25 @@ const drawRidge = (roof, canvas) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, undefined)
|
}, undefined)
|
||||||
|
|
||||||
if (acrossRoof !== undefined) {
|
if (acrossRoof !== undefined) {
|
||||||
if (currentRoof.x1 === currentRoof.x2) {
|
if (currentRoof.x1 === currentRoof.x2) {
|
||||||
if (ridgeAcrossLength < Math.abs(currentRoof.x1 - acrossRoof.x1)) {
|
if (ridgeAcrossLength < Math.abs(currentRoof.x1 - acrossRoof.x1)) {
|
||||||
ridgeAcrossLength = Math.round((Math.round(Math.abs(currentRoof.x1 - acrossRoof.x1) * 10) / 10 - currentRoof.length) * 10) / 10
|
ridgeAcrossLength = Math.round(Math.round(Math.abs(currentRoof.x1 - acrossRoof.x1) * 10) - currentRoof.attributes.planeSize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (currentRoof.y1 === currentRoof.y2) {
|
if (currentRoof.y1 === currentRoof.y2) {
|
||||||
if (ridgeAcrossLength < Math.abs(currentRoof.y1 - acrossRoof.y1)) {
|
if (ridgeAcrossLength < Math.abs(currentRoof.y1 - acrossRoof.y1)) {
|
||||||
ridgeAcrossLength = Math.round((Math.round(Math.abs(currentRoof.y1 - acrossRoof.y1) * 10) / 10 - currentRoof.length) * 10) / 10
|
ridgeAcrossLength = Math.round(Math.round(Math.abs(currentRoof.y1 - acrossRoof.y1) * 10) - currentRoof.attributes.planeSize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ridgeBaseLength = ridgeBaseLength / 10
|
||||||
|
ridgeMaxLength = ridgeMaxLength / 10
|
||||||
|
ridgeAcrossLength = ridgeAcrossLength / 10
|
||||||
|
|
||||||
|
console.log('ridgeBaseLength', ridgeBaseLength, 'ridgeMaxLength', ridgeMaxLength, 'ridgeAcrossLength', ridgeAcrossLength)
|
||||||
|
|
||||||
if (ridgeBaseLength > 0 && ridgeMaxLength > 0 && ridgeAcrossLength > 0) {
|
if (ridgeBaseLength > 0 && ridgeMaxLength > 0 && ridgeAcrossLength > 0) {
|
||||||
let ridgeLength = Math.min(ridgeMaxLength, ridgeAcrossLength)
|
let ridgeLength = Math.min(ridgeMaxLength, ridgeAcrossLength)
|
||||||
if (currentRoof.x1 === currentRoof.x2) {
|
if (currentRoof.x1 === currentRoof.x2) {
|
||||||
@ -1769,6 +1567,9 @@ const drawRidge = (roof, canvas) => {
|
|||||||
attributes: { roofId: roof.id },
|
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)
|
canvas.add(ridge)
|
||||||
roof.ridges.push(ridge)
|
roof.ridges.push(ridge)
|
||||||
roof.innerLines.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.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) => !(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))
|
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)
|
canvas.add(newRidge)
|
||||||
roof.ridges.push(newRidge)
|
roof.ridges.push(newRidge)
|
||||||
roof.innerLines.push(newRidge)
|
roof.innerLines.push(newRidge)
|
||||||
@ -1884,7 +1687,7 @@ const drawHips = (roof, canvas) => {
|
|||||||
stroke: 'red',
|
stroke: 'red',
|
||||||
strokeWidth: 1,
|
strokeWidth: 1,
|
||||||
name: LINE_TYPE.SUBLINE.HIP,
|
name: LINE_TYPE.SUBLINE.HIP,
|
||||||
attributes: { roofId: roof.id, currentRoof: currentRoof.id },
|
attributes: { roofId: roof.id, currentRoof: currentRoof.id, actualSize: 0 },
|
||||||
})
|
})
|
||||||
canvas.add(hip1)
|
canvas.add(hip1)
|
||||||
const hip1Base = ((Math.abs(hip1.x1 - hip1.x2) + Math.abs(hip1.y1 - hip1.y2)) / 2) * 10
|
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',
|
stroke: 'red',
|
||||||
strokeWidth: 1,
|
strokeWidth: 1,
|
||||||
name: LINE_TYPE.SUBLINE.HIP,
|
name: LINE_TYPE.SUBLINE.HIP,
|
||||||
attributes: {
|
attributes: { roofId: roof.id, currentRoof: currentRoof.id, actualSize: 0 },
|
||||||
roofId: roof.id,
|
|
||||||
currentRoof: currentRoof.id,
|
|
||||||
planeSize: currentRoof.length,
|
|
||||||
actualSize: currentRoof.length,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
canvas.add(hip2)
|
canvas.add(hip2)
|
||||||
const hip2Base = ((Math.abs(hip2.x1 - hip2.x2) + Math.abs(hip2.y1 - hip2.y2)) / 2) * 10
|
const hip2Base = ((Math.abs(hip2.x1 - hip2.x2) + Math.abs(hip2.y1 - hip2.y2)) / 2) * 10
|
||||||
@ -1977,16 +1775,11 @@ const drawHips = (roof, canvas) => {
|
|||||||
stroke: 'red',
|
stroke: 'red',
|
||||||
strokeWidth: 1,
|
strokeWidth: 1,
|
||||||
name: LINE_TYPE.SUBLINE.HIP,
|
name: LINE_TYPE.SUBLINE.HIP,
|
||||||
attributes: {
|
attributes: { roofId: roof.id, currentRoof: currentRoof.id, actualSize: 0 },
|
||||||
roofId: roof.id,
|
|
||||||
currentRoof: currentRoof.id,
|
|
||||||
planeSize: currentRoof.length,
|
|
||||||
actualSize: currentRoof.length,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
canvas.add(hip)
|
canvas.add(hip)
|
||||||
const hipBase = ((Math.abs(hip.x1 - hip.x2) + Math.abs(hip.y1 - hip.y2)) / 2) * 10
|
const hipBase = ((Math.abs(hip.x1 - hip.x2) + Math.abs(hip.y1 - hip.y2)) / 2) * 10
|
||||||
const hipHeight = Math.round(hipBase / Math.tan(((90 - currentRoof.attributes.degree) * Math.PI) / 180))
|
const hipHeight = Math.round(hipBase / Math.tan(((90 - currentDegree) * Math.PI) / 180))
|
||||||
hip.attributes.planeSize = Math.round(Math.sqrt(Math.pow(hip.x1 - hip.x2, 2) + Math.pow(hip.y1 - hip.y2, 2))) * 10
|
hip.attributes.planeSize = Math.round(Math.sqrt(Math.pow(hip.x1 - hip.x2, 2) + Math.pow(hip.y1 - hip.y2, 2))) * 10
|
||||||
if (prevDegree === currentDegree) {
|
if (prevDegree === currentDegree) {
|
||||||
hip.attributes.actualSize = Math.round(Math.sqrt(Math.pow(hip.attributes.planeSize, 2) + Math.pow(hipHeight, 2)))
|
hip.attributes.actualSize = Math.round(Math.sqrt(Math.pow(hip.attributes.planeSize, 2) + Math.pow(hipHeight, 2)))
|
||||||
@ -2040,92 +1833,6 @@ const checkValley = (polygon, line1, line2) => {
|
|||||||
return isValley
|
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개 이상 이어지지 않은 라인 포인트 계산
|
3개 이상 이어지지 않은 라인 포인트 계산
|
||||||
모임지붕에서 point는 3개 이상의 라인과 접해야 함.
|
모임지붕에서 point는 3개 이상의 라인과 접해야 함.
|
||||||
@ -2216,6 +1923,8 @@ const connectLinePoint = (polygon) => {
|
|||||||
stroke: 'purple',
|
stroke: 'purple',
|
||||||
strokeWidth: 1,
|
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.canvas.add(line)
|
||||||
polygon.innerLines.push(line)
|
polygon.innerLines.push(line)
|
||||||
})
|
})
|
||||||
@ -2272,6 +1981,8 @@ const connectLinePoint = (polygon) => {
|
|||||||
stroke: 'purple',
|
stroke: 'purple',
|
||||||
strokeWidth: 1,
|
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.canvas.add(line)
|
||||||
polygon.innerLines.push(line)
|
polygon.innerLines.push(line)
|
||||||
})
|
})
|
||||||
@ -2576,6 +2287,8 @@ const changeEavesRoof = (currentRoof, canvas) => {
|
|||||||
hipX2 = midX - addHipX2
|
hipX2 = midX - addHipX2
|
||||||
hipY2 = midY - addHipY2
|
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) => {
|
hipLines.forEach((hip) => {
|
||||||
@ -2753,6 +2466,8 @@ const changeGableRoof = (currentRoof, canvas) => {
|
|||||||
})
|
})
|
||||||
currentRoof.attributes.ridgeCoordinate = { x1: ridge.x2, y1: ridge.y2 }
|
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], {
|
let hip1 = new QLine([currentRoof.x1, currentRoof.y1, midX, midY], {
|
||||||
fontSize: roof.fontSize,
|
fontSize: roof.fontSize,
|
||||||
@ -2814,6 +2529,14 @@ const changeHipAndGableRoof = (currentRoof, canvas) => {
|
|||||||
if (wallLine.length > 0) {
|
if (wallLine.length > 0) {
|
||||||
wallLine = wallLine[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 midX = (currentRoof.x1 + currentRoof.x2) / 2 // 지붕의 X 중심
|
||||||
const midY = (currentRoof.y1 + currentRoof.y2) / 2 // 지붕의 Y 중심
|
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 }
|
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], {
|
const hip1 = new QLine([currentRoof.x1, currentRoof.y1, midX + hipX2, midY + hipY2], {
|
||||||
@ -2937,10 +2662,15 @@ const changeHipAndGableRoof = (currentRoof, canvas) => {
|
|||||||
attributes: {
|
attributes: {
|
||||||
roofId: roof.id,
|
roofId: roof.id,
|
||||||
currentRoof: currentRoof.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)
|
canvas?.add(hip1)
|
||||||
roof.innerLines.push(hip1)
|
roof.innerLines.push(hip1)
|
||||||
|
|
||||||
@ -2956,6 +2686,10 @@ const changeHipAndGableRoof = (currentRoof, canvas) => {
|
|||||||
actualSize: currentRoof.length,
|
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)
|
canvas?.add(hip2)
|
||||||
roof.innerLines.push(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], {
|
const gableLine = new QLine([hip.x2, hip.y2, currentRoof.attributes.ridgeCoordinate.x1, currentRoof.attributes.ridgeCoordinate.y1], {
|
||||||
fontSize: roof.fontSize,
|
fontSize: roof.fontSize,
|
||||||
stroke: 'red',
|
stroke: 'red',
|
||||||
@ -2983,10 +2717,15 @@ const changeHipAndGableRoof = (currentRoof, canvas) => {
|
|||||||
attributes: {
|
attributes: {
|
||||||
roofId: roof.id,
|
roofId: roof.id,
|
||||||
currentRoof: currentRoof.id,
|
currentRoof: currentRoof.id,
|
||||||
planeSize: currentRoof.length,
|
actualSize: 0,
|
||||||
actualSize: currentRoof.length,
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
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)
|
canvas?.add(gableLine)
|
||||||
roof.innerLines.push(gableLine)
|
roof.innerLines.push(gableLine)
|
||||||
})
|
})
|
||||||
@ -3016,6 +2755,18 @@ const changeJerkInHeadRoof = (currentRoof, canvas) => {
|
|||||||
wallLine = wallLine[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 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 midX = (currentRoof.x1 + currentRoof.x2) / 2 // 지붕의 X 중심
|
||||||
const midY = (currentRoof.y1 + currentRoof.y2) / 2 // 지붕의 Y 중심
|
const midY = (currentRoof.y1 + currentRoof.y2) / 2 // 지붕의 Y 중심
|
||||||
const midWallX = (wallLine.x1 + wallLine.x2) / 2 // 벽의 X 중심
|
const midWallX = (wallLine.x1 + wallLine.x2) / 2 // 벽의 X 중심
|
||||||
@ -3137,6 +2888,8 @@ const changeJerkInHeadRoof = (currentRoof, canvas) => {
|
|||||||
hipX2 = midX - xWidth
|
hipX2 = midX - xWidth
|
||||||
hipY2 = midY - yWidth
|
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
|
let hipX1 = (Math.sign(currentRoof.x1 - midX) * currentRoof.attributes.width) / 2
|
||||||
@ -3150,10 +2903,14 @@ const changeJerkInHeadRoof = (currentRoof, canvas) => {
|
|||||||
attributes: {
|
attributes: {
|
||||||
roofId: roof.id,
|
roofId: roof.id,
|
||||||
currentRoofId: currentRoof.id,
|
currentRoofId: currentRoof.id,
|
||||||
planeSize: currentRoof.length,
|
actualSize: 0,
|
||||||
actualSize: currentRoof.length,
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
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)
|
canvas?.add(gable1)
|
||||||
roof.innerLines.push(gable1)
|
roof.innerLines.push(gable1)
|
||||||
|
|
||||||
@ -3168,10 +2925,13 @@ const changeJerkInHeadRoof = (currentRoof, canvas) => {
|
|||||||
attributes: {
|
attributes: {
|
||||||
roofId: roof.id,
|
roofId: roof.id,
|
||||||
currentRoofId: currentRoof.id,
|
currentRoofId: currentRoof.id,
|
||||||
planeSize: currentRoof.length,
|
actualSize: 0,
|
||||||
actualSize: currentRoof.length,
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
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)
|
canvas?.add(gable2)
|
||||||
roof.innerLines.push(gable2)
|
roof.innerLines.push(gable2)
|
||||||
|
|
||||||
@ -3183,10 +2943,11 @@ const changeJerkInHeadRoof = (currentRoof, canvas) => {
|
|||||||
attributes: {
|
attributes: {
|
||||||
roofId: roof.id,
|
roofId: roof.id,
|
||||||
currentRoofId: currentRoof.id,
|
currentRoofId: currentRoof.id,
|
||||||
planeSize: currentRoof.length,
|
actualSize: 0,
|
||||||
actualSize: currentRoof.length,
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
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)
|
canvas?.add(gable3)
|
||||||
roof.innerLines.push(gable3)
|
roof.innerLines.push(gable3)
|
||||||
|
|
||||||
@ -3198,10 +2959,13 @@ const changeJerkInHeadRoof = (currentRoof, canvas) => {
|
|||||||
attributes: {
|
attributes: {
|
||||||
roofId: roof.id,
|
roofId: roof.id,
|
||||||
currentRoofId: currentRoof.id,
|
currentRoofId: currentRoof.id,
|
||||||
planeSize: currentRoof.length,
|
actualSize: 0,
|
||||||
actualSize: currentRoof.length,
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
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)
|
canvas?.add(hip1)
|
||||||
roof.innerLines.push(hip1)
|
roof.innerLines.push(hip1)
|
||||||
|
|
||||||
@ -3213,10 +2977,13 @@ const changeJerkInHeadRoof = (currentRoof, canvas) => {
|
|||||||
attributes: {
|
attributes: {
|
||||||
roofId: roof.id,
|
roofId: roof.id,
|
||||||
currentRoofId: currentRoof.id,
|
currentRoofId: currentRoof.id,
|
||||||
planeSize: currentRoof.length,
|
actualSize: 0,
|
||||||
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)
|
canvas?.add(hip2)
|
||||||
roof.innerLines.push(hip2)
|
roof.innerLines.push(hip2)
|
||||||
}
|
}
|
||||||
@ -3312,6 +3079,9 @@ const changeWallRoof = (currentRoof, canvas) => {
|
|||||||
canvas?.remove(line)
|
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) {
|
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 prevSignX = Math.sign(prevRoof.x1 - prevRoof.x2)
|
||||||
const prevSignY = Math.sign(prevRoof.y1 - prevRoof.y2)
|
const prevSignY = Math.sign(prevRoof.y1 - prevRoof.y2)
|
||||||
@ -3417,6 +3187,8 @@ const changeWallRoof = (currentRoof, canvas) => {
|
|||||||
y2: ridge.y2 - diffY,
|
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], {
|
let hip1 = new QLine([currentRoof.x1, currentRoof.y1, wallMidX, wallMidY], {
|
||||||
fontSize: roof.fontSize,
|
fontSize: roof.fontSize,
|
||||||
@ -3426,10 +3198,14 @@ const changeWallRoof = (currentRoof, canvas) => {
|
|||||||
attributes: {
|
attributes: {
|
||||||
roofId: roof.id,
|
roofId: roof.id,
|
||||||
currentRoofId: currentRoof.id,
|
currentRoofId: currentRoof.id,
|
||||||
planeSize: currentRoof.length,
|
actualSize: 0,
|
||||||
actualSize: currentRoof.length,
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
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], {
|
let hip2 = new QLine([currentRoof.x2, currentRoof.y2, wallMidX, wallMidY], {
|
||||||
fontSize: roof.fontSize,
|
fontSize: roof.fontSize,
|
||||||
stroke: 'red',
|
stroke: 'red',
|
||||||
@ -3442,6 +3218,10 @@ const changeWallRoof = (currentRoof, canvas) => {
|
|||||||
actualSize: currentRoof.length,
|
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(hip1)
|
||||||
canvas?.add(hip2)
|
canvas?.add(hip2)
|
||||||
roof.innerLines.push(hip1)
|
roof.innerLines.push(hip1)
|
||||||
@ -3505,8 +3285,13 @@ export const changeCurrentRoof = (currentRoof, canvas) => {
|
|||||||
newRoof.setWall(wall)
|
newRoof.setWall(wall)
|
||||||
|
|
||||||
newRoof.lines.forEach((line, index) => {
|
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 = {
|
line.attributes = {
|
||||||
roofId: newRoof.id,
|
roofId: newRoof.id,
|
||||||
|
planeSize: lineLength,
|
||||||
|
actualSize: lineLength,
|
||||||
wallLine: wall.lines[index].id,
|
wallLine: wall.lines[index].id,
|
||||||
type: wall.lines[index].attributes.type,
|
type: wall.lines[index].attributes.type,
|
||||||
offset: wall.lines[index].attributes.offset,
|
offset: wall.lines[index].attributes.offset,
|
||||||
@ -3561,6 +3346,19 @@ const reDrawPolygon = (polygon, canvas) => {
|
|||||||
line.attributes = l.attributes
|
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)
|
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
|
|
||||||
}
|
|
||||||
17
yarn.lock
17
yarn.lock
@ -527,6 +527,11 @@
|
|||||||
resolved "https://registry.npmjs.org/@js-joda/core/-/core-5.6.3.tgz"
|
resolved "https://registry.npmjs.org/@js-joda/core/-/core-5.6.3.tgz"
|
||||||
integrity sha512-T1rRxzdqkEXcou0ZprN1q9yDRlvzCPLqmlNt5IIsGBzoEVgLCCYrKEwc84+TvsXuAc95VAZwtWD2zVsKPY4bcA==
|
integrity sha512-T1rRxzdqkEXcou0ZprN1q9yDRlvzCPLqmlNt5IIsGBzoEVgLCCYrKEwc84+TvsXuAc95VAZwtWD2zVsKPY4bcA==
|
||||||
|
|
||||||
|
"@kurkle/color@^0.3.0":
|
||||||
|
version "0.3.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@kurkle/color/-/color-0.3.2.tgz#5acd38242e8bde4f9986e7913c8fdf49d3aa199f"
|
||||||
|
integrity sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==
|
||||||
|
|
||||||
"@mapbox/node-pre-gyp@^1.0.0":
|
"@mapbox/node-pre-gyp@^1.0.0":
|
||||||
version "1.0.11"
|
version "1.0.11"
|
||||||
resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz#417db42b7f5323d79e93b34a6d7a2a12c0df43fa"
|
resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz#417db42b7f5323d79e93b34a6d7a2a12c0df43fa"
|
||||||
@ -4337,6 +4342,13 @@ chalk@^2.4.2:
|
|||||||
escape-string-regexp "^1.0.5"
|
escape-string-regexp "^1.0.5"
|
||||||
supports-color "^5.3.0"
|
supports-color "^5.3.0"
|
||||||
|
|
||||||
|
chart.js@^4.4.6:
|
||||||
|
version "4.4.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-4.4.6.tgz#da39b84ca752298270d4c0519675c7659936abec"
|
||||||
|
integrity sha512-8Y406zevUPbbIBA/HRk33khEmQPk5+cxeflWE/2rx1NJsjVWMPw/9mSP9rxHP5eqi6LNoPBVMfZHxbwLSgldYA==
|
||||||
|
dependencies:
|
||||||
|
"@kurkle/color" "^0.3.0"
|
||||||
|
|
||||||
"chokidar@>=3.0.0 <4.0.0", chokidar@^3.5.3:
|
"chokidar@>=3.0.0 <4.0.0", chokidar@^3.5.3:
|
||||||
version "3.6.0"
|
version "3.6.0"
|
||||||
resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz"
|
resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz"
|
||||||
@ -5832,6 +5844,11 @@ rbush@^3.0.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
quickselect "^2.0.0"
|
quickselect "^2.0.0"
|
||||||
|
|
||||||
|
react-chartjs-2@^5.2.0:
|
||||||
|
version "5.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-chartjs-2/-/react-chartjs-2-5.2.0.tgz#43c1e3549071c00a1a083ecbd26c1ad34d385f5d"
|
||||||
|
integrity sha512-98iN5aguJyVSxp5U3CblRLH67J8gkfyGNbiK3c+l1QI/G4irHMPQw44aEPmjVag+YKTyQ260NcF82GTQ3bdscA==
|
||||||
|
|
||||||
react-color-palette@^7.2.2:
|
react-color-palette@^7.2.2:
|
||||||
version "7.2.2"
|
version "7.2.2"
|
||||||
resolved "https://registry.npmjs.org/react-color-palette/-/react-color-palette-7.2.2.tgz"
|
resolved "https://registry.npmjs.org/react-color-palette/-/react-color-palette-7.2.2.tgz"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user