Merge branch 'dev' into feature/test-jy
# Conflicts: # src/hooks/roofcover/usePropertiesSetting.js # src/hooks/roofcover/useRoofAllocationSetting.js
This commit is contained in:
commit
57ee521410
3
public/static/images/canvas/estiment_arr.svg
Normal file
3
public/static/images/canvas/estiment_arr.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg width="11" height="7" viewBox="0 0 11 7" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M1 1L5.5 5.5L10 1" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 210 B |
3
public/static/images/canvas/estiment_arr_color.svg
Normal file
3
public/static/images/canvas/estiment_arr_color.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg width="11" height="7" viewBox="0 0 11 7" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path id="Vector 7174" d="M1 1L5.5 5.5L10 1" stroke="#C2D0DD" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 229 B |
@ -1,128 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import { Select, SelectItem } from '@nextui-org/react'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useAxios } from '@/hooks/useAxios'
|
||||
|
||||
export default function RoofSelect() {
|
||||
const [roofMaterials, setRoofMaterials] = useState([])
|
||||
const [manufacturers, setManufacturers] = useState([])
|
||||
const [trestles, setTrestles] = useState([])
|
||||
const [modules, setModules] = useState([])
|
||||
const [originTrestles, setOriginTrestles] = useState([])
|
||||
|
||||
const [roofMaterialId, setRoofMaterialId] = useState(null)
|
||||
const [manufacturerId, setManufacturerId] = useState(null)
|
||||
const [trestleId, setTrestleId] = useState(null)
|
||||
|
||||
const { get } = useAxios()
|
||||
|
||||
useEffect(() => {
|
||||
get({ url: '/api/roof-material/roof-material-infos' }).then((res) => {
|
||||
//TODO: error handling
|
||||
if (!res) return
|
||||
|
||||
setRoofMaterials(res)
|
||||
})
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (!roofMaterialId) {
|
||||
return
|
||||
}
|
||||
|
||||
get({ url: `/api/roof-material/roof-material-infos/${roofMaterialId}/trestles` }).then((res) => {
|
||||
if (res.length === 0) {
|
||||
return
|
||||
}
|
||||
setOriginTrestles(res)
|
||||
const manufactural = res.map((trestle) => {
|
||||
return { id: trestle.manufacturerId, name: trestle.manufacturerName }
|
||||
})
|
||||
// Remove duplicates
|
||||
const uniqueManufactural = Array.from(new Set(manufactural.map((a) => a.id))).map((id) => {
|
||||
return manufactural.find((a) => a.id === id)
|
||||
})
|
||||
|
||||
setManufacturers(uniqueManufactural)
|
||||
})
|
||||
}, [roofMaterialId])
|
||||
|
||||
useEffect(() => {
|
||||
if (!manufacturerId) {
|
||||
return
|
||||
}
|
||||
|
||||
const trestles = originTrestles.filter((trestle) => trestle.manufacturerId === manufacturerId)
|
||||
setTrestles(trestles)
|
||||
}, [manufacturerId])
|
||||
|
||||
useEffect(() => {
|
||||
if (!trestleId) {
|
||||
return
|
||||
}
|
||||
get({ url: `/api/module/module-infos?roofMaterialId=${roofMaterialId}&trestleId=${trestleId}` }).then((res) => {
|
||||
if (res.length === 0) {
|
||||
return
|
||||
}
|
||||
setModules(res)
|
||||
})
|
||||
}, [trestleId])
|
||||
|
||||
const handleRoofMaterialOnChange = (e) => {
|
||||
const roofMaterialId = e.target.value
|
||||
setRoofMaterialId(roofMaterialId)
|
||||
setManufacturers([])
|
||||
setManufacturerId(null)
|
||||
setTrestleId(null)
|
||||
setTrestles([])
|
||||
setModules([])
|
||||
}
|
||||
|
||||
const handleManufacturersOnChange = (e) => {
|
||||
const manufacturerId = Number(e.target.value)
|
||||
setTrestles([])
|
||||
setManufacturerId(manufacturerId)
|
||||
setTrestleId(null)
|
||||
setModules([])
|
||||
}
|
||||
|
||||
const handleTrestlesOnChange = (e) => {
|
||||
const trestleId = Number(e.target.value)
|
||||
setTrestleId(trestleId)
|
||||
setModules([])
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex w-full flex-wrap md:flex-nowrap gap-4">
|
||||
{roofMaterials.length > 0 && (
|
||||
<Select label="지붕재" className="max-w-xs" onChange={handleRoofMaterialOnChange}>
|
||||
{roofMaterials.map((roofMaterial) => (
|
||||
<SelectItem key={roofMaterial.id}>{roofMaterial.name}</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
)}
|
||||
{manufacturers.length > 0 && (
|
||||
<Select label="제조 회사" className="max-w-xs" onChange={handleManufacturersOnChange}>
|
||||
{manufacturers.map((manufacturer) => (
|
||||
<SelectItem key={manufacturer.id}>{manufacturer.name}</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
)}
|
||||
{trestles.length > 0 && (
|
||||
<Select label="가대" className="max-w-xs" onChange={handleTrestlesOnChange}>
|
||||
{trestles.map((trestle) => (
|
||||
<SelectItem key={trestle.id}>{trestle.name}</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
)}
|
||||
{modules.length > 0 && (
|
||||
<Select label="설치가능 모듈" className="max-w-xs">
|
||||
{modules.map((module) => (
|
||||
<SelectItem key={module.id}>{module.name}</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -1,14 +1,8 @@
|
||||
import Roof2 from '@/components/Roof2'
|
||||
import RoofSelect from '@/app/roof2/RoofSelect'
|
||||
|
||||
export default async function Roof2Page() {
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<div className="m-2">
|
||||
<RoofSelect />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col justify-center my-8 pt-20">
|
||||
<Roof2 />
|
||||
</div>
|
||||
|
||||
@ -29,13 +29,6 @@ export default function InitSettingsModal(props) {
|
||||
//const { get, post } = useAxios()
|
||||
|
||||
useEffect(() => {
|
||||
get({ url: '/api/roof-material/roof-material-infos' }).then((res) => {
|
||||
//TODO: error handling
|
||||
if (!res) return
|
||||
|
||||
setRoofMaterials(res)
|
||||
})
|
||||
|
||||
get({ url: `/api/canvas-management/canvas-basic-settings/by-object/${objectNo}` }).then((res) => {
|
||||
if (res.length == 0) return
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ import { contextPopupPositionState } from '@/store/popupAtom'
|
||||
|
||||
export default function ColorPickerModal(props) {
|
||||
const contextPopupPosition = useRecoilValue(contextPopupPositionState) // 현재 메뉴
|
||||
const { isShow, setIsShow, pos = contextPopupPosition, color = '#ff0000', setColor, id } = props
|
||||
const { isShow, setIsShow, pos = contextPopupPosition, color = '#ff0000', setColor, id, isConfig = false } = props
|
||||
const { getMessage } = useMessage()
|
||||
const [originColor, setOriginColor] = useState(color)
|
||||
const { closePopup } = usePopup()
|
||||
@ -29,7 +29,7 @@ export default function ColorPickerModal(props) {
|
||||
setIsShow(false)
|
||||
}
|
||||
console.log(id)
|
||||
closePopup(id)
|
||||
closePopup(id, isConfig)
|
||||
}}
|
||||
>
|
||||
닫기
|
||||
@ -49,7 +49,7 @@ export default function ColorPickerModal(props) {
|
||||
if (setColor) setColor(originColor)
|
||||
if (setIsShow) setIsShow(false)
|
||||
|
||||
closePopup(id)
|
||||
closePopup(id, isConfig)
|
||||
}}
|
||||
>
|
||||
{getMessage('common.message.save')}
|
||||
|
||||
@ -3,12 +3,14 @@ import { useEffect } from 'react'
|
||||
import '@/styles/contents.scss'
|
||||
import { useRecoilState } from 'recoil'
|
||||
import { contextMenuListState, contextMenuState } from '@/store/contextMenu'
|
||||
import { useTempGrid } from '@/hooks/useTempGrid'
|
||||
|
||||
export default function QContextMenu(props) {
|
||||
const { contextRef, canvasProps, handleKeyup } = props
|
||||
const [contextMenu, setContextMenu] = useRecoilState(contextMenuState)
|
||||
const [contextMenuList, setContextMenuList] = useRecoilState(contextMenuListState)
|
||||
const activeObject = canvasProps?.getActiveObject() //액티브된 객체를 가져옴
|
||||
const { tempGridMode, setTempGridMode } = useTempGrid()
|
||||
|
||||
let contextType = ''
|
||||
|
||||
@ -32,6 +34,7 @@ export default function QContextMenu(props) {
|
||||
|
||||
const handleContextMenu = (e) => {
|
||||
// e.preventDefault() //기존 contextmenu 막고
|
||||
if (tempGridMode) return
|
||||
const position = {
|
||||
x: window.innerWidth / 2 < e.pageX ? e.pageX - 240 : e.pageX,
|
||||
y: window.innerHeight / 2 < e.pageY ? getYPosition(e) : e.pageY,
|
||||
|
||||
@ -14,15 +14,7 @@ const fonts = [
|
||||
{ name: '@Yu Gothic UI', value: '@Yu Gothic UI' },
|
||||
{ name: 'Yu Gothic UI', value: 'Yu Gothic UI' },
|
||||
]
|
||||
const fontOptions = [
|
||||
{ name: '보통', value: 'normal' },
|
||||
{ name: '기울임꼴', value: 'italic' },
|
||||
{
|
||||
name: '굵게',
|
||||
value: 'bold',
|
||||
},
|
||||
{ name: '굵은 기울임꼴', value: 'boldAndItalic' },
|
||||
]
|
||||
|
||||
const fontSizes = [
|
||||
...Array.from({ length: 4 }).map((_, index) => {
|
||||
return { name: index + 8, value: index + 8 }
|
||||
@ -34,20 +26,10 @@ const fontSizes = [
|
||||
{ name: 48, value: 48 },
|
||||
{ name: 72, value: 72 },
|
||||
]
|
||||
const fontColors = [
|
||||
{ name: '검정색', value: 'black' },
|
||||
{ name: '빨강색', value: 'red' },
|
||||
{ name: '파랑색', value: 'blue' },
|
||||
{ name: '회색', value: 'gray' },
|
||||
{ name: '황색', value: 'yellow' },
|
||||
{ name: '녹색', value: 'green' },
|
||||
{ name: '분홍색', value: 'pink' },
|
||||
{ name: '황금색', value: 'gold' },
|
||||
{ name: '남색', value: 'darkblue' },
|
||||
]
|
||||
|
||||
export default function FontSetting(props) {
|
||||
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
||||
const { id, setIsShow, pos = contextPopupPosition, type } = props
|
||||
const { id, setIsShow, pos = contextPopupPosition, type, isConfig = false } = props
|
||||
const { getMessage } = useMessage()
|
||||
const { closePopup } = usePopup()
|
||||
const [globalFont, setGlobalFont] = useRecoilState(globalFontAtom)
|
||||
@ -57,7 +39,26 @@ export default function FontSetting(props) {
|
||||
const [selectedFontWeight, setSelectedFontWeight] = useState(currentFont.fontWeight)
|
||||
const [selectedFontSize, setSelectedFontSize] = useState(currentFont.fontSize)
|
||||
const [selectedFontColor, setSelectedFontColor] = useState(currentFont.fontColor)
|
||||
|
||||
const fontOptions = [
|
||||
{ name: getMessage('font.style.normal'), value: 'normal' },
|
||||
{ name: getMessage('font.style.italic'), value: 'italic' },
|
||||
{
|
||||
name: getMessage('font.style.bold'),
|
||||
value: 'bold',
|
||||
},
|
||||
{ name: getMessage('font.style.bold.italic'), value: 'boldAndItalic' },
|
||||
]
|
||||
const fontColors = [
|
||||
{ name: getMessage('color.black'), value: 'black' },
|
||||
{ name: getMessage('color.red'), value: 'red' },
|
||||
{ name: getMessage('color.blue'), value: 'blue' },
|
||||
{ name: getMessage('color.gray'), value: 'gray' },
|
||||
{ name: getMessage('color.yellow'), value: 'yellow' },
|
||||
{ name: getMessage('color.green'), value: 'green' },
|
||||
{ name: getMessage('color.pink'), value: 'pink' },
|
||||
{ name: getMessage('color.gold'), value: 'gold' },
|
||||
{ name: getMessage('color.darkblue'), value: 'darkblue' },
|
||||
]
|
||||
const handleSaveBtn = () => {
|
||||
setGlobalFont((prev) => {
|
||||
return {
|
||||
@ -83,7 +84,7 @@ export default function FontSetting(props) {
|
||||
className="modal-close"
|
||||
onClick={() => {
|
||||
if (setIsShow) setIsShow(false)
|
||||
closePopup(id)
|
||||
closePopup(id, isConfig)
|
||||
}}
|
||||
>
|
||||
닫기
|
||||
|
||||
@ -5,5 +5,9 @@ import { Fragment } from 'react'
|
||||
|
||||
export default function PopupManager() {
|
||||
const [popup, setPopup] = useRecoilState(popupState)
|
||||
return popup.children?.map((child) => <Fragment key={child.id}>{child.component}</Fragment>)
|
||||
|
||||
return [
|
||||
...popup?.config.map((child) => <Fragment key={child.id}>{child.component}</Fragment>),
|
||||
...popup?.other.map((child) => <Fragment key={child.id}>{child.component}</Fragment>),
|
||||
]
|
||||
}
|
||||
|
||||
@ -8,13 +8,41 @@ import { useMessage } from '@/hooks/useMessage'
|
||||
import { useCanvasMenu } from '@/hooks/common/useCanvasMenu'
|
||||
import SingleDatePicker from '../common/datepicker/SingleDatePicker'
|
||||
import EstimateFileUploader from './EstimateFileUploader'
|
||||
import { useAxios } from '@/hooks/useAxios'
|
||||
import { globalLocaleStore } from '@/store/localeAtom'
|
||||
import { isObjectNotEmpty } from '@/util/common-utils'
|
||||
import dayjs from 'dayjs'
|
||||
import { useCommonCode } from '@/hooks/common/useCommonCode'
|
||||
import Select from 'react-select'
|
||||
import { useEstimateController } from '@/hooks/floorPlan/estimate/useEstimateController'
|
||||
|
||||
export default function Estimate({ params }) {
|
||||
const [objectNo, setObjectNo] = useState('')
|
||||
const [files, setFiles] = useState([]) //첨부파일
|
||||
const [objectNo, setObjectNo] = useState('') //물건번호
|
||||
const [planNo, setPlanNo] = useState('') //플랜번호
|
||||
const [files, setFiles] = useState([]) // 보내는 첨부파일
|
||||
|
||||
//견적특이사항 접고 펼치기
|
||||
const [hidden, setHidden] = useState(false)
|
||||
|
||||
//공통코드
|
||||
const { findCommonCode } = useCommonCode()
|
||||
const [honorificCodeList, setHonorificCodeList] = useState([]) //경칭 공통코드
|
||||
|
||||
const [startDate, setStartDate] = useState(new Date())
|
||||
const singleDatePickerProps = {
|
||||
startDate,
|
||||
setStartDate,
|
||||
}
|
||||
|
||||
const sessionState = useRecoilValue(sessionStore)
|
||||
const objectRecoil = useRecoilValue(floorPlanObjectState)
|
||||
|
||||
//견적서 상세데이터
|
||||
const { state, setState } = useEstimateController(params.pid)
|
||||
|
||||
const globalLocaleState = useRecoilValue(globalLocaleStore)
|
||||
const { get, post } = useAxios(globalLocaleState)
|
||||
|
||||
const { getMessage } = useMessage()
|
||||
|
||||
const { setMenuNumber } = useCanvasMenu()
|
||||
@ -27,46 +55,54 @@ export default function Estimate({ params }) {
|
||||
setUploadFiles: setFiles,
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setObjectNo(objectRecoil.floorPlanObjectNo)
|
||||
}, [objectRecoil])
|
||||
|
||||
useEffect(() => {
|
||||
if (objectNo) {
|
||||
//Q101X278191023001
|
||||
console.log('세션정보::::', sessionState)
|
||||
//상세API호출
|
||||
}
|
||||
}, [objectNo])
|
||||
|
||||
useEffect(() => {
|
||||
setMenuNumber(5)
|
||||
setObjectNo(objectRecoil.floorPlanObjectNo)
|
||||
setPlanNo(params.pid)
|
||||
|
||||
// 공통코드
|
||||
const code1 = findCommonCode(200800)
|
||||
if (code1 != null) {
|
||||
setHonorificCodeList(code1)
|
||||
}
|
||||
|
||||
//견적특이사항 API호출
|
||||
//http://localhost:8080/api/estimate/special-note-list
|
||||
}, [])
|
||||
|
||||
//견적일 set
|
||||
useEffect(() => {
|
||||
let estimateDatej = dayjs(startDate).format('YYYY-MM-DD')
|
||||
setState({ estimateDate: estimateDatej })
|
||||
}, [startDate])
|
||||
|
||||
return (
|
||||
<div className="sub-content estimate">
|
||||
<div className="sub-content-inner">
|
||||
{/* 물건번호, 견적서번호, 등록일, 변경일시 시작 */}
|
||||
{/* <form onSubmit={handleSubmit(onValid)}> */}
|
||||
<div className="sub-content-box">
|
||||
<div className="sub-table-box">
|
||||
<div className="estimate-list-wrap one">
|
||||
<div className="estimate-box">
|
||||
<div className="estimate-tit">{getMessage('estimate.detail.objectNo')}</div>
|
||||
<div className="estimate-name">
|
||||
{objectNo} (Plan No: {params.pid})
|
||||
{objectNo} (Plan No: {planNo})
|
||||
</div>
|
||||
</div>
|
||||
<div className="estimate-box">
|
||||
<div className="estimate-tit">{getMessage('estimate.detail.estimateNo')}</div>
|
||||
<div className="estimate-name">5242310200065242</div>
|
||||
<div className="estimate-tit">{getMessage('estimate.detail.docNo')}</div>
|
||||
<div className="estimate-name">{state.docNo}</div>
|
||||
</div>
|
||||
<div className="estimate-box">
|
||||
<div className="estimate-tit">{getMessage('estimate.detail.createDatetime')}</div>
|
||||
<div className="estimate-name">9999.09.28</div>
|
||||
<div className="estimate-tit">{getMessage('estimate.detail.drawingEstimateCreateDate')}</div>
|
||||
<div className="estimate-name">
|
||||
{state?.drawingEstimateCreateDate ? `${dayjs(state.drawingEstimateCreateDate).format('YYYY.MM.DD')}` : ''}
|
||||
</div>
|
||||
</div>
|
||||
<div className="estimate-box">
|
||||
<div className="estimate-tit">{getMessage('estimate.detail.lastEditDatetime')}</div>
|
||||
<div className="estimate-name">9999.09.28 06:36</div>
|
||||
<div className="estimate-name">{state?.lastEditDatetime ? `${dayjs(state.lastEditDatetime).format('YYYY.MM.DD HH:mm')}` : ''}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -99,7 +135,7 @@ export default function Estimate({ params }) {
|
||||
</th>
|
||||
<td>
|
||||
<div className="date-picker" style={{ width: '350px' }}>
|
||||
<SingleDatePicker />
|
||||
<SingleDatePicker {...singleDatePickerProps} />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@ -113,64 +149,152 @@ export default function Estimate({ params }) {
|
||||
</th>
|
||||
<td>
|
||||
<div className="input-wrap" style={{ width: '350px' }}>
|
||||
<input type="text" className="input-light" defaultValue={'물건정보에서 입력한 담당자명 표시'} />
|
||||
<input
|
||||
type="text"
|
||||
className="input-light"
|
||||
defaultValue={state?.charger}
|
||||
onChange={(e) => {
|
||||
//담당자 charger
|
||||
// console.log('담당자:::::', e.target.value)
|
||||
setState({ charger: e.target.value })
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
{/* 안건명 */}
|
||||
<th>
|
||||
{getMessage('estimate.detail.title')} <span className="important">*</span>
|
||||
{getMessage('estimate.detail.objectName')} <span className="important">*</span>
|
||||
</th>
|
||||
<td colSpan={3}>
|
||||
<div className="form-flex-wrap">
|
||||
<div className="input-wrap mr5" style={{ width: '610px' }}>
|
||||
<input type="text" className="input-light" defaultValue={'안건명:::'} />
|
||||
<input
|
||||
type="text"
|
||||
className="input-light"
|
||||
defaultValue={state?.objectName}
|
||||
onChange={(e) => {
|
||||
//안건명 objectName
|
||||
// console.log('안건명::::', e.target.value)
|
||||
setState({ objectName: e.target.value })
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="input-wrap" style={{ width: '200px' }}>
|
||||
<input type="text" className="input-light" defaultValue={'경칭?'} />
|
||||
<div className="select-wrap" style={{ width: '200px' }}>
|
||||
<Select
|
||||
id="objectNameOmit"
|
||||
instanceId="objectNameOmit"
|
||||
className="react-select-custom"
|
||||
classNamePrefix="custom"
|
||||
placeholder="Select"
|
||||
options={honorificCodeList}
|
||||
onChange={(e) => {
|
||||
if (isObjectNotEmpty(e)) {
|
||||
setState({ objectNameOmit: e.clCodeNm })
|
||||
} else {
|
||||
// console.log('XXX')
|
||||
setState({ objectNameOmit: '' })
|
||||
}
|
||||
}}
|
||||
getOptionLabel={(x) => x.clCodeNm}
|
||||
getOptionValue={(x) => x.clCode}
|
||||
isClearable={true}
|
||||
isSearchable={false}
|
||||
value={honorificCodeList.filter(function (option) {
|
||||
return option.clCodeNm === state.objectNameOmit
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
{/* 메모 */}
|
||||
<th>{getMessage('estimate.detail.remarks')}</th>
|
||||
<td colSpan={3}>물건정보에서 입력한 메모 표시</td>
|
||||
{/* 물건정보에서 입력한 메모 */}
|
||||
<th>{getMessage('estimate.detail.objectRemarks')}</th>
|
||||
<td colSpan={3}>{state?.objectRemarks}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
{/* 주문분류 */}
|
||||
<th>
|
||||
{getMessage('estimate.detail.orderType')} <span className="important">*</span>
|
||||
{getMessage('estimate.detail.estimateType')} <span className="important">*</span>
|
||||
</th>
|
||||
<td colSpan={3}>
|
||||
<div className="radio-wrap"></div>
|
||||
<div className="radio-wrap">
|
||||
<div className="d-check-radio light mr10">
|
||||
<input
|
||||
type="radio"
|
||||
name="estimateType"
|
||||
id="YJSS"
|
||||
value={'YJSS'}
|
||||
checked={state?.estimateType === 'YJSS' ? true : false}
|
||||
onChange={(e) => {
|
||||
setState({ estimateType: e.target.value })
|
||||
}}
|
||||
/>
|
||||
<label htmlFor="YJSS">住宅PKG</label>
|
||||
</div>
|
||||
<div className="d-check-radio light">
|
||||
<input
|
||||
type="radio"
|
||||
name="estimateType"
|
||||
id="YJOD"
|
||||
value={'YJOD'}
|
||||
checked={state?.estimateType === 'YJOD' ? true : false}
|
||||
onChange={(e) => {
|
||||
setState({ estimateType: e.target.value })
|
||||
}}
|
||||
/>
|
||||
<label htmlFor="YJOD">積上げ( YJOD )</label>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
{/* 지붕재・사양시공 최대4개*/}
|
||||
<th>{getMessage('estimate.detail.roofCns')}</th>
|
||||
<td colSpan={3}>
|
||||
<div className="form-flex-wrap mb5">
|
||||
<div className="input-wrap mr5" style={{ width: '610px' }}>
|
||||
<input type="text" className="input-light" defaultValue={'ハゼ式折板(角ハゼ)'} readOnly />
|
||||
</div>
|
||||
<div className="input-wrap" style={{ width: '200px' }}>
|
||||
<input type="text" className="input-light" defaultValue={'標準施工'} readOnly />
|
||||
</div>
|
||||
</div>
|
||||
<div className="form-flex-wrap mb5"></div>
|
||||
<div className="form-flex-wrap mb5"></div>
|
||||
{/* 마지막div엔 mb5 제외 */}
|
||||
<div className="form-flex-wrap"></div>
|
||||
{state?.roofMaterialIdMulti?.split('、').map((row, index) => {
|
||||
//지붕재
|
||||
let roofList = row
|
||||
let roofListLength = state?.roofMaterialIdMulti?.split('、').length
|
||||
let style = 'mb5'
|
||||
if (roofListLength == index + 1) {
|
||||
style = ''
|
||||
}
|
||||
//사양시공
|
||||
let constructSpecificationMulti = state?.constructSpecificationMulti?.split('、')
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={`form-flex-wrap ${style}`}>
|
||||
<div className="input-wrap mr5" style={{ width: '610px' }}>
|
||||
<input type="text" className="input-light" defaultValue={roofList} readOnly />
|
||||
</div>
|
||||
<div className="input-wrap" style={{ width: '200px' }}>
|
||||
<input type="text" className="input-light" defaultValue={constructSpecificationMulti[index]} readOnly />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
})}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
{/* 비고 */}
|
||||
<th>{getMessage('estimate.detail.note')}</th>
|
||||
<th>{getMessage('estimate.detail.remarks')}</th>
|
||||
<td colSpan={3}>
|
||||
<div className="input-wrap">
|
||||
<input type="text" className="input-light" />
|
||||
<input
|
||||
type="text"
|
||||
className="input-light"
|
||||
defaultValue={state?.remarks}
|
||||
onChange={(e) => {
|
||||
//비고
|
||||
// console.log('비고:::::', e.target.value)
|
||||
setState({ remarks: e.target.value })
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@ -200,25 +324,6 @@ export default function Estimate({ params }) {
|
||||
<th>{getMessage('estimate.detail.header.fileList1')}</th>
|
||||
<td>
|
||||
<EstimateFileUploader {...fileUploadProps} />
|
||||
{/* <div className="drag-file-box">
|
||||
<div className="btn-area">
|
||||
<Button type="button" className="btn-origin grey" onClick={handleButtonClick}>
|
||||
{getMessage('estimate.detail.fileList.btn')}
|
||||
</Button>
|
||||
<input
|
||||
type="file"
|
||||
id="fileUpload"
|
||||
name="fileUpload"
|
||||
ref={fileInputRef}
|
||||
onChange={onChangeFiles}
|
||||
style={{ display: 'none' }}
|
||||
/>
|
||||
</div>
|
||||
<div className="drag-file-area">
|
||||
<p>Drag file here</p>
|
||||
<ul className="file-list"></ul>
|
||||
</div>
|
||||
</div> */}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@ -246,21 +351,40 @@ export default function Estimate({ params }) {
|
||||
<h3 className="product">{getMessage('estimate.detail.header.specialEstimate')}</h3>
|
||||
<div className="product_tit">{getMessage('estimate.detail.header.specialEstimateProductInfo')}</div>
|
||||
</div>
|
||||
<div className="left-unit-box">
|
||||
<button className={`estimate-arr-btn down mr5 ${hidden ? '' : 'on'}`} onClick={() => setHidden(false)}></button>
|
||||
<button className={`estimate-arr-btn up ${hidden ? 'on' : ''}`} onClick={() => setHidden(true)}></button>
|
||||
</div>
|
||||
</div>
|
||||
{/* 공통코드영역시작 */}
|
||||
<div className="special-note-check-wrap"></div>
|
||||
{/* 공통코드영역끝 */}
|
||||
{/* 견적특이사항 내용영역시작 */}
|
||||
<div className="calculation-estimate"></div>
|
||||
{/* 견적특이사항 내용영역끝 */}
|
||||
{/* 견적특이사항 끝 */}
|
||||
{/* 견적 특이사항 코드영역시작 */}
|
||||
<div className={`estimate-check-wrap ${hidden ? 'hide' : ''}`}>
|
||||
<div className="estimate-check-inner">
|
||||
<div className="special-note-check-wrap"></div>
|
||||
{/* 견적특이사항 선택한 내용?영역시작 */}
|
||||
<div className="calculation-estimate">
|
||||
<dl>
|
||||
<dt>제목11??</dt>
|
||||
<dd>제목1 비고</dd>
|
||||
</dl>
|
||||
<dl>
|
||||
<dt>제목22??</dt>
|
||||
<dd>제목2 비고</dd>
|
||||
</dl>
|
||||
</div>
|
||||
{/* 견적특이사항 선택한 내용?영역끝 */}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 견적 특이사항 코드영역 끝 */}
|
||||
|
||||
{/* 견적특이사항 영역끝 */}
|
||||
{/* 제품정보 시작 */}
|
||||
<div className="table-box-title-wrap">
|
||||
<div className="title-wrap">
|
||||
<h3>{getMessage('estimate.detail.header.specialEstimateProductInfo')}</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div className="estimate-wrap">
|
||||
<div className="esimate-wrap">
|
||||
<div className="estimate-list-wrap one">
|
||||
<div className="estimate-box">
|
||||
<div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.totPcs')}</div>
|
||||
@ -285,7 +409,7 @@ export default function Estimate({ params }) {
|
||||
</div>
|
||||
</div>
|
||||
{/* YJOD면 아래영역 숨김 */}
|
||||
<div className="common-table bt-able">
|
||||
<div className="common-table bt-able" style={{ display: state?.estimateType === 'YJSS' ? '' : 'none' }}>
|
||||
<table>
|
||||
<colgroup>
|
||||
<col style={{ width: '160px' }} />
|
||||
@ -319,18 +443,35 @@ export default function Estimate({ params }) {
|
||||
<div className="estimate-product-option">
|
||||
<div className="product-price-wrap">
|
||||
<div className="product-price-tit">{getMessage('estimate.detail.header.showPrice')}</div>
|
||||
<div className="select-wrap"></div>
|
||||
<div className="select-wrap">
|
||||
<select className="select-light" name="" id="">
|
||||
<option value="">111</option>
|
||||
<option value="">222</option>
|
||||
</select>
|
||||
</div>
|
||||
<button className="btn-origin grey ml5">{getMessage('estimate.detail.showPrice.btn1')}</button>
|
||||
</div>
|
||||
<div className="product-edit-wrap">
|
||||
<div className="product-edit-explane">
|
||||
<div className="click-check">
|
||||
<ul className="product-edit-explane">
|
||||
<li className="explane-item item01">
|
||||
<span className="ico"></span>
|
||||
{getMessage('estimate.detail.showPrice.description')}
|
||||
</div>
|
||||
</div>
|
||||
{getMessage('estimate.detail.showPrice.description1')}
|
||||
</li>
|
||||
<li className="explane-item item02">
|
||||
<span className="ico"></span>
|
||||
{getMessage('estimate.detail.showPrice.description2')}
|
||||
</li>
|
||||
<li className="explane-item item03">
|
||||
<span className="ico"></span>
|
||||
{getMessage('estimate.detail.showPrice.description3')}
|
||||
</li>
|
||||
<li className="explane-item item04">
|
||||
<span className="ico"></span>
|
||||
{getMessage('estimate.detail.showPrice.description4')}
|
||||
</li>
|
||||
</ul>
|
||||
<div className="product-edit-btn">
|
||||
<button className="btn-origin navy mr5">
|
||||
<button className="btn-origin navy mr5" type="submit">
|
||||
<span className="plus"></span>
|
||||
{getMessage('estimate.detail.showPrice.btn2')}
|
||||
</button>
|
||||
@ -348,6 +489,7 @@ export default function Estimate({ params }) {
|
||||
</div>
|
||||
</div>
|
||||
{/* 기본정보끝 */}
|
||||
{/* </form> */}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -32,6 +32,8 @@ import { menusState, menuTypeState } from '@/store/menuAtom'
|
||||
import useMenu from '@/hooks/common/useMenu'
|
||||
import { MENU } from '@/common/common'
|
||||
|
||||
import { useEstimateController } from '@/hooks/floorPlan/estimate/useEstimateController'
|
||||
|
||||
export default function CanvasMenu(props) {
|
||||
const { menuNumber, setMenuNumber } = props
|
||||
const pathname = usePathname()
|
||||
@ -49,8 +51,9 @@ export default function CanvasMenu(props) {
|
||||
const sessionState = useRecoilValue(sessionStore)
|
||||
const globalLocale = useRecoilValue(globalLocaleStore)
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
const { handleZoomClear } = useCanvasEvent()
|
||||
const { handleZoomClear, handleZoom } = useCanvasEvent()
|
||||
const { handleMenu } = useMenu()
|
||||
const { handleEstimateSubmit } = useEstimateController()
|
||||
|
||||
const { getMessage } = useMessage()
|
||||
const { currentCanvasPlan, saveCanvas } = usePlan()
|
||||
@ -129,7 +132,7 @@ export default function CanvasMenu(props) {
|
||||
|
||||
const handlePopup = () => {
|
||||
const id = uuidv4()
|
||||
addPopup(id, 0, <SettingModal01 id={id} />)
|
||||
addPopup(id, 1, <SettingModal01 id={id} />, true)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
@ -201,14 +204,18 @@ export default function CanvasMenu(props) {
|
||||
<button
|
||||
className="control-btn minus"
|
||||
onClick={() => {
|
||||
canvas.setZoom(canvas.getZoom() - 0.1)
|
||||
handleZoom(false)
|
||||
}}
|
||||
></button>
|
||||
<span>{canvasZoom}%</span>
|
||||
<button className="control-btn plus" onClick={handleZoomClear}></button>
|
||||
<button
|
||||
className="control-btn plus"
|
||||
onClick={() => {
|
||||
handleZoom(true)
|
||||
}}
|
||||
></button>
|
||||
</div>
|
||||
<div className="btn-from">
|
||||
<button className="btn07" onClick={handleClear}></button>
|
||||
<button className="btn08" onClick={handleSaveCanvas}></button>
|
||||
<button className="btn09"></button>
|
||||
</div>
|
||||
@ -222,7 +229,7 @@ export default function CanvasMenu(props) {
|
||||
<span className="ico ico01"></span>
|
||||
<span>{getMessage('plan.menu.estimate.docDown')}</span>
|
||||
</button>
|
||||
<button className="btn-frame gray ico-flx">
|
||||
<button className="btn-frame gray ico-flx" onClick={handleEstimateSubmit}>
|
||||
<span className="ico ico02"></span>
|
||||
<span>{getMessage('plan.menu.estimate.save')}</span>
|
||||
</button>
|
||||
|
||||
@ -50,7 +50,11 @@ export default function CircuitTrestleSetting({ id }) {
|
||||
Next
|
||||
</button>
|
||||
)}
|
||||
{tabNum === 3 && <button className="btn-frame modal act">保存 (仮割り当て)</button>}
|
||||
{tabNum === 3 && (
|
||||
<button className="btn-frame modal act">
|
||||
{`${getMessage('modal.common.save')} (${getMessage('modal.circuit.trestle.setting.alloc.trestle')})`}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -114,7 +114,7 @@ export default function PowerConditionalSelect({ setTabNum }) {
|
||||
</div>
|
||||
</div>
|
||||
<div className="circuit-right-wrap mb10">
|
||||
<button className="btn-frame self mr5">追加</button>
|
||||
<button className="btn-frame self mr5">{getMessage('modal.common.add')}</button>
|
||||
</div>
|
||||
<div className="circuit-data-form">
|
||||
<span className="normal-font">
|
||||
@ -132,12 +132,12 @@ export default function PowerConditionalSelect({ setTabNum }) {
|
||||
<div className="slope-wrap">
|
||||
<div className="d-check-box pop mb15">
|
||||
<input type="checkbox" id="ch03" />
|
||||
<label htmlFor="ch03">同一傾斜同一方面の面積の場合、同じ面として回路を分ける。</label>
|
||||
<label htmlFor="ch03"> {getMessage('modal.circuit.trestle.setting.power.conditional.select.check1')}</label>
|
||||
</div>
|
||||
<div className="d-check-box pop">
|
||||
<input type="checkbox" id="ch04" />
|
||||
<label className="red" htmlFor="ch04">
|
||||
MAX接続(過積)で回路を分ける。
|
||||
{getMessage('modal.circuit.trestle.setting.power.conditional.select.check2')}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -18,8 +18,8 @@ export default function StepUp({}) {
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>シリアル枚数</th>
|
||||
<th>総回路数</th>
|
||||
<th>{getMessage('modal.circuit.trestle.setting.step.up.allocation.serial.amount')}</th>
|
||||
<th>{getMessage('modal.circuit.trestle.setting.step.up.allocation.total.amount')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@ -49,14 +49,14 @@ export default function StepUp({}) {
|
||||
</div>
|
||||
<div className="circuit-table-flx-wrap">
|
||||
<div className="circuit-table-flx-box">
|
||||
<div className="bold-font mb10">接続する</div>
|
||||
<div className="bold-font mb10">{getMessage('modal.circuit.trestle.setting.step.up.allocation.connected')}</div>
|
||||
<div className="roof-module-table mb10">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th style={{ width: '140px' }}>名称</th>
|
||||
<th>回路数</th>
|
||||
<th>昇圧回路数</th>
|
||||
<th style={{ width: '140px' }}>{getMessage('modal.circuit.trestle.setting.power.conditional.select.name')}</th>
|
||||
<th>{getMessage('modal.circuit.trestle.setting.power.conditional.select.circuit.amount')}</th>
|
||||
<th>{getMessage('modal.circuit.trestle.setting.step.up.allocation.circuit.amount')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@ -80,7 +80,7 @@ export default function StepUp({}) {
|
||||
</div>
|
||||
<div className="bottom-wrap">
|
||||
<div className="circuit-right-wrap mb10">
|
||||
<button className="btn-frame self mr5">追加</button>
|
||||
<button className="btn-frame self mr5">{getMessage('modal.common.add')}</button>
|
||||
</div>
|
||||
<div className="circuit-data-form">
|
||||
<span className="normal-font">
|
||||
@ -90,13 +90,13 @@ export default function StepUp({}) {
|
||||
</div>
|
||||
</div>
|
||||
<div className="circuit-table-flx-box">
|
||||
<div className="bold-font mb10">昇圧オプション</div>
|
||||
<div className="bold-font mb10">{getMessage('modal.circuit.trestle.setting.step.up.allocation.option')}</div>
|
||||
<div className="roof-module-table mb10">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>名称</th>
|
||||
<th>昇圧回路数</th>
|
||||
<th>{getMessage('modal.circuit.trestle.setting.power.conditional.select.name')}</th>
|
||||
<th>{getMessage('modal.circuit.trestle.setting.step.up.allocation.circuit.amount')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@ -113,7 +113,7 @@ export default function StepUp({}) {
|
||||
</div>
|
||||
<div className="bottom-wrap">
|
||||
<div className="circuit-right-wrap mb10">
|
||||
<button className="btn-frame self mr5">追加</button>
|
||||
<button className="btn-frame self mr5">{getMessage('modal.common.add')}</button>
|
||||
</div>
|
||||
<div className="circuit-data-form">
|
||||
<span className="normal-font">
|
||||
@ -124,7 +124,7 @@ export default function StepUp({}) {
|
||||
</div>
|
||||
</div>
|
||||
<div className="circuit-count-input">
|
||||
<span className="normal-font">綿調道区分</span>
|
||||
<span className="normal-font">{getMessage('modal.module.basic.setting.module.cotton.classification')}</span>
|
||||
<div className="input-grid mr5" style={{ width: '40px' }}>
|
||||
<input type="text" className="input-origin block" />
|
||||
</div>
|
||||
@ -395,7 +395,7 @@ export default function StepUp({}) {
|
||||
<div className="slope-wrap">
|
||||
<div className="outline-form">
|
||||
<span className="mr10" style={{ width: 'auto' }}>
|
||||
モニターの選択
|
||||
{getMessage('modal.circuit.trestle.setting.step.up.allocation.select.monitor')}
|
||||
</span>
|
||||
<div className="grid-select mr10">
|
||||
<QSelectBox title={'電力検出ユニット (モニター付き)'} option={SelectOption01} />
|
||||
|
||||
@ -4,13 +4,13 @@ export default function PassivityCircuitAllocation() {
|
||||
const { getMessage } = useMessage()
|
||||
const moduleData = {
|
||||
header: [
|
||||
{ name: getMessage('屋根面'), prop: 'roofShape' },
|
||||
{ name: getMessage('modal.panel.batch.statistic.roof.shape'), prop: 'roofShape' },
|
||||
{
|
||||
name: getMessage('Q.TRON M-G2'),
|
||||
prop: 'moduleName',
|
||||
},
|
||||
{
|
||||
name: getMessage('発電量 (kW)'),
|
||||
name: `${getMessage('modal.panel.batch.statistic.power.generation.amount')}(kW)`,
|
||||
prop: 'powerGeneration',
|
||||
},
|
||||
],
|
||||
|
||||
@ -5,9 +5,7 @@ import { useMessage } from '@/hooks/useMessage'
|
||||
import { canvasState, dotLineGridSettingState, dotLineIntervalSelector } from '@/store/canvasAtom'
|
||||
import { useRecoilState, useRecoilValue, useResetRecoilState, useSetRecoilState } from 'recoil'
|
||||
import { onlyNumberInputChange } from '@/util/input-utils'
|
||||
import { fabric } from 'fabric'
|
||||
import { gridColorState } from '@/store/gridAtom'
|
||||
import { gridDisplaySelector, settingModalGridOptionsState } from '@/store/settingAtom'
|
||||
import { settingModalGridOptionsState } from '@/store/settingAtom'
|
||||
import { useAxios } from '@/hooks/useAxios'
|
||||
import { useSwal } from '@/hooks/useSwal'
|
||||
import { usePopup } from '@/hooks/usePopup'
|
||||
@ -33,7 +31,7 @@ export default function DotLineGrid(props) {
|
||||
// const [modalOption, setModalOption] = useRecoilState(modalState); //modal 열림닫힘 state
|
||||
const [objectNo, setObjectNo] = useState('test123240912001') // 이후 삭제 필요
|
||||
const [close, setClose] = useState(false)
|
||||
const { id, setIsShow, pos = { x: 840, y: -815 } } = props
|
||||
const { id, setIsShow, pos = { x: 840, y: -815 }, isConfig = false } = props
|
||||
const setSettingModalGridOptions = useSetRecoilState(settingModalGridOptionsState)
|
||||
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
@ -142,7 +140,7 @@ export default function DotLineGrid(props) {
|
||||
await post({ url: `/api/canvas-management/canvas-grid-settings`, data: patternData }).then((res) => {
|
||||
swalFire({ text: getMessage(res.returnMessage) })
|
||||
setDotLineGridSettingState({ ...currentSetting })
|
||||
closePopup(id)
|
||||
closePopup(id, isConfig)
|
||||
})
|
||||
} catch (error) {
|
||||
swalFire({ text: getMessage(res.returnMessage), icon: 'error' })
|
||||
@ -213,7 +211,7 @@ export default function DotLineGrid(props) {
|
||||
className="modal-close"
|
||||
onClick={() => {
|
||||
setIsShow(false)
|
||||
closePopup(id)
|
||||
closePopup(id, isConfig)
|
||||
}}
|
||||
>
|
||||
닫기
|
||||
|
||||
@ -78,14 +78,14 @@ export default function GridOption() {
|
||||
// 점 선 그리드 설정 모달
|
||||
setShowDotLineGridModal(selectedOption.selected)
|
||||
|
||||
addPopup(dotLineId, 2, <DotLineGrid {...dotLineGridProps} />)
|
||||
addPopup(dotLineId, 2, <DotLineGrid {...dotLineGridProps} />, true)
|
||||
} else if (selectedOption.id === 3) {
|
||||
// 흡착점 모드
|
||||
setAdsorptionPointAddMode(selectedOption.selected)
|
||||
} else if (selectedOption.id === 4) {
|
||||
// 그리드 색 설정 모달
|
||||
setShowColorPickerModal(selectedOption.selected)
|
||||
addPopup(colorId, 2, <ColorPickerModal {...colorPickerProps} />)
|
||||
addPopup(colorId, 2, <ColorPickerModal {...colorPickerProps} />, true)
|
||||
}
|
||||
|
||||
setGridOptions(newGridOptions)
|
||||
@ -94,6 +94,7 @@ export default function GridOption() {
|
||||
const dotLineGridProps = {
|
||||
id: dotLineId,
|
||||
setIsShow: setShowDotLineGridModal,
|
||||
isConfig: true,
|
||||
pos: {
|
||||
x: 845,
|
||||
y: 180,
|
||||
@ -106,6 +107,7 @@ export default function GridOption() {
|
||||
setColor: setGridColor,
|
||||
isShow: showColorPickerModal,
|
||||
setIsShow: setShowColorPickerModal,
|
||||
isConfig: true,
|
||||
pos: {
|
||||
x: 785,
|
||||
y: 180,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import DimensionLineSetting from '@/components/floor-plan/modal/setting01/dimensionLine/DimensionLineSetting'
|
||||
@ -67,6 +67,7 @@ export default function SecondOption() {
|
||||
id: fontId,
|
||||
pos: { x: 745, y: 180 },
|
||||
setIsShow: setShowFontSettingModal,
|
||||
isConfig: true,
|
||||
}
|
||||
const planSizeProps = {
|
||||
id: planSizeId,
|
||||
@ -86,36 +87,41 @@ export default function SecondOption() {
|
||||
case 'font1': {
|
||||
//문자 글꼴변경
|
||||
setShowFontSettingModal(true)
|
||||
setShowDimensionLineSettingModal(false)
|
||||
fontProps.type = 'commonText'
|
||||
fontProps.id = fontId + 1
|
||||
addPopup(fontId + 1, 2, <FontSetting {...fontProps} />)
|
||||
addPopup(fontId + 1, 2, <FontSetting {...fontProps} />, true)
|
||||
break
|
||||
}
|
||||
|
||||
case 'font2': {
|
||||
//흐름 방향 글꼴 변경
|
||||
setShowFontSettingModal(true)
|
||||
setShowDimensionLineSettingModal(false)
|
||||
fontProps.type = 'flowText'
|
||||
fontProps.id = fontId + 2
|
||||
addPopup(fontId + 2, 2, <FontSetting {...fontProps} />)
|
||||
addPopup(fontId + 2, 2, <FontSetting {...fontProps} />, true)
|
||||
break
|
||||
}
|
||||
|
||||
case 'font3': {
|
||||
//치수 글꼴변경
|
||||
setShowFontSettingModal(true)
|
||||
|
||||
setShowDimensionLineSettingModal(false)
|
||||
fontProps.type = 'lengthText'
|
||||
fontProps.id = fontId + 3
|
||||
addPopup(fontId + 3, 2, <FontSetting {...fontProps} />)
|
||||
addPopup(fontId + 3, 2, <FontSetting {...fontProps} />, true)
|
||||
break
|
||||
}
|
||||
|
||||
case 'font4': {
|
||||
//회로번호 글꼴변경
|
||||
setShowFontSettingModal(true)
|
||||
setShowDimensionLineSettingModal(false)
|
||||
fontProps.type = 'circuitNumberText'
|
||||
fontProps.id = fontId
|
||||
addPopup(fontId, 2, <FontSetting {...fontProps} />)
|
||||
addPopup(fontId, 2, <FontSetting {...fontProps} />, true)
|
||||
break
|
||||
}
|
||||
|
||||
@ -123,7 +129,7 @@ export default function SecondOption() {
|
||||
//치수선 설정
|
||||
if (!showDimensionLineSettingModal) {
|
||||
setShowDimensionLineSettingModal(true)
|
||||
addPopup(dimensionId, 2, <DimensionLineSetting {...dimensionProps} />)
|
||||
addPopup(dimensionId, 2, <DimensionLineSetting {...dimensionProps} />, true)
|
||||
} else {
|
||||
setShowDimensionLineSettingModal(false)
|
||||
closePopup(dimensionId)
|
||||
@ -134,7 +140,8 @@ export default function SecondOption() {
|
||||
case 'planSize': {
|
||||
//도면크기 설정
|
||||
setShowPlanSizeSettingModal(true)
|
||||
addPopup(planSizeId, 2, <PlanSizeSetting {...planSizeProps} />)
|
||||
setShowDimensionLineSettingModal(false)
|
||||
addPopup(planSizeId, 2, <PlanSizeSetting {...planSizeProps} />, true)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,7 +11,7 @@ import { useRecoilValue } from 'recoil'
|
||||
import { usePopup } from '@/hooks/usePopup'
|
||||
|
||||
export default function SettingModal01(props) {
|
||||
const { setShowDotLineGridModal, setShowFontSettingModal, id } = props
|
||||
const { setShowDotLineGridModal, setShowFontSettingModal, id, isConfig } = props
|
||||
console.log(props)
|
||||
const [buttonAct, setButtonAct] = useState(1)
|
||||
const { getMessage } = useMessage()
|
||||
@ -27,7 +27,7 @@ export default function SettingModal01(props) {
|
||||
<div className={`modal-pop-wrap sm mount`}>
|
||||
<div className="modal-head">
|
||||
<h1 className="title">{getMessage('modal.canvas.setting')}</h1>
|
||||
<button className="modal-close" onClick={() => closePopup(id)}>
|
||||
<button className="modal-close" onClick={() => closePopup(id, true)}>
|
||||
닫기
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@ -87,6 +87,7 @@ export default function DimensionLineSetting(props) {
|
||||
setFontColor: setOriginFontColor,
|
||||
fontSize: originFontSize,
|
||||
setFontSize: setOriginFontSize,
|
||||
isConfig: true,
|
||||
id: fontModalId,
|
||||
pos: {
|
||||
x: 455,
|
||||
@ -97,17 +98,17 @@ export default function DimensionLineSetting(props) {
|
||||
const popupHandle = (type) => {
|
||||
switch (type) {
|
||||
case 'color':
|
||||
addPopup(colorModalId, 3, <ColorPickerModal {...colorPickerProps} />)
|
||||
addPopup(colorModalId, 3, <ColorPickerModal {...colorPickerProps} />, true)
|
||||
break
|
||||
case 'font':
|
||||
addPopup(fontModalId, 3, <FontSetting {...fontProps} />)
|
||||
addPopup(fontModalId, 3, <FontSetting {...fontProps} />, true)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<WithDraggable isShow={true} pos={pos}>
|
||||
<div className={`modal-pop-wrap xxxm`}>
|
||||
<div className={`modal-pop-wrap xxxm mount`}>
|
||||
<div className="modal-head">
|
||||
<h1 className="title">{getMessage('modal.canvas.setting.font.plan.absorption.dimension.line')} </h1>
|
||||
<button
|
||||
|
||||
@ -15,14 +15,14 @@ export default function PlanSizeSetting(props) {
|
||||
|
||||
return (
|
||||
<WithDraggable isShow={true} pos={pos}>
|
||||
<div className={`modal-pop-wrap xsm`}>
|
||||
<div className={`modal-pop-wrap xsm mount`}>
|
||||
<div className="modal-head">
|
||||
<h1 className="title">{getMessage('modal.canvas.setting.font.plan.absorption.plan.size.setting')}</h1>
|
||||
<button
|
||||
className="modal-close"
|
||||
onClick={() => {
|
||||
setIsShow(false)
|
||||
closePopup(id)
|
||||
closePopup(id, true)
|
||||
}}
|
||||
>
|
||||
닫기
|
||||
|
||||
153
src/hooks/floorPlan/estimate/useEstimateController.js
Normal file
153
src/hooks/floorPlan/estimate/useEstimateController.js
Normal file
@ -0,0 +1,153 @@
|
||||
import { useAxios } from '@/hooks/useAxios'
|
||||
import { useEffect, useReducer, useState } from 'react'
|
||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||
import { globalLocaleStore } from '@/store/localeAtom'
|
||||
import { estimateState, floorPlanObjectState } from '@/store/floorPlanObjectAtom'
|
||||
import { isObjectNotEmpty } from '@/util/common-utils'
|
||||
const reducer = (prevState, nextState) => {
|
||||
return { ...prevState, ...nextState }
|
||||
}
|
||||
|
||||
// Constants
|
||||
const ESTIMATE_API_ENDPOINT = '/api/estimates' // API 엔드포인트 정의
|
||||
|
||||
const defaultEstimateData = {
|
||||
estimateDate: new Date(), //견적일
|
||||
charger: '', //담당자
|
||||
objectName: '', //안건명
|
||||
objectNameOmit: '', //경칭코드
|
||||
estimateType: 'YJOD', //주문분류
|
||||
remarks: '', //비고
|
||||
estimateOption: '', //견적특이사항
|
||||
// itemList: [{ id: 1, name: '' }],
|
||||
//아이템에 필요없는거 빼기
|
||||
itemList: [
|
||||
{
|
||||
amount: '',
|
||||
fileUploadFlg: '',
|
||||
itemChangeFlg: '',
|
||||
itemGroup: '',
|
||||
itemId: '', //키값??
|
||||
itemName: '',
|
||||
itemNo: '',
|
||||
moduleFlg: '',
|
||||
objectNo: '',
|
||||
pkgMaterialFlg: '',
|
||||
planNo: '',
|
||||
pnowW: '',
|
||||
salePrice: '',
|
||||
saleTotPrice: '',
|
||||
specification: '',
|
||||
unit: '',
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
// Helper functions
|
||||
// const updateItemInList = (itemList, id, 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))
|
||||
}
|
||||
|
||||
export const useEstimateController = (planNo) => {
|
||||
const globalLocaleState = useRecoilValue(globalLocaleStore)
|
||||
const objectRecoil = useRecoilValue(floorPlanObjectState)
|
||||
const [estimateData, setEstimateData] = useRecoilState(estimateState)
|
||||
|
||||
const { get, post } = useAxios(globalLocaleState)
|
||||
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const { promisePost } = useAxios()
|
||||
const [state, setState] = useReducer(reducer, defaultEstimateData)
|
||||
|
||||
useEffect(() => {
|
||||
if (!isLoading) {
|
||||
if (objectRecoil.floorPlanObjectNo && planNo) {
|
||||
fetchSetting()
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
// 상세 조회
|
||||
const fetchSetting = async () => {
|
||||
try {
|
||||
await get({ url: `/api/estimate/${objectRecoil.floorPlanObjectNo}/${planNo}/detail` }).then((res) => {
|
||||
if (isObjectNotEmpty(res)) {
|
||||
setState(res)
|
||||
}
|
||||
})
|
||||
setIsLoading(true)
|
||||
} catch (error) {
|
||||
console.error('견적서 상세조회 Error: ', error)
|
||||
setIsLoading(true)
|
||||
}
|
||||
}
|
||||
|
||||
// const updateItem = (id, updates) => {
|
||||
const updateItem = (itemId, updates) => {
|
||||
setState({
|
||||
// itemList: updateItemInList(state.itemList, id, updates),
|
||||
itemList: updateItemInList(state.itemList, itemId, updates),
|
||||
})
|
||||
}
|
||||
|
||||
const addItem = () => {
|
||||
// const newId = Math.max(...state.itemList.map((item) => item.id)) + 1
|
||||
const newItemId = Math.max(...state.itemList.map((item) => item.itemId)) + 1
|
||||
setState({
|
||||
// itemList: [...state.itemList, { id: newId, name: '' }],
|
||||
//셋팅할필요없는거 빼기
|
||||
itemList: [
|
||||
...state.itemList,
|
||||
{
|
||||
itemId: newItemId,
|
||||
amount: '',
|
||||
fileUploadFlg: '',
|
||||
itemChangeFlg: '',
|
||||
itemGroup: '',
|
||||
itemName: '',
|
||||
itemNo: '',
|
||||
moduleFlg: '',
|
||||
objectNo: '',
|
||||
pkgMaterialFlg: '',
|
||||
planNo: '',
|
||||
pnowW: '',
|
||||
salePrice: '',
|
||||
saleTotPrice: '',
|
||||
specification: '',
|
||||
unit: '',
|
||||
},
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setEstimateData({ ...state })
|
||||
}, [state])
|
||||
|
||||
//견적서 저장
|
||||
const handleEstimateSubmit = async () => {
|
||||
console.log('::담긴 estimateData:::', estimateData)
|
||||
return
|
||||
try {
|
||||
const result = await promisePost({
|
||||
url: ESTIMATE_API_ENDPOINT,
|
||||
data: estimateData,
|
||||
})
|
||||
return result
|
||||
} catch (error) {
|
||||
console.error('Failed to submit estimate:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
state,
|
||||
setState,
|
||||
updateItem,
|
||||
addItem,
|
||||
handleEstimateSubmit,
|
||||
fetchSetting,
|
||||
}
|
||||
}
|
||||
@ -223,9 +223,9 @@ export function useCanvasSetting() {
|
||||
const option1 = settingModalFirstOptions.option1
|
||||
|
||||
// 'allocDisplay' 할당 표시
|
||||
// 'outlineDisplay' 외벽선 표시 'outerLine', 'wallLine'
|
||||
// 'outlineDisplay' 외벽선 표시 'outerLine', POLYGON_TYPE.WALL
|
||||
// 'gridDisplay' 그리드 표시 'lindGrid', 'dotGrid'
|
||||
// 'lineDisplay' 지붕선 표시 'roof', 'roofBase'
|
||||
// 'lineDisplay' 지붕선 표시 'roof', POLYGON_TYPE.ROOF
|
||||
// 'wordDisplay' 문자 표시
|
||||
// 'circuitNumDisplay' 회로번호 표시
|
||||
// 'flowDisplay' 흐름방향 표시 'arrow'
|
||||
@ -248,7 +248,7 @@ export function useCanvasSetting() {
|
||||
optionName = ['lindGrid', 'dotGrid']
|
||||
break
|
||||
case 'lineDisplay': //지붕선 표시
|
||||
optionName = ['roof', 'roofBase']
|
||||
optionName = ['roof', POLYGON_TYPE.ROOF]
|
||||
break
|
||||
case 'wordDisplay': //문자 표시
|
||||
optionName = ['6']
|
||||
|
||||
@ -13,9 +13,9 @@ export function useFirstOption() {
|
||||
const option1 = settingModalFirstOptions.option1
|
||||
|
||||
// 'allocDisplay' 할당 표시
|
||||
// 'outlineDisplay' 외벽선 표시 'outerLine', 'wallLine'
|
||||
// 'outlineDisplay' 외벽선 표시 'outerLine', POLYGON_TYPE.WALL
|
||||
// 'gridDisplay' 그리드 표시 'lindGrid', 'dotGrid'
|
||||
// 'lineDisplay' 지붕선 표시 'roof', 'roofBase'
|
||||
// 'lineDisplay' 지붕선 표시 'roof', POLYGON_TYPE.ROOF
|
||||
// 'wordDisplay' 문자 표시
|
||||
// 'circuitNumDisplay' 회로번호 표시
|
||||
// 'flowDisplay' 흐름방향 표시 'arrow'
|
||||
@ -37,7 +37,7 @@ export function useFirstOption() {
|
||||
optionName = ['lineGrid', 'dotGrid', 'adsorptionPoint', 'tempGrid']
|
||||
break
|
||||
case 'lineDisplay': //지붕선 표시
|
||||
optionName = ['roof', 'roofBase']
|
||||
optionName = ['roof', POLYGON_TYPE.ROOF]
|
||||
break
|
||||
case 'wordDisplay': //문자 표시
|
||||
optionName = ['6']
|
||||
|
||||
@ -15,7 +15,7 @@ import {
|
||||
outerLineLength2State,
|
||||
outerLineTypeState,
|
||||
} from '@/store/outerLineAtom'
|
||||
import { calculateIntersection, distanceBetweenPoints, findClosestPoint, polygonToTurfPolygon } from '@/util/canvas-util'
|
||||
import { calculateIntersection, distanceBetweenPoints, findClosestPoint, isPointOnLine, polygonToTurfPolygon } from '@/util/canvas-util'
|
||||
import { fabric } from 'fabric'
|
||||
import { useAdsorptionPoint } from '@/hooks/useAdsorptionPoint'
|
||||
import { useSwal } from '@/hooks/useSwal'
|
||||
@ -23,6 +23,7 @@ import { booleanPointInPolygon } from '@turf/turf'
|
||||
import { usePopup } from '@/hooks/usePopup'
|
||||
import { calculateAngle } from '@/util/qpolygon-utils'
|
||||
import { QPolygon } from '@/components/fabric/QPolygon'
|
||||
import { POLYGON_TYPE } from '@/common/common'
|
||||
|
||||
// 보조선 작성
|
||||
export function useAuxiliaryDrawing(id) {
|
||||
@ -80,7 +81,7 @@ export function useAuxiliaryDrawing(id) {
|
||||
|
||||
useEffect(() => {
|
||||
// innerLines가 있을경우 삭제
|
||||
const roofs = canvas?.getObjects().filter((obj) => obj.name === 'roofBase')
|
||||
const roofs = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
|
||||
if (roofs.length === 0) {
|
||||
swalFire({ text: '지붕형상이 없습니다.' })
|
||||
closePopup(id)
|
||||
@ -561,7 +562,7 @@ export function useAuxiliaryDrawing(id) {
|
||||
return
|
||||
}
|
||||
|
||||
const roofBases = canvas.getObjects().filter((obj) => obj.name === 'roofBase')
|
||||
const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
|
||||
/*const allLines = [...auxiliaryLines]
|
||||
|
||||
roofBases.forEach((roofBase) => {
|
||||
@ -611,9 +612,41 @@ export function useAuxiliaryDrawing(id) {
|
||||
},
|
||||
)
|
||||
lineHistory.current.push(newLine)
|
||||
lineHistory.current = lineHistory.current.filter((history) => history !== line1)
|
||||
removeLine(line1)
|
||||
intersectionPoints.current.push(...interSectionPointsWithRoofLines)
|
||||
return
|
||||
} else if (interSectionPointsWithRoofLines.length === 1) {
|
||||
//지붕선과 만나는 점이 하나일 경우
|
||||
const distance1 = distanceBetweenPoints({ x: line1.x1, y: line1.y1 }, interSectionPointsWithRoofLines[0])
|
||||
const distance2 = distanceBetweenPoints({ x: line1.x2, y: line1.y2 }, interSectionPointsWithRoofLines[0])
|
||||
|
||||
if (!(distance1 === 0 || distance2 === 0)) {
|
||||
if (distance1 >= distance2) {
|
||||
const newLine = addLine([line1.x1, line1.y1, interSectionPointsWithRoofLines[0].x, interSectionPointsWithRoofLines[0].y], {
|
||||
stroke: 'black',
|
||||
strokeWidth: 1,
|
||||
selectable: false,
|
||||
name: 'auxiliaryLine',
|
||||
isFixed: true,
|
||||
})
|
||||
lineHistory.current.push(newLine)
|
||||
lineHistory.current = lineHistory.current.filter((history) => history !== line1)
|
||||
removeLine(line1)
|
||||
} else {
|
||||
const newLine = addLine([line1.x2, line1.y2, interSectionPointsWithRoofLines[0].x, interSectionPointsWithRoofLines[0].y], {
|
||||
stroke: 'black',
|
||||
strokeWidth: 1,
|
||||
selectable: false,
|
||||
name: 'auxiliaryLine',
|
||||
isFixed: true,
|
||||
})
|
||||
lineHistory.current.push(newLine)
|
||||
lineHistory.current = lineHistory.current.filter((history) => history !== line1)
|
||||
removeLine(line1)
|
||||
}
|
||||
intersectionPoints.current.push(interSectionPointsWithRoofLines[0])
|
||||
}
|
||||
}
|
||||
|
||||
//보조선과 만나는 점을 찾는다.
|
||||
@ -659,6 +692,7 @@ export function useAuxiliaryDrawing(id) {
|
||||
})
|
||||
}
|
||||
lineHistory.current.push(newLine)
|
||||
lineHistory.current = lineHistory.current.filter((history) => history !== line1)
|
||||
removeLine(line1)
|
||||
})
|
||||
|
||||
@ -742,7 +776,7 @@ export function useAuxiliaryDrawing(id) {
|
||||
return
|
||||
}
|
||||
|
||||
const roofBases = canvas.getObjects().filter((obj) => obj.name === 'roofBase')
|
||||
const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
|
||||
|
||||
//lineHistory.current에 있는 선들 중 startPoint와 endPoint가 겹치는 line은 제거
|
||||
// 겹치는 선 하나는 canvas에서 제거한다.
|
||||
@ -772,9 +806,13 @@ export function useAuxiliaryDrawing(id) {
|
||||
})
|
||||
const roofInnerLines = innerLines.filter((line) => {
|
||||
const inPolygon1 =
|
||||
tempPolygonPoints.some((point) => point.x === line.x1 && point.y === line.y1) || roofBase.inPolygon({ x: line.x1, y: line.y1 })
|
||||
tempPolygonPoints.some((point) => point.x === line.x1 && point.y === line.y1) ||
|
||||
roofBase.inPolygon({ x: line.x1, y: line.y1 }) ||
|
||||
roofBase.lines.some((line) => isPointOnLine(line, { x: line.x1, y: line.y1 }))
|
||||
const inPolygon2 =
|
||||
tempPolygonPoints.some((point) => point.x === line.x2 && point.y === line.y2) || roofBase.inPolygon({ x: line.x2, y: line.y2 })
|
||||
tempPolygonPoints.some((point) => point.x === line.x2 && point.y === line.y2) ||
|
||||
roofBase.inPolygon({ x: line.x2, y: line.y2 }) ||
|
||||
roofBase.lines.some((line) => isPointOnLine(line, { x: line.x2, y: line.y2 }))
|
||||
|
||||
if (inPolygon1 && inPolygon2) {
|
||||
line.attributes = { ...line.attributes, roofId: roofBase.id }
|
||||
|
||||
@ -160,7 +160,7 @@ export function useEavesGableEdit(id) {
|
||||
attributes,
|
||||
})
|
||||
|
||||
const roofBases = canvas?.getObjects().filter((obj) => obj.name === 'roofBase')
|
||||
const roofBases = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
|
||||
|
||||
roofBases.forEach((roof) => {
|
||||
roof.innerLines.forEach((line) => {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { useEffect, useRef } from 'react'
|
||||
import { LINE_TYPE, POLYGON_TYPE } from '@/common/common'
|
||||
import { useRecoilValue, useResetRecoilState } from 'recoil'
|
||||
import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil'
|
||||
import { canvasState, currentObjectState } from '@/store/canvasAtom'
|
||||
import { useMode } from '@/hooks/useMode'
|
||||
import { usePolygon } from '@/hooks/usePolygon'
|
||||
|
||||
@ -82,12 +82,12 @@ export function useRoofAllocationSetting(id) {
|
||||
const [selectedRoofMaterial, setSelectedRoofMaterial] = useState(roofMaterials[0])
|
||||
|
||||
useEffect(() => {
|
||||
const roofBases = canvas.getObjects().filter((obj) => obj.name === 'roofBase')
|
||||
const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
|
||||
if (roofBases.length === 0) {
|
||||
swalFire({ text: '할당할 지붕이 없습니다.' })
|
||||
closePopup(id)
|
||||
}
|
||||
// if (type === 'roofBase') {
|
||||
// if (type === POLYGON_TYPE.ROOF) {
|
||||
// // 지붕면 할당
|
||||
//
|
||||
// } else if ('roof') {
|
||||
@ -105,7 +105,7 @@ export function useRoofAllocationSetting(id) {
|
||||
|
||||
// 선택한 지붕재로 할당
|
||||
const handleSave = () => {
|
||||
const roofBases = canvas.getObjects().filter((obj) => obj.name === 'roofBase')
|
||||
const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
|
||||
const wallLines = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.WALL)
|
||||
roofBases.forEach((roofBase) => {
|
||||
try {
|
||||
@ -118,7 +118,7 @@ export function useRoofAllocationSetting(id) {
|
||||
canvas.remove(line)
|
||||
})
|
||||
|
||||
canvas.remove(roofBase)
|
||||
// canvas.remove(roofBase)
|
||||
})
|
||||
|
||||
wallLines.forEach((wallLine) => {
|
||||
|
||||
@ -185,7 +185,7 @@ export function useRoofShapePassivitySetting(id) {
|
||||
}
|
||||
|
||||
const handleLineToPolygon = () => {
|
||||
const roofBases = canvas.getObjects().filter((obj) => obj.name === 'roofBase')
|
||||
const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
|
||||
const exceptObjs = canvas.getObjects().filter((obj) => obj.name !== 'outerLine' && obj.parent?.name !== 'outerLine')
|
||||
const lines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
|
||||
exceptObjs.forEach((obj) => {
|
||||
|
||||
@ -383,7 +383,7 @@ export function useRoofShapeSetting(id) {
|
||||
|
||||
canvas
|
||||
.getObjects()
|
||||
.filter((obj) => obj.name === 'roofBase')
|
||||
.filter((obj) => obj.name === POLYGON_TYPE.ROOF)
|
||||
.forEach((obj) => {
|
||||
canvas.remove(...obj.innerLines)
|
||||
canvas.remove(obj)
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { useState } from 'react'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { canvasSizeState, canvasState, canvasZoomState, currentObjectState, fontFamilyState, fontSizeState } from '@/store/canvasAtom'
|
||||
@ -18,6 +18,10 @@ export function useCanvasEvent() {
|
||||
const lengthTextOption = useRecoilValue(fontSelector('lengthText'))
|
||||
const { modifiedPlanFlag, setModifiedPlanFlag } = usePlan()
|
||||
|
||||
useEffect(() => {
|
||||
canvas?.setZoom(canvasZoom / 100)
|
||||
}, [canvasZoom])
|
||||
|
||||
// 기본적인 이벤트 필요시 추가
|
||||
const attachDefaultEventOnCanvas = () => {
|
||||
removeEventOnCanvas()
|
||||
@ -365,6 +369,14 @@ export function useCanvasEvent() {
|
||||
})
|
||||
}
|
||||
|
||||
const handleZoom = (isZoom) => {
|
||||
if (isZoom) {
|
||||
setCanvasZoom(canvasZoom + 10)
|
||||
} else {
|
||||
setCanvasZoom(canvasZoom - 10)
|
||||
}
|
||||
}
|
||||
|
||||
const handleZoomClear = () => {
|
||||
setCanvasZoom(100)
|
||||
canvas.set({ zoom: 1 })
|
||||
@ -376,5 +388,6 @@ export function useCanvasEvent() {
|
||||
setCanvasForEvent,
|
||||
attachDefaultEventOnCanvas,
|
||||
handleZoomClear,
|
||||
handleZoom,
|
||||
}
|
||||
}
|
||||
|
||||
@ -244,7 +244,7 @@ export function useContextMenu() {
|
||||
if (temp.length > 0) menu = temp
|
||||
}
|
||||
|
||||
handleClick(null, menu)
|
||||
if (menu) handleClick(null, menu)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
@ -256,8 +256,6 @@ export function useContextMenu() {
|
||||
}, [currentContextMenu])
|
||||
|
||||
useEffect(() => {
|
||||
console.log('currentObject', currentObject)
|
||||
|
||||
if (currentObject?.name) {
|
||||
console.log(currentObject?.name)
|
||||
switch (currentObject.name) {
|
||||
@ -477,7 +475,7 @@ export function useContextMenu() {
|
||||
{
|
||||
id: 'dimensionLineDisplayEdit',
|
||||
name: getMessage('contextmenu.display.edit'),
|
||||
component: <DimensionLineSetting id={popupId} />,
|
||||
component: <DimensionLineSetting id={popupId} isConfig={false} />,
|
||||
},
|
||||
],
|
||||
])
|
||||
|
||||
@ -4,47 +4,90 @@ import { contextPopupState, popupState } from '@/store/popupAtom'
|
||||
export function usePopup() {
|
||||
const [popup, setPopup] = useRecoilState(popupState)
|
||||
const [contextMenuPopup, setContextMenuPopup] = useRecoilState(contextPopupState)
|
||||
const addPopup = (id, depth, component) => {
|
||||
setPopup({ children: [...filterDepth(depth), { id: id, depth: depth, component: component }] })
|
||||
|
||||
const addPopup = (id, depth, component, isConfig = false) => {
|
||||
setPopup({
|
||||
config: isConfig ? [...filterDepth(depth, isConfig), { id, depth, component, isConfig }] : [...popup.config],
|
||||
other: !isConfig ? [...filterDepth(depth, isConfig), { id, depth, component, isConfig }] : [...popup.other],
|
||||
})
|
||||
}
|
||||
|
||||
const closePopup = (id) => {
|
||||
const closePopup = (id, isConfig = false) => {
|
||||
if (contextMenuPopup) setContextMenuPopup(null)
|
||||
setPopup({ children: [...filterChildrenPopup(id).filter((child) => child.id !== id)] })
|
||||
if (isConfig) {
|
||||
setPopup({
|
||||
config: [...filterChildrenPopup(id, isConfig).filter((child) => child.id !== id)],
|
||||
other: popup.other,
|
||||
})
|
||||
} else {
|
||||
setPopup({
|
||||
config: popup.config,
|
||||
other: [...filterChildrenPopup(id, isConfig).filter((child) => child.id !== id)],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const filterPopup = (depth) => {
|
||||
setPopup({ children: [...filterDepth(depth)] })
|
||||
setPopup({
|
||||
config: [...filterDepth(depth)],
|
||||
other: [],
|
||||
})
|
||||
}
|
||||
|
||||
const filterChildrenPopup = (id) => {
|
||||
const target = popup.children.filter((child) => child.id === id)
|
||||
if (target.length !== 0) {
|
||||
return popup.children.filter((child) => child.depth <= target[0].depth)
|
||||
const filterChildrenPopup = (id, isConfig) => {
|
||||
let target = []
|
||||
if (isConfig) {
|
||||
target = popup?.config.filter((child) => child.id === id)
|
||||
} else {
|
||||
target = popup?.other.filter((child) => child.id === id)
|
||||
}
|
||||
|
||||
return popup.children
|
||||
if (target.length !== 0) {
|
||||
if (isConfig) {
|
||||
return popup?.config.filter((child) => child.depth <= target[0].depth)
|
||||
} else {
|
||||
return popup?.other.filter((child) => child.depth <= target[0].depth)
|
||||
}
|
||||
} else {
|
||||
if (isConfig) {
|
||||
return popup.config
|
||||
} else {
|
||||
return popup.other
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const closePopups = (ids) => {
|
||||
setPopup({ children: [...popup.children.filter((child) => !ids.includes(child.id))] })
|
||||
setPopup({
|
||||
config: [...popup?.config.filter((child) => !ids.includes(child.id))],
|
||||
other: [...popup?.other.filter((child) => !ids.includes(child.id))],
|
||||
})
|
||||
}
|
||||
|
||||
const closeAll = () => {
|
||||
setPopup({ children: [] })
|
||||
setPopup({
|
||||
other: [],
|
||||
config: [],
|
||||
})
|
||||
}
|
||||
|
||||
const closePrevPopup = () => {
|
||||
setPopup({ children: [...popup.children.slice(popup.children.length - 1)] })
|
||||
setPopup({
|
||||
config: [...popup?.slice(popup?.length - 1)],
|
||||
other: [],
|
||||
})
|
||||
}
|
||||
|
||||
const filterDepth = (depth) => {
|
||||
return [...popup.children.filter((child) => child.depth !== depth)]
|
||||
const filterDepth = (depth, isConfig) => {
|
||||
if (isConfig) {
|
||||
return [...popup?.config.filter((child) => child.depth < depth)]
|
||||
} else {
|
||||
return [...popup?.other.filter((child) => child.depth < depth)]
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
popup,
|
||||
setPopup,
|
||||
addPopup,
|
||||
closePopup,
|
||||
closePopups,
|
||||
|
||||
@ -123,6 +123,7 @@
|
||||
"modal.module.basic.setting.auto.placement": "設定値に自動配置",
|
||||
"plan.menu.module.circuit.setting.circuit.trestle.setting": "回路と架台の設定",
|
||||
"modal.circuit.trestle.setting": "回路と架台設定",
|
||||
"modal.circuit.trestle.setting.alloc.trestle": "仮割り当て",
|
||||
"modal.circuit.trestle.setting.power.conditional.select": "パワーコンディショナーを選択",
|
||||
"modal.circuit.trestle.setting.power.conditional.select.name": "名称",
|
||||
"modal.circuit.trestle.setting.power.conditional.select.rated.output": "定格出力",
|
||||
@ -130,6 +131,8 @@
|
||||
"modal.circuit.trestle.setting.power.conditional.select.max.connection": "最大接続枚数",
|
||||
"modal.circuit.trestle.setting.power.conditional.select.max.overload": "過積最大枚数",
|
||||
"modal.circuit.trestle.setting.power.conditional.select.output.current": "出力電流",
|
||||
"modal.circuit.trestle.setting.power.conditional.select.check1": "同一傾斜同一方面の面積の場合、同じ面として回路を分ける。",
|
||||
"modal.circuit.trestle.setting.power.conditional.select.check2": "MAX接続(過積)で回路を分ける。",
|
||||
"modal.circuit.trestle.setting.circuit.allocation": "回路割り当て",
|
||||
"modal.circuit.trestle.setting.circuit.allocation.auto": "自動回路割り当て",
|
||||
"modal.circuit.trestle.setting.circuit.allocation.passivity": "手動回路割当",
|
||||
@ -140,6 +143,12 @@
|
||||
"modal.circuit.trestle.setting.circuit.allocation.passivity.all.power.conditional.reset": "すべての回路番号の初期化",
|
||||
"modal.circuit.trestle.setting.circuit.allocation.passivity.circuit.num.fix": "番号確定",
|
||||
"modal.circuit.trestle.setting.step.up.allocation": "昇圧設定",
|
||||
"modal.circuit.trestle.setting.step.up.allocation.serial.amount": "シリアル枚数",
|
||||
"modal.circuit.trestle.setting.step.up.allocation.total.amount": "総回路数",
|
||||
"modal.circuit.trestle.setting.step.up.allocation.connected": "接続する",
|
||||
"modal.circuit.trestle.setting.step.up.allocation.circuit.amount": "昇圧回路数",
|
||||
"modal.circuit.trestle.setting.step.up.allocation.option": "昇圧オプション",
|
||||
"modal.circuit.trestle.setting.step.up.allocation.select.monitor": "モニターの選択",
|
||||
"plan.menu.module.circuit.setting.plan.orientation": "図面方位の適用",
|
||||
"plan.menu.estimate": "見積",
|
||||
"plan.menu.estimate.roof.alloc": "屋根面の割り当て",
|
||||
@ -474,6 +483,19 @@
|
||||
"commons.east": "ドン",
|
||||
"commons.south": "南",
|
||||
"commons.north": "北",
|
||||
"font.style.normal": "보통(JA)",
|
||||
"font.style.italic": "기울임꼴(JA)",
|
||||
"font.style.bold": "굵게(JA)",
|
||||
"font.style.bold.italic": "굵은 기울임꼴(JA)",
|
||||
"color.black": "검정색(JA)",
|
||||
"color.red": "빨강색(JA)",
|
||||
"color.blue": "파랑색(JA)",
|
||||
"color.gray": "회색(JA)",
|
||||
"color.yellow": "황색(JA)",
|
||||
"color.green": "녹색(JA)",
|
||||
"color.pink": "분홍색(JA)",
|
||||
"color.gold": "황금색(JA)",
|
||||
"color.darkblue": "남색(JA)",
|
||||
"site.name": "Q.CAST III",
|
||||
"site.sub_name": "太陽光発電システム図面管理サイト",
|
||||
"board.notice.title": "お知らせ",
|
||||
@ -814,7 +836,10 @@
|
||||
"estimate.detail.sepcialEstimateProductInfo.calcFormula2": "PKG単価 (W)×PKG容量(W)",
|
||||
"estimate.detail.header.showPrice": "価格表示",
|
||||
"estimate.detail.showPrice.btn1": "Pricing",
|
||||
"estimate.detail.showPrice.description": "クリックして製品の特異性を確認する",
|
||||
"estimate.detail.showPrice.description1": "製品価格 OPEN",
|
||||
"estimate.detail.showPrice.description2": "追加, 変更資材",
|
||||
"estimate.detail.showPrice.description3": "添付必須",
|
||||
"estimate.detail.showPrice.description4": "クリックして製品の特異性を確認する",
|
||||
"estimate.detail.showPrice.btn2": "製品を追加",
|
||||
"estimate.detail.showPrice.btn3": "製品削除"
|
||||
}
|
||||
|
||||
@ -127,6 +127,7 @@
|
||||
"modal.module.basic.setting.auto.placement": "설정값으로 자동 배치",
|
||||
"plan.menu.module.circuit.setting.circuit.trestle.setting": "회로 및 가대 설정",
|
||||
"modal.circuit.trestle.setting": "회로 및 가대설정",
|
||||
"modal.circuit.trestle.setting.alloc.trestle": "가대할당",
|
||||
"modal.circuit.trestle.setting.power.conditional.select": "파워컨디셔너 선택",
|
||||
"modal.circuit.trestle.setting.power.conditional.select.name": "명칭",
|
||||
"modal.circuit.trestle.setting.power.conditional.select.rated.output": "정격출력",
|
||||
@ -134,6 +135,8 @@
|
||||
"modal.circuit.trestle.setting.power.conditional.select.max.connection": "최대접속매수",
|
||||
"modal.circuit.trestle.setting.power.conditional.select.max.overload": "과적최대매수",
|
||||
"modal.circuit.trestle.setting.power.conditional.select.output.current": "출력전류",
|
||||
"modal.circuit.trestle.setting.power.conditional.select.check1": "동일경사 동일 방면의 면적인 경우, 같은 면으로서 회로를 나눈다.",
|
||||
"modal.circuit.trestle.setting.power.conditional.select.check2": "MAX 접속(과적)으로 회로를 나눈다.",
|
||||
"modal.circuit.trestle.setting.circuit.allocation": "회로 할당",
|
||||
"modal.circuit.trestle.setting.circuit.allocation.auto": "자동 회로 할당",
|
||||
"modal.circuit.trestle.setting.circuit.allocation.passivity": "수동 회로 할당",
|
||||
@ -144,6 +147,12 @@
|
||||
"modal.circuit.trestle.setting.circuit.allocation.passivity.all.power.conditional.reset": "모든 회로번호 초기화",
|
||||
"modal.circuit.trestle.setting.circuit.allocation.passivity.circuit.num.fix": "번호 확정",
|
||||
"modal.circuit.trestle.setting.step.up.allocation": "승압 설정",
|
||||
"modal.circuit.trestle.setting.step.up.allocation.serial.amount": "직렬매수",
|
||||
"modal.circuit.trestle.setting.step.up.allocation.total.amount": "총 회로수",
|
||||
"modal.circuit.trestle.setting.step.up.allocation.connected": "연결함",
|
||||
"modal.circuit.trestle.setting.step.up.allocation.circuit.amount": "승압회로수",
|
||||
"modal.circuit.trestle.setting.step.up.allocation.option": "승압옵션",
|
||||
"modal.circuit.trestle.setting.step.up.allocation.select.monitor": "모니터 선택",
|
||||
"plan.menu.module.circuit.setting.plan.orientation": "도면 방위 적용",
|
||||
"plan.menu.estimate": "견적서",
|
||||
"plan.menu.estimate.roof.alloc": "지붕면 할당",
|
||||
@ -480,6 +489,19 @@
|
||||
"commons.east": "동",
|
||||
"commons.south": "남",
|
||||
"commons.north": "북",
|
||||
"font.style.normal": "보통",
|
||||
"font.style.italic": "기울임꼴",
|
||||
"font.style.bold": "굵게",
|
||||
"font.style.bold.italic": "굵은 기울임꼴",
|
||||
"color.black": "검정색",
|
||||
"color.red": "빨강색",
|
||||
"color.blue": "파랑색",
|
||||
"color.gray": "회색",
|
||||
"color.yellow": "황색",
|
||||
"color.green": "녹색",
|
||||
"color.pink": "분홍색",
|
||||
"color.gold": "황금색",
|
||||
"color.darkblue": "남색",
|
||||
"site.name": "Q.CAST III",
|
||||
"site.sub_name": "태양광 발전 시스템 도면관리 사이트",
|
||||
"board.notice.title": "공지사항",
|
||||
@ -820,7 +842,10 @@
|
||||
"estimate.detail.sepcialEstimateProductInfo.calcFormula2": "PKG단가(W) * PKG용량(W)",
|
||||
"estimate.detail.header.showPrice": "가격표시",
|
||||
"estimate.detail.showPrice.btn1": "Pricing",
|
||||
"estimate.detail.showPrice.description": "클릭하여 제품 특이사항 확인",
|
||||
"estimate.detail.showPrice.description1": "제품 가격 OPEN",
|
||||
"estimate.detail.showPrice.description2": "추가, 변경 자재",
|
||||
"estimate.detail.showPrice.description3": "첨부필수",
|
||||
"estimate.detail.showPrice.description4": "클릭하여 제품 특이사항 확인",
|
||||
"estimate.detail.showPrice.btn2": "제품추가",
|
||||
"estimate.detail.showPrice.btn3": "제품삭제"
|
||||
}
|
||||
|
||||
@ -7,3 +7,9 @@ export const floorPlanObjectState = atom({
|
||||
},
|
||||
dangerouslyAllowMutability: true,
|
||||
})
|
||||
|
||||
export const estimateState = atom({
|
||||
key: `estimateState`,
|
||||
default: {},
|
||||
dangerouslyAllowMutability: true,
|
||||
})
|
||||
|
||||
@ -7,7 +7,8 @@ import { atom } from 'recoil'
|
||||
export const popupState = atom({
|
||||
key: 'popupState',
|
||||
default: {
|
||||
children: [],
|
||||
config: [],
|
||||
other: [],
|
||||
},
|
||||
dangerouslyAllowMutability: true,
|
||||
})
|
||||
|
||||
@ -5,7 +5,7 @@ export const settingModalFirstOptionsState = atom({
|
||||
default: {
|
||||
option1: [
|
||||
{ id: 1, column: 'allocDisplay', name: 'modal.canvas.setting.first.option.alloc', selected: false },
|
||||
{ id: 2, column: 'outlineDisplay', name: 'modal.canvas.setting.first.option.outline', selected: false },
|
||||
{ id: 2, column: 'outlineDisplay', name: 'modal.canvas.setting.first.option.outline', selected: true },
|
||||
{ id: 3, column: 'gridDisplay', name: 'modal.canvas.setting.first.option.grid', selected: false },
|
||||
{ id: 4, column: 'lineDisplay', name: 'modal.canvas.setting.first.option.roof.line', selected: false },
|
||||
{ id: 5, column: 'wordDisplay', name: 'modal.canvas.setting.first.option.word', selected: false },
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -239,6 +239,7 @@ footer{
|
||||
nav{
|
||||
.nav-list{
|
||||
.nav-item{
|
||||
a,
|
||||
button{
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
@ -37,9 +37,10 @@
|
||||
top: 50%;
|
||||
left: 0;
|
||||
transform: translateY(-50%);
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
background: url(../../public/static/images/main/id_icon.svg)no-repeat center;
|
||||
background-size: cover;
|
||||
}
|
||||
}
|
||||
.store-arr{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -521,9 +521,11 @@ export function isPointOnLine(line, point) {
|
||||
const a = line.y2 - line.y1
|
||||
const b = line.x1 - line.x2
|
||||
const c = line.x2 * line.y1 - line.x1 * line.y2
|
||||
return a * point.x + b * point.y + c === 0
|
||||
}
|
||||
const result = Math.abs(a * point.x + b * point.y + c) / 100
|
||||
|
||||
// 점이 선 위에 있는지 확인
|
||||
return result <= 10
|
||||
}
|
||||
/**
|
||||
* 점과 가까운 line 찾기
|
||||
* @param point
|
||||
|
||||
@ -1,6 +1,14 @@
|
||||
import { fabric } from 'fabric'
|
||||
import { QLine } from '@/components/fabric/QLine'
|
||||
import { calculateIntersection, distanceBetweenPoints, findClosestPoint, getDegreeByChon, getDirectionByPoint } from '@/util/canvas-util'
|
||||
import {
|
||||
calculateIntersection,
|
||||
distanceBetweenPoints,
|
||||
findClosestPoint,
|
||||
getDegreeByChon,
|
||||
getDirectionByPoint,
|
||||
isPointOnLine,
|
||||
} from '@/util/canvas-util'
|
||||
|
||||
import { QPolygon } from '@/components/fabric/QPolygon'
|
||||
import * as turf from '@turf/turf'
|
||||
import { LINE_TYPE, POLYGON_TYPE } from '@/common/common'
|
||||
@ -958,10 +966,13 @@ export default function offsetPolygon(vertices, offset) {
|
||||
}
|
||||
}
|
||||
|
||||
export const splitPolygonWithLines = (polygon) => {
|
||||
/*export const splitPolygonWithLines = (polygon) => {
|
||||
const roofs = []
|
||||
const allLines = [...polygon.innerLines]
|
||||
|
||||
const polygonLines = polygon.lines
|
||||
const innerLines = polygon.innerLines
|
||||
|
||||
allLines.forEach((line) => {
|
||||
line.startPoint = { x: line.x1, y: line.y1 }
|
||||
line.endPoint = { x: line.x2, y: line.y2 }
|
||||
@ -984,10 +995,10 @@ export const splitPolygonWithLines = (polygon) => {
|
||||
})
|
||||
})
|
||||
|
||||
/**
|
||||
/!**
|
||||
* 좌표 테스트용
|
||||
*/
|
||||
/*allLines.forEach((line) => {
|
||||
*!/
|
||||
/!*allLines.forEach((line) => {
|
||||
const text = new fabric.Text(`(${line.startPoint.x},${line.startPoint.y})`, {
|
||||
left: line.startPoint.x,
|
||||
top: line.startPoint.y,
|
||||
@ -1016,10 +1027,10 @@ export const splitPolygonWithLines = (polygon) => {
|
||||
|
||||
polygon.canvas.add(text)
|
||||
polygon.canvas.renderAll()
|
||||
})*/
|
||||
/**
|
||||
})*!/
|
||||
/!**
|
||||
* 좌표 테스트용 끝
|
||||
*/
|
||||
*!/
|
||||
|
||||
polygon.points.forEach((point, index) => {
|
||||
allLines.forEach((line) => {
|
||||
@ -1165,6 +1176,273 @@ export const splitPolygonWithLines = (polygon) => {
|
||||
polygon.canvas.add(roof)
|
||||
polygon.canvas.renderAll()
|
||||
})
|
||||
}*/
|
||||
export const splitPolygonWithLines = (polygon) => {
|
||||
const canvas = polygon.canvas
|
||||
polygon.set({ visible: false })
|
||||
let innerLines = [...polygon.innerLines]
|
||||
let polygonLines = [...polygon.lines]
|
||||
const roofs = []
|
||||
|
||||
let delIndexs = []
|
||||
let newLines = []
|
||||
|
||||
polygonLines.forEach((line, index) => {
|
||||
line.tempIndex = index
|
||||
innerLines.forEach((innerLine) => {
|
||||
let newLine1, newLine2
|
||||
if (isPointOnLine(line, innerLine.startPoint)) {
|
||||
// 해당 line을 startPoint로 나눈 line2개를 canvas에 추가 하고 기존 line을 제거한다.
|
||||
newLine1 = new QLine([line.x1, line.y1, innerLine.startPoint.x, innerLine.startPoint.y], {
|
||||
fontSize: polygon.fontSize,
|
||||
stroke: 'black',
|
||||
strokeWidth: 3,
|
||||
})
|
||||
|
||||
newLine2 = new QLine([innerLine.startPoint.x, innerLine.startPoint.y, line.x2, line.y2], {
|
||||
fontSize: polygon.fontSize,
|
||||
stroke: 'black',
|
||||
strokeWidth: 3,
|
||||
})
|
||||
delIndexs.push(polygonLines.indexOf(line))
|
||||
canvas.remove(polygonLines[polygonLines.indexOf(line)])
|
||||
if (newLine1.length / 10 > 10) {
|
||||
newLines.push(newLine1)
|
||||
}
|
||||
if (newLine2.length / 10 > 10) {
|
||||
newLines.push(newLine2)
|
||||
}
|
||||
}
|
||||
if (isPointOnLine(line, innerLine.endPoint)) {
|
||||
newLine1 = new QLine([line.x1, line.y1, innerLine.endPoint.x, innerLine.endPoint.y], {
|
||||
fontSize: polygon.fontSize,
|
||||
stroke: 'black',
|
||||
strokeWidth: 3,
|
||||
})
|
||||
|
||||
newLine2 = new QLine([innerLine.endPoint.x, innerLine.endPoint.y, line.x2, line.y2], {
|
||||
fontSize: polygon.fontSize,
|
||||
stroke: 'black',
|
||||
strokeWidth: 3,
|
||||
})
|
||||
delIndexs.push(polygonLines.indexOf(line))
|
||||
canvas.remove(polygonLines[polygonLines.indexOf(line)])
|
||||
if (newLine1.length / 10 > 10) {
|
||||
newLines.push(newLine1)
|
||||
}
|
||||
if (newLine2.length / 10 > 10) {
|
||||
newLines.push(newLine2)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
polygonLines = polygonLines.filter((line) => !delIndexs.includes(line.tempIndex))
|
||||
polygonLines = [...polygonLines, ...newLines]
|
||||
|
||||
const allLines = [...polygonLines, ...innerLines]
|
||||
|
||||
/**
|
||||
* 왼쪽 상단을 startPoint로 전부 변경
|
||||
*/
|
||||
allLines.forEach((line) => {
|
||||
let startPoint // 시작점
|
||||
let endPoint // 끝점
|
||||
if (line.x1 < line.x2) {
|
||||
startPoint = { x: line.x1, y: line.y1 }
|
||||
endPoint = { x: line.x2, y: line.y2 }
|
||||
} else if (line.x1 > line.x2) {
|
||||
startPoint = { x: line.x2, y: line.y2 }
|
||||
endPoint = { x: line.x1, y: line.y1 }
|
||||
} else {
|
||||
if (line.y1 < line.y2) {
|
||||
startPoint = { x: line.x1, y: line.y1 }
|
||||
endPoint = { x: line.x2, y: line.y2 }
|
||||
} else {
|
||||
startPoint = { x: line.x2, y: line.y2 }
|
||||
endPoint = { x: line.x1, y: line.y1 }
|
||||
}
|
||||
}
|
||||
|
||||
line.startPoint = startPoint
|
||||
line.endPoint = endPoint
|
||||
})
|
||||
|
||||
polygonLines.forEach((line) => {
|
||||
const startPoint = line.startPoint // 시작점
|
||||
let arrivalPoint = line.endPoint // 도착점
|
||||
|
||||
let currentPoint = startPoint
|
||||
const roofPoints = [startPoint]
|
||||
|
||||
const startLine = line
|
||||
const visitPoints = [startPoint]
|
||||
const visitLines = [startLine]
|
||||
let cnt = 0
|
||||
|
||||
while (!isSamePoint(currentPoint, arrivalPoint)) {
|
||||
line.set({ stroke: 'red' })
|
||||
canvas.renderAll()
|
||||
let nextLines = allLines.filter(
|
||||
(line2) =>
|
||||
(isSamePoint(line2.startPoint, currentPoint) || isSamePoint(line2.endPoint, currentPoint)) &&
|
||||
line !== line2 &&
|
||||
innerLines.includes(line2) &&
|
||||
!visitLines.includes(line2),
|
||||
)
|
||||
|
||||
if (nextLines.length === 0) {
|
||||
nextLines = allLines.filter(
|
||||
(line2) =>
|
||||
(isSamePoint(line2.startPoint, currentPoint) || isSamePoint(line2.endPoint, currentPoint)) &&
|
||||
line !== line2 &&
|
||||
!visitLines.includes(line2),
|
||||
)
|
||||
}
|
||||
|
||||
if (!nextLines) {
|
||||
break
|
||||
}
|
||||
|
||||
let comparisonPoints = []
|
||||
|
||||
nextLines.forEach((nextLine) => {
|
||||
if (isSamePoint(nextLine.startPoint, currentPoint)) {
|
||||
comparisonPoints.push(nextLine.endPoint)
|
||||
} else {
|
||||
comparisonPoints.push(nextLine.startPoint)
|
||||
}
|
||||
})
|
||||
|
||||
comparisonPoints = comparisonPoints.filter((point) => !visitPoints.some((visitPoint) => isSamePoint(visitPoint, point)))
|
||||
comparisonPoints = comparisonPoints.filter((point) => !isSamePoint(point, currentPoint))
|
||||
|
||||
const minDistancePoint = comparisonPoints.reduce((prev, current) => {
|
||||
const prevDistance = Math.sqrt(Math.pow(prev.x - arrivalPoint.x, 2) + Math.pow(prev.y - arrivalPoint.y, 2))
|
||||
const currentDistance = Math.sqrt(Math.pow(current.x - arrivalPoint.x, 2) + Math.pow(current.y - arrivalPoint.y, 2))
|
||||
|
||||
return prevDistance < currentDistance ? prev : current
|
||||
}, comparisonPoints[0])
|
||||
|
||||
nextLines.forEach((nextLine) => {
|
||||
if (isSamePoint(nextLine.startPoint, minDistancePoint) || isSamePoint(nextLine.endPoint, minDistancePoint)) {
|
||||
visitLines.push(nextLine)
|
||||
}
|
||||
})
|
||||
|
||||
currentPoint = { ...minDistancePoint }
|
||||
roofPoints.push(currentPoint)
|
||||
cnt++
|
||||
if (cnt > 100) {
|
||||
throw new Error('무한루프')
|
||||
}
|
||||
}
|
||||
roofs.push(roofPoints)
|
||||
})
|
||||
|
||||
const newRoofs = removeDuplicatePolygons(roofs)
|
||||
newRoofs.forEach((roofPoint, index) => {
|
||||
let defense, pitch
|
||||
const polygonLines = [...polygon.lines]
|
||||
|
||||
let representLines = []
|
||||
let representLine
|
||||
|
||||
// 지붕을 그리면서 기존 polygon의 line중 연결된 line을 찾는다.
|
||||
polygonLines.forEach((line) => {
|
||||
let startFlag = false
|
||||
let endFlag = false
|
||||
const startPoint = line.startPoint
|
||||
const endPoint = line.endPoint
|
||||
roofPoint.forEach((point, index) => {
|
||||
if (isSamePoint(point, startPoint)) {
|
||||
startFlag = true
|
||||
}
|
||||
if (isSamePoint(point, endPoint)) {
|
||||
endFlag = true
|
||||
}
|
||||
})
|
||||
|
||||
if (startFlag && endFlag) {
|
||||
representLines.push(line)
|
||||
}
|
||||
})
|
||||
|
||||
// representLines중 가장 긴 line을 찾는다.
|
||||
representLines.forEach((line) => {
|
||||
if (!representLine) {
|
||||
representLine = line
|
||||
} else {
|
||||
if (representLine.length < line.length) {
|
||||
representLine = line
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const direction = representLine.direction
|
||||
|
||||
switch (direction) {
|
||||
case 'top':
|
||||
defense = 'east'
|
||||
break
|
||||
case 'right':
|
||||
defense = 'south'
|
||||
break
|
||||
case 'bottom':
|
||||
defense = 'west'
|
||||
break
|
||||
case 'left':
|
||||
defense = 'north'
|
||||
break
|
||||
}
|
||||
pitch = polygon.lines[index].attributes?.pitch ?? 0
|
||||
|
||||
const roof = new QPolygon(roofPoint, {
|
||||
fontSize: polygon.fontSize,
|
||||
stroke: 'black',
|
||||
fill: 'transparent',
|
||||
strokeWidth: 3,
|
||||
name: POLYGON_TYPE.ROOF,
|
||||
originX: 'center',
|
||||
originY: 'center',
|
||||
selectable: true,
|
||||
defense: defense,
|
||||
direction: defense,
|
||||
pitch: pitch,
|
||||
})
|
||||
|
||||
polygon.canvas.add(roof)
|
||||
canvas.remove(polygon)
|
||||
polygon.canvas.renderAll()
|
||||
})
|
||||
}
|
||||
|
||||
function normalizePoint(point) {
|
||||
return {
|
||||
x: Math.round(point.x),
|
||||
y: Math.round(point.y),
|
||||
}
|
||||
}
|
||||
|
||||
function arePolygonsEqual(polygon1, polygon2) {
|
||||
if (polygon1.length !== polygon2.length) return false
|
||||
|
||||
const normalizedPolygon1 = polygon1.map(normalizePoint).sort((a, b) => a.x - b.x || a.y - b.y)
|
||||
const normalizedPolygon2 = polygon2.map(normalizePoint).sort((a, b) => a.x - b.x || a.y - b.y)
|
||||
|
||||
return normalizedPolygon1.every((point, index) => arePointsEqual(point, normalizedPolygon2[index]))
|
||||
}
|
||||
|
||||
function removeDuplicatePolygons(polygons) {
|
||||
const uniquePolygons = []
|
||||
|
||||
polygons.forEach((polygon) => {
|
||||
const isDuplicate = uniquePolygons.some((uniquePolygon) => arePolygonsEqual(polygon, uniquePolygon))
|
||||
if (!isDuplicate) {
|
||||
uniquePolygons.push(polygon)
|
||||
}
|
||||
})
|
||||
|
||||
return uniquePolygons
|
||||
}
|
||||
|
||||
const isSamePoint = (a, b) => {
|
||||
@ -3348,7 +3626,7 @@ function createRoofPaddingPolygon(polygon, lines, arcSegments = 0) {
|
||||
}
|
||||
|
||||
function arePointsEqual(point1, point2) {
|
||||
return point1.x === point2.x && point1.y === point2.y
|
||||
return Math.abs(point1.x - point2.x) <= 1 && Math.abs(point1.y - point2.y) <= 1
|
||||
}
|
||||
|
||||
function arraysHaveSamePoints(array1, array2) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user