qcast-front/src/hooks/option/useCanvasSetting.js

1082 lines
40 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useContext, useEffect, useState } from 'react'
import { useRecoilState, useRecoilValue, useResetRecoilState, useSetRecoilState } from 'recoil'
import {
adsorptionPointModeState,
adsorptionRangeState,
canvasSettingState,
canvasState,
currentMenuState,
dotLineGridSettingState,
planSizeSettingState,
} from '@/store/canvasAtom'
import { globalLocaleStore } from '@/store/localeAtom'
import { useMessage } from '@/hooks/useMessage'
import { useAxios } from '@/hooks/useAxios'
import { useSwal } from '@/hooks/useSwal'
import {
addedRoofsState,
basicSettingState,
correntObjectNoState,
corridorDimensionSelector,
roofMaterialsAtom,
selectedRoofMaterialSelector,
settingModalFirstOptionsState,
settingModalGridOptionsState,
settingModalSecondOptionsState,
} from '@/store/settingAtom'
import { MENU, POLYGON_TYPE } from '@/common/common'
import { globalFontAtom } from '@/store/fontAtom'
import { dimensionLineSettingsState } from '@/store/commonUtilsAtom'
import { gridColorState } from '@/store/gridAtom'
import { useColor } from 'react-color-palette'
import { useMasterController } from '@/hooks/common/useMasterController'
import PlacementShapeSetting, { ROOF_MATERIAL_LAYOUT } from '@/components/floor-plan/modal/placementShape/PlacementShapeSetting'
import { useCanvasMenu } from '../common/useCanvasMenu'
import { usePopup } from '../usePopup'
import { FloorPlanContext } from '@/app/floor-plan/FloorPlanProvider'
import { moduleSelectionDataState, selectedModuleState } from '@/store/selectedModuleOptions'
import { useCommonCode } from '@/hooks/common/useCommonCode'
import { useCanvasPopupStatusController } from '@/hooks/common/useCanvasPopupStatusController'
import { v4 as uuidv4 } from 'uuid'
import { useEvent } from '@/hooks/useEvent'
import { logger } from '@/util/logger'
import { useText } from '@/hooks/useText'
import { usePolygon } from '@/hooks/usePolygon'
import { getDegreeByChon } from '@/util/canvas-util'
const defaultDotLineGridSetting = {
INTERVAL: {
type: 2 /* 1: 가로,세로 간격 수동, 2: 비율 간격 */,
ratioInterval: 910,
verticalInterval: 910,
horizontalInterval: 910,
dimension: 1 /* 치수 */,
},
DOT: false,
LINE: false,
}
// hook 추가 시 executeEffect를 인자로 받고 false일 경우 useEffect를 실행하지 않는다. default true
// 아래 hook에 추가 된 함수만 사용하고 싶을 경우 false로 인자 전달
export function useCanvasSetting(executeEffect = true) {
const canvas = useRecoilValue(canvasState)
/** canvas가 null이 아닐 때에만 getObjects 호출 */
const canvasObjects = canvas ? canvas.getObjects() : []
const [correntObjectNo, setCorrentObjectNo] = useRecoilState(correntObjectNoState)
const [settingModalFirstOptions, setSettingModalFirstOptions] = useRecoilState(settingModalFirstOptionsState)
const [settingModalSecondOptions, setSettingModalSecondOptions] = useRecoilState(settingModalSecondOptionsState)
const resetSettingModalFirstOptions = useResetRecoilState(settingModalFirstOptionsState)
const resetSettingModalSecondOptions = useResetRecoilState(settingModalSecondOptionsState)
const [selectedFont, setSelectedFont] = useState()
const [selectedFontWeight, setSelectedFontWeight] = useState()
const [selectedFontSize, setSelectedFontSize] = useState()
const [selectedFontColor, setSelectedFontColor] = useState()
const [globalFont, setGlobalFont] = useRecoilState(globalFontAtom)
const resetGlobalFont = useResetRecoilState(globalFontAtom)
const [adsorptionPointMode, setAdsorptionPointMode] = useRecoilState(adsorptionPointModeState)
const [adsorptionRange, setAdsorptionRange] = useRecoilState(adsorptionRangeState)
const [planSizeSettingMode, setPlanSizeSettingMode] = useRecoilState(planSizeSettingState)
const resetPlanSizeSettingMode = useResetRecoilState(planSizeSettingState)
const [dimensionLineSettings, setDimensionLineSettings] = useRecoilState(dimensionLineSettingsState)
const resetDimensionLineSettings = useResetRecoilState(dimensionLineSettingsState)
const setSettingModalGridOptions = useSetRecoilState(settingModalGridOptionsState)
const [dotLineGridSetting, setDotLineGridSettingState] = useRecoilState(dotLineGridSettingState)
const resetDotLineGridSetting = useResetRecoilState(dotLineGridSettingState)
const [currentSetting, setCurrentSetting] = useState(
JSON.stringify(dotLineGridSetting) === JSON.stringify(defaultDotLineGridSetting) ? { ...defaultDotLineGridSetting } : { ...dotLineGridSetting },
)
const [gridColor, setGridColor] = useRecoilState(gridColorState)
const [color, setColor] = useColor(gridColor ?? '#FF0000')
const { selectedMenu, setSelectedMenu } = useCanvasMenu()
const { initEvent } = useEvent()
const [settingsData, setSettingsData] = useState({
...settingModalFirstOptions,
...settingModalSecondOptions,
...globalFont,
...dotLineGridSetting,
...planSizeSettingMode,
...dimensionLineSettings,
...color,
})
const [settingsDataSave, setSettingsDataSave] = useState()
const { option1, option2, dimensionDisplay } = settingModalFirstOptions
const { option4 } = settingModalSecondOptions
const corridorDimension = useRecoilValue(corridorDimensionSelector)
const globalLocaleState = useRecoilValue(globalLocaleStore)
const { get, post } = useAxios(globalLocaleState)
const { getMessage } = useMessage()
const { swalFire } = useSwal()
const [canvasSetting, setCanvasSetting] = useRecoilState(canvasSettingState)
const [basicSetting, setBasicSettings] = useRecoilState(basicSettingState)
const { getRoofMaterialList, getModuleTypeItemList } = useMasterController()
const [roofMaterials, setRoofMaterials] = useRecoilState(roofMaterialsAtom)
const [addedRoofs, setAddedRoofs] = useRecoilState(addedRoofsState)
const setCurrentMenu = useSetRecoilState(currentMenuState)
const resetModuleSelectionData = useResetRecoilState(moduleSelectionDataState) /* 다음으로 넘어가는 최종 데이터 */
const resetSelectedModules = useResetRecoilState(selectedModuleState) /* 선택된 모듈 */
const { trigger: orientationTrigger } = useCanvasPopupStatusController(1)
const { trigger: moduleSelectedDataTrigger } = useCanvasPopupStatusController(2)
const [raftCodes, setRaftCodes] = useState([]) /* 서까래 정보 */
const { findCommonCode } = useCommonCode()
const [currentRoof, setCurrentRoof] = useState(null) /* 현재 선택된 지붕재 정보 */
const { addPopup } = usePopup()
const [popupId, setPopupId] = useState(uuidv4())
const { changeCorridorDimensionText } = useText()
const { setPolygonLinesActualSize } = usePolygon()
const SelectOptions = [
{ id: 1, name: getMessage('modal.canvas.setting.grid.dot.line.setting.line.origin'), value: 1 },
{ id: 2, name: '1/2', value: 1 / 2 },
{ id: 3, name: '1/4', value: 1 / 4 },
{ id: 4, name: '1/10', value: 1 / 10 },
]
const selectedRoofMaterial = useRecoilValue(selectedRoofMaterialSelector) // 선택된 지붕재 정보
const { floorPlanState } = useContext(FloorPlanContext) // 플랜 상태
const { closePopup, closeAll } = usePopup() // 팝업 닫기
useEffect(() => {
if (!executeEffect) {
return
}
// Roof materials are now initialized in Header.jsx to avoid duplicate calls
}, [executeEffect])
/**
* 지붕재 초기세팅 (fallback - primary initialization is in Header.jsx)
* This function is only called when roof materials are needed but not yet loaded
*/
const addRoofMaterials = async () => {
if (roofMaterials.length !== 0) {
return roofMaterials
}
const { data } = await getRoofMaterialList()
const roofLists = data.map((item, idx) => ({
...item,
id: item.roofMatlCd,
name: item.roofMatlNm,
selected: idx === 0,
index: idx,
nameJp: item.roofMatlNmJp,
length: item.lenBase && parseInt(item.lenBase),
width: item.widBase && parseInt(item.widBase),
raft: item.raftBase && parseInt(item.raftBase),
layout: ['ROOF_ID_SLATE', 'ROOF_ID_SINGLE'].includes(item.roofMatlCd) ? ROOF_MATERIAL_LAYOUT.STAIRS : ROOF_MATERIAL_LAYOUT.PARALLEL,
hajebichi: item.roofPchBase && parseInt(item.roofPchBase),
pitch: item.inclBase ? parseInt(item.inclBase) : 4,
angle: getDegreeByChon(item.inclBase ? parseInt(item.inclBase) : 4), //item.angle ? parseInt(item.angle) : 21.8,
}))
setRoofMaterials(roofLists)
return roofLists
}
useEffect(() => {
if (!executeEffect) {
return
}
if (addedRoofs.length > 0 && addedRoofs[0].planNo === basicSetting.planNo) {
const raftCodeList = findCommonCode('203800')
setRaftCodes(raftCodeList)
setCurrentRoof({
...addedRoofs[0],
planNo: addedRoofs[0].planNo,
roofSizeSet: String(basicSetting.roofSizeSet),
roofAngleSet: basicSetting.roofAngleSet,
})
}
}, [addedRoofs])
/*useEffect(() => {
if (!executeEffect) {
return
}
if (roofMaterials.length === 0) {
return
}
const selectedRoofMaterial = roofMaterials[0]
if (addedRoofs.length === 0) {
const newAddedRoofs = []
newAddedRoofs.push({ ...selectedRoofMaterial, selected: true, index: 0 })
setAddedRoofs(newAddedRoofs)
}
setBasicSettings({ ...basicSetting, selectedRoofMaterial: selectedRoofMaterial })
}, [roofMaterials])*/
useEffect(() => {
if (!canvas) {
return
}
if (!executeEffect) {
return
}
const roofs = canvasObjects.filter((obj) => obj.name === POLYGON_TYPE.ROOF)
if (roofs.length > 0) {
roofs.forEach((roof) => {
setPolygonLinesActualSize(roof)
})
changeCorridorDimensionText()
canvas.renderAll()
}
}, [corridorDimension, canvasSetting])
useEffect(() => {
if (!executeEffect) {
return
}
if (settingsDataSave !== undefined) onClickOption2()
}, [settingsData])
const getFonts = (itemValue) => {
if (!itemValue) return { id: 1, name: 'MS PGothic', value: 'MS PGothic' }
const data = [
{ id: 1, name: 'MS PGothic', value: 'MS PGothic' },
{ id: 2, name: '@Yu Gothic', value: '@Yu Gothic' },
{ id: 3, name: 'Yu Gothic', value: 'Yu Gothic' },
{ id: 4, name: '@Yu Gothic UI', value: '@Yu Gothic UI' },
{ id: 5, name: 'Yu Gothic UI', value: 'Yu Gothic UI' },
].filter((font) => font.value === itemValue)
if (data.length !== 0) {
return data[0]
} else {
return { id: 1, name: 'MS PGothic', value: 'MS PGothic' }
}
}
const getFontSizes = (itemValue) => {
if (!itemValue) return { id: 16, name: 16, value: 16 }
const data = [
...Array.from({ length: 4 }).map((_, index) => {
return { id: index + 8, name: index + 8, value: index + 8 }
}),
...Array.from({ length: 9 }).map((_, index) => {
return { id: (index + 6) * 2, name: (index + 6) * 2, value: (index + 6) * 2 }
}),
{ id: 36, name: 36, value: 36 },
{ id: 48, name: 48, value: 48 },
{ id: 72, name: 72, value: 72 },
].filter((fontSize) => fontSize.value === itemValue)
if (data.length !== 0) {
return data[0]
} else {
return { id: 16, name: 16, value: 16 }
}
}
const getFontStyles = (itemValue) => {
if (!itemValue) return { id: 'normal', name: getMessage('font.style.normal'), value: 'normal' }
const data = [
{ id: 'normal', name: getMessage('font.style.normal'), value: 'normal' },
{ id: 'italic', name: getMessage('font.style.italic'), value: 'italic' },
{ id: 'bold', name: getMessage('font.style.bold'), value: 'bold' },
{ id: 'boldAndItalic', name: getMessage('font.style.bold.italic'), value: 'boldAndItalic' },
].filter((fontStyle) => fontStyle.value === itemValue)
if (data.length !== 0) {
return data[0]
} else {
return { id: 'normal', name: getMessage('font.style.normal'), value: 'normal' }
}
}
const getFontColors = (itemValue) => {
if (!itemValue) return { id: 'black', name: getMessage('color.black'), value: 'black' }
const data = [
{ id: 'black', name: getMessage('color.black'), value: 'black' },
{ id: 'red', name: getMessage('color.red'), value: 'red' },
{ id: 'blue', name: getMessage('color.blue'), value: 'blue' },
{ id: 'gray', name: getMessage('color.gray'), value: 'gray' },
{ id: 'yellow', name: getMessage('color.yellow'), value: 'yellow' },
{ id: 'green', name: getMessage('color.green'), value: 'green' },
{ id: 'pink', name: getMessage('color.pink'), value: 'pink' },
{ id: 'gold', name: getMessage('color.gold'), value: 'gold' },
{ id: 'darkblue', name: getMessage('color.darkblue'), value: 'darkblue' },
].filter((fontColor) => fontColor.value === itemValue)
if (data.length !== 0) {
return data[0]
} else {
return { id: 'black', name: getMessage('color.black'), value: 'black' }
}
}
/**
* 기본설정(PlacementShapeSetting) 조회 및 초기화
*/
const fetchBasicSettings = async (planNo, openPoint, objectNo) => {
// objectNo가 전달되면 사용, 아니면 Recoil state의 correntObjectNo 사용
const targetObjectNo = objectNo || correntObjectNo
// 지붕재 데이터가 없으면 Header.jsx에서 로드될 때까지 기다림
let materials = roofMaterials
if (!materials || materials.length === 0) {
logger.log('Waiting for roofMaterials to be loaded from Header.jsx...')
// 최대 2초간 기다림 (20번 × 100ms)
let waitCount = 0
while ((!materials || materials.length === 0) && waitCount < 20) {
await new Promise(resolve => setTimeout(resolve, 100))
materials = roofMaterials
waitCount++
}
if (!materials || materials.length === 0) {
logger.log('roofMaterials still not loaded after 2 seconds, proceeding without them')
// 비상시에만 addRoofMaterials 호출 (fallback)
materials = await addRoofMaterials()
logger.log('roofMaterials loaded via fallback:', materials)
} else {
logger.log('roofMaterials loaded from Header.jsx:', materials)
}
}
try {
const apiUrl = `/api/canvas-management/canvas-basic-settings/by-object/${targetObjectNo}/${planNo}`
logger.log('🔍 fetchBasicSettings API 호출:', apiUrl)
logger.log('🔍 targetObjectNo:', targetObjectNo, 'planNo:', planNo)
await get({
url: apiUrl,
}).then((res) => {
logger.log('📦 API 응답 데이터:', res)
let roofsRow = {}
let roofsArray = {}
if (res.length > 0) {
roofsRow = res.map((item) => {
return {
planNo: item.planNo,
roofSizeSet: String(item.roofSizeSet),
roofAngleSet: item.roofAngleSet,
}
})
roofsArray = res.map((item) => {
return {
planNo: item.planNo,
roofApply: item.roofApply,
roofSeq: item.roofSeq,
roofMatlCd: item.roofMatlCd,
roofWidth: item.roofWidth,
roofHeight: item.roofHeight,
roofHajebichi: item.roofHajebichi,
roofGap: item.roofGap,
roofLayout: item.roofLayout,
roofPitch: item.roofPitch,
roofAngle: item.roofAngle,
}
})
/* 데이터 존재 시 화면 닫기(메뉴/저장 클릭 시 제외) */
if (openPoint !== 'canvasMenus' && openPoint !== 'basicSettingSave') {
//closePopup(popupId)
closeAll()
}
} else {
roofsRow = [
{
planNo: planNo,
roofSizeSet: '1',
roofAngleSet: 'slope',
},
]
roofsArray = [
{
planNo: planNo,
roofApply: true,
roofSeq: 0,
roofMatlCd: 'ROOF_ID_WA_53A',
roofWidth: 265,
roofHeight: 235,
roofHajebichi: 0,
roofGap: 'HEI_455',
roofLayout: 'P',
roofPitch: 4,
roofAngle: 21.8,
},
]
/** 메뉴에서 배치면 초기설정 클릭 시 실행하지 않음 */
if (openPoint === null) {
logger.log('🎯 팝업 열기 조건 충족: openPoint=', openPoint, 'res.length=', res.length)
/** 배치면 초기설정 미저장 상태이면 화면 열기 */
// 다른 closeAll 호출이 끝날 때까지 기다림
//setTimeout(() => {
const placementInitialProps = {
id : popupId,
pos : {
x: 50,
y: 180,
},
planNo : planNo,
openPoint: 'useCanvasSetting',
}
addPopup(popupId, 1, <PlacementShapeSetting {...placementInitialProps} />)
// },50)
} else {
logger.log('❌ 팝업 열기 조건 불충족: openPoint=', openPoint)
}
}
/** 데이터 설정 */
const addRoofs = []
for (let i = 0; i < roofsArray.length; i++) {
materials.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,
planNo: roofsRow[i].planNo,
roofSizeSet: roofsRow[i].roofSizeSet,
roofAngleSet: roofsRow[i].roofAngleSet,
pitch: roofsArray[i].roofPitch,
angle: roofsArray[i].roofAngle,
})
}
})
}
if (addRoofs.length > 0) {
setBasicSettings((prev) => {
return {
...basicSetting,
roofMaterials: addRoofs[0],
planNo: roofsRow[0].planNo,
roofSizeSet: roofsRow[0].roofSizeSet,
roofAngleSet: roofsRow[0].roofAngleSet,
roofsData: roofsArray,
selectedRoofMaterial: addRoofs.find((roof) => roof.selected),
}
})
setAddedRoofs(addRoofs)
if (openPoint !== 'basicSettingSave') {
// canvasSetting은 현재 값을 유지하고 basicSetting만 업데이트
// 새로고침 시 canvasSetting이 바뀌는 문제 방지
// 단, 플랜이 변경된 경우에는 항상 업데이트
const isPlanChanged = canvasSetting?.planNo !== undefined && Number(canvasSetting.planNo) !== Number(roofsRow[0].planNo)
if (!canvasSetting?.roofSizeSet || isPlanChanged) {
setCanvasSetting({
...basicSetting,
roofMaterials: addRoofs[0],
planNo: roofsRow[0].planNo,
roofSizeSet: roofsRow[0].roofSizeSet,
roofAngleSet: roofsRow[0].roofAngleSet,
roofsData: roofsArray,
selectedRoofMaterial: addRoofs.find((roof) => roof.selected),
})
}
}
}
})
} catch (error) {
console.error('Data fetching error:', error)
}
// setCanvasSetting({ ...basicSetting })
}
/**
* 저장/복사저장 시 지붕 크기에 따른 메뉴 설정
*/
const setMenuByRoofSize = (roofSizeSet) => {
if (['2', '3'].includes(String(roofSizeSet))) {
setSelectedMenu('surface')
setCurrentMenu(MENU.BATCH_CANVAS.BATCH_DRAWING)
} else {
setSelectedMenu('outline')
setCurrentMenu(MENU.ROOF_COVERING.EXTERIOR_WALL_LINE)
}
}
/**
* 기본설정(PlacementShapeSetting) 저장
*/
const basicSettingSave = async (params, options = {}) => {
const { skipSideEffects = false } = options
try {
const patternData = {
objectNo: correntObjectNo,
planNo: Number(params.planNo),
roofSizeSet: Number(params.roofSizeSet),
roofAngleSet: params.roofAngleSet,
roofMaterialsAddList: [
{
planNo: Number(params.planNo),
roofApply: true,
roofSeq: 0,
roofMatlCd:
params.roofsData.roofMatlCd === null || params.roofsData.roofMatlCd === undefined ? 'ROOF_ID_WA_53A' : params.roofsData.roofMatlCd,
roofWidth:
params.roofsData.roofWidth === null || params.roofsData.roofWidth === undefined
? params.selectedRoofMaterial.widBase
? Number(params.selectedRoofMaterial.widBase)
: 0
: Number(params.roofsData.roofWidth),
roofHeight:
params.roofsData.roofHeight === null || params.roofsData.roofHeight === undefined
? params.selectedRoofMaterial.lenBase
? Number(params.selectedRoofMaterial.lenBase)
: 0
: Number(params.roofsData.roofHeight),
roofHajebichi:
params.selectedRoofMaterial.hajebichi === null || params.selectedRoofMaterial.hajebichi === undefined
? 0
: Number(params.selectedRoofMaterial.hajebichi),
roofGap:
params.selectedRoofMaterial.raft === null || params.selectedRoofMaterial.raft === undefined
? params.selectedRoofMaterial.raftBaseCd
: params.roofsData.roofGap,
roofLayout: params.roofsData.roofLayout === null || params.roofsData.roofLayout === undefined ? 'P' : params.roofsData.roofLayout,
roofPitch:
params.roofsData.roofPitch === null || params.roofsData.roofPitch === undefined || params.roofsData.roofPitch === ''
? 0
: params.roofsData.roofPitch,
roofAngle:
params.roofsData.roofAngle === null || params.roofsData.roofAngle === undefined || params.roofsData.roofAngle === ''
? 0
: params.roofsData.roofAngle,
},
],
}
await post({ url: `/api/canvas-management/canvas-basic-settings`, data: patternData }).then((res) => {
swalFire({ text: getMessage(res.returnMessage) })
/** BasicSettings Recoil 설정 */
setBasicSettings({ ...params })
})
if (!skipSideEffects) {
/** CanvasSetting Recoil 설정 - roofSizeSet을 문자열로 변환 */
setCanvasSetting({
...basicSetting,
roofSizeSet: String(params.roofSizeSet),
})
/** 메뉴 설정 */
//setMenuByRoofSize(params.roofSizeSet)
}
//메뉴를 찾아가지 못하는 오류 발생으로 변경
setMenuByRoofSize(params.roofSizeSet)
/** 배치면초기설정 조회 */
fetchBasicSettings(params.planNo, 'basicSettingSave')
/** 모듈 선택 데이터 초기화 */
resetModuleSelectionData()
//1번 초기화
orientationTrigger({ compasDeg: 0, common: {}, module: {} })
//2번 초기화
moduleSelectedDataTrigger({ roofConstructions: [] })
const isModuleExist = canvas.getObjects().some((obj) => obj.name === POLYGON_TYPE.MODULE)
if (!isModuleExist) {
resetSelectedModules()
}
} catch (error) {
swalFire({ text: error.message, icon: 'error' })
}
}
/**
* 기본설정(PlacementShapeSetting) 복사 저장
*/
const basicSettingCopySave = async (params) => {
try {
// roofsData가 단일 항목인 경우, 모든 추가된 지붕재(addedRoofs)를 사용하여 다중 항목으로 확장
let roofMaterialsList = []
if (params.roofsData && params.roofsData.length === 1) {
// 단일 항목인 경우 addedRoofs의 모든 항목을 사용
if (addedRoofs && addedRoofs.length > 0) {
roofMaterialsList = addedRoofs.map((roof, index) => ({
planNo: Number(params.planNo),
roofApply: roof.selected || index === 0, // 첫 번째 또는 선택된 항목
roofSeq: index,
roofMatlCd: roof.roofMatlCd,
roofWidth: roof.width || roof.roofWidth,
roofHeight: roof.length || roof.roofHeight,
roofHajebichi: roof.hajebichi || 0,
roofGap: roof.raft || roof.roofGap,
roofLayout: roof.layout || 'P',
roofPitch: roof.pitch || 0,
roofAngle: roof.angle || 0,
}))
} else {
// addedRoofs가 비어있을 경우 원래 단일 항목 사용
roofMaterialsList = params.roofsData.map((item) => ({
planNo: Number(item.planNo),
roofApply: item.roofApply,
roofSeq: item.roofSeq,
roofMatlCd: item.roofMatlCd,
roofWidth: item.roofWidth,
roofHeight: item.roofHeight,
roofHajebichi: item.roofHajebichi,
roofGap: item.roofGap,
roofLayout: item.roofLayout,
roofPitch: item.roofPitch,
roofAngle: item.roofAngle,
}))
}
} else {
// 다중 항목인 경우 기존 방식 사용
roofMaterialsList = params.roofsData.map((item) => ({
planNo: Number(item.planNo),
roofApply: item.roofApply,
roofSeq: item.roofSeq,
roofMatlCd: item.roofMatlCd,
roofWidth: item.roofWidth,
roofHeight: item.roofHeight,
roofHajebichi: item.roofHajebichi,
roofGap: item.roofGap,
roofLayout: item.roofLayout,
roofPitch: item.roofPitch,
roofAngle: item.roofAngle,
}))
}
const patternData = {
objectNo: correntObjectNo,
planNo: Number(params.planNo),
roofSizeSet: Number(params.roofSizeSet),
roofAngleSet: params.roofAngleSet,
roofMaterialsAddList: roofMaterialsList,
}
await post({ url: `/api/canvas-management/canvas-basic-settings`, data: patternData }).then((res) => {
swalFire({ text: getMessage(res.returnMessage) })
})
/** CanvasSetting Recoil 설정 - roofSizeSet을 문자열로 변환 */
setCanvasSetting({
...basicSetting,
roofSizeSet: String(params.roofSizeSet),
})
/** 메뉴 설정 */
setMenuByRoofSize(params.roofSizeSet)
/** 배치면초기설정 조회 */
fetchBasicSettings(Number(params.planNo), 'basicSettingSave')
/** 모듈 선택 데이터 초기화 */
resetModuleSelectionData()
// moduleSelectedDataTrigger({ common: {}, module: {}, roofConstructions: [] })
const isModuleExist = canvas.getObjects().some((obj) => obj.name === POLYGON_TYPE.MODULE)
if (!isModuleExist) {
resetSelectedModules()
}
} catch (error) {
swalFire({ text: error.message, icon: 'error' })
}
}
/**
* CanvasSetting 조회 및 초기화
*/
const fetchSettings = async () => {
try {
initEvent()
const res = await get({ url: `/api/canvas-management/canvas-settings/by-object/${correntObjectNo}` })
if (Object.keys(res).length > 0) {
const optionData1 = settingModalFirstOptions.option1.map((item) => ({ ...item, selected: res[item.column] }))
const optionData2 = settingModalFirstOptions.option2.map((item) => ({ ...item, selected: res[item.column] }))
const optionData3 = settingModalSecondOptions.option3.map((item) => ({ ...item }))
const optionData4 = settingModalSecondOptions.option4.map((item) => ({ ...item, selected: res[item.column] }))
const optionData5 = settingModalFirstOptions.dimensionDisplay.map((item) => ({ ...item }))
/** 흡착점 ON/OFF */
setAdsorptionPointMode(res.adsorpPoint)
/** 치수선 설정 */
setDimensionLineSettings({ ...dimensionLineSettings, pixel: res.originPixel, color: res.originColor })
/** 도면크기 설정 */
setPlanSizeSettingMode({
...planSizeSettingMode,
originHorizon: res.originHorizon,
originVertical: res.originVertical,
})
if (canvas) {
canvas.setWidth(res.originHorizon)
canvas.setHeight(res.originVertical)
canvas.renderAll()
}
/** 데이터 설정 */
setSettingModalFirstOptions({
...settingModalFirstOptions,
option1: optionData1,
option2: optionData2,
dimensionDisplay: optionData5,
})
setSettingModalSecondOptions({
...settingModalSecondOptions,
option3: optionData3,
option4: optionData4,
})
const fontPatternData = {
commonText: {
/** 문자 글꼴 조회 데이터 */
fontFamily: getFonts(res.wordFont),
fontWeight: getFontStyles(res.wordFontStyle),
fontSize: getFontSizes(res.wordFontSize),
fontColor: getFontColors(res.wordFontColor),
},
flowText: {
/** 흐름방향 글꼴 조회 데이터 */
fontFamily: getFonts(res.flowFont),
fontWeight: getFontStyles(res.flowFontStyle),
fontSize: getFontSizes(res.flowFontSize),
fontColor: getFontColors(res.flowFontColor),
},
dimensionLineText: {
/** 치수 글꼴 조회 데이터 */
fontFamily: getFonts(res.dimensioFont),
fontWeight: getFontStyles(res.dimensioFontStyle),
fontSize: getFontSizes(res.dimensioFontSize),
fontColor: getFontColors(res.dimensioFontColor),
},
circuitNumberText: {
/** 회로번호 글꼴 조회 데이터 */
fontFamily: getFonts(res.circuitNumFont),
fontWeight: getFontStyles(res.circuitNumFontStyle),
fontSize: getFontSizes(res.circuitNumFontSize),
fontColor: getFontColors(res.circuitNumFontColor),
},
lengthText: {
/** 치수선 글꼴 조회 데이터 */
fontFamily: getFonts(res.lengthFont),
fontWeight: getFontStyles(res.lengthFontStyle),
fontSize: getFontSizes(res.lengthFontSize),
fontColor: getFontColors(res.lengthFontColor),
},
}
/** 조회된 글꼴 데이터 set */
setGlobalFont(fontPatternData)
/** 점/선 그리드 */
const patternData = {
INTERVAL: {
type: res.gridType,
horizontalInterval: res.gridHorizon * 10,
verticalInterval: res.gridVertical * 10,
ratioInterval: res.gridRatio * 10,
dimension: res.gridDimen,
},
DOT: res.dotGridDisplay,
LINE: res.lineGridDisplay,
}
setDotLineGridSettingState(patternData)
/** 그리드 색 설정 */
setGridColor(res.gridColor)
} else {
/** 조회된 글꼴 데이터가 없는 경우 (데이터 초기화) */
/** 흡착점 ON/OFF */
setAdsorptionPointMode(true)
/** 치수선 설정 */
resetDimensionLineSettings()
/** 도면크기 설정 */
resetPlanSizeSettingMode()
/** 데이터 설정 */
resetSettingModalFirstOptions()
resetSettingModalSecondOptions()
/** 데이터 초기화 */
resetGlobalFont()
/** 점/선 그리드 */
setDotLineGridSettingState({ ...defaultDotLineGridSetting })
/** 그리드 색 설정 */
setGridColor('#FF0000')
}
frontSettings()
} catch (error) {
console.error('Data fetching error:', error)
}
}
/**
* CanvasSetting 옵션 클릭 후 저장
*/
const onClickOption2 = async () => {
/** 서버에 전송할 데이터 */
const dataToSend = {
firstOption1: option1.map((item) => ({
column: item.column,
selected: item.selected,
})),
firstOption2: option2.map((item) => ({
column: item.column,
selected: item.selected,
})),
firstOption3: dimensionDisplay.map((item) => ({
column: item.column,
selected: item.selected,
})),
secondOption2: option4.map((item) => ({
column: item.column,
selected: item.selected,
range: item.range,
})),
}
const patternData = {
/** 견적서 번호 */
objectNo: correntObjectNo,
/** 디스플레이 설정(다중) */
allocDisplay: dataToSend.firstOption1[0].selected,
outlineDisplay: dataToSend.firstOption1[1].selected,
gridDisplay: dataToSend.firstOption1[2].selected,
lineDisplay: dataToSend.firstOption1[3].selected,
wordDisplay: dataToSend.firstOption1[4].selected,
circuitNumDisplay: dataToSend.firstOption1[5].selected,
flowDisplay: dataToSend.firstOption1[6].selected,
trestleDisplay: dataToSend.firstOption1[7].selected,
imageDisplay: dataToSend.firstOption1[8].selected,
totalDisplay: dataToSend.firstOption1[9].selected,
/** 차수 표시(단 건) */
corridorDimension: dataToSend.firstOption3[0].selected,
realDimension: dataToSend.firstOption3[1].selected,
noneDimension: dataToSend.firstOption3[2].selected,
/** 화면 표시(단 건) */
onlyBorder: dataToSend.firstOption2[0].selected,
lineHatch: dataToSend.firstOption2[1].selected,
allPainted: dataToSend.firstOption2[2].selected,
/** 흡착범위 설정(단 건) */
adsorpRangeSmall: dataToSend.secondOption2[0].selected,
adsorpRangeSmallSemi: dataToSend.secondOption2[1].selected,
adsorpRangeMedium: dataToSend.secondOption2[2].selected,
adsorpRangeLarge: dataToSend.secondOption2[3].selected,
/** 흡착점 ON/OFF */
adsorpPoint: adsorptionPointMode,
//??: adsorptionRange, 사용여부 확인 필요
/** 문자 글꼴 설정 */
wordFont: globalFont.commonText.fontFamily?.value ?? 'MS PGothic',
wordFontStyle: globalFont.commonText.fontWeight?.value ?? 'normal',
wordFontSize: globalFont.commonText.fontSize?.value ?? 28,
wordFontColor: globalFont.commonText.fontColor?.value ?? 'black',
/** 흐름방향 글꼴 설정 */
flowFont: globalFont.flowText.fontFamily?.value ?? 'MS PGothic',
flowFontStyle: globalFont.flowText.fontWeight?.value ?? 'normal',
flowFontSize: globalFont.flowText.fontSize?.value ?? 28,
flowFontColor: globalFont.flowText.fontColor?.value ?? 'black',
/** 치수 글꼴 설정 */
dimensioFont: globalFont.dimensionLineText.fontFamily?.value ?? 'MS PGothic',
dimensioFontStyle: globalFont.dimensionLineText.fontWeight?.value ?? 'normal',
dimensioFontSize: globalFont.dimensionLineText.fontSize?.value ?? 28,
dimensioFontColor: globalFont.dimensionLineText.fontColor?.value ?? 'black',
/** 회로번호 글꼴 설정 */
circuitNumFont: globalFont.circuitNumberText.fontFamily?.value ?? 'MS PGothic',
circuitNumFontStyle: globalFont.circuitNumberText.fontWeight?.value ?? 'normal',
circuitNumFontSize: globalFont.circuitNumberText.fontSize?.value ?? 36,
circuitNumFontColor: globalFont.circuitNumberText.fontColor?.value ?? 'black',
/** 치수선 글꼴 설정 */
lengthFont: globalFont.lengthText.fontFamily?.value ?? 'MS PGothic',
lengthFontStyle: globalFont.lengthText.fontWeight?.value ?? 'normal',
lengthFontSize: globalFont.lengthText.fontSize?.value ?? 16,
lengthFontColor: globalFont.lengthText.fontColor?.value ?? 'black',
/** 치수선 설정 */
originPixel: dimensionLineSettings.pixel,
originColor: dimensionLineSettings.color,
/** 도면크기 설정 */
originHorizon: planSizeSettingMode.originHorizon,
originVertical: planSizeSettingMode.originVertical,
/** 점/선 그리드 */
dotGridDisplay: dotLineGridSetting.DOT,
lineGridDisplay: dotLineGridSetting.LINE,
gridType: dotLineGridSetting.INTERVAL.type,
gridHorizon: dotLineGridSetting.INTERVAL.horizontalInterval / 10,
gridVertical: dotLineGridSetting.INTERVAL.verticalInterval / 10,
gridRatio: dotLineGridSetting.INTERVAL.ratioInterval / 10,
gridDimen: dotLineGridSetting.INTERVAL.dimension,
/** 그리드 색 설정 */
gridColor: gridColor,
}
logger.log('patternData ', patternData)
/**
* 저장 API 호출
*/
await post({ url: `/api/canvas-management/canvas-settings`, data: patternData })
.then((res) => {
//swalFire({ text: getMessage(res.returnMessage) })
/** Canvas 디스플레이 설정 시 해당 옵션 적용 */
frontSettings()
/** 저장 후 재조회 */
fetchSettings()
})
.catch((error) => {
//swalFire({ text: getMessage(res.returnMessage), icon: 'error' })
swalFire({ text: error.message, icon: 'error' })
})
//setAdsorptionRange(item.range)
}
/**
* Canvas 디스플레이 설정 시 해당 옵션 적용
*/
const frontSettings = async () => {
const option1 = settingModalFirstOptions.option1
/**
* 'allocDisplay' 할당 표시
* 'outlineDisplay' 외벽선 표시 'outerLine', POLYGON_TYPE.WALL
* 'gridDisplay' 그리드 표시 'lindGrid', 'dotGrid'
* 'lineDisplay' 지붕선 표시 'roof', POLYGON_TYPE.ROOF
* 'wordDisplay' 문자 표시
* 'circuitNumDisplay' 회로번호 표시
* 'flowDisplay' 흐름방향 표시 'arrow', 'flowText'
* 'trestleDisplay' 가대 표시
* 'imageDisplay' 이미지 표시
* 'totalDisplay' 집계표 표시
*/
/**
* 옵션명 optionName
* 옵션상태 optionSelected
*/
let optionName
let optionSelected
for (let i = 0; i < option1.length; i++) {
switch (option1[i].column) {
case 'allocDisplay':
optionName = []
break
case 'outlineDisplay':
optionName = ['outerLine', POLYGON_TYPE.WALL]
break
case 'gridDisplay':
optionName = ['lindGrid', 'dotGrid', 'tempGrid']
break
case 'lineDisplay':
optionName = ['roof', POLYGON_TYPE.ROOF]
break
case 'wordDisplay':
optionName = ['commonText']
break
case 'circuitNumDisplay':
optionName = ['circuitNumber']
break
case 'flowDisplay':
optionName = ['arrow', 'flowText']
break
case 'trestleDisplay':
optionName = ['rack', 'smartRack', 'bracket', 'eaveBar', 'halfEaveBar']
break
case 'imageDisplay':
optionName = ['backGroundImage']
break
case 'totalDisplay':
/** 작업할 필요 없음 */
optionName = []
break
}
/** 표시 선택 상태(true/false)*/
optionSelected = option1[i].selected
canvasObjects
.filter((obj) => optionName.includes(obj.name))
.forEach((obj) => {
obj.set({ visible: optionSelected })
})
canvas?.renderAll()
}
}
return {
canvas,
correntObjectNo,
settingModalFirstOptions,
setSettingModalFirstOptions,
settingModalSecondOptions,
setSettingModalSecondOptions,
adsorptionPointMode,
setAdsorptionPointMode,
adsorptionRange,
setAdsorptionRange,
fetchSettings,
fetchBasicSettings,
basicSettingCopySave,
frontSettings,
globalFont,
setGlobalFont,
selectedFont,
setSelectedFont,
selectedFontWeight,
setSelectedFontWeight,
selectedFontSize,
setSelectedFontSize,
selectedFontColor,
setSelectedFontColor,
dimensionLineSettings,
setDimensionLineSettings,
planSizeSettingMode,
setPlanSizeSettingMode,
SelectOptions,
currentSetting,
setCurrentSetting,
dotLineGridSettingState,
setDotLineGridSettingState,
resetDotLineGridSetting,
setSettingModalGridOptions,
gridColor,
setGridColor,
color,
setColor,
canvasSetting,
setCanvasSetting,
basicSetting,
setBasicSettings,
basicSettingSave,
settingsData,
setSettingsData,
settingsDataSave,
setSettingsDataSave,
addedRoofs,
setAddedRoofs,
}
}