Merge branch 'dev' of ssh://git.jetbrains.space/nalpari/q-cast-iii/qcast-front into qcast-pub

# Conflicts:
#	src/components/floor-plan/modal/circuitTrestle/CircuitTrestleSetting.jsx
This commit is contained in:
김민식 2025-01-15 18:53:28 +09:00
commit 187f670442
33 changed files with 1649 additions and 654 deletions

View File

@ -38,6 +38,7 @@
"recoil": "^0.7.7",
"sweetalert2": "^11.14.1",
"sweetalert2-react-content": "^5.0.7",
"swr": "^2.3.0",
"usehooks-ts": "^3.1.0",
"uuid": "^10.0.0"
},

View File

@ -0,0 +1,25 @@
import { NextResponse } from 'next/server'
const defaultData = [
{
id: 1,
name: 'John Doe',
email: 'john.doe@example.com',
},
{
id: 2,
name: 'Jane Lee',
email: 'jane.lee@example.com',
},
]
export async function GET(req, res) {
return NextResponse.json(defaultData)
}
export const POST = async (req, res) => {
const { id, name, email } = await req.json()
const newData = { id, name, email }
console.log('🚀 ~ POST ~ newData:', newData)
return NextResponse.json([...defaultData, newData])
}

View File

@ -2,7 +2,7 @@
import { correntObjectNoState } from '@/store/settingAtom'
import { notFound, usePathname, useSearchParams } from 'next/navigation'
import { createContext, useReducer, useState } from 'react'
import { createContext, useReducer, useState, useEffect } from 'react'
import { useSetRecoilState } from 'recoil'
const reducer = (prevState, nextState) => {
@ -48,12 +48,14 @@ const FloorPlanProvider = ({ children }) => {
const objectNo = searchParams.get('objectNo')
const pid = searchParams.get('pid')
if (pathname === '/floor-plan') {
if (pid === undefined || pid === '' || pid === null || objectNo === undefined || objectNo === '' || objectNo === null) {
notFound()
useEffect(() => { // 오류 발생으로 useEffect 사용
if (pathname === '/floor-plan') {
if (pid === undefined || pid === '' || pid === null || objectNo === undefined || objectNo === '' || objectNo === null) {
notFound()
}
setCurrentObjectNo(objectNo)
}
setCurrentObjectNo(objectNo)
}
}, [pid, objectNo])
const [floorPlanState, setFloorPlanState] = useState({
// 플랜 파일 업로드 모달 오픈 제어

View File

@ -164,6 +164,11 @@ export const SAVE_KEY = [
'moduleCompass',
'isFixed',
'modules',
'rackLen',
'itemId',
'supFitQty',
'supFitIntvlPct',
'rackLen',
]
export const OBJECT_PROTOTYPE = [fabric.Line.prototype, fabric.Polygon.prototype, fabric.Triangle.prototype]

View File

@ -23,6 +23,8 @@ import QSelectBox from './common/select/QSelectBox'
import SampleReducer from './sample/SampleReducer'
import styles from './playground.module.css'
import useSWR from 'swr'
import useSWRMutation from 'swr/mutation'
export default function Playground() {
const [useCadFile, setUseCadFile] = useRecoilState(useCadFileState)
@ -32,7 +34,7 @@ export default function Playground() {
const fileRef = useRef(null)
const queryRef = useRef(null)
const [zoom, setZoom] = useState(20)
const { get, promiseGet, promisePost } = useAxios()
const { get, promiseGet, post, promisePost, getFetcher, postFetcher } = useAxios()
const testVar = process.env.NEXT_PUBLIC_TEST
const converterUrl = process.env.NEXT_PUBLIC_CONVERTER_API_URL
const { getMessage } = useMessage()
@ -252,6 +254,19 @@ export default function Playground() {
})
}
const [callFlag, setCallFlag] = useState(false)
// const { data: tutoData, error, isLoading } = useSWR('http://localhost:8080/api/tutorial', getFetcher)
const { data: tutoData, error, isLoading } = useSWR(callFlag ? 'http://localhost:8080/api/tutorial' : null, getFetcher)
const { trigger, isMutating } = useSWRMutation('http://localhost:8080/api/tutorial', postFetcher)
if (isLoading) {
return <div>Loading...</div>
}
if (error) {
return <div>Error...</div>
}
return (
<>
<div className="container mx-auto p-4 m-4 border">
@ -548,14 +563,30 @@ export default function Playground() {
<div className="my-2">
<Button onClick={() => setManagementState({})}>GlobalDataProvider 초기화</Button>
</div>
<div className="my-2">
{/* <div className="my-2">
<p>{managementStateLoaded?.objectNo}</p>
</div>
</div> */}
<div className="my-2">
<Button onClick={() => swalFire({ text: 'alert 테스트입니다.', type: 'alert', confirmFn: () => console.log('Alert!!!') })}>
Sweetalert - alert
</Button>
</div>
<div className="my-2">
{tutoData &&
tutoData.map((item) => (
<div key={item.id}>
{item.name} / {item.email}
</div>
))}
</div>
<div className="my-2">
<Button onClick={() => setCallFlag(true)}>getFetcher call</Button>
</div>
<div className="my-2">
<Button disabled={isMutating} onClick={() => trigger({ id: 3, name: 'seulda kim', email: 'seulda.kim@interplug.co.kr' })}>
insert data
</Button>
</div>
</div>
</>
)

View File

@ -74,7 +74,6 @@ export default function Estimate({}) {
const { estimateContextState, setEstimateContextState, addItem, handleEstimateFileDownload } = useEstimateController(currentPid)
const { selectedPlan } = usePlan()
const router = useRouter()
// List
const [specialNoteList, setSpecialNoteList] = useState([])
@ -299,12 +298,16 @@ export default function Estimate({}) {
} else {
if (originFiles.length > 0) {
if (isEmptyArray(files)) {
let file
file = originFiles.filter((item) => item.delFlg === '0')
setEstimateContextState({
originFiles: file,
})
setOriginFiles(file)
if (originFiles[0].planNo !== estimateContextState.planNo) {
setOriginFiles([])
} else {
let file
file = originFiles.filter((item) => item.delFlg === '0')
setEstimateContextState({
originFiles: file,
})
setOriginFiles(file)
}
}
}
}
@ -1002,6 +1005,7 @@ export default function Estimate({}) {
<div className="estimate-box">
<div className="estimate-tit">{getMessage('estimate.detail.objectNo')}</div>
<div className="estimate-name">
{/* {objectNo} (Plan No: {estimateContextState.planNo}) */}
{objectNo} (Plan No: {planNo})
</div>
</div>

View File

@ -4,11 +4,14 @@ import { useMessage } from '@/hooks/useMessage'
import { useAxios } from '@/hooks/useAxios'
import { useRecoilValue } from 'recoil'
import { floorPlanObjectState, estimateState } from '@/store/floorPlanObjectAtom'
import { usePathname } from 'next/navigation'
export default function DocDownOptionPop({ planNo, setEstimatePopupOpen, docDownPopLockFlg }) {
const { getMessage } = useMessage()
const { promisePost } = useAxios()
const pathName = usePathname()
//EXCEL, PDF
const [schDownload, setSchDownload] = useState('EXCEL')
// EXCEL
@ -62,6 +65,7 @@ export default function DocDownOptionPop({ planNo, setEstimatePopupOpen, docDown
}
const options = { responseType: 'blob' }
await promisePost({ url: url, data: params, option: options })
.then((resultData) => {
if (resultData) {
@ -85,7 +89,9 @@ export default function DocDownOptionPop({ planNo, setEstimatePopupOpen, docDown
window.URL.revokeObjectURL(fileUrl)
// lockFlg = 1 !
estimateRecoilState.lockFlg = '1'
docDownPopLockFlg()
if (pathName.includes('/floor-plan')) {
docDownPopLockFlg()
}
}
})
.catch((error) => {

View File

@ -282,7 +282,8 @@ export default function EstimateCopyPop({ planNo, setEstimateCopyPopupOpen }) {
type="text"
className="input-light"
required
defaultValue={estimateContextState?.charger}
// defaultValue={estimateContextState?.charger}
defaultValue={session?.userNm}
onChange={(e) => {
setCopyReceiveUser(e.target.value)
}}
@ -292,18 +293,18 @@ export default function EstimateCopyPop({ planNo, setEstimateCopyPopupOpen }) {
</div>
</div>
<div className="footer-btn-wrap">
<button type="button" className="btn-origin grey mr5" onClick={() => setEstimateCopyPopupOpen(false)}>
{getMessage('estimate.detail.estimateCopyPopup.close')}
</button>
<button
type="button"
className="btn-origin navy"
className="btn-origin navy mr5"
onClick={() => {
handleEstimateCopy(sendPlanNo, copyReceiveUser, saleStoreId, otherSaleStoreId)
}}
>
{getMessage('estimate.detail.estimateCopyPopup.copyBtn')}
</button>
<button type="button" className="btn-origin grey" onClick={() => setEstimateCopyPopupOpen(false)}>
{getMessage('estimate.detail.estimateCopyPopup.close')}
</button>
</div>
</div>
</div>

View File

@ -2,7 +2,7 @@
import { useContext, useEffect, useState } from 'react'
import { usePathname, useRouter } from 'next/navigation'
import { usePathname, useRouter, useSearchParams } from 'next/navigation'
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
@ -34,7 +34,7 @@ import { addedRoofsState, basicSettingState, selectedRoofMaterialSelector, setti
import { placementShapeDrawingPointsState } from '@/store/placementShapeDrawingAtom'
import { commonUtilsState } from '@/store/commonUtilsAtom'
import { menusState, menuTypeState } from '@/store/menuAtom'
import { estimateState } from '@/store/floorPlanObjectAtom'
import { estimateState, floorPlanObjectState } from '@/store/floorPlanObjectAtom'
import { pwrGnrSimTypeState } from '@/store/simulatorAtom'
import { isObjectNotEmpty } from '@/util/common-utils'
@ -63,7 +63,7 @@ export default function CanvasMenu(props) {
const canvas = useRecoilValue(canvasState)
const { handleZoomClear, handleZoom } = useCanvasEvent()
const { handleMenu } = useMenu()
// const urlParams = useSearchParams()
const { handleEstimateSubmit, fetchSetting } = useEstimateController()
const estimateRecoilState = useRecoilValue(estimateState)
const [estimatePopupOpen, setEstimatePopupOpen] = useState(false)
@ -91,6 +91,8 @@ export default function CanvasMenu(props) {
const [buttonStyle4, setButtonStyle4] = useState('') //
const [buttonStyle5, setButtonStyle5] = useState('') //
const setFloorPlanObjectNo = useSetRecoilState(floorPlanObjectState) //
//
const { objectNo, pid } = floorPlanState
@ -147,7 +149,7 @@ export default function CanvasMenu(props) {
text: getMessage('stuff.detail.move.confirmMsg'),
type: 'confirm',
confirmFn: () => {
router.push(`/management/stuff/detail?objectNo=${objectNo}`)
router.push(`/management/stuff/detail?objectNo=${objectNo}`, { scroll: false })
},
})
break
@ -170,9 +172,20 @@ export default function CanvasMenu(props) {
setType('module')
break
case 5:
setMenuNumber(menu.index)
setCurrentMenu(menu.title)
router.push(`/floor-plan/estimate/5?pid=${pid}&objectNo=${objectNo}`)
// let pid = urlParams.get('pid')
promiseGet({ url: `/api/estimate/${objectNo}/${pid}/detail` }).then((res) => {
if (res.status === 200) {
const estimateDetail = res.data
if (estimateDetail.docNo) {
setMenuNumber(menu.index)
setCurrentMenu(menu.title)
setFloorPlanObjectNo({ floorPlanObjectNo: objectNo })
router.push(`/floor-plan/estimate/5?pid=${pid}&objectNo=${objectNo}`)
} else {
swalFire({ text: getMessage('estimate.menu.move.valid1') })
}
}
})
break
case 6:
promiseGet({ url: `/api/estimate/${objectNo}/${pid}/detail` }).then((res) => {
@ -190,7 +203,7 @@ export default function CanvasMenu(props) {
break
}
if (menu.index !== 6 && menu.index !== 0) {
if (menu.index !== 6 && menu.index !== 0 && menu.index !== 5) {
setMenuNumber(menu.index)
setCurrentMenu(menu.title)
}
@ -296,10 +309,9 @@ export default function CanvasMenu(props) {
}, [type, globalLocale])
useEffect(() => {
console.log('🚀 ~ useEffect ~ basicSetting:', basicSetting)
if (Object.keys(basicSetting).length === 0 || !basicSetting.roofSizeSet) return
// setMenuNumber(1)
if (!selectedRoofMaterial) return
//if (Object.keys(basicSetting).length === 0 || !basicSetting.roofSizeSet) return
setMenuNumber(1)
// if ([2, 3].some((num) => num === canvasSetting?.roofSizeSet)) {
// setMenuNumber(3)
// setType('surface')
@ -309,7 +321,8 @@ export default function CanvasMenu(props) {
// setType('outline')
// setCurrentMenu(MENU.ROOF_COVERING.EXTERIOR_WALL_LINE)
// }
}, [basicSetting])
}, [selectedRoofMaterial])
const checkMenuState = (menu) => {
return (['2', '3'].includes(canvasSetting?.roofSizeSet) && menu.index === 2) || (menuNumber === 4 && menu.index === 2)
}
@ -510,17 +523,20 @@ export default function CanvasMenu(props) {
{menuNumber === 5 && (
<>
<div className="ico-btn-from">
{/* <button className="btn-frame gray ico-flx" onClick={() => setEstimatePopupOpen(true)}> */}
<button type="button" style={{ display: buttonStyle1 }} className="btn-frame gray ico-flx" onClick={() => setEstimatePopupOpen(true)}>
<span className="ico ico01"></span>
<span className="name">{getMessage('plan.menu.estimate.docDown')}</span>
</button>
<button type="button" style={{ display: buttonStyle2 }} className="btn-frame gray ico-flx" onClick={handleEstimateSubmit}>
{/* <button type="button" style={{ display: buttonStyle }} className="btn-frame gray ico-flx" onClick={handleEstimateSubmit}> */}
<span className="ico ico02"></span>
<span className="name">{getMessage('plan.menu.estimate.save')}</span>
</button>
<button
type="button"
style={{ display: buttonStyle3 }}
// style={{ display: buttonStyle }}
className="btn-frame gray ico-flx"
onClick={() => {
handleEstimateReset()
@ -534,6 +550,7 @@ export default function CanvasMenu(props) {
<button
type="button"
style={{ display: buttonStyle4 }}
// style={{ display: buttonStyle }}
className="btn-frame gray ico-flx"
onClick={() => {
setEstimateCopyPopupOpen(true)
@ -546,11 +563,11 @@ export default function CanvasMenu(props) {
<button
type="button"
style={{ display: buttonStyle5 }}
// style={{ display: buttonStyle }}
className="btn-frame gray ico-flx"
onClick={() => {
//
alert('작업중입니다')
// handleEstimateLockController(estimateRecoilState)
handleEstimateLockController(estimateRecoilState)
}}
>
<span className="ico ico05"></span>

View File

@ -9,7 +9,7 @@ import ModuleTabContents from './ModuleTabContents'
import { useDebounceCallback, useDebounceValue } from 'usehooks-ts'
import { moduleSelectionDataState } from '@/store/selectedModuleOptions'
export default function Module({}) {
export default function Module({ setTabNum }) {
const { getMessage } = useMessage()
const addedRoofs = useRecoilValue(addedRoofsState) //
const [roofTab, setRoofTab] = useState(0) //
@ -92,8 +92,8 @@ export default function Module({}) {
<QSelectBox
options={moduleList}
value={moduleSelectionInitParams}
targetKey={'moduleTpCd'}
sourceKey={'itemTp'}
targetKey={'moduleItemId'}
sourceKey={'itemId'}
showKey={'itemNm'}
onChange={handleChangeModule}
/>

View File

@ -20,8 +20,6 @@ export default function ModuleTabContents({
const { getMessage } = useMessage()
const canvasSetting = useRecoilValue(canvasSettingState) //
const [roofMaterial, setRoofMaterial] = useState(addRoof) //`
const globalPitch = useRecoilValue(pitchSelector) //
const globalPitchText = useRecoilValue(pitchTextSelector) //
const { findCommonCode } = useCommonCode()
@ -55,10 +53,6 @@ export default function ModuleTabContents({
const [isExistData, setIsExistData] = useState(false)
useEffect(() => {
console.log('addRoof', addRoof)
}, [])
//
const handleChangeRaftBase = (option) => {
setSelectedRaftBase(option)
@ -88,6 +82,7 @@ export default function ModuleTabContents({
...moduleSelectionInitParams,
...roofBaseParams,
roofBaseCd: option.roofBaseCd,
inclCd: addRoof.pitch,
})
setSelectedRoofBase(option)
}
@ -158,7 +153,12 @@ export default function ModuleTabContents({
useEffect(() => {
if (isObjectNotEmpty(selectedRoofBase) && isObjectNotEmpty(selectedConstruction)) {
const newRoofConstructions = { roofIndex: roofTab, trestle: selectedRoofBase, construction: selectedConstruction }
const newRoofConstructions = {
roofIndex: roofTab,
addRoof: addRoof,
trestle: selectedRoofBase,
construction: selectedConstruction,
}
const index = tempModuleSelectionData.roofConstructions.findIndex((obj) => obj.roofIndex === roofTab)
if (index > -1) {
@ -196,6 +196,7 @@ export default function ModuleTabContents({
...moduleSelectionInitParams,
...roofBaseParams,
roofBaseCd: selectedRoofBase.roofBaseCd,
inclCd: addRoof.pitch,
})
}
}, [selectedRoofBase])
@ -280,26 +281,11 @@ export default function ModuleTabContents({
<div className="module-table-flex-wrap tab2">
<div className="module-flex-item">
<div className="module-flex-item-tit">
{getMessage('modal.module.basic.setting.module.roof.material')}{roofMaterial.nameJp}{globalPitch}
{getMessage('modal.module.basic.setting.module.roof.material')}{roofMaterial.nameJp}
{addRoof.roofAngleSet === 'slope' ? roofMaterial.pitch : roofMaterial.angle}
{globalPitchText}
</div>
<div className="eaves-keraba-table">
<div className="eaves-keraba-item">
<div className="eaves-keraba-th">マンドンピッチ</div>
<div className="eaves-keraba-td">
<div className="grid-select">
<QSelectBox
options={raftCodes}
value={addRoof}
sourceKey={'clCode'}
targetKey={'raftBaseCd'}
showKey={'clCodeNm'}
disabled={roofMaterial.raftAuth === 'R' ? true : false}
onChange={handleChangeRaftBase}
/>
</div>
</div>
</div>
<div className="eaves-keraba-item">
{roofMaterial && ['C'].includes(roofMaterial.lenAuth) && (
<>
@ -461,7 +447,7 @@ export default function ModuleTabContents({
type="checkbox"
id={`ch01_${roofTab}`}
disabled={cvrYn === 'N' ? true : false}
checked={cvrChecked}
defaultChecked={cvrChecked}
onChange={handleCvrChecked}
/>
<label htmlFor={`ch01_${roofTab}`}>{getMessage('modal.module.basic.setting.module.eaves.bar.fitting')}</label>
@ -471,7 +457,7 @@ export default function ModuleTabContents({
type="checkbox"
id={`ch02_${roofTab}`}
disabled={snowGdPossYn === 'N' ? true : false}
checked={snowGdChecked}
defaultChecked={snowGdChecked}
onChange={handleSnowGdChecked}
/>
<label htmlFor={`ch02_${roofTab}`}>{getMessage('modal.module.basic.setting.module.blind.metal.fitting')}</label>

View File

@ -4,17 +4,19 @@ import { useModuleBasicSetting } from '@/hooks/module/useModuleBasicSetting'
import { checkedModuleState } from '@/store/canvasAtom'
import { useRecoilValue, useRecoilState, useSetRecoilState } from 'recoil'
import { selectedModuleState, moduleSelectionDataState } from '@/store/selectedModuleOptions'
import { useModulePlace } from '@/hooks/module/useModulePlace'
const Placement = forwardRef((props, refs) => {
const { getMessage } = useMessage()
const selectedModules = useRecoilValue(selectedModuleState)
const [isChidori, setIsChidori] = useState('false')
const [setupLocation, setSetupLocation] = useState('center')
const [isMaxSetup, setIsMaxSetup] = useState('false')
const { makeModuleInstArea } = useModuleBasicSetting()
const [selectedItems, setSelectedItems] = useState({})
const setCheckedModules = useSetRecoilState(checkedModuleState)
const { makeModuleInstArea } = useModuleBasicSetting()
const { selectedModules } = useModulePlace()
const setCheckedModules = useSetRecoilState(checkedModuleState)
const moduleSelectionData = useRecoilValue(moduleSelectionDataState)
//
@ -92,9 +94,9 @@ const Placement = forwardRef((props, refs) => {
</thead>
<tbody>
{selectedModules.itemList &&
selectedModules.itemList.map((item) => (
selectedModules.itemList.map((item, index) => (
<>
<tr>
<tr key={index}>
<td className="al-c">
<div className="d-check-box no-text pop">
<input type="checkbox" id={item.itemId} name={item.itemId} onChange={handleSelectedItem} />

View File

@ -12,14 +12,13 @@ import { get } from 'react-hook-form'
import { correntObjectNoState } from '@/store/settingAtom'
import { useRecoilValue } from 'recoil'
import { GlobalDataContext } from '@/app/GlobalDataProvider'
import { useRecoilState } from 'recoil'
import { modelState, pcsCheckState, powerConditionalState } from '@/store/circuitTrestleAtom'
import { POLYGON_TYPE } from '@/common/common'
import { useSwal } from '@/hooks/useSwal'
import { canvasState } from '@/store/canvasAtom'
import { selectedModuleState } from '@/store/selectedModuleOptions'
import { useTrestle } from '@/hooks/module/useTrestle'
const ALLOCATION_TYPE = {
AUTO: 'auto',

View File

@ -41,15 +41,35 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, set
hajebichi: useRef(null),
}
// (//)
const roofSizeSetArray = [
{ id: 'ra01', name: 'roofSizeSet', value: '1', message: 'modal.placement.initial.setting.size.roof' },
{ id: 'ra02', name: 'roofSizeSet', value: '2', message: 'modal.placement.initial.setting.size.actual' },
{ id: 'ra03', name: 'roofSizeSet', value: '3', message: 'modal.placement.initial.setting.size.none.pitch' },
]
// (/)
const roofAngleSetArray = [
{ id: 'ra04', name: 'roofAngleSet', value: 'slope', message: 'modal.placement.initial.setting.roof.pitch'},
{ id: 'ra05', name: 'roofAngleSet', value: 'flat', message: 'modal.placement.initial.setting.roof.angle' },
]
//
useEffect(() => {
if (!basicSetting || !currentRoof || Object.keys(currentRoof).length === 0 || Object.keys(basicSetting).length === 0) return
const raftCodeList = findCommonCode('203800')
setRaftCodes(raftCodeList)
setCurrentRoof({ ...currentRoof, roofSizeSet: basicSetting.roofMaterials.roofSizeSet, roofAngleSet: basicSetting.roofMaterials.roofAngleSet })
if (addedRoofs[0].roofAngleSet && addedRoofs[0].roofAngleSet?.length > 0) {
setCurrentRoof({ ...currentRoof, roofSizeSet: String(addedRoofs[0].roofSizeSet), roofAngleSet: addedRoofs[0].roofAngleSet })
} else if (basicSetting.roofAngleSet && basicSetting.roofAngleSet?.length > 0) {
setCurrentRoof({ ...currentRoof, roofSizeSet: String(basicSetting.roofSizeSet), roofAngleSet: basicSetting.roofAngleSet })
}
}, [])
useEffect(() => {
console.log('🚀 ~ fetchBasicSettings ~ currentRoof :', currentRoof)
if(!currentRoof) return
setBasicSettings({
...basicSetting,
roofSizeSet: String(currentRoof.roofSizeSet),
@ -110,10 +130,8 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, set
}
const newAddedRoofs = [...addedRoofs]
if (addedRoofs.length === 1) {
newAddedRoofs[0] = { ...roofInfo }
setAddedRoofs(newAddedRoofs)
}
newAddedRoofs[0] = { ...roofInfo }
setAddedRoofs(newAddedRoofs)
console.log('save Info', {
...basicSetting,
@ -178,74 +196,42 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, set
</div>
</th>
<td>
<div className="pop-form-radio">
<div className="d-check-radio pop">
<input
type="radio"
name="roofSizeSet"
id="ra01"
value="1" // roofSizeSet '1'
checked={currentRoof?.roofSizeSet === '1'} //
//onChange={(e) => setBasicSettings({ ...basicSetting, roofSizeSet: e.target.value })} //
onClick={() => handleRoofSizeSetChange('1')}
/>
<label htmlFor="ra01">{getMessage('modal.placement.initial.setting.size.roof')}</label>
</div>
<div className="d-check-radio pop">
<input
type="radio"
name="roofSizeSet"
id="ra02"
value="2" // roofSizeSet '2'
checked={currentRoof?.roofSizeSet === '2'} //
//onChange={(e) => setBasicSettings({ ...basicSetting, roofSizeSet: e.target.value })} //
onClick={() => handleRoofSizeSetChange('2')}
/>
<label htmlFor="ra02">{getMessage('modal.placement.initial.setting.size.actual')}</label>
</div>
<div className="d-check-radio pop">
<input
type="radio"
name="roofSizeSet"
id="ra03"
value="3" // roofSizeSet '3'
checked={currentRoof?.roofSizeSet === '3'} //
//onChange={(e) => setBasicSettings({ ...basicSetting, roofSizeSet: e.target.value })} //
onClick={() => handleRoofSizeSetChange('3')}
/>
<label htmlFor="ra03">{getMessage('modal.placement.initial.setting.size.none.pitch')}</label>
</div>
<div className="pop-form-radio">
{currentRoof &&
roofSizeSetArray.map((item) => (
<div className="d-check-radio pop" key={item.id}>
<input type="radio"
id={item.id}
name={item.name}
value={item.value}
checked={currentRoof?.roofSizeSet === item.value}
onChange={(e) => setCurrentRoof({ ...currentRoof, roofSizeSet: e.target.value })}
/>
<label htmlFor={item.id}>{getMessage(item.message)}</label>
</div>
))
}
</div>
</td>
</tr>
<tr>
<th>{getMessage('modal.placement.initial.setting.roof.angle.setting')}</th>
<td>
<div className="pop-form-radio">
<div className="d-check-radio pop">
<input
type="radio"
name="roofAngleSet"
id="ra04"
value="slope" //
checked={currentRoof?.roofAngleSet === 'slope'} //
//onChange={(e) => setBasicSettings({ ...basicSetting, roofAngleSet: e.target.value })} //
onClick={() => handleRoofAngleSetChange('slope')}
/>
<label htmlFor="ra04">{getMessage('modal.placement.initial.setting.roof.pitch')}</label>
</div>
<div className="d-check-radio pop">
<input
type="radio"
name="roofAngleSet"
id="ra05"
value="flat" //
checked={currentRoof?.roofAngleSet === 'flat'} //
//onChange={(e) => setBasicSettings({ ...basicSetting, roofAngleSet: e.target.value })} //
onClick={() => handleRoofAngleSetChange('flat')}
/>
<label htmlFor="ra05">{getMessage('modal.placement.initial.setting.roof.angle')}</label>
</div>
<div className="pop-form-radio">
{currentRoof &&
roofAngleSetArray.map((item) => (
<div className="d-check-radio pop" key={item.id}>
<input type="radio"
id={item.id}
name={item.name}
value={item.value}
checked={currentRoof?.roofAngleSet === item.value}
onChange={(e) => setCurrentRoof({ ...currentRoof, roofAngleSet: e.target.value })}
/>
<label htmlFor={item.id}>{getMessage(item.message)}</label>
</div>
))
}
</div>
</td>
</tr>
@ -264,7 +250,7 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, set
currentRoof?.roofSizeSet === '3' ? getMessage('modal.placement.initial.setting.size.none.pitch') : currentRoof?.roofMatlNm
}
ref={roofRef.roofCd}
options={roofMaterials.map((roof, index) => {
options={roofMaterials.map((roof) => {
return { ...roof, name: globalLocale === 'ko' ? roof.roofMatlNm : roof.roofMatlNmJp }
})}
value={currentRoof?.roofSizeSet === '3' ? null : currentRoof?.roofMatlCd}

View File

@ -19,6 +19,8 @@ import { globalLocaleStore } from '@/store/localeAtom'
import { stuffSearchState } from '@/store/stuffAtom'
import { QcastContext } from '@/app/QcastProvider'
import { usePopup } from '@/hooks/usePopup'
export const ToggleonMouse = (e, act, target) => {
const listWrap = e.target.closest(target)
const ListItem = Array.from(listWrap.childNodes)
@ -39,6 +41,8 @@ export default function Header(props) {
const [stuffSearch, setStuffSearch] = useRecoilState(stuffSearchState)
const { closeAll } = usePopup()
const { userSession } = props
const [sessionState, setSessionState] = useRecoilState(sessionStore)
const { getMessage } = useMessage()
@ -176,6 +180,9 @@ export default function Header(props) {
onClick={() => {
// moveHome()
removeStuffRecoil(menu)
if (pathName === '/') {
window.location.reload()
}
}}
>
{getMessage(menu.name)}
@ -226,6 +233,9 @@ export default function Header(props) {
...stuffSearch,
code: 'DELETE',
})
if (pathName === '/') {
window.location.reload()
}
}}
></Link>
</h1>
@ -239,6 +249,7 @@ export default function Header(props) {
href="#"
onClick={() => {
setUserInfoModal(true)
closeAll()
}}
>
<button className="profile">{userSession.userNm}</button>

View File

@ -272,7 +272,7 @@ export default function Stuff() {
stuffSearchParams.pageNo = stuffSearchParams.pageNo
async function fetchData() {
const apiUrl = `/api/object/list?saleStoreId=${session?.storeId}&${queryStringFormatter(stuffSearchParams)}`
const apiUrl = `/api/object/list?${queryStringFormatter(stuffSearchParams)}`
await get({ url: apiUrl }).then((res) => {
if (!isEmptyArray(res)) {
setGridProps({ ...gridProps, gridData: res, count: res[0].totCnt })
@ -302,7 +302,7 @@ export default function Stuff() {
setPageNo(1)
async function fetchData() {
const apiUrl = `/api/object/list?saleStoreId=${session?.storeId}&${queryStringFormatter(stuffSearchParams)}`
const apiUrl = `/api/object/list?${queryStringFormatter(stuffSearchParams)}`
await get({ url: apiUrl }).then((res) => {
if (!isEmptyArray(res)) {
setGridProps({ ...gridProps, gridData: res, count: res[0].totCnt })

View File

@ -10,7 +10,7 @@ import { globalLocaleStore } from '@/store/localeAtom'
import { isEmptyArray, isNotEmptyArray, isObjectNotEmpty, queryStringFormatter } from '@/util/common-utils'
import { useMessage } from '@/hooks/useMessage'
import { useForm } from 'react-hook-form'
import { useRecoilValue, useSetRecoilState, useResetRecoilState, useRecoilState } from 'recoil'
import { useRecoilValue, useSetRecoilState, useRecoilState } from 'recoil'
import { SessionContext } from '@/app/SessionProvider'
import FindAddressPop from './popup/FindAddressPop'
import PlanRequestPop from './popup/PlanRequestPop'
@ -19,18 +19,16 @@ import { useCommonCode } from '@/hooks/common/useCommonCode'
import StuffPlanQGrid from './StuffPlanQGrid'
import { floorPlanObjectState } from '@/store/floorPlanObjectAtom'
import { GlobalDataContext } from '@/app/GlobalDataProvider'
// import { ManagementContext } from '@/app/management/ManagementProvider'
import DocDownOptionPop from '../estimate/popup/DocDownOptionPop'
import { stuffSearchState } from '@/store/stuffAtom'
import { QcastContext } from '@/app/QcastProvider'
import { useCanvasMenu } from '@/hooks/common/useCanvasMenu'
import { useSwal } from '@/hooks/useSwal'
export default function StuffDetail() {
const [stuffSearch, setStuffSearch] = useRecoilState(stuffSearchState)
const { swalFire } = useSwal()
const { setIsGlobalLoading } = useContext(QcastContext)
const resetStuffRecoil = useResetRecoilState(stuffSearchState)
const stuffSearchParams = useRecoilValue(stuffSearchState)
const setFloorPlanObjectNo = useSetRecoilState(floorPlanObjectState) //
@ -41,6 +39,8 @@ export default function StuffDetail() {
const [showButton, setShowButton] = useState('') //, ,
const { setMenuNumber } = useCanvasMenu()
//
const { commonCode, findCommonCode } = useCommonCode()
const [selOptions, setSelOptions] = useState('') // 1
@ -123,13 +123,13 @@ export default function StuffDetail() {
field: 'planNo',
headerName: getMessage('stuff.detail.planGridHeader.planNo'),
width: 100,
cellStyle: { justifyContent: 'center', cursor: 'pointer' },
cellStyle: { alignItems: 'center', cursor: 'pointer' },
},
{
field: 'orderFlg',
headerName: getMessage('stuff.detail.planGridHeader.orderFlg'),
width: 80,
cellStyle: { justifyContent: 'center', cursor: 'pointer' },
cellStyle: { alignItems: 'center', cursor: 'pointer' },
cellRenderer: (params) => {
//1
let orderFlg
@ -142,31 +142,30 @@ export default function StuffDetail() {
headerName: getMessage('stuff.detail.planGridHeader.moduleModel'),
flex: 1,
wrapText: true,
cellStyle: { justifyContent: 'flex-start' /* 좌측정렬*/, cursor: 'pointer' },
// cellRenderer: (params) => {
// let origin = params.value
// console.log(':::', origin)
// if (origin !== null) {
// return (
// <>
// {origin?.split('').map((it, idx) => (
// <span key={idx} className="block">
// {it}
// <br />
// </span>
// ))}
// </>
// )
// } else {
// return null
// }
// },
cellStyle: { alignItems: 'flex-start' /* 좌측정렬*/, cursor: 'pointer' },
cellRenderer: (params) => {
let origin = params.value
if (origin !== null) {
return (
<>
{origin?.split('、').map((it, idx) => (
<span key={idx} className="block">
{it}
<br />
</span>
))}
</>
)
} else {
return null
}
},
},
{
field: 'capacity',
headerName: getMessage('stuff.detail.planGridHeader.capacity'),
width: 120,
cellStyle: { justifyContent: 'flex-end' /* 우측정렬*/, cursor: 'pointer' },
cellStyle: { alignItems: 'flex-end' /* 우측정렬*/, cursor: 'pointer' },
cellRenderer: (params) => {
let origin = params.value
let capacity
@ -185,7 +184,7 @@ export default function StuffDetail() {
width: 140,
wrapText: true,
autoHeight: true,
cellStyle: { justifyContent: 'flex-start' /* 좌측정렬*/, cursor: 'pointer' },
cellStyle: { alignItems: 'flex-start' /* 좌측정렬*/, cursor: 'pointer' },
cellRenderer: (params) => {
let origin = params.value
if (origin !== null) {
@ -209,7 +208,7 @@ export default function StuffDetail() {
headerName: getMessage('stuff.detail.planGridHeader.constructSpecificationMulti'),
wrapText: true,
autoHeight: true,
cellStyle: { justifyContent: 'flex-start' /* 좌측정렬*/, cursor: 'pointer' },
cellStyle: { alignItems: 'flex-start' /* 좌측정렬*/, cursor: 'pointer' },
cellRenderer: (params) => {
let origin = params.value
if (origin !== null) {
@ -233,7 +232,7 @@ export default function StuffDetail() {
headerName: getMessage('stuff.detail.planGridHeader.supportMethodIdMulti'),
wrapText: true,
autoHeight: true,
cellStyle: { justifyContent: 'flex-start' /* 좌측정렬*/, cursor: 'pointer' },
cellStyle: { alignItems: 'flex-start' /* 좌측정렬*/, cursor: 'pointer' },
cellRenderer: (params) => {
let origin = params.value
if (origin !== null) {
@ -258,7 +257,7 @@ export default function StuffDetail() {
flex: 1,
wrapText: true,
autoHeight: true,
cellStyle: { justifyContent: 'flex-start' /* 좌측정렬*/, cursor: 'pointer' },
cellStyle: { alignItems: 'flex-start' /* 좌측정렬*/, cursor: 'pointer' },
cellRenderer: (params) => {
let origin = params.value
if (origin !== null) {
@ -290,10 +289,10 @@ export default function StuffDetail() {
display: 'none',
}
}
if (managementState?.createUser === 'T01' && session?.userId !== 'T01') {
//createUser T01 T01 !!!!!!!!
//buttonStyle = { display: 'none' }
}
// if (managementState?.createUser === 'T01' && session?.userId !== 'T01') {
//createUser T01 T01 !!!!!!!!
//buttonStyle = { display: 'none' }
// }
return (
<>
<div className="grid-cell-btn">
@ -649,7 +648,8 @@ export default function StuffDetail() {
// saltAreaFlg 1 true
form.setValue('saltAreaFlg', managementState.saltAreaFlg === '1' ? true : false)
//
form.setValue('installHeight', managementState.installHeight)
let installHeight = managementState.installHeight ? managementState.installHeight.split('.')[0] : '0'
form.setValue('installHeight', installHeight)
// null 0
if (managementState.conType === null) {
form.setValue('conType', '0')
@ -983,7 +983,10 @@ export default function StuffDetail() {
} else {
form.setValue('saltAreaFlg', false)
}
form.setValue('installHeight', info.installHeight)
let installHeight = info.installHeight ? info.installHeight.split('.')[0] : '0'
form.setValue('installHeight', installHeight)
form.setValue('remarks', info.remarks)
if (info.saleStoreLevel === '1') {
@ -1582,16 +1585,20 @@ export default function StuffDetail() {
)
}
//
//
const getCellDoubleClicked = (params) => {
if (params.data.estimateDate != null) {
if (params?.column?.colId !== 'estimateDate') {
if (params?.data?.planNo && params?.data?.objectNo) {
setIsGlobalLoading(true)
let objectNo = params?.data?.objectNo
let planNo = params?.data?.planNo
router.push(`/floor-plan/estimate/5?pid=${planNo}&objectNo=${objectNo}`)
if (params?.column?.colId !== 'estimateDate') {
if (params?.data?.planNo && params?.data?.objectNo) {
let objectNo = params?.data?.objectNo
let planNo = params?.data?.planNo
const param = {
pid: planNo,
objectNo: objectNo,
}
setMenuNumber(null)
const url = `/floor-plan?${queryStringFormatter(param)}`
router.push(url)
}
}
}

View File

@ -705,6 +705,18 @@ export default function StuffSearchCondition() {
stuffSearch.pageSize = 100
setSchSelSaleStoreId('')
setOtherSaleStoreId('')
} else {
setStartDate(stuffSearch?.schFromDt ? stuffSearch.schFromDt : dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD'))
setEndDate(stuffSearch?.schToDt ? stuffSearch.schToDt : dayjs(new Date()).format('YYYY-MM-DD'))
setObjectNo(stuffSearch.schObjectNo ? stuffSearch.schObjectNo : objectNo)
setSaleStoreName(stuffSearch.schSaleStoreName ? stuffSearch.schSaleStoreName : saleStoreName)
setAddress(stuffSearch.schAddress ? stuffSearch.schAddress : address)
setobjectName(stuffSearch.schObjectName ? stuffSearch.schObjectName : objectName)
setDispCompanyName(stuffSearch.schDispCompanyName ? stuffSearch.schDispCompanyName : dispCompanyName)
setReceiveUser(stuffSearch.schReceiveUser ? stuffSearch.schReceiveUser : receiveUser)
setDateType(stuffSearch.schDateType ? stuffSearch.schDateType : dateType)
setTempFlg(stuffSearch.schTempFlg ? stuffSearch.schTempFlg : tempFlg)
setMyDataCheck(stuffSearch.schMyDataCheck)
}
} else {
setStartDate(stuffSearch?.schFromDt ? stuffSearch.schFromDt : dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD'))

View File

@ -12,9 +12,9 @@ import { QcastContext } from '@/app/QcastProvider'
import { useMessage } from '@/hooks/useMessage'
import { floorPlanObjectState } from '@/store/floorPlanObjectAtom'
import { isObjectNotEmpty, queryStringFormatter } from '@/util/common-utils'
import { useCanvasMenu } from '@/hooks/common/useCanvasMenu'
import { GlobalDataContext } from '@/app/GlobalDataProvider'
// import { ManagementContext } from '@/app/management/ManagementProvider'
import { SessionContext } from '@/app/SessionProvider'
export default function StuffSubHeader({ type }) {
@ -29,6 +29,9 @@ export default function StuffSubHeader({ type }) {
const { managementState } = useContext(GlobalDataContext)
const [buttonStyle, setButtonStyle] = useState('')
const { setMenuNumber } = useCanvasMenu()
useEffect(() => {
window.scrollTo(0, 0)
}, [])
@ -55,6 +58,8 @@ export default function StuffSubHeader({ type }) {
pid: '1',
objectNo: objectNo,
}
setMenuNumber(null)
const url = `/floor-plan?${queryStringFormatter(param)}`
router.push(url)

View File

@ -169,13 +169,13 @@ export default function PlanRequestPop(props) {
gridData: [],
isPageable: false,
gridColumns: [
// {
// field: 'planStatName',
// headerName: getMessage('stuff.planReqPopup.gridHeader.planStatName'),
// minWidth: 150,
// checkboxSelection: true,
// showDisabledCheckboxes: true,
// },
{
field: '',
headerName: '',
minWidth: 50,
checkboxSelection: true,
showDisabledCheckboxes: true,
},
{
field: 'planReqNo',
headerName: getMessage('stuff.planReqPopup.gridHeader.planReqNo'),

View File

@ -107,10 +107,8 @@ export function useMasterController() {
* @returns
*/
const getTrestleDetailList = async (params) => {
const paramString = getQueryString(params)
console.log('🚀🚀 ~ getTrestleDetailList ~ paramString:', paramString)
return await get({ url: '/api/v1/master/getTrestleDetailList' + paramString }).then((res) => {
console.log('🚀🚀 ~ getTrestleDetailList ~ res:', res)
// const paramString = getQueryString(params)
return await post({ url: '/api/v1/master/getTrestleDetailList', data: params }).then((res) => {
return res
})
}

View File

@ -6,7 +6,7 @@ import { estimateState, floorPlanObjectState } from '@/store/floorPlanObjectAtom
import { isObjectNotEmpty, isEmptyArray } from '@/util/common-utils'
import { SessionContext } from '@/app/SessionProvider'
import { useMessage } from '@/hooks/useMessage'
import { useRouter } from 'next/navigation'
import { useRouter, useSearchParams } from 'next/navigation'
import { FloorPlanContext } from '@/app/floor-plan/FloorPlanProvider'
import { QcastContext } from '@/app/QcastProvider'
import { useSwal } from '@/hooks/useSwal'
@ -35,11 +35,14 @@ export const useEstimateController = (planNo) => {
const [isLoading, setIsLoading] = useState(false)
const { estimateContextState, setEstimateContextState } = useContext(FloorPlanContext)
const searchParams = useSearchParams()
useEffect(() => {
if (planNo && !isLoading) {
if (objectRecoil.floorPlanObjectNo && planNo) {
fetchSetting(objectRecoil.floorPlanObjectNo, planNo)
//견적서 화면에서 새로고침시 리코일 사라지는 현상 대응
let recoilObjectNo = objectRecoil.floorPlanObjectNo ? objectRecoil.floorPlanObjectNo : searchParams.get('objectNo')
if (recoilObjectNo && planNo) {
fetchSetting(recoilObjectNo, planNo)
}
}
}, [])
@ -85,6 +88,8 @@ export const useEstimateController = (planNo) => {
setIsGlobalLoading(false)
} catch (error) {
console.error('견적서 상세조회 Error: ', error)
swalFire({ text: getMessage('estimate.menu.move.valid1') })
setIsLoading(true)
setIsGlobalLoading(false)
}

View File

@ -129,6 +129,8 @@ export function useModuleBasicSetting() {
//설치 범위 지정 클릭 이벤트
const toggleSelection = (setupSurface) => {
console.log('setupSurface', setupSurface)
const isExist = selectedModuleInstSurfaceArray.some((obj) => obj.parentId === setupSurface.parentId)
//최초 선택일때
if (!isExist) {
@ -1122,7 +1124,7 @@ export function useModuleBasicSetting() {
}
})
// moduleSetupSurface.set({ modules: moduleSetupArray })
moduleSetupSurface.set({ modules: moduleSetupArray })
// const moduleArray = [...moduleIsSetup]
// moduleArray.push({
@ -1134,247 +1136,6 @@ export function useModuleBasicSetting() {
// calculateForApi()
}
const calculateForApi = () => {
const moduleSufaces = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE)
const results = []
moduleSufaces.forEach((moduleSurface) => {
const centerPoints = []
const direction = moduleSurface.direction
const modules = moduleSurface.modules
modules.forEach((module, index) => {
module.tempIndex = index
const { x, y } = module.getCenterPoint()
const { width, height } = module
centerPoints.push({ x, y, width: Math.floor(width), height: Math.floor(height), index })
})
if (centerPoints.length === 0) return
//완전 노출 하면
let exposedBottom = 0
// 반 노출 하면
let exposedHalfBottom = 0
// 완전 노출 상면
let exposedTop = 0
//반 노출 상면
let exposedHalfTop = 0
// 완전 접면
let touchDimension = 0
//반접면
let halfTouchDimension = 0
// 노출하면 체크
centerPoints.forEach((centerPoint, index) => {
const { x, y, width, height } = centerPoint
// centerPoints중에 현재 centerPoint와 x값이 같고, y값이 y-height값과 같은 centerPoint가 있는지 확인
let bottomCell
let bottomLeftPoint
let bottomRightPoint
let leftBottomCnt
let rightBottomCnt
switch (direction) {
case 'south':
bottomCell = centerPoints.filter((centerPoint) => Math.abs(centerPoint.x - x) < 2 && Math.abs(centerPoint.y - (y + height)) < 2)
bottomLeftPoint = { x: x - width / 2, y: y + height }
bottomRightPoint = { x: x + width / 2, y: y + height }
break
case 'north':
bottomCell = centerPoints.filter((centerPoint) => Math.abs(centerPoint.x - x) < 2 && Math.abs(centerPoint.y - (y - height)) < 2)
bottomLeftPoint = { x: x + width / 2, y: y - height }
bottomRightPoint = { x: x - width / 2, y: y - height }
break
case 'east':
bottomCell = centerPoints.filter((centerPoint) => Math.abs(centerPoint.x - (x - width)) < 2 && Math.abs(centerPoint.y - y) < 2)
bottomLeftPoint = { x: x + width, y: y + height / 2 }
bottomRightPoint = { x: x + width, y: y - height / 2 }
break
case 'west':
bottomCell = centerPoints.filter((centerPoint) => Math.abs(centerPoint.x - (x + width)) < 2 && Math.abs(centerPoint.y - y) < 2)
bottomLeftPoint = { x: x - width, y: y - height / 2 }
bottomRightPoint = { x: x - width, y: y + height / 2 }
break
}
if (bottomCell.length === 1) {
return
}
// 바로 아래에 셀이 없는 경우 물떼세 배치가 왼쪽 되어있는 셀을 찾는다.
leftBottomCnt = centerPoints.filter(
(centerPoint) => Math.abs(centerPoint.x - bottomLeftPoint.x) < 2 && Math.abs(centerPoint.y - bottomLeftPoint.y) < 2,
).length
rightBottomCnt = centerPoints.filter(
(centerPoint) => Math.abs(centerPoint.x - bottomRightPoint.x) < 2 && Math.abs(centerPoint.y - bottomRightPoint.y) < 2,
).length
if (leftBottomCnt + rightBottomCnt === 1) {
exposedHalfBottom++
return
}
})
// 노출상면 체크
centerPoints.forEach((centerPoint, index) => {
const { x, y, width, height } = centerPoint
let topCell
let topLeftPoint
let topRightPoint
let leftTopCnt
let rightTopCnt
switch (direction) {
case 'south':
topCell = centerPoints.filter((centerPoint) => Math.abs(centerPoint.x - x) < 2 && Math.abs(centerPoint.y - (y - height)) < 2)
topLeftPoint = { x: x - width / 2, y: y - height }
topRightPoint = { x: x + width / 2, y: y - height }
break
case 'north':
topCell = centerPoints.filter((centerPoint) => Math.abs(centerPoint.x - x) < 2 && Math.abs(centerPoint.y - (y + height)) < 2)
topLeftPoint = { x: x + width / 2, y: y + height }
topRightPoint = { x: x - width / 2, y: y + height }
break
case 'east':
topCell = centerPoints.filter((centerPoint) => Math.abs(centerPoint.x - (x - width)) < 2 && Math.abs(centerPoint.y - y) < 2)
topLeftPoint = { x: x - width, y: y + height / 2 }
topRightPoint = { x: x - width, y: y - height / 2 }
break
case 'west':
topCell = centerPoints.filter((centerPoint) => Math.abs(centerPoint.x - (x + width)) < 2 && Math.abs(centerPoint.y - y) < 2)
topLeftPoint = { x: x + width, y: y - height / 2 }
topRightPoint = { x: x + width, y: y + height / 2 }
break
}
if (topCell.length === 1) {
touchDimension++
return
}
leftTopCnt = centerPoints.filter(
(centerPoint) => Math.abs(centerPoint.x - topLeftPoint.x) < 2 && Math.abs(centerPoint.y - topLeftPoint.y) < 2,
).length
rightTopCnt = centerPoints.filter(
(centerPoint) => Math.abs(centerPoint.x - topRightPoint.x) < 2 && Math.abs(centerPoint.y - topRightPoint.y) < 2,
).length
if (leftTopCnt + rightTopCnt === 2) {
touchDimension++
return
}
if (leftTopCnt + rightTopCnt === 1) {
exposedHalfTop++
halfTouchDimension++
return
}
if (leftTopCnt + rightTopCnt === 0) {
exposedTop++
}
})
// 완전 노출 하면 계산
/*const cells = canvas.getObjects().filter((obj) => polygon.id === obj.parentId)
const points = cells.map((cell) => {
return cell.getCenterPoint()
})*/
const groupPoints = groupCoordinates(centerPoints, modules[0], direction)
groupPoints.forEach((group) => {
let maxY = group.reduce((acc, cur) => (acc.y > cur.y ? acc : cur)).y
let minY = group.reduce((acc, cur) => (acc.y < cur.y ? acc : cur)).y
let maxX = group.reduce((acc, cur) => (acc.x > cur.x ? acc : cur)).x
let minX = group.reduce((acc, cur) => (acc.x < cur.x ? acc : cur)).x
let maxYCenterPoint = group.filter((centerPoint) => Math.abs(centerPoint.y - maxY) < 2)
let minYCenterPoint = group.filter((centerPoint) => Math.abs(centerPoint.y - minY) < 2)
let maxXCenterPoint = group.filter((centerPoint) => Math.abs(centerPoint.x - maxX) < 2)
let minXCenterPoint = group.filter((centerPoint) => Math.abs(centerPoint.x - minX) < 2)
switch (direction) {
case 'south':
exposedBottom += maxYCenterPoint.length
break
case 'north':
exposedBottom += minYCenterPoint.length
break
case 'east':
exposedBottom += maxXCenterPoint.length
break
case 'west':
exposedBottom += minXCenterPoint.length
break
}
})
results.push({
exposedBottom,
exposedHalfBottom,
exposedTop,
exposedHalfTop,
touchDimension,
halfTouchDimension,
})
console.log({
direction,
exposedBottom,
exposedHalfBottom,
exposedTop,
exposedHalfTop,
touchDimension,
halfTouchDimension,
})
})
return results
}
// polygon 내부 cell들의 centerPoint 배열을 그룹화 해서 반환
const groupCoordinates = (points, moduleExample, direction) => {
const groups = []
const visited = new Set()
const width = Math.floor(moduleExample.width)
const height = Math.floor(moduleExample.height)
const horizonPadding = 0 // 가로 패딩
const verticalPadding = 0 // 세로 패딩
function isAdjacent(p1, p2) {
const dx = Math.abs(p1.x - p2.x)
const dy = Math.abs(p1.y - p2.y)
return (
(Math.abs(width + horizonPadding - dx) < 2 && dy < 2) ||
(dx < 2 && Math.abs(dy - height + verticalPadding)) < 2 ||
(Math.abs(dx - width / 2 + horizonPadding / 2) < 2 && Math.abs(dy - height + verticalPadding) < 2)
)
}
function dfs(point, group) {
visited.add(`${point.x},${point.y}`)
group.push(point)
for (const nextPoint of points) {
const key = `${nextPoint.x},${nextPoint.y}`
if (!visited.has(key) && isAdjacent(point, nextPoint)) {
dfs(nextPoint, group)
}
}
}
for (const point of points) {
const key = `${point.x},${point.y}`
if (!visited.has(key)) {
const group = []
dfs(point, group)
groups.push(group)
}
}
return groups
}
const coordToTurfPolygon = (points) => {
const coordinates = points.map((point) => [point.x, point.y])
coordinates.push(coordinates[0])

View File

@ -1,184 +1,84 @@
import { useEffect, useState } from 'react'
import { useRecoilState, useRecoilValue } from 'recoil'
import { useContext, useEffect, useState } from 'react'
import { GlobalDataContext } from '@/app/GlobalDataProvider'
import { moduleSelectionDataState, selectedModuleState } from '@/store/selectedModuleOptions'
import { useMasterController } from '@/hooks/common/useMasterController'
import { useCommonCode } from '@/hooks/common/useCommonCode'
import { canvasState } from '@/store/canvasAtom'
import { selectedModuleState, moduleSelectionInitParamsState } from '@/store/selectedModuleOptions'
import { pitchSelector } from '@/store/canvasAtom'
export function useModuleSelection(props) {
const { managementState, setManagementState, managementStateLoaded } = useContext(GlobalDataContext)
const globalPitch = useRecoilValue(pitchSelector) //피치
const [roughnessCodes, setRoughnessCodes] = useState([]) //면조도 목록
const [windSpeedCodes, setWindSpeedCodes] = useState([]) //기준풍속 목록
const [moduleList, setModuleList] = useState([{}]) //모듈 목록
const [selectedModules, setSelectedModules] = useRecoilState(selectedModuleState) //선택된 모듈
const [selectedSurfaceType, setSelectedSurfaceType] = useState({}) //선택된 면조도
const [installHeight, setInstallHeight] = useState('0') //설치 높이
const [standardWindSpeed, setStandardWindSpeed] = useState({}) //기준풍속
const [verticalSnowCover, setVerticalSnowCover] = useState('0') //수직적설량
const [moduleSelectionInitParams, setModuleSelectionInitParams] = useRecoilState(moduleSelectionInitParamsState) //모듈 기본 데이터 ex) 면조도, 높이등등
const { getModuleTypeItemList } = useMasterController()
const { findCommonCode } = useCommonCode()
//탭별 파라메터 초기화
useEffect(() => {
setInstallHeight(managementState?.installHeight)
setStandardWindSpeed(managementState?.standardWindSpeedId)
setVerticalSnowCover(managementState?.verticalSnowCover)
setSelectedSurfaceType(managementState?.surfaceType)
const initParams = {
illuminationTp: managementState?.surfaceTypeValue, //면조도
instHt: managementState?.installHeight, //설치높이
stdWindSpeed: managementState?.standardWindSpeedId, //기준풍속
stdSnowLd: managementState?.verticalSnowCover, //기준적설량
inclCd: globalPitch,
}
setModuleSelectionInitParams(initParams)
}, [managementState])
export function useModulePlace() {
const canvas = useRecoilValue(canvasState)
const moduleSelectionData = useRecoilValue(moduleSelectionDataState) //다음으로 넘어가는 최종 데이터
const [trestleDetailParams, setTrestleDetailParams] = useState([])
const [trestleDetailList, setTrestleDetailList] = useState([])
const selectedModules = useRecoilValue(selectedModuleState)
const { getTrestleDetailList } = useMasterController()
useEffect(() => {
// 113700 면조도
const roughnessCodeList = findCommonCode('113700')
roughnessCodeList.forEach((obj) => {
obj.name = obj.clCodeNm
obj.id = obj.clCode
})
setRoughnessCodes(roughnessCodeList)
// 202000 풍속
const windCodeList = findCommonCode('202000')
windCodeList.forEach((obj) => {
obj.name = obj.clCodeNm
obj.id = obj.clCode
})
setWindSpeedCodes(windCodeList)
//지붕재 선택
const roofsIds = props.addedRoofs.filter((obj) => obj.roofMatlCd).map((obj) => obj.roofMatlCd)
if (roofsIds.length === 0) {
return
}
//새로고침시 데이터 날아가는거 방지
if (!managementState) {
setManagementState(managementStateLoaded)
}
getModuleData(roofsIds)
console.log('🚀 ~ useModulePlace ~ moduleSelectionData:', moduleSelectionData)
}, [])
const getModuleData = async (roofsIds) => {
const list = await getModuleTypeItemList(roofsIds)
//selectbox에 이름을 넣는다
list.data.forEach((item) => {
item.name = item.itemNm
useEffect(() => {
const common = moduleSelectionData.common
const roofConstructions = moduleSelectionData.roofConstructions
const listParams = roofConstructions.map((item) => {
return {
...common,
moduleTpCd: selectedModules.itemTp,
roofMatlCd: item.trestle.roofMatlCd,
trestleMkrCd: item.trestle.trestleMkrCd,
constMthdCd: item.trestle.constMthdCd,
roofBaseCd: item.trestle.roofBaseCd,
constTp: item.construction.constTp,
mixMatlNo: selectedModules.mixMatlNo,
roofPitch: selectedModules.roofPchBase ? selectedModules.roofPchBase : null,
inclCd: String(item.addRoof.pitch),
roofIndex: item.addRoof.index,
}
})
//셀렉트박스 데이터 초기화
setModuleList(list.data)
}
const handleChangeModule = (option) => {
//선택된 모듈
setSelectedModules(option) //선택값 저장
setTrestleDetailParams(listParams)
}, [moduleSelectionData])
//init 데이터에 선택된 모듈 추가
setModuleSelectionInitParams({
...moduleSelectionInitParams,
moduleTpCd: option.itemTp,
})
}
const handleChangeSurfaceType = (option) => {
setModuleSelectionInitParams({
...moduleSelectionInitParams,
illuminationTp: option.clCode,
})
}
const handleChangeWindSpeed = (option) => {
setModuleSelectionInitParams({
...moduleSelectionInitParams,
surfaceType: option.clCode,
})
}
const handleChangeInstallHeight = (option) => {
setInstallHeight(option)
setModuleSelectionInitParams({
...moduleSelectionInitParams,
instHt: option,
})
}
const handleChangeVerticalSnowCover = (option) => {
setVerticalSnowCover(option)
setModuleSelectionInitParams({
...moduleSelectionInitParams,
stdSnowLd: option,
})
const getTrestleDetailListData = async () => {
const trestleDetailList = await getTrestleDetailList(trestleDetailParams)
setTrestleDetailList(trestleDetailList)
}
useEffect(() => {
console.log('installHeight', installHeight)
}, [installHeight])
if (trestleDetailParams.length > 0) {
getTrestleDetailListData(trestleDetailParams)
}
}, [trestleDetailParams])
useEffect(() => {
console.log('verticalSnowCover', verticalSnowCover)
}, [verticalSnowCover])
console.log('🚀 ~ useModulePlace ~ trestleDetailList:', trestleDetailList)
//TODO: 설치높이, 기준적설량 debounce 적용해서 추가해야됨
//지붕을 가져옴
canvas
.getObjects()
.filter((roof) => roof.name === 'roof')
.forEach((roof) => {
const roofIndex = roof.roofMaterial.index //지붕의 지붕재의 순번
trestleDetailList.forEach((detail) => {
if (Number(detail.data.roofIndex) === roofIndex) {
//roof에 상세 데이터 추가
roof.set({ trestleDetail: detail.data })
// useEffect(() => {
// getConstructionListData(constructionListParams)
// }, [constructionListParams])
//surface에 상세 데이터 추가
canvas
.getObjects()
.filter((surface) => surface.name === 'moduleSetupSurface' && surface.parentId === roof.id)
.forEach((surface) => {
surface.set({ trestleDetail: detail.data })
})
}
})
// const getConstructionListData = async (params) => {
// if (params.trestleMkrCd && params.constMthdCd && params.roofBaseCd) {
// const optionsList = await getConstructionList(params)
// console.log('optionsList', optionsList)
// setConstructionList(optionsList.data)
// }
// }
//state 배열에 데이터 추가 함수
// const addRoofTabParams = (key, value, excludeArray = []) => {
// const index = roofTabParams.findIndex((obj) => obj.roofTab === roofTab)
// if (index !== -1) {
// roofTabParams[index][key] = value
// if (excludeArray.length > 0) {
// excludeArray.forEach((exclude) => {
// roofTabParams[index][exclude] = ''
// })
// }
// setRoofTabParams((prev) => [...prev.slice(0, index), roofTabParams[index], ...prev.slice(index + 1)])
// }
// }
console.log('roof', roof)
})
}, [trestleDetailList])
return {
moduleSelectionInitParams,
selectedModules,
roughnessCodes,
windSpeedCodes,
managementState,
moduleList,
selectedSurfaceType,
installHeight,
standardWindSpeed,
verticalSnowCover,
handleChangeModule,
handleChangeSurfaceType,
handleChangeWindSpeed,
handleChangeInstallHeight,
handleChangeVerticalSnowCover,
}
}

View File

@ -38,7 +38,6 @@ export function useModuleSelection(props) {
instHt: managementState?.installHeight, //설치높이
stdWindSpeed: managementState?.standardWindSpeedId, //기준풍속
stdSnowLd: managementState?.verticalSnowCover, //기준적설량
inclCd: globalPitch,
}
setModuleSelectionInitParams(initParams)
}, [managementState])
@ -93,6 +92,7 @@ export function useModuleSelection(props) {
setModuleSelectionInitParams({
...moduleSelectionInitParams,
moduleTpCd: option.itemTp,
moduleItemId: option.itemId,
})
}
@ -129,11 +129,11 @@ export function useModuleSelection(props) {
}
useEffect(() => {
console.log('installHeight', installHeight)
// console.log('installHeight', installHeight)
}, [installHeight])
useEffect(() => {
console.log('verticalSnowCover', verticalSnowCover)
// console.log('verticalSnowCover', verticalSnowCover)
}, [verticalSnowCover])
//TODO: 설치높이, 기준적설량 debounce 적용해서 추가해야됨

View File

@ -0,0 +1,988 @@
import { useRecoilValue } from 'recoil'
import { canvasState } from '@/store/canvasAtom'
import { POLYGON_TYPE } from '@/common/common'
import { moduleSelectionDataState } from '@/store/selectedModuleOptions'
// 회로 및 가대설정
export const useTrestle = () => {
const canvas = useRecoilValue(canvasState)
const moduleSelectionData = useRecoilValue(moduleSelectionDataState) //다음으로 넘어가는 최종 데이터
const apply = () => {
//처마력바가 체크되어 있는 경우 exposedBottomPoints를 이용해 처마력바 그려줘야함.
// exposedBottomPoints는 노출 최하면 들의 centerPoint 배열.
const surfaces = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE)
// 기존 eaveBar를 삭제
canvas.getObjects().forEach((obj) => {
if (obj.name === 'eaveBar' || obj.name === 'rack') {
canvas.remove(obj)
}
})
surfaces.forEach((surface) => {
const parent = canvas.getObjects().find((obj) => obj.id === surface.parentId)
const roofMaterialIndex = parent.roofMaterial.index
const construction = moduleSelectionData.roofConstructions.find((construction) => construction.roofIndex === roofMaterialIndex).construction
let isEaveBar = construction.setupCover
let isSnowGuard = construction.setupSnowCover
const direction = parent.direction
const rack = surface.trestleDetail.rack
let { rackQty, rackIntvlPct } = surface.trestleDetail
const rackInfos = Object.keys(rack).map((key) => {
return { key, value: rack[key] }
})
const result = calculateForApi(surface)
const centerPoints = result.centerPoints
const exposedBottomModules = [] // 아래 두면이 모두 노출 되어있는 경우
const leftExposedHalfBottomModules = [] // 왼쪽 면만 노출되어있는 경우
const rightExposedHalfBottomPoints = [] // 오른쪽 면만 노출되어 있는 경우
const modules = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE)
modules.forEach((module) => {
const { x, y } = module.getCenterPoint()
const isExposedBottom = result.exposedBottomPoints.some((point) => Math.abs(point.x - x) < 2 && Math.abs(point.y - y) < 2)
const isLeftExposedHalfBottom = result.leftExposedHalfBottomPoints.some((point) => Math.abs(point.x - x) < 2 && Math.abs(point.y - y) < 2)
const isRightExposedHalfBottom = result.rightExposedHalfBottomPoints.some((point) => Math.abs(point.x - x) < 2 && Math.abs(point.y - y) < 2)
if (isExposedBottom) {
exposedBottomModules.push(module)
}
if (isLeftExposedHalfBottom) {
leftExposedHalfBottomModules.push(module)
}
if (isRightExposedHalfBottom) {
rightExposedHalfBottomPoints.push(module)
}
})
canvas
.getObjects()
.filter((obj) => obj.name === 'eaveBar')
.forEach((obj) => {
canvas.remove(obj)
})
if (isEaveBar) {
// 처마력바설치 true인 경우 설치
exposedBottomModules.forEach((module) => {
//TODO : 방향별로 처마력바 설치해야함
const bottomPoints = findTopTwoPoints([...module.points], direction)
if (!bottomPoints) return
const eaveBar = new fabric.Line([bottomPoints[0].x, bottomPoints[0].y, bottomPoints[1].x, bottomPoints[1].y], {
name: 'eaveBar',
stroke: 'blue',
strokeWidth: 4,
selectable: false,
})
canvas.add(eaveBar)
canvas.renderAll()
})
}
// 가대 설치를 위한 가장 아래 모듈로부터 위로 몇단인지 계산
// 오른쪽,왼쪽 둘 다 아래에 아무것도 없는, 처마 커버를 필요로 하는 모듈
exposedBottomModules.forEach((module) => {
const { width, height } = module
let { x: startX, y: startY } = { ...module.getCenterPoint() }
let { x, y } = { ...module.getCenterPoint() }
//TODO : 방향별로 가대 설치해야함
let leftRows = 1
let rightRows = 1
let centerRows = 1
let hasNextModule = true
let findLeft = true
let findRight = true
//우선 절반을 나눈 뒤 왼쪽부터 찾는다.
while (hasNextModule) {
//바로 위에 있는지 확인한다.
let nextModule = findNextModule({ x, y, width, height }, centerPoints, direction)
if (nextModule) {
// 바로 위 모듈을 찾는다.
leftRows++
x = nextModule.x
y = nextModule.y
} else {
// 바로 위가 없을 경우 먼저 왼쪽위가 있는지 확인 한다.
if (findLeft) {
nextModule = findNextLeftModule({ x, y, width, height }, centerPoints, direction)
findLeft = false
} else {
nextModule = findNextRightModule({ x, y, width, height }, centerPoints, direction)
findLeft = true
}
if (nextModule) {
// 바로 위 모듈을 찾는다.
leftRows++
x = nextModule.x
y = nextModule.y
} else {
hasNextModule = false
}
}
}
hasNextModule = true
x = startX
y = startY
// 오른쪽 찾는다.
while (hasNextModule) {
//바로 위에 있는지 확인한다.
let nextModule = findNextModule({ x, y, width, height }, centerPoints, direction)
if (nextModule) {
// 바로 위 모듈을 찾는다.
rightRows++
x = nextModule.x
y = nextModule.y
} else {
// 바로 위가 없을 경우 먼저 왼쪽위가 있는지 확인 한다.
if (findRight) {
nextModule = findNextRightModule({ x, y, width, height }, centerPoints, direction)
findRight = false
} else {
nextModule = findNextLeftModule({ x, y, width, height }, centerPoints, direction)
findRight = true
}
if (nextModule) {
// 바로 위 모듈을 찾는다.
rightRows++
x = nextModule.x
y = nextModule.y
} else {
hasNextModule = false
}
}
}
hasNextModule = true
x = startX
y = startY
// 센터 찾는다.
while (hasNextModule) {
//바로 위에 있는지 확인한다.
let nextModule = findNextModule({ x, y, width, height }, centerPoints, direction)
if (nextModule) {
// 바로 위 모듈을 찾는다.
centerRows++
x = nextModule.x
y = nextModule.y
} else {
hasNextModule = false
}
}
// 모듈의 왼쪽 부터 그릴 랙 정보를 가져온다.
const leftRacks = rackInfos.find((rack) => {
return rack.value.moduleRows === leftRows
})?.value.racks
// 모듈의 오른쪽 부터 그릴 랙 정보를 가져온다.
const rightRacks = rackInfos.find((rack) => {
return rack.value.moduleRows === rightRows
})?.value.racks
// 해당 rack으로 그려준다.
const centerRacks = rackInfos.find((rack) => {
return rack.value.moduleRows === centerRows
})?.value.racks
drawRacks(leftRacks, rackQty, rackIntvlPct, module, direction, 'L')
drawRacks(rightRacks, rackQty, rackIntvlPct, module, direction, 'R')
if (rackQty === 3) {
//rack 갯수가 3개인 경우는 중간렉도 추가해줘야함
drawRacks(centerRacks, rackQty, rackIntvlPct, module, direction, 'C')
}
})
// 왼쪽아래에 모듈이 없는 모듈들
leftExposedHalfBottomModules.forEach((module) => {
const { width, height } = module
let { x: startX, y: startY } = { ...module.getCenterPoint() }
let { x, y } = { ...module.getCenterPoint() }
//TODO : 방향별로 가대 설치해야함
let leftRows = 1
let hasNextModule = true
let findLeft = true
//우선 절반을 나눈 뒤 왼쪽부터 찾는다.
while (hasNextModule) {
//바로 위에 있는지 확인한다.
let nextModule = findNextModule({ x, y, width, height }, centerPoints, direction)
if (nextModule) {
// 바로 위 모듈을 찾는다.
leftRows++
x = nextModule.x
y = nextModule.y
} else {
// 바로 위가 없을 경우 먼저 왼쪽위가 있는지 확인 한다.
if (findLeft) {
nextModule = findNextLeftModule({ x, y, width, height }, centerPoints, direction)
findLeft = false
} else {
nextModule = nextModule = findNextRightModule({ x, y, width, height }, centerPoints, direction)
findLeft = true
}
if (nextModule) {
// 바로 위 모듈을 찾는다.
leftRows++
x = nextModule.x
y = nextModule.y
} else {
hasNextModule = false
}
}
}
// 모듈의 왼쪽 부터 그릴 랙 정보를 가져온다.
const leftRacks = rackInfos.find((rack) => {
return rack.value.moduleRows === leftRows
})?.value.racks
drawRacks(leftRacks, rackQty, rackIntvlPct, module, direction, 'L')
})
// 오른쪽 아래에 모듈이 없는 모듈들
rightExposedHalfBottomPoints.forEach((module) => {
const { width, height } = module
let { x: startX, y: startY } = { ...module.getCenterPoint() }
let { x, y } = { ...module.getCenterPoint() }
//TODO : 방향별로 가대 설치해야함
let rightRows = 1
let hasNextModule = true
let findRight = true
// 오른쪽 찾는다.
while (hasNextModule) {
//바로 위에 있는지 확인한다.
let nextModule = findNextModule({ x, y, width, height }, centerPoints, direction)
if (nextModule) {
// 바로 위 모듈을 찾는다.
rightRows++
x = nextModule.x
y = nextModule.y
} else {
// 바로 위가 없을 경우 먼저 왼쪽위가 있는지 확인 한다.
if (findRight) {
nextModule = findNextRightModule({ x, y, width, height }, centerPoints, direction)
findRight = false
} else {
nextModule = findNextLeftModule({ x, y, width, height }, centerPoints, direction)
findRight = true
}
if (nextModule) {
// 바로 위 모듈을 찾는다.
rightRows++
x = nextModule.x
y = nextModule.y
} else {
hasNextModule = false
}
}
}
// 모듈의 오른쪽 부터 그릴 랙 정보를 가져온다.
const rightRacks = rackInfos.find((rack) => {
return rack.value.moduleRows === rightRows
})?.value.racks
// 해당 rack으로 그려준다.
drawRacks(rightRacks, rackQty, rackIntvlPct, module, direction, 'R')
})
})
/*switch (rackYn) {
case 'Y': {
// rack이 Y일 경우 rackQty가 필요함
break
}
case 'N': {
break
}
}*/
// 지지금구 설치
installBracket()
}
const findNextModule = (currentPoint, centerPoints, direction) => {
let { x, y, width, height } = currentPoint
width = Math.floor(width)
height = Math.floor(height)
let result
switch (direction) {
case 'south': {
result = centerPoints.find((centerPoint) => Math.abs(centerPoint.x - x) < 2 && Math.abs(centerPoint.y - (y - height)) < 2)
break
}
case 'north': {
result = centerPoints.find((centerPoint) => Math.abs(centerPoint.x - x) < 2 && Math.abs(centerPoint.y - (y + height)) < 2)
break
}
case 'east': {
result = centerPoints.find((centerPoint) => Math.abs(centerPoint.x - (x - width)) < 2 && Math.abs(centerPoint.y - y) < 2)
break
}
case 'west': {
result = centerPoints.find((centerPoint) => Math.abs(centerPoint.x - (x + width)) < 2 && Math.abs(centerPoint.y - y) < 2)
break
}
}
return result
}
const findNextLeftModule = (currentPoint, centerPoints, direction) => {
const { x, y, width, height } = currentPoint
let result
let topLeftPoint
switch (direction) {
case 'south': {
topLeftPoint = { x: x - width / 2, y: y - height }
break
}
case 'north': {
topLeftPoint = { x: x + width / 2, y: y + height }
break
}
case 'east': {
topLeftPoint = { x: x - width, y: y + height / 2 }
break
}
case 'west': {
topLeftPoint = { x: x + width, y: y - height / 2 }
break
}
}
result = centerPoints.find((centerPoint) => Math.abs(centerPoint.x - topLeftPoint.x) < 2 && Math.abs(centerPoint.y - topLeftPoint.y) < 2)
return result
}
const findNextRightModule = (currentPoint, centerPoints, direction) => {
const { x, y, width, height } = currentPoint
let result
let topRightPoint
switch (direction) {
case 'south': {
topRightPoint = { x: x + width / 2, y: y - height }
break
}
case 'north': {
topRightPoint = { x: x - width / 2, y: y + height }
break
}
case 'east': {
topRightPoint = { x: x - width, y: y - height / 2 }
break
}
case 'west': {
topRightPoint = { x: x + width, y: y + height / 2 }
break
}
}
result = centerPoints.find((centerPoint) => Math.abs(centerPoint.x - topRightPoint.x) < 2 && Math.abs(centerPoint.y - topRightPoint.y) < 2)
return result
}
const drawRacks = (rackInfos, rackQty, rackIntvlPct, module, direction, l) => {
const { width, height } = module
let startPointX, startPointY
switch (l) {
case 'L': {
// 왼쪽부분 시작 점
if (direction === 'south') {
startPointX = module.left + width / rackIntvlPct
startPointY = module.top + module.height
break
} else if (direction === 'east') {
startPointX = module.left + width
startPointY = module.top + height - height / rackIntvlPct
break
} else if (direction === 'west') {
startPointX = module.left
startPointY = module.top + height / rackIntvlPct
break
} else if (direction === 'north') {
startPointX = module.left + width - width / rackIntvlPct
startPointY = module.top
break
}
}
case 'R': {
// 오른쪽부분 시작 점
if (direction === 'south') {
startPointX = module.left + module.width - width / rackIntvlPct
startPointY = module.top + module.height / 2 + height / 2
break
} else if (direction === 'east') {
startPointX = module.left + width
startPointY = module.top + height / rackIntvlPct
break
} else if (direction === 'west') {
startPointX = module.left
startPointY = module.top + height - height / rackIntvlPct
break
} else if (direction === 'north') {
startPointX = module.left + width / rackIntvlPct
startPointY = module.top
break
}
}
case 'C': {
// 중간부분 시작점
if (direction === 'south') {
const x = module.left + module.width / 2
const y = module.top + module.height / 2
startPointX = x
startPointY = y + height / 2
break
} else if (direction === 'east') {
const x = module.left + width
const y = module.top + module.height / 2
startPointX = x
startPointY = y
break
} else if (direction === 'west') {
const x = module.left
const y = module.top + module.height / 2
startPointX = x
startPointY = y
break
} else if (direction === 'north') {
const x = module.left + module.width / 2
const y = module.top
startPointX = x
startPointY = y
break
}
}
}
switch (direction) {
case 'south': {
rackInfos.forEach((rackInfo) => {
const { rackLen, itemId, supFitQty, supFitIntvlPct } = rackInfo
const rackLength = rackLen / 10
const rack = new fabric.Line([startPointX, startPointY, startPointX, startPointY - rackLength], {
name: 'rack',
stroke: 'red',
strokeWidth: 4,
selectable: false,
fill: 'red',
shadow: {
color: 'black', // Outline color
blur: 10,
offsetX: 0,
offsetY: 0,
},
supFitQty,
supFitIntvlPct,
rackLen,
rackId: itemId,
direction: 'top',
})
canvas.add(rack)
canvas.renderAll()
startPointY -= rackLength + 3
})
break
}
case 'east': {
rackInfos.forEach((rackInfo) => {
const { rackLen, itemId, supFitQty, supFitIntvlPct } = rackInfo
const rackLength = rackLen / 10
const rack = new fabric.Line([startPointX, startPointY, startPointX - rackLength, startPointY], {
name: 'rack',
stroke: 'red',
shadow: {
color: 'black', // Outline color
blur: 10,
offsetX: 0,
offsetY: 0,
},
strokeWidth: 4,
selectable: false,
supFitQty,
supFitIntvlPct,
rackLen,
rackId: itemId,
direction: 'left',
})
canvas.add(rack)
canvas.renderAll()
startPointX -= rackLength + 3
})
break
}
case 'west': {
rackInfos.forEach((rackInfo) => {
const { rackLen, itemId, supFitQty, supFitIntvlPct } = rackInfo
const rackLength = rackLen / 10
const rack = new fabric.Line([startPointX, startPointY, startPointX + rackLength, startPointY], {
name: 'rack',
stroke: 'red',
shadow: {
color: 'black', // Outline color
blur: 10,
offsetX: 0,
offsetY: 0,
},
strokeWidth: 4,
selectable: false,
supFitQty,
supFitIntvlPct,
rackLen,
rackId: itemId,
direction: 'right',
})
canvas.add(rack)
canvas.renderAll()
startPointX += rackLength + 3
})
break
}
case 'north': {
rackInfos.forEach((rackInfo) => {
const { rackLen, itemId, supFitQty, supFitIntvlPct } = rackInfo
const rackLength = rackLen / 10
const rack = new fabric.Line([startPointX, startPointY, startPointX, startPointY + rackLength], {
name: 'rack',
stroke: 'red',
shadow: {
color: 'black', // Outline color
blur: 10,
offsetX: 0,
offsetY: 0,
},
strokeWidth: 4,
selectable: false,
supFitQty,
supFitIntvlPct,
rackLen,
rackId: itemId,
direction: 'bottom',
})
canvas.add(rack)
canvas.renderAll()
startPointY += rackLength + 3
})
break
}
}
}
const installBracket = () => {
const racks = canvas.getObjects().filter((obj) => obj.name === 'rack')
// name이 bracket인 객체를 찾아서 삭제
canvas.getObjects().forEach((obj) => {
if (obj.name === 'bracket') {
canvas.remove(obj)
}
})
canvas.renderAll()
racks.forEach((rack) => {
const { x1, y1, x2, y2, direction, supFitQty, supFitIntvlPct, rackLen } = rack
const moduleLength = 10
if (direction === 'top') {
const result = getBracketPoints(supFitQty, supFitIntvlPct)
result.forEach((percent) => {
const bracket = new fabric.Rect({
left: x2 - moduleLength / 3,
top: y2 + (rackLen / 10) * percent,
fill: 'green',
name: 'bracket',
width: moduleLength,
height: moduleLength,
selectable: false,
})
canvas.add(bracket)
})
canvas.renderAll()
} else if (direction === 'left') {
const result = getBracketPoints(supFitQty, supFitIntvlPct)
result.forEach((percent) => {
const bracket = new fabric.Rect({
left: x2 + (rackLen / 10) * percent,
top: y2 - moduleLength / 3,
fill: 'green',
name: 'bracket',
width: moduleLength,
height: moduleLength,
selectable: false,
})
canvas.add(bracket)
})
canvas.renderAll()
} else if (direction === 'right') {
const result = getBracketPoints(supFitQty, supFitIntvlPct)
result.forEach((percent) => {
const bracket = new fabric.Rect({
left: x2 - (rackLen / 10) * percent,
top: y2 - moduleLength / 3,
fill: 'green',
name: 'bracket',
width: moduleLength,
height: moduleLength,
selectable: false,
})
canvas.add(bracket)
})
canvas.renderAll()
} else if (direction === 'bottom') {
const result = getBracketPoints(supFitQty, supFitIntvlPct)
result.forEach((percent) => {
const bracket = new fabric.Rect({
left: x2 - moduleLength / 3,
top: y2 - (rackLen / 10) * percent,
fill: 'green',
name: 'bracket',
width: moduleLength,
height: moduleLength,
selectable: false,
})
canvas.add(bracket)
})
canvas.renderAll()
}
})
}
const getBracketPoints = (n, percent) => {
if (n < 2) {
throw new Error('Number of points must be at least 2')
}
const points = []
const topY = percent / 100 // 맨 위의 점의 y 좌표 (10%)
const bottomY = 1 - percent / 100 // 맨 아래의 점의 y 좌표 (90%)
// 맨 위의 점 추가
points.push(topY)
// 중간 점들 추가
const interval = (bottomY - topY) / (n - 1)
for (let i = 1; i < n - 1; i++) {
points.push(topY + i * interval)
}
// 맨 아래의 점 추가
points.push(bottomY)
return points
}
function findTopTwoPoints(points, direction) {
switch (direction) {
case 'south':
points.sort((a, b) => b.y - a.y)
break
case 'north':
points.sort((a, b) => a.y - b.y)
break
case 'east':
points.sort((a, b) => b.x - a.x)
break
case 'west':
points.sort((a, b) => a.x - b.x)
break
}
return points.slice(0, 2)
}
const calculateForApi = (moduleSurface) => {
const centerPoints = []
const direction = moduleSurface.direction
const modules = moduleSurface.modules
modules.forEach((module, index) => {
module.tempIndex = index
const { x, y } = module.getCenterPoint()
const { width, height } = module
centerPoints.push({ x, y, width: Math.floor(width), height: Math.floor(height), index })
})
if (centerPoints.length === 0) return
//완전 노출 하면
let exposedBottom = 0
// 반 노출 하면
let exposedHalfBottom = 0
// 완전 노출 상면
let exposedTop = 0
//반 노출 상면
let exposedHalfTop = 0
// 완전 접면
let touchDimension = 0
//반접면
let halfTouchDimension = 0
// 노출bottom면의 points
let exposedBottomPoints = []
// 반노출 bottom면의 points
let leftExposedHalfBottomPoints = []
let rightExposedHalfBottomPoints = []
centerPoints.forEach((centerPoint, index) => {
const { x, y, width, height } = centerPoint
// centerPoints중에 현재 centerPoint와 x값이 같고, y값이 y-height값과 같은 centerPoint가 있는지 확인
let bottomCell
let bottomLeftPoint
let bottomRightPoint
let leftBottomCnt
let rightBottomCnt
switch (direction) {
case 'south':
bottomCell = centerPoints.filter((centerPoint) => Math.abs(centerPoint.x - x) < 2 && Math.abs(centerPoint.y - (y + height)) < 2)
bottomLeftPoint = { x: x - width / 2, y: y + height }
bottomRightPoint = { x: x + width / 2, y: y + height }
break
case 'north':
bottomCell = centerPoints.filter((centerPoint) => Math.abs(centerPoint.x - x) < 2 && Math.abs(centerPoint.y - (y - height)) < 2)
bottomLeftPoint = { x: x + width / 2, y: y - height }
bottomRightPoint = { x: x - width / 2, y: y - height }
break
case 'east':
bottomCell = centerPoints.filter((centerPoint) => Math.abs(centerPoint.x - (x + width)) < 2 && Math.abs(centerPoint.y - y) < 2)
bottomLeftPoint = { x: x + width, y: y + height / 2 }
bottomRightPoint = { x: x + width, y: y - height / 2 }
break
case 'west':
bottomCell = centerPoints.filter((centerPoint) => Math.abs(centerPoint.x - (x - width)) < 2 && Math.abs(centerPoint.y - y) < 2)
bottomLeftPoint = { x: x - width, y: y - height / 2 }
bottomRightPoint = { x: x - width, y: y + height / 2 }
break
}
if (bottomCell.length === 1) {
return
}
// 바로 아래에 셀이 없는 경우 물떼세 배치가 왼쪽 되어있는 셀을 찾는다.
leftBottomCnt = centerPoints.filter(
(centerPoint) => Math.abs(centerPoint.x - bottomLeftPoint.x) < 2 && Math.abs(centerPoint.y - bottomLeftPoint.y) < 2,
).length
rightBottomCnt = centerPoints.filter(
(centerPoint) => Math.abs(centerPoint.x - bottomRightPoint.x) < 2 && Math.abs(centerPoint.y - bottomRightPoint.y) < 2,
).length
if (leftBottomCnt + rightBottomCnt === 1) {
exposedHalfBottom++
if (leftBottomCnt === 1) {
rightExposedHalfBottomPoints.push(centerPoint)
}
if (rightBottomCnt === 1) {
leftExposedHalfBottomPoints.push(centerPoint)
}
} else if (leftBottomCnt + rightBottomCnt === 0) {
exposedBottomPoints.push(centerPoint)
}
})
// 노출상면 및 접면 체크
centerPoints.forEach((centerPoint, index) => {
const { x, y, width, height } = centerPoint
let topCell
let topLeftPoint
let topRightPoint
let leftTopCnt
let rightTopCnt
switch (direction) {
case 'south':
topCell = centerPoints.filter((centerPoint) => Math.abs(centerPoint.x - x) < 2 && Math.abs(centerPoint.y - (y - height)) < 2)
topLeftPoint = { x: x - width / 2, y: y - height }
topRightPoint = { x: x + width / 2, y: y - height }
break
case 'north':
topCell = centerPoints.filter((centerPoint) => Math.abs(centerPoint.x - x) < 2 && Math.abs(centerPoint.y - (y + height)) < 2)
topLeftPoint = { x: x + width / 2, y: y + height }
topRightPoint = { x: x - width / 2, y: y + height }
break
case 'east':
topCell = centerPoints.filter((centerPoint) => Math.abs(centerPoint.x - (x - width)) < 2 && Math.abs(centerPoint.y - y) < 2)
topLeftPoint = { x: x - width, y: y + height / 2 }
topRightPoint = { x: x - width, y: y - height / 2 }
break
case 'west':
topCell = centerPoints.filter((centerPoint) => Math.abs(centerPoint.x - (x + width)) < 2 && Math.abs(centerPoint.y - y) < 2)
topLeftPoint = { x: x + width, y: y - height / 2 }
topRightPoint = { x: x + width, y: y + height / 2 }
break
}
if (topCell.length === 1) {
touchDimension++
return
}
leftTopCnt = centerPoints.filter(
(centerPoint) => Math.abs(centerPoint.x - topLeftPoint.x) < 2 && Math.abs(centerPoint.y - topLeftPoint.y) < 2,
).length
rightTopCnt = centerPoints.filter(
(centerPoint) => Math.abs(centerPoint.x - topRightPoint.x) < 2 && Math.abs(centerPoint.y - topRightPoint.y) < 2,
).length
if (leftTopCnt + rightTopCnt === 2) {
touchDimension++
return
}
if (leftTopCnt + rightTopCnt === 1) {
exposedHalfTop++
halfTouchDimension++
return
}
if (leftTopCnt + rightTopCnt === 0) {
exposedTop++
}
})
// 완전 노출 하면 계산
/*const cells = canvas.getObjects().filter((obj) => polygon.id === obj.parentId)
const points = cells.map((cell) => {
return cell.getCenterPoint()
})*/
const groupPoints = groupCoordinates(centerPoints, modules[0], direction)
groupPoints.forEach((group) => {
let maxY = group.reduce((acc, cur) => (acc.y > cur.y ? acc : cur)).y
let minY = group.reduce((acc, cur) => (acc.y < cur.y ? acc : cur)).y
let maxX = group.reduce((acc, cur) => (acc.x > cur.x ? acc : cur)).x
let minX = group.reduce((acc, cur) => (acc.x < cur.x ? acc : cur)).x
let maxYCenterPoints = group.filter((centerPoint) => Math.abs(centerPoint.y - maxY) < 2)
let minYCenterPoints = group.filter((centerPoint) => Math.abs(centerPoint.y - minY) < 2)
let maxXCenterPoints = group.filter((centerPoint) => Math.abs(centerPoint.x - maxX) < 2)
let minXCenterPoints = group.filter((centerPoint) => Math.abs(centerPoint.x - minX) < 2)
switch (direction) {
case 'south':
exposedBottom += maxYCenterPoints.length
break
case 'north':
exposedBottom += minYCenterPoints.length
break
case 'east':
exposedBottom += maxXCenterPoints.length
break
case 'west':
exposedBottom += minXCenterPoints.length
break
}
})
return {
exposedBottom,
exposedHalfBottom,
exposedTop,
exposedHalfTop,
touchDimension,
halfTouchDimension,
exposedBottomPoints,
leftExposedHalfBottomPoints,
rightExposedHalfBottomPoints,
centerPoints,
}
}
// polygon 내부 cell들의 centerPoint 배열을 그룹화 해서 반환
const groupCoordinates = (points, moduleExample, direction) => {
const groups = []
const visited = new Set()
const width = Math.floor(moduleExample.width)
const height = Math.floor(moduleExample.height)
const horizonPadding = 0 // 가로 패딩
const verticalPadding = 0 // 세로 패딩
function isAdjacent(p1, p2) {
const dx = Math.abs(p1.x - p2.x)
const dy = Math.abs(p1.y - p2.y)
return (
(Math.abs(width + horizonPadding - dx) < 2 && dy < 2) ||
(dx < 2 && Math.abs(dy - height + verticalPadding)) < 2 ||
(Math.abs(dx - width / 2 + horizonPadding / 2) < 2 && Math.abs(dy - height + verticalPadding) < 2)
)
}
function dfs(point, group) {
visited.add(`${point.x},${point.y}`)
group.push(point)
for (const nextPoint of points) {
const key = `${nextPoint.x},${nextPoint.y}`
if (!visited.has(key) && isAdjacent(point, nextPoint)) {
dfs(nextPoint, group)
}
}
}
for (const point of points) {
const key = `${point.x},${point.y}`
if (!visited.has(key)) {
const group = []
dfs(point, group)
groups.push(group)
}
}
return groups
}
return { apply }
}

View File

@ -326,35 +326,37 @@ export function useCanvasSetting() {
]
}
// 데이터 설정
// 데이터 설정
const addRoofs = []
roofMaterials?.map((material) => {
if (material.roofMatlCd === roofsArray[0].roofMatlCd) {
addRoofs.push({
...material,
selected: true,
index: 0,
width: roofsArray[0].roofWidth,
length: roofsArray[0].roofHeight,
hajebichi: roofsArray[0].roofHajebichi,
raft: roofsArray[0].roofGap,
layout: roofsArray[0].roofLayout,
roofSizeSet: roofsRow[0].roofSizeSet,
roofAngleSet: roofsRow[0].roofAngleSet,
pitch: roofsArray[0].roofPitch,
angle: roofsArray[0].roofAngle,
})
setAddedRoofs(addRoofs)
setBasicSettings({
...basicSetting,
roofMaterials: addRoofs[0],
roofSizeSet: roofsRow[0].roofSizeSet,
roofAngleSet: roofsRow[0].roofAngleSet,
roofsData: roofsArray,
selectedRoofMaterial: addRoofs[0],
})
}
for (let i = 0; i < roofsArray.length; i++) {
roofMaterials?.map((material) => {
if (material.roofMatlCd === roofsArray[i].roofMatlCd) {
addRoofs.push({
...material,
selected: roofsArray[i].roofApply,
index: i,
width: roofsArray[i].roofWidth,
length: roofsArray[i].roofHeight,
hajebichi: roofsArray[i].roofHajebichi,
raft: roofsArray[i].roofGap,
layout: roofsArray[i].roofLayout,
roofSizeSet: roofsRow[i].roofSizeSet,
roofAngleSet: roofsRow[i].roofAngleSet,
pitch: roofsArray[i].roofPitch,
angle: roofsArray[i].roofAngle,
})
}
})
}
console.log('🚀 ~ fetchBasicSettings ~ addRoofs:', addRoofs)
setAddedRoofs(addRoofs)
setBasicSettings({
...basicSetting,
roofMaterials: addRoofs[0],
roofSizeSet: roofsRow[0].roofSizeSet,
roofAngleSet: roofsRow[0].roofAngleSet,
roofsData: roofsArray,
selectedRoofMaterial: addRoofs[0],
})
})
} catch (error) {

View File

@ -151,11 +151,18 @@ export function useRoofAllocationSetting(id) {
pitch: roofsArray[i].roofPitch,
angle: roofsArray[i].roofAngle,
})
setCurrentRoofList(selectRoofs)
}
})
}
setBasicSetting({ ...basicSetting, roofsData: roofsArray })
setCurrentRoofList(selectRoofs)
//setBasicSetting({ ...basicSetting, roofsData: roofsArray })
setBasicSetting({
...basicSetting,
roofSizeSet: res[0].roofSizeSet,
roofAngleSet: res[0].roofAngleSet,
roofsData: roofsArray,
})
})
} catch (error) {
console.error('Data fetching error:', error)

View File

@ -95,5 +95,17 @@ export function useAxios(lang = '') {
return await getInstances(url).delete(url, option)
}
return { get, promiseGet, post, promisePost, put, promisePut, patch, promisePatch, del, promiseDel }
const getFetcher = async (url) => {
const res = await get({ url })
console.log('🚀 ~ getFetcher ~ res:', res)
return res
}
const postFetcher = async (url, { arg }) => {
const res = await post({ url, data: arg })
console.log('🚀 ~ postFetcher ~ res:', res)
return res
}
return { get, promiseGet, post, promisePost, put, promisePut, patch, promisePatch, del, promiseDel, getFetcher, postFetcher }
}

View File

@ -14,7 +14,7 @@ import { useCanvas } from '@/hooks/useCanvas'
import { SAVE_KEY } from '@/common/common'
import { readImage, removeImage } from '@/lib/fileAction'
import { FloorPlanContext } from '@/app/floor-plan/FloorPlanProvider'
import { useEstimateController } from '@/hooks/floorPlan/estimate/useEstimateController'
export function usePlan(params = {}) {
const { floorPlanState } = useContext(FloorPlanContext)
@ -31,8 +31,9 @@ export function usePlan(params = {}) {
const { swalFire } = useSwal()
const { getMessage } = useMessage()
const { get, promisePost, promisePut, promiseDel } = useAxios()
const { get, promisePost, promisePut, promiseDel, promiseGet } = useAxios()
const { setEstimateContextState } = useEstimateController()
/**
* 마우스 포인터의 가이드라인을 제거합니다.
*/
@ -203,12 +204,44 @@ export function usePlan(params = {}) {
* 현재 plan의 작업상태를 저장 이동
*/
const handleCurrentPlan = async (newCurrentId) => {
if (pathname === '/floor-plan') {
if (!currentCanvasPlan || currentCanvasPlan.id !== newCurrentId) {
await saveCanvas()
}
}
updateCurrentPlan(newCurrentId)
const orderingNo = plans?.find((obj) => obj.id === newCurrentId).ordering
const objectNo = floorPlanState.objectNo
await promiseGet({ url: `/api/estimate/${objectNo}/${orderingNo}/detail` })
.then((res) => {
if (res.status === 200) {
const estimateDetail = res.data
if (estimateDetail.docNo) {
res.data.resetFlag = 'N'
if (res.data.itemList.length > 0) {
res.data.itemList.map((item) => {
item.delFlg = '0'
})
}
if (res.data.pkgAsp === null || res.data.pkgAsp == undefined) {
res.data.pkgAsp = '0.00'
} else {
const number = parseFloat(res.data.pkgAsp)
const roundedNumber = isNaN(number) ? '0.00' : number.toFixed(2)
res.data.pkgAsp = roundedNumber.toString()
}
setEstimateContextState(res.data)
if (pathname === '/floor-plan') {
if (!currentCanvasPlan || currentCanvasPlan.id !== newCurrentId) {
saveCanvas()
}
}
updateCurrentPlan(newCurrentId)
} else {
swalFire({ text: getMessage('estimate.menu.move.valid1') })
}
}
})
.catch((error) => {
swalFire({ text: getMessage('estimate.menu.move.valid1') })
})
}
const updateCurrentPlan = (newCurrentId) => {

View File

@ -938,6 +938,7 @@
"estimate.detail.unlock.alertMsg": "見積書を修正して保存",
"estimate.detail.alert.delFile": "添付ファイルを完全に削除するには[保存]ボタンをクリックしてください",
"estimate.detail.alert.selectDelItem": "削除する商品を選択してください.",
"estimate.menu.move.valid1": "回路を分割していないため、見積もりを呼び出すことはできません.",
"simulator.title.sub1": "物件番号",
"simulator.title.sub2": "作成日",
"simulator.title.sub3": "システム容量",

View File

@ -947,6 +947,7 @@
"estimate.detail.unlock.alertMsg": "견적서를 수정하고, 저장하십시오",
"estimate.detail.alert.delFile": "첨부파일을 완전히 삭제하려면 [저장]버튼을 클릭하십시오.",
"estimate.detail.alert.selectDelItem": "삭제할 제품을 선택하세요.",
"estimate.menu.move.valid1": "회로를 나누지 않았기 때문에 견적서를 호출할 수 없습니다.",
"simulator.title.sub1": "물건번호",
"simulator.title.sub2": "작성일",
"simulator.title.sub3": "시스템 용량",

View File

@ -712,7 +712,7 @@ export const drawShedRoof = (roofId, canvas) => {
export const drawRidgeRoof = (roofId, canvas) => {
const roof = canvas?.getObjects().find((object) => object.id === roofId)
const hasNonParallelLines = roof.lines.filter((line) => line.x1 !== line.x2 && line.y1 !== line.y2)
const hasNonParallelLines = roof.lines.filter((line) => Math.abs(line.x1 - line.x2) > 1 && Math.abs(line.y1 - line.y2) > 1)
if (hasNonParallelLines.length > 0) {
alert('대각선이 존재합니다.')
return
@ -721,6 +721,7 @@ export const drawRidgeRoof = (roofId, canvas) => {
drawHips(roof, canvas)
connectLinePoint(roof, canvas)
modifyRidge(roof, canvas)
drawCenterLine(roof, canvas)
}
/**
@ -1532,7 +1533,8 @@ const changeEavesRoof = (currentRoof, canvas) => {
line.name !== LINE_TYPE.SUBLINE.RIDGE &&
line.name !== LINE_TYPE.SUBLINE.HIP &&
line.name !== LINE_TYPE.SUBLINE.VALLEY &&
line.name !== OUTER_LINE_TYPE.OUTER_LINE,
line.name !== OUTER_LINE_TYPE.OUTER_LINE &&
line.name !== 'wallLine',
)
.forEach((line) => {
roof.innerLines = roof.innerLines.filter((l) => l.id !== line.id)
@ -1704,7 +1706,8 @@ const changeGableRoof = (currentRoof, canvas) => {
line.name !== LINE_TYPE.SUBLINE.RIDGE &&
line.name !== LINE_TYPE.SUBLINE.HIP &&
line.name !== LINE_TYPE.SUBLINE.VALLEY &&
line.name !== OUTER_LINE_TYPE.OUTER_LINE,
line.name !== OUTER_LINE_TYPE.OUTER_LINE &&
line.name !== 'wallLine',
)
.forEach((line) => {
roof.innerLines = roof.innerLines.filter((l) => l.id !== line.id)
@ -1890,7 +1893,8 @@ const changeHipAndGableRoof = (currentRoof, canvas) => {
line.name !== LINE_TYPE.SUBLINE.RIDGE &&
line.name !== LINE_TYPE.SUBLINE.HIP &&
line.name !== LINE_TYPE.SUBLINE.VALLEY &&
line.name !== OUTER_LINE_TYPE.OUTER_LINE,
line.name !== OUTER_LINE_TYPE.OUTER_LINE &&
line.name !== 'wallLine',
)
.forEach((line) => {
roof.innerLines = roof.innerLines.filter((l) => l.id !== line.id)
@ -2127,7 +2131,8 @@ const changeJerkInHeadRoof = (currentRoof, canvas) => {
line.name !== LINE_TYPE.SUBLINE.RIDGE &&
line.name !== LINE_TYPE.SUBLINE.HIP &&
line.name !== LINE_TYPE.SUBLINE.VALLEY &&
line.name !== OUTER_LINE_TYPE.OUTER_LINE,
line.name !== OUTER_LINE_TYPE.OUTER_LINE &&
line.name !== 'wallLine',
)
.forEach((line) => {
roof.innerLines = roof.innerLines.filter((l) => l.id !== line.id)
@ -2407,7 +2412,8 @@ const changeWallRoof = (currentRoof, canvas) => {
line.name !== LINE_TYPE.SUBLINE.RIDGE &&
line.name !== LINE_TYPE.SUBLINE.HIP &&
line.name !== LINE_TYPE.SUBLINE.VALLEY &&
line.name !== OUTER_LINE_TYPE.OUTER_LINE,
line.name !== OUTER_LINE_TYPE.OUTER_LINE &&
line.name !== 'wallLine',
)
.forEach((line) => {
roof.innerLines = roof.innerLines.filter((l) => l.id !== line.id)
@ -2702,6 +2708,187 @@ const reDrawPolygon = (polygon, canvas) => {
return newPolygon
}
const drawCenterLine = (roof, canvas) => {
const roofLines = roof.lines
roofLines.forEach((currentRoof) => {
const hips = roof.innerLines.filter(
(line) => (currentRoof.x1 === line.x1 && currentRoof.y1 === line.y1) || (currentRoof.x2 === line.x1 && currentRoof.y2 === line.y1),
)
const ridge = roof.innerLines
.filter((line) => line.name === LINE_TYPE.SUBLINE.RIDGE)
.filter((line) => {
if (currentRoof.x1 === currentRoof.x2) {
if (line.x1 === line.x2) {
return line
}
} else {
if (line.y1 === line.y2) {
return line
}
}
})
.reduce((prev, current) => {
let currentDistance, prevDistance
if (Math.sign(currentRoof.x1 - currentRoof.x2) === 0) {
currentDistance = Math.abs(currentRoof.y1 - current.y1)
prevDistance = prev ? Math.abs(currentRoof.y1 - prev.y1) : Infinity
} else {
currentDistance = Math.abs(currentRoof.y1 - current.y2)
prevDistance = prev ? Math.abs(currentRoof.y1 - current.y2) : Infinity
}
return prevDistance < currentDistance ? prev : current
}, null)
let points = []
if (hips.length === 2 && Math.abs(hips[0].x2 - hips[1].x2) < 1 && Math.abs(hips[0].y2 - hips[1].y2) < 1) {
// console.log('삼각')
const x1 = (currentRoof.x1 + currentRoof.x2) / 2
const y1 = (currentRoof.y1 + currentRoof.y2) / 2
points.push(x1, y1, hips[0].x2, hips[0].y2)
} else {
// console.log('삼각 아님')
if (
((ridge.x1 === hips[0].x2 && ridge.y1 === hips[0].y2) || (ridge.x2 === hips[0].x2 && ridge.y2 === hips[0].y2)) &&
((ridge.x1 === hips[1].x2 && ridge.y1 === hips[1].y2) || (ridge.x2 === hips[1].x2 && ridge.y2 === hips[1].y2))
) {
//사각이면 마루와 현재 라인 사이에 길이를 구한다
// console.log('사각')
if (Math.sign(currentRoof.x1 - currentRoof.x2) === 0) {
const yPoints = [currentRoof.y1, currentRoof.y2, ridge.y1, ridge.y2].sort((a, b) => a - b)
const x1 = (currentRoof.x1 + currentRoof.x2) / 2
const x2 = (ridge.x1 + ridge.x2) / 2
const y = (yPoints[1] + yPoints[2]) / 2
if (
((currentRoof.y1 <= y && y <= currentRoof.y2) || (currentRoof.y2 <= y && y <= currentRoof.y1)) &&
((ridge.y1 <= y && y <= ridge.y2) || (ridge.y2 <= y && y <= ridge.y1))
) {
points.push(x1, y, x2, y)
}
} else {
const xPoints = [currentRoof.x1, currentRoof.x2, ridge.x1, ridge.x2].sort((a, b) => a - b)
const y1 = (currentRoof.y1 + currentRoof.y2) / 2
const y2 = (ridge.y1 + ridge.y2) / 2
const x = (xPoints[1] + xPoints[2]) / 2
if (
((currentRoof.x1 <= x && x <= currentRoof.x2) || (currentRoof.x2 <= x && x <= currentRoof.x1)) &&
((ridge.x1 <= x && x <= ridge.x2) || (ridge.x2 <= x && x <= ridge.x1))
) {
points.push(x, y1, x, y2)
}
}
} else {
//사각 아님
if (Math.sign(currentRoof.x1 - currentRoof.x2) === 0) {
let xPoints = []
xPoints.push(ridge?.x1, ridge?.x2)
hips.forEach((hip) => {
xPoints.push(hip.x2)
})
let maxPoint = xPoints.reduce((prev, current) => {
const currentDistance = Math.abs(currentRoof.x1 - current)
const prevDistance = prev ? Math.abs(currentRoof.x1 - prev) : 0
return prevDistance > currentDistance ? prev : current
}, null)
xPoints = xPoints.filter((point) => point === maxPoint)
let oppositeLine
if (xPoints.length === 1) {
if (ridge?.x1 === xPoints[0] || ridge?.x2 === xPoints[0]) {
oppositeLine = ridge
}
if (oppositeLine === undefined) {
oppositeLine = hips.find((hip) => hip.x2 === xPoints[0])
}
points.push(currentRoof.x1, oppositeLine.y2, oppositeLine.x2, oppositeLine.y2)
} else if (xPoints.length > 1) {
xPoints = [...new Set(xPoints)] // 중복제거
if (ridge.length > 0) {
let boolX1 = xPoints.some((x) => x === ridge.x1)
let boolX2 = xPoints.some((x) => x === ridge.x2)
if (boolX1 && boolX2) {
oppositeLine = ridge
}
if (oppositeLine) {
const sortPoints = [currentRoof.y1, currentRoof.y2, oppositeLine.y1, oppositeLine.y2].sort((a, b) => a - b)
const y = (sortPoints[1] + sortPoints[2]) / 2
if (
((currentRoof.y1 <= y && y <= currentRoof.y2) || (currentRoof.y2 <= y && y <= currentRoof.y1)) &&
((oppositeLine.y1 <= y && y <= oppositeLine.y2) || (oppositeLine.y2 <= y && y <= oppositeLine.y1))
) {
points.push(currentRoof.x1, y, oppositeLine.x1, y)
}
}
}
}
} else {
let yPoints = []
yPoints.push(ridge?.y1, ridge?.y2)
hips.forEach((hip) => {
yPoints.push(hip.y2)
})
const maxPoint = yPoints.reduce((prev, current) => {
const currentDistance = Math.abs(currentRoof.y1 - current)
const prevDistance = prev ? Math.abs(currentRoof.y1 - prev) : 0
return prevDistance > currentDistance ? prev : current
})
yPoints = yPoints.filter((y) => y === maxPoint)
let oppositeLine
if (yPoints.length === 1) {
if (ridge?.y1 === yPoints[0] || ridge?.y2 === yPoints[0]) {
oppositeLine = ridge
}
if (oppositeLine === undefined) {
oppositeLine = hips.find((hip) => hip.y2 === yPoints[0])
}
points.push(oppositeLine.x2, currentRoof.y1, oppositeLine.x2, oppositeLine.y2)
} else if (yPoints.length > 1) {
let boolY1 = yPoints.some((y) => y === ridge.y1)
let boolY2 = yPoints.some((y) => y === ridge.y2)
if (boolY1 && boolY2) {
oppositeLine = ridge
}
if (oppositeLine) {
const sortPoints = [currentRoof.x1, currentRoof.x2, oppositeLine.x1, oppositeLine.x2].sort((a, b) => a - b)
const x = (sortPoints[1] + sortPoints[2]) / 2
if (
((currentRoof.x1 <= x && x <= currentRoof.x2) || (currentRoof.x2 <= x && x <= currentRoof.x1)) &&
((oppositeLine.x1 <= x && x <= oppositeLine.x2) || (oppositeLine.x2 <= x && x <= oppositeLine.x1))
) {
points.push(x, currentRoof.y1, x, oppositeLine.y1)
}
}
}
}
}
}
if (points !== null) {
const pitchSizeLine = new QLine(points, {
stroke: '#000000',
strokeWidth: 2,
strokeDashArray: [5, 5],
selectable: false,
fontSize: roof.fontSize,
attributes: {
roofId: roof.id,
type: 'pitchSizeLine',
},
})
canvas.add(pitchSizeLine)
canvas.renderAll()
const adjust = Math.sqrt(
Math.pow(Math.round(Math.abs(pitchSizeLine.x1 - pitchSizeLine.x2) * 10), 2) +
Math.pow(Math.round(Math.abs(pitchSizeLine.y1 - pitchSizeLine.y2) * 10), 2),
)
const currentDegree = currentRoof.attributes.pitch > 0 ? getDegreeByChon(currentRoof.attributes.pitch) : currentRoof.attributes.degree
const height = Math.tan(currentDegree * (Math.PI / 180)) * adjust
const lengthText = Math.round(Math.sqrt(Math.pow(adjust, 2) + Math.pow(height, 2)))
pitchSizeLine.setLengthText(lengthText)
}
})
}
function createRoofMarginPolygon(polygon, lines, arcSegments = 0) {
const offsetEdges = []