Merge branch 'dev' into feature/test-jy

# Conflicts:
#	src/util/qpolygon-utils.js
This commit is contained in:
Jaeyoung Lee 2024-11-14 17:32:53 +09:00
commit 9752925cd1
61 changed files with 7068 additions and 4970 deletions

View File

@ -28,3 +28,4 @@ Allpainted : allPainted
치수선: dimensionLine 치수선: dimensionLine
복도치수: planeSize 복도치수: planeSize
실제치수: actualSize 실제치수: actualSize
모듈설치면: moduleSetupSurface

View File

@ -0,0 +1,4 @@
<svg width="17" height="16" viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.75 3.94737H12.0625C13.6425 3.94737 14.4325 3.94737 15 4.31991C15.2457 4.48119 15.4567 4.68842 15.6208 4.92979C16 5.48734 16 6.26349 16 7.81579C16 8.02719 16.0003 8.01008 16 8.36842M8.5 3.94737L8.02492 3.01388C7.63134 2.24053 7.27167 1.46209 6.39944 1.14074C6.01742 1 5.58102 1 4.7082 1C3.34585 1 2.66467 1 2.15355 1.28023C1.7892 1.47999 1.48857 1.77535 1.28524 2.13331C1 2.63547 1 3.30469 1 4.64315V6.89474C1 10.3682 1 12.105 2.09835 13.1841C3.11612 14.184 4.7087 14.2574 7.75 14.2627" stroke="white" stroke-linecap="round"/>
<path d="M12.7793 14.5884C13.3445 15.1372 14.2609 15.1372 14.8261 14.5884C15.3914 14.0396 15.3914 13.1498 14.8261 12.601L13.5469 11.3589C13.0473 10.8739 12.2733 10.8175 11.71 11.19M11.7208 9.51684C11.1556 8.96805 10.2392 8.96805 9.67397 9.51684C9.10876 10.0656 9.10876 10.9554 9.67397 11.5042L10.9532 12.7463C11.462 13.2403 12.2554 13.2896 12.8211 12.8942" stroke="white" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -0,0 +1,20 @@
'ues client'
import { createContext, useEffect, useState } from 'react'
export const ManagementContext = createContext({
managementState: {},
setManagementState: () => {},
})
const ManagementProvider = ({ children }) => {
const [managementState, setManagementState] = useState({})
useEffect(() => {
console.log('🚀 ~ managementState:', managementState)
}, [managementState])
return <ManagementContext.Provider value={{ managementState, setManagementState }}>{children}</ManagementContext.Provider>
}
export default ManagementProvider

View File

@ -0,0 +1,7 @@
'use client'
import ManagementProvider from './ManagementProvider'
export default function ManagementLayout({ children }) {
return <ManagementProvider>{children}</ManagementProvider>
}

View File

@ -61,6 +61,8 @@ export const LINE_TYPE = {
DEFAULT: 'default', DEFAULT: 'default',
EAVES: 'eaves', EAVES: 'eaves',
GABLE: 'gable', GABLE: 'gable',
GABLE_LEFT: 'gableLeft', //케라바 왼쪽
GABLE_RIGHT: 'gableRight', //케라바 오른쪽
WALL: 'wall', WALL: 'wall',
HIPANDGABLE: 'hipAndGable', HIPANDGABLE: 'hipAndGable',
JERKINHEAD: 'jerkinhead', JERKINHEAD: 'jerkinhead',
@ -74,30 +76,20 @@ export const LINE_TYPE = {
HIP: 'hip', HIP: 'hip',
RIDGE: 'ridge', RIDGE: 'ridge',
GABLE: 'gable', GABLE: 'gable',
VALLEY: 'valley',
VERGE: 'verge', VERGE: 'verge',
ONESIDE_FLOW_RIDGE: 'onesideFlowRidge', //한쪽흐름 용마루
YOSEMUNE: 'yosemune', //요세무네
VALLEY: 'valley', //골짜기
L_ABANDON_VALLEY: 'lAbandonValley', //l의버림계곡
MANSARD: 'mansard', //맨사드
WALL_COLLECTION: 'wallCollection', //벽취합
WALL_COLLECTION_TYPE: 'wallCollectionType', //벽취합(형)
WALL_COLLECTION_FLOW: 'wallCollectionFlow', //벽취합(흐름)
WALL_COLLECTION_FLOW_LEFT: 'wallCollectionFlowLeft', //벽취합(흐름 왼쪽)
WALL_COLLECTION_FLOW_RIGHT: 'wallCollectionFlowRight', //벽취합(흐름 오른쪽)
}, },
} }
export const LineType = {
EAVES: 'eaves', // 처마
RIDGE: 'ridge', // 용마루....
YOSEMUNE: 'yosemune', //요세무네
ONESIDE_FLOW_RIDGE: 'onesideFlowRidge', //한쪽흐름 용마루
WALL_COLLECTION: 'wallCollection', //벽취합
WALL_COLLECTION_TYPE: 'wallCollectionType', //벽취합(형)
WALL_COLLECTION_FLOW: 'wallCollectionFlow', //벽취합(흐름)
WALL_COLLECTION_FLOW_LEFT: 'wallCollectionFlowLeft', //벽취합(흐름 왼쪽)
WALL_COLLECTION_FLOW_RIGHT: 'wallCollectionFlowRight', //벽취합(흐름 오른쪽)
KERABA: 'keraba', //케라바
KERABA_LEFT: 'kerabaLeft', //케라바 왼쪽
KERABA_RIGHT: 'kerabaRight', //케라바 오른쪽
VALLEY: 'valley', //골짜기
L_ABANDON_VALLEY: 'lAbandonValley', //l의버림계곡
MANSARD: 'mansard', //맨사드
NO_SETTING: 'noSetting', //설정없음
}
// 오브젝트 배치 > 개구배치, 그림자배치 // 오브젝트 배치 > 개구배치, 그림자배치
export const BATCH_TYPE = { export const BATCH_TYPE = {
OPENING: 'opening', OPENING: 'opening',
@ -119,6 +111,7 @@ export const POLYGON_TYPE = {
ROOF: 'roof', ROOF: 'roof',
WALL: 'wall', WALL: 'wall',
TRESTLE: 'trestle', TRESTLE: 'trestle',
MODULE_SETUP_SURFACE: 'moduleSetupSurface',
} }
export const SAVE_KEY = [ export const SAVE_KEY = [
@ -161,6 +154,13 @@ export const SAVE_KEY = [
'groupId', 'groupId',
'planeSize', 'planeSize',
'actualSize', 'actualSize',
'surfaceId',
'lines',
'offset',
'arrow',
'surfaceCompass',
'moduleCompass',
'isFixed',
] ]
export const OBJECT_PROTOTYPE = [fabric.Line.prototype, fabric.Polygon.prototype, fabric.Triangle.prototype] export const OBJECT_PROTOTYPE = [fabric.Line.prototype, fabric.Polygon.prototype, fabric.Triangle.prototype]

View File

@ -1,9 +1,8 @@
'use client' 'use client'
import React, { useEffect, useState } from 'react' import { useEffect, useState, useContext } from 'react'
import { useRouter } from 'next/navigation' import { useRouter } from 'next/navigation'
import { useRecoilState, useRecoilValue } from 'recoil' import { useRecoilState, useRecoilValue } from 'recoil'
import { sessionStore } from '@/store/commonAtom'
import { useAxios } from '@/hooks/useAxios' import { useAxios } from '@/hooks/useAxios'
import { globalLocaleStore } from '@/store/localeAtom' import { globalLocaleStore } from '@/store/localeAtom'
import MainContents from './main/MainContents' import MainContents from './main/MainContents'
@ -12,8 +11,10 @@ import { stuffSearchState } from '@/store/stuffAtom'
import '@/styles/contents.scss' import '@/styles/contents.scss'
import ChangePasswordPop from './main/ChangePasswordPop' import ChangePasswordPop from './main/ChangePasswordPop'
import { searchState } from '@/store/boardAtom' import { searchState } from '@/store/boardAtom'
import { SessionContext } from '@/app/SessionProvider'
export default function MainPage() { export default function MainPage() {
const sessionState = useRecoilValue(sessionStore) const { session } = useContext(SessionContext)
const globalLocaleState = useRecoilValue(globalLocaleStore) const globalLocaleState = useRecoilValue(globalLocaleStore)
@ -33,14 +34,14 @@ export default function MainPage() {
const [searchForm, setSearchForm] = useRecoilState(searchState) const [searchForm, setSearchForm] = useRecoilState(searchState)
useEffect(() => { useEffect(() => {
if (sessionState.pwdInitYn === 'Y') { if (session.pwdInitYn === 'Y') {
fetchObjectList() fetchObjectList()
} }
}, [sessionState]) }, [session])
const fetchObjectList = async () => { const fetchObjectList = async () => {
try { try {
const apiUrl = `/api/main-page/object/${sessionState?.storeId}/list` const apiUrl = `/api/main-page/object/${session?.storeId}/list`
await promiseGet({ await promiseGet({
url: apiUrl, url: apiUrl,
}).then((res) => { }).then((res) => {
@ -95,7 +96,7 @@ export default function MainPage() {
return ( return (
<> <>
{(sessionState?.pwdInitYn !== 'N' && ( {(session?.pwdInitYn !== 'N' && (
<> <>
<div className="background-bord"></div> <div className="background-bord"></div>
<div className="main-contents"> <div className="main-contents">

View File

@ -83,7 +83,7 @@ export default function Login() {
/////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////
// //
setSession({ setSession({
userId: 'NEW016610', userId: 'NEW0166102',
saleStoreId: null, saleStoreId: null,
name: null, name: null,
mail: null, mail: null,
@ -101,7 +101,7 @@ export default function Login() {
custCd: '100000', custCd: '100000',
}) })
setSessionState({ setSessionState({
userId: 'NEW016610', userId: 'NEW0166102',
saleStoreId: null, saleStoreId: null,
name: null, name: null,
mail: null, mail: null,

View File

@ -3,76 +3,69 @@ import QSelectBox from '@/components/common/select/QSelectBox'
import { usePopup } from '@/hooks/usePopup' import { usePopup } from '@/hooks/usePopup'
import { useState } from 'react' import { useState } from 'react'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { useRecoilState, useRecoilValue } from 'recoil' import { useRecoilValue } from 'recoil'
import { fontSelector, globalFontAtom } from '@/store/fontAtom'
import { contextPopupPositionState } from '@/store/popupAtom' import { contextPopupPositionState } from '@/store/popupAtom'
const fonts = [ const fonts = [
{ name: 'MS PGothic', value: 'MS PGothic' }, { id: 1, name: 'MS PGothic', value: 'MS PGothic' },
{ name: '@Yu Gothic', value: '@Yu Gothic' }, { id: 2, name: '@Yu Gothic', value: '@Yu Gothic' },
{ name: 'Yu Gothic', value: 'Yu Gothic' }, { id: 3, name: 'Yu Gothic', value: 'Yu Gothic' },
{ name: '@Yu Gothic UI', value: '@Yu Gothic UI' }, { id: 4, name: '@Yu Gothic UI', value: '@Yu Gothic UI' },
{ name: 'Yu Gothic UI', value: 'Yu Gothic UI' }, { id: 5, name: 'Yu Gothic UI', value: 'Yu Gothic UI' },
3,
] ]
const fontSizes = [ const fontSizes = [
...Array.from({ length: 4 }).map((_, index) => { ...Array.from({ length: 4 }).map((_, index) => {
return { name: index + 8, value: index + 8 } return { id: index + 8, name: index + 8, value: index + 8 }
}), }),
...Array.from({ length: 9 }).map((_, index) => { ...Array.from({ length: 9 }).map((_, index) => {
return { name: (index + 6) * 2, value: (index + 6) * 2 } return { id: (index + 6) * 2, name: (index + 6) * 2, value: (index + 6) * 2 }
}), }),
{ name: 36, value: 36 }, { id: 36, name: 36, value: 36 },
{ name: 48, value: 48 }, { id: 48, name: 48, value: 48 },
{ name: 72, value: 72 }, { id: 72, name: 72, value: 72 },
] ]
export default function FontSetting(props) { export default function FontSetting(props) {
const contextPopupPosition = useRecoilValue(contextPopupPositionState) const contextPopupPosition = useRecoilValue(contextPopupPositionState)
const { id, setIsShow, pos = contextPopupPosition, type, isConfig = false } = props const { id, setIsShow, pos = contextPopupPosition, type, isConfig = false, onSave, font } = props
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { closePopup } = usePopup() const { closePopup } = usePopup()
const [globalFont, setGlobalFont] = useRecoilState(globalFontAtom) const [selectedFont, setSelectedFont] = useState(font.fontFamily)
const currentFont = useRecoilValue(fontSelector(type)) const [selectedFontWeight, setSelectedFontWeight] = useState(font.fontWeight)
const [selectedFontSize, setSelectedFontSize] = useState(font.fontSize)
const [selectedFont, setSelectedFont] = useState(currentFont.fontFamily) const [selectedFontColor, setSelectedFontColor] = useState(font.fontColor)
const [selectedFontWeight, setSelectedFontWeight] = useState(currentFont.fontWeight)
const [selectedFontSize, setSelectedFontSize] = useState(currentFont.fontSize)
const [selectedFontColor, setSelectedFontColor] = useState(currentFont.fontColor)
const fontOptions = [ const fontOptions = [
{ name: getMessage('font.style.normal'), value: 'normal' }, { id: 'normal', name: getMessage('font.style.normal'), value: 'normal' },
{ name: getMessage('font.style.italic'), value: 'italic' }, { id: 'italic', name: getMessage('font.style.italic'), value: 'italic' },
{ {
id: 'bold',
name: getMessage('font.style.bold'), name: getMessage('font.style.bold'),
value: 'bold', value: 'bold',
}, },
{ name: getMessage('font.style.bold.italic'), value: 'boldAndItalic' }, { id: 'boldAndItalic', name: getMessage('font.style.bold.italic'), value: 'boldAndItalic' },
] ]
const fontColors = [ const fontColors = [
{ name: getMessage('color.black'), value: 'black' }, { id: 'black', name: getMessage('color.black'), value: 'black' },
{ name: getMessage('color.red'), value: 'red' }, { id: 'red', name: getMessage('color.red'), value: 'red' },
{ name: getMessage('color.blue'), value: 'blue' }, { id: 'blue', name: getMessage('color.blue'), value: 'blue' },
{ name: getMessage('color.gray'), value: 'gray' }, { id: 'gray', name: getMessage('color.gray'), value: 'gray' },
{ name: getMessage('color.yellow'), value: 'yellow' }, { id: 'yellow', name: getMessage('color.yellow'), value: 'yellow' },
{ name: getMessage('color.green'), value: 'green' }, { id: 'green', name: getMessage('color.green'), value: 'green' },
{ name: getMessage('color.pink'), value: 'pink' }, { id: 'pink', name: getMessage('color.pink'), value: 'pink' },
{ name: getMessage('color.gold'), value: 'gold' }, { id: 'gold', name: getMessage('color.gold'), value: 'gold' },
{ name: getMessage('color.darkblue'), value: 'darkblue' }, { id: 'darkblue', name: getMessage('color.darkblue'), value: 'darkblue' },
] ]
const handleSaveBtn = () => { const handleSaveBtn = () => {
setGlobalFont((prev) => { onSave({
return { fontFamily: selectedFont,
...prev, fontSize: selectedFontSize,
[type]: { fontColor: selectedFontColor,
fontFamily: selectedFont, fontWeight: selectedFontWeight,
fontSize: selectedFontSize,
fontColor: selectedFontColor,
fontWeight: selectedFontWeight,
},
}
}) })
if (setIsShow) setIsShow(false) if (setIsShow) setIsShow(false)
closePopup(id) closePopup(id, isConfig)
} }
return ( return (
@ -125,7 +118,8 @@ export default function FontSetting(props) {
style={{ style={{
fontFamily: selectedFont?.value ?? '', fontFamily: selectedFont?.value ?? '',
fontSize: selectedFontSize?.value ?? '12px', fontSize: selectedFontSize?.value ?? '12px',
fontWeight: '400', fontStyle: selectedFontWeight?.value.toLowerCase().includes('italic') ? 'italic' : 'normal',
fontWeight: selectedFontWeight?.value.toLowerCase().includes('bold') ? 'bold' : 'normal',
color: selectedFontColor?.value ?? 'black', color: selectedFontColor?.value ?? 'black',
}} }}
> >

View File

@ -18,8 +18,8 @@ export default function QSelectBox({ title = '', options, onChange, value, disab
<div className={`sort-select ${openSelect ? 'active' : ''}`} onClick={disabled ? () => {} : () => setOpenSelect(!openSelect)}> <div className={`sort-select ${openSelect ? 'active' : ''}`} onClick={disabled ? () => {} : () => setOpenSelect(!openSelect)}>
<p>{selected}</p> <p>{selected}</p>
<ul className="select-item-wrap"> <ul className="select-item-wrap">
{options?.map((option) => ( {options?.map((option, index) => (
<li key={option.id} className="select-item" onClick={() => handleClickSelectOption(option)}> <li key={option.id || index} className="select-item" onClick={() => handleClickSelectOption(option)}>
<button key={option.id + 'btn'}>{option.name}</button> <button key={option.id + 'btn'}>{option.name}</button>
</li> </li>
))} ))}

View File

@ -15,9 +15,12 @@ import { useCommonCode } from '@/hooks/common/useCommonCode'
import { useEstimateController } from '@/hooks/floorPlan/estimate/useEstimateController' import { useEstimateController } from '@/hooks/floorPlan/estimate/useEstimateController'
import { SessionContext } from '@/app/SessionProvider' import { SessionContext } from '@/app/SessionProvider'
import Select, { components } from 'react-select' import Select, { components } from 'react-select'
// import EstimateItemTable from './EstimateItemTable' import { convertNumberToPriceDecimal } from '@/util/common-utils'
import ProductFeaturesPop from './popup/ProductFeaturesPop'
import { v4 as uuidv4 } from 'uuid'
export default function Estimate({ params }) { export default function Estimate({ params }) {
const [itemChangeYn, setItemChangeYn] = useState(false)
const { session } = useContext(SessionContext) const { session } = useContext(SessionContext)
const [objectNo, setObjectNo] = useState('') // const [objectNo, setObjectNo] = useState('') //
const [planNo, setPlanNo] = useState('') // const [planNo, setPlanNo] = useState('') //
@ -27,15 +30,24 @@ export default function Estimate({ params }) {
const [showContentCode, setShowContentCode] = useState('ATTR001') const [showContentCode, setShowContentCode] = useState('ATTR001')
const [productFeaturesPopupOpen, setProductFeaturesPopupOpen] = useState(false) //
const [showProductFeatureData, setShowProductFeatureData] = useState([]) //
const [selection, setSelection] = useState(new Set())
// //
const [hidden, setHidden] = useState(false) const [hidden, setHidden] = useState(false)
//
const [displayItemList, setDisplayItemList] = useState([])
// //
const { findCommonCode } = useCommonCode() const { findCommonCode } = useCommonCode()
const [honorificCodeList, setHonorificCodeList] = useState([]) // const [honorificCodeList, setHonorificCodeList] = useState([]) //
const [storePriceList, setStorePriceList] = useState([]) // option const [storePriceList, setStorePriceList] = useState([]) // option
const [tempPriceCd, setTempPriceCd] = useState('')
const [startDate, setStartDate] = useState(new Date()) const [startDate, setStartDate] = useState(new Date())
const singleDatePickerProps = { const singleDatePickerProps = {
startDate, startDate,
@ -45,9 +57,7 @@ export default function Estimate({ params }) {
const objectRecoil = useRecoilValue(floorPlanObjectState) const objectRecoil = useRecoilValue(floorPlanObjectState)
// //
const { state, setState } = useEstimateController(params.pid) const { state, setState, addItem, handleEstimateFileDownload } = useEstimateController(params.pid)
const [itemList, setItemList] = useState([])
// List // List
const [specialNoteList, setSpecialNoteList] = useState([]) const [specialNoteList, setSpecialNoteList] = useState([])
@ -75,6 +85,17 @@ export default function Estimate({ params }) {
if (code1 != null) { if (code1 != null) {
setHonorificCodeList(code1) setHonorificCodeList(code1)
} }
//
const param = {
saleStoreId: session.storeId,
}
const apiUrl = `/api/display-item/item-list?${queryStringFormatter(param)}`
get({ url: apiUrl }).then((res) => {
if (res.length > 0) {
setDisplayItemList(res)
}
})
}, []) }, [])
useEffect(() => { useEffect(() => {
@ -154,50 +175,175 @@ export default function Estimate({ params }) {
await promisePost({ url: 'api/file/fileDelete', data: delParams }).then((res) => { await promisePost({ url: 'api/file/fileDelete', data: delParams }).then((res) => {
if (res.status === 204) { if (res.status === 204) {
setOriginFiles(originFiles.filter((file) => file.objectNo === objectNo && file.no !== no)) setOriginFiles(originFiles.filter((file) => file.objectNo === objectNo && file.no !== no))
setState({
fileList: originFiles.filter((file) => file.objectNo === objectNo && file.no !== no),
})
} }
}) })
} }
// // option
useEffect(() => { useEffect(() => {
if (isNotEmptyArray(state.itemList)) { if (state.estimateType !== '') {
setItemList(state.itemList) const param = {
} saleStoreId: session.storeId,
}, [state?.itemList]) sapSalesStoreCd: session.custCd,
docTpCd: state?.estimateType,
// option
useEffect(() => {
const param = {
saleStoreId: session.storeId,
sapSalesStoreCd: session.custCd,
docTpCd: state?.estimateType,
}
const apiUrl = `/api/estimate/price/store-price-list?${queryStringFormatter(param)}`
get({ url: apiUrl }).then((res) => {
if (isNotEmptyArray(res?.data)) {
setStorePriceList(res.data)
} }
})
const apiUrl = `/api/estimate/price/store-price-list?${queryStringFormatter(param)}`
get({ url: apiUrl }).then((res) => {
if (isNotEmptyArray(res?.data)) {
setStorePriceList(res.data)
}
})
}
}, [state?.estimateType]) }, [state?.estimateType])
// option
useEffect(() => {
if (tempPriceCd !== '') {
const param = {
saleStoreId: session.storeId,
sapSalesStoreCd: session.custCd,
docTpCd: tempPriceCd,
}
const apiUrl = `/api/estimate/price/store-price-list?${queryStringFormatter(param)}`
get({ url: apiUrl }).then((res) => {
if (isNotEmptyArray(res?.data)) {
setStorePriceList(res.data)
}
})
}
}, [tempPriceCd])
//Pricing //Pricing
const handlePricing = async (priceCd) => { const handlePricing = async (priceCd) => {
const param = { const param = {
saleStoreId: session.storeId, saleStoreId: session.storeId,
sapSalesStoreCd: session.custCd, sapSalesStoreCd: session.custCd,
docTpCd: state.estimateType, docTpCd: state.estimateType,
priceCd: priceCd, priceCd: session.storeLvl === '1' ? tempPriceCd : priceCd,
itemIdList: [], // itemIdList: state.itemList, //
} }
console.log('param::', param) // console.log('::', param)
return
await promisePost({ url: '/api/estimate/price/item-price-list', data: param }).then((res) => { await promisePost({ url: '/api/estimate/price/item-price-list', data: param }).then((res) => {
console.log('프라이싱결과::::::', res) if (res) {
// ............SUCK!!! if (res.status === 200) {
const data = res.data
if (data.result.code === 200) {
if (isNotEmptyArray(data.data2)) {
// ............
//..
//itemList itemId unitPrice 0
//itemId
setState({
priceCd: session.storeLvl === '1' ? tempPriceCd : priceCd,
})
}
}
}
}
}) })
} }
//row
const onChangeSelect = (dispOrder) => {
const newSelection = new Set(selection)
if (newSelection.has(dispOrder)) {
newSelection.delete(dispOrder)
} else {
newSelection.add(dispOrder)
}
setSelection(newSelection)
}
//
const onChangeDisplayItem = (itemId, dispOrder) => {
const param = {
itemId: itemId,
}
const apiUrl = `/api/display-item/item-detail?${queryStringFormatter(param)}`
let updateList = []
let updates = {}
get({ url: apiUrl }).then((res) => {
console.log('아이템상세정보:::::::', res)
updates.objectNo = objectNo
updates.planNo = planNo
updates.itemId = res.itemId
updates.itemNo = res.itemNo
updates.itemName = res.itemName
updates.itemChangeFlg = '1' // 1
updates.partAdd = '1' //1 NEW
updates.fileUploadFlg = res.fileUploadFlg
updates.unit = res.unit
updates.unitPrice = res.salePrice //unitPrice salePrice
updates.moduleFlg = res.moduleFlg
updates.pkgMaterialFlg = res.pkgMaterialFlg
updates.pnowW = res.pnowW
updates.salePrice = res.salePrice
updates.specification = res.specification
updates.unit = res.unit
updates.specialNoteCd = res.spnAttrCds
updates.itemGroup = res.itemGroup
updates.delFlg = '0' // 0
updateList = state.itemList.map((item) => {
if (item.dispOrder === dispOrder) {
return { ...item, ...updates }
} else {
return item
}
})
setState({
itemList: updateList,
})
setItemChangeYn(true)
})
}
//
const removeItem = () => {
const array = [...selection]
let delList = []
state.itemList.filter((row) => {
array.map((row2) => {
if (row2 === row.dispOrder) {
delList.push({ ...row })
}
})
})
const updateList = state.itemList.map((item) => {
const isDeleted = delList.some((row) => item.dispOrder === row.dispOrder)
return {
...item,
delFlg: isDeleted ? '1' : '0',
}
})
setState({
itemList: updateList,
})
setSelection(new Set())
setItemChangeYn(true)
}
useEffect(() => {
if (itemChangeYn) {
console.log('아이템에 뭔가 변화가 일어났어', itemChangeYn)
console.log('아이템상태가져오기::::::::::', state.itemList)
}
// false ..
setItemChangeYn(false)
}, [itemChangeYn])
return ( return (
<div className="sub-content estimate"> <div className="sub-content estimate">
<div className="sub-content-inner"> <div className="sub-content-inner">
@ -249,7 +395,7 @@ export default function Estimate({ params }) {
<tr> <tr>
{/* 1차 판매점명 */} {/* 1차 판매점명 */}
<th>{getMessage('estimate.detail.saleStoreId')}</th> <th>{getMessage('estimate.detail.saleStoreId')}</th>
<td></td> <td>{state?.firstSaleStoreName}</td>
{/* 견적일 */} {/* 견적일 */}
<th> <th>
{getMessage('estimate.detail.estimateDate')} <span className="important">*</span> {getMessage('estimate.detail.estimateDate')} <span className="important">*</span>
@ -263,7 +409,7 @@ export default function Estimate({ params }) {
<tr> <tr>
{/* 2차 판매점명 */} {/* 2차 판매점명 */}
<th>{getMessage('estimate.detail.otherSaleStoreId')}</th> <th>{getMessage('estimate.detail.otherSaleStoreId')}</th>
<td></td> <td>{state?.agencySaleStoreName}</td>
{/* 담당자 */} {/* 담당자 */}
<th> <th>
{getMessage('estimate.detail.receiveUser')} <span className="important">*</span> {getMessage('estimate.detail.receiveUser')} <span className="important">*</span>
@ -386,8 +532,8 @@ export default function Estimate({ params }) {
return ( return (
<> <>
<div className={`form-flex-wrap ${style}`}> <div className={`form-flex-wrap ${style}`} key={uuidv4()}>
<div className="input-wrap mr5" style={{ width: '610px' }}> <div className="input-wrap mr5" style={{ width: '610px' }} key={`roof${index}`}>
<input type="text" className="input-light" defaultValue={roofList} readOnly /> <input type="text" className="input-light" defaultValue={roofList} readOnly />
</div> </div>
<div className="input-wrap" style={{ width: '200px' }}> <div className="input-wrap" style={{ width: '200px' }}>
@ -472,10 +618,17 @@ export default function Estimate({ params }) {
{originFiles.length > 0 && {originFiles.length > 0 &&
originFiles.map((originFile) => { originFiles.map((originFile) => {
return ( return (
<li className="file-item"> <li className="file-item" key={uuidv4()}>
<span> <span onClick={() => handleEstimateFileDownload(originFile)}>
{originFile.faileName} {originFile.faileName}
<button className="delete" onClick={() => deleteOriginFile(originFile.objectNo, originFile.no)}></button> <button
type="button"
className="delete"
onClick={(e) => {
deleteOriginFile(originFile.objectNo, originFile.no)
e.stopPropagation()
}}
></button>
</span> </span>
</li> </li>
) )
@ -505,39 +658,41 @@ export default function Estimate({ params }) {
<div className="estimate-check-inner"> <div className="estimate-check-inner">
<div className="special-note-check-wrap"> <div className="special-note-check-wrap">
{/* SpecialNoteList반복문 */} {/* SpecialNoteList반복문 */}
{specialNoteList.map((row) => { {specialNoteList.length > 0 &&
return ( specialNoteList.map((row) => {
<div return (
className="special-note-check-item" <div
onClick={(event) => { key={uuidv4()}
settingShowContent(row.code, event) className="special-note-check-item"
}} onClick={(event) => {
> settingShowContent(row.code, event)
<div className="d-check-box light"> }}
<input >
type="checkbox" <div className="d-check-box light">
id={row.code} <input
checked={!!row.text} type="checkbox"
disabled={row.code === 'ATTR001' ? true : false} id={row.code}
onChange={(event) => { checked={!!row.text}
setSpecialNoteList((specialNote) => disabled={row.code === 'ATTR001' ? true : false}
specialNote.map((temp) => (temp.code === row.code ? { ...temp, text: !temp.text } : temp)), onChange={(event) => {
) setSpecialNoteList((specialNote) =>
settingShowContent(row.code, event) specialNote.map((temp) => (temp.code === row.code ? { ...temp, text: !temp.text } : temp)),
}} )
/> settingShowContent(row.code, event)
<label htmlFor={row.code}>{row.codeNm}</label> }}
/>
<label htmlFor={row.code}>{row.codeNm}</label>
</div>
</div> </div>
</div> )
) })}
})}
</div> </div>
{/* 견적특이사항 선택한 내용 영역시작 */} {/* 견적특이사항 선택한 내용 영역시작 */}
<div className="calculation-estimate"> <div className="calculation-estimate">
{specialNoteList.map((row) => { {specialNoteList.map((row) => {
if (row.code === showContentCode) { if (row.code === showContentCode) {
return ( return (
<dl> <dl key={uuidv4()}>
<dt>{row.codeNm}</dt> <dt>{row.codeNm}</dt>
<dd dangerouslySetInnerHTML={{ __html: row.remarks }}></dd> <dd dangerouslySetInnerHTML={{ __html: row.remarks }}></dd>
</dl> </dl>
@ -561,24 +716,24 @@ export default function Estimate({ params }) {
<div className="esimate-wrap"> <div className="esimate-wrap">
<div className="estimate-list-wrap one"> <div className="estimate-list-wrap one">
<div className="estimate-box"> <div className="estimate-box">
<div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.totPcs')}</div> <div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.totAmount')}</div>
<div className="estimate-name blue">74</div> <div className="estimate-name blue">{convertNumberToPriceDecimal(state?.totAmount)}</div>
</div> </div>
<div className="estimate-box"> <div className="estimate-box">
<div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.vol')}</div> <div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.totVolKw')}</div>
<div className="estimate-name blue">8300</div> <div className="estimate-name blue">{convertNumberToPriceDecimal(state?.totVolKw)}</div>
</div> </div>
<div className="estimate-box"> <div className="estimate-box">
<div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.netAmt')}</div> <div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.supplyPrice')}</div>
<div className="estimate-name blue">6,798,900</div> <div className="estimate-name blue">{convertNumberToPriceDecimal(state?.supplyPrice)}</div>
</div> </div>
<div className="estimate-box"> <div className="estimate-box">
<div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.vat')}</div> <div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.vatPrice')}</div>
<div className="estimate-name blue">679,890</div> <div className="estimate-name blue">{convertNumberToPriceDecimal(state?.vatPrice)}</div>
</div> </div>
<div className="estimate-box"> <div className="estimate-box">
<div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.totPrice')}</div> <div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.totPrice')}</div>
<div className="estimate-name red">7,478,790</div> <div className="estimate-name red">{convertNumberToPriceDecimal(state?.totPrice)}</div>
</div> </div>
</div> </div>
</div> </div>
@ -620,15 +775,16 @@ export default function Estimate({ params }) {
<div className="select-wrap"> <div className="select-wrap">
{session?.storeLvl === '1' ? ( {session?.storeLvl === '1' ? (
<select <select
key={uuidv4()}
className="select-light" className="select-light"
onChange={(e) => { onChange={(e) => {
setState({ priceCd: e.target.value }) setTempPriceCd(e.target.value)
}} }}
> >
{storePriceList.length > 0 && storePriceList.map((row) => <option value={row.priceCd}>{row.priceNm}</option>)} {storePriceList.length > 0 && storePriceList.map((row) => <option value={row.priceCd}>{row.priceNm}</option>)}
</select> </select>
) : ( ) : (
<select className="select-light"> <select key={uuidv4()} className="select-light">
<option value="UNIT_PRICE">{getMessage('estimate.detail.header.unitPrice')}</option> <option value="UNIT_PRICE">{getMessage('estimate.detail.header.unitPrice')}</option>
</select> </select>
)} )}
@ -636,7 +792,6 @@ export default function Estimate({ params }) {
<button <button
className="btn-origin grey ml5" className="btn-origin grey ml5"
onClick={() => { onClick={() => {
// console.log('priceCd::', state.priceCd)
handlePricing(state.priceCd) handlePricing(state.priceCd)
}} }}
> >
@ -663,13 +818,13 @@ export default function Estimate({ params }) {
</li> </li>
</ul> </ul>
<div className="product-edit-btn"> <div className="product-edit-btn">
<button className="btn-origin navy mr5" type="submit"> <button className="btn-origin navy mr5" type="button" onClick={addItem}>
<span className="plus"></span> <span className="plus"></span>
{getMessage('estimate.detail.showPrice.btn2')} {getMessage('estimate.detail.showPrice.addItem')}
</button> </button>
<button className="btn-origin grey"> <button className="btn-origin grey" type="button" onClick={removeItem}>
<span className="minus"></span> <span className="minus"></span>
{getMessage('estimate.detail.showPrice.btn3')} {getMessage('estimate.detail.showPrice.delItem')}
</button> </button>
</div> </div>
</div> </div>
@ -696,59 +851,120 @@ export default function Estimate({ params }) {
<label htmlFor="ch97"></label> <label htmlFor="ch97"></label>
</div> </div>
</th> </th>
<th>{getMessage('estimate.detail.itemTableHeader.col1')}</th> <th>{getMessage('estimate.detail.itemTableHeader.dispOrder')}</th>
<th>{getMessage('estimate.detail.itemTableHeader.col2')}</th> <th>{getMessage('estimate.detail.itemTableHeader.itemId')}</th>
<th>{getMessage('estimate.detail.itemTableHeader.col3')}</th> <th>{getMessage('estimate.detail.itemTableHeader.itemNo')}</th>
<th>{getMessage('estimate.detail.itemTableHeader.col4')}</th> <th>{getMessage('estimate.detail.itemTableHeader.amount')}</th>
<th>{getMessage('estimate.detail.itemTableHeader.col5')}</th> <th>{getMessage('estimate.detail.itemTableHeader.unit')}</th>
<th>{getMessage('estimate.detail.itemTableHeader.col6')}</th> <th>{getMessage('estimate.detail.itemTableHeader.salePrice')}</th>
<th>{getMessage('estimate.detail.itemTableHeader.col7')}</th> <th>{getMessage('estimate.detail.itemTableHeader.saleTotPrice')}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr> {state?.itemList.length > 0 &&
<td className="al-c"> state.itemList.map((item, index) => {
<div className="d-check-box light no-text"> return (
<input type="checkbox" id="ch98" /> <tr key={uuidv4()}>
<label htmlFor="ch98"></label> <td className="al-c">
</div> <div className="d-check-box light no-text">
</td> <input
<td className="al-r">100</td> type="checkbox"
<td> id={item?.dispOrder}
<div className="form-flex-wrap"> onChange={() => onChangeSelect(item.dispOrder)}
<div className="select-wrap mr5">{/* <Select /> */}</div> checked={selection.has(item.dispOrder) ? true : false}
<div className="btn-area"> />
<span className="tb_ico change_check"></span> <label htmlFor={item?.dispOrder}></label>
</div> </div>
</div> </td>
</td> <td className="al-r">{item?.dispOrder * 100}</td>
<td> <td>
<div className="form-flex-wrap"> <div className="form-flex-wrap">
<div className="name">HNW-MC4-CHN30</div> <div className="select-wrap mr5">
<div className="icon-wrap"> <Select
<span className="tb_ico file_check"></span> id="long-value-select1"
<button className="grid-tip"></button> instanceId="long-value-select1"
</div> className="react-select-custom"
</div> classNamePrefix="custom"
</td> placeholder="Select"
<td> options={displayItemList}
<div className="input-wrap" style={{ width: '100%' }}> onChange={(e) => {
<input type="text" className="input-light al-r" defaultValue={'20'} /> if (isObjectNotEmpty(e)) {
</div> onChangeDisplayItem(e.itemId, item.dispOrder)
</td> }
<td>セット</td> }}
<td> getOptionLabel={(x) => x.itemName}
<div className="form-flex-wrap"> getOptionValue={(x) => x.itemId}
<div className="input-wrap mr5"> isClearable={true}
<input type="text" className="input-light al-r" defaultValue={'278,050'} /> isDisabled={false}
</div> value={displayItemList.filter(function (option) {
<div className="btn-area"> return option.itemId === item.itemId
<span className="tb_ico open_check"></span> })}
</div> />
</div> </div>
</td> {/* {item?.partAdd === '1' && ( */}
<td className="al-r">5,561,000</td> {item?.itemChangeFlg === '1' && (
</tr> <div className="btn-area">
<span className="tb_ico change_check"></span>
</div>
)}
</div>
</td>
<td>
<div className="form-flex-wrap">
<div className="name">{item?.itemNo}</div>
<div className="icon-wrap">
{item?.fileUploadFlg === '1' && <span className="tb_ico file_check"></span>}
{item?.specialNoteCd && (
<button
type="button"
className="grid-tip"
onClick={() => {
setProductFeaturesPopupOpen(true)
setShowProductFeatureData(item?.specialNoteCd)
}}
></button>
)}
</div>
</div>
</td>
<td>
<div className="input-wrap" style={{ width: '100%' }}>
<input
type="text"
className="input-light al-r"
defaultValue={convertNumberToPriceDecimal(item?.amount)}
onChange={(e) => {
//onChangeDisplayItem
//itemChangeFlg = 1, partAdd = 0
console.log('수량변경::::::::', e.target.value)
}}
/>
</div>
</td>
<td>{item.unit}</td>
<td>
<div className="form-flex-wrap">
<div className="input-wrap mr5">
<input
type="text"
className="input-light al-r"
value={convertNumberToPriceDecimal(item?.salePrice)}
onChange={(e) => {
//onChangeDisplayItem
//itemChangeFlg, partAdd
console.log('단가변경:::::::', e.target.value)
}}
/>
</div>
{/* <div className="btn-area">
<span className="tb_ico open_check">OPEN아이콘 처리</span>
</div> */}
</div>
</td>
<td className="al-r">{convertNumberToPriceDecimal(item?.saleTotPrice)}</td>
</tr>
)
})}
</tbody> </tbody>
</table> </table>
</div> </div>
@ -758,6 +974,13 @@ export default function Estimate({ params }) {
</div> </div>
{/* 기본정보끝 */} {/* 기본정보끝 */}
</div> </div>
{productFeaturesPopupOpen && (
<ProductFeaturesPop
specialNoteList={specialNoteList}
showProductFeatureData={showProductFeatureData}
setProductFeaturesPopupOpen={setProductFeaturesPopupOpen}
/>
)}
</div> </div>
) )
} }

View File

@ -0,0 +1,67 @@
'use client'
import { useEffect, useState } from 'react'
import { useMessage } from '@/hooks/useMessage'
export default function ProductFeaturesPop({ specialNoteList, showProductFeatureData, setProductFeaturesPopupOpen }) {
const [showSpecialNoteList, setShowSpecialNoteList] = useState([])
const { getMessage } = useMessage()
useEffect(() => {
let pushData = []
specialNoteList.map((row) => {
let option = showProductFeatureData.split('、')
option.map((row2) => {
if (row.code === row2) {
pushData.push(row)
}
})
})
setShowSpecialNoteList(pushData)
}, [specialNoteList])
return (
<div className="modal-popup">
<div className="modal-dialog">
<div className="modal-content">
<div className="modal-header">
<h1 className="title">{getMessage('estimate.detail.productFeaturesPopup.title')}</h1>
<button
type="button"
className="modal-close"
onClick={() => {
setProductFeaturesPopupOpen(false)
}}
>
{getMessage('estimate.detail.productFeaturesPopup.close')}
</button>
</div>
<div className="modal-body">
<div className="modal-body-inner border">
<div className="calculation-estimate usemodal">
{showSpecialNoteList.length > 0 &&
showSpecialNoteList.map((row) => {
return (
<dl>
<dt>{row.codeNm}</dt>
<dd dangerouslySetInnerHTML={{ __html: row.remarks }}></dd>
</dl>
)
})}
</div>
</div>
<div className="footer-btn-wrap">
<button
type="button"
className="btn-origin navy"
onClick={() => {
setProductFeaturesPopupOpen(false)
}}
>
{getMessage('estimate.detail.productFeaturesPopup.close')}
</button>
</div>
</div>
</div>
</div>
</div>
)
}

View File

@ -131,15 +131,14 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
this.on('selected', () => { this.on('selected', () => {
Object.keys(this.controls).forEach((controlKey) => { Object.keys(this.controls).forEach((controlKey) => {
if (controlKey !== 'ml' && controlKey !== 'mr') { this.setControlVisible(controlKey, false)
this.setControlVisible(controlKey, false)
}
}) })
this.set({ hasBorders: false })
}) })
this.on('removed', () => { this.on('removed', () => {
// const children = getAllRelatedObjects(this.id, this.canvas) // const children = getAllRelatedObjects(this.id, this.canvas)
const children = this.canvas.getObjects().filter((obj) => obj.parentId === this.id && obj.name === 'lengthText') const children = this.canvas.getObjects().filter((obj) => obj.parentId === this.id)
children.forEach((child) => { children.forEach((child) => {
this.canvas.remove(child) this.canvas.remove(child)
}) })
@ -167,6 +166,10 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
}, },
initLines() { initLines() {
// if (this.lines.length > 0) {
// return
// }
this.lines = [] this.lines = []
this.points.forEach((point, i) => { this.points.forEach((point, i) => {
@ -189,6 +192,12 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
}) })
}, },
containsPoint: function (point) {
const isInside = this.inPolygon(point)
this.set('selectable', isInside)
return isInside
},
// 보조선 그리기 // 보조선 그리기
drawHelpLine() { drawHelpLine() {
// drawHelpLineInHexagon(this, pitch) // drawHelpLineInHexagon(this, pitch)
@ -263,9 +272,11 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
this.texts = [] this.texts = []
points.forEach((start, i) => { points.forEach((start, i) => {
const end = points[(i + 1) % points.length] const end = points[(i + 1) % points.length]
// planeSize: Math.round(Math.sqrt(Math.pow(newLine.x1 - newLine.x2, 2) + Math.pow(newLine.y1 - newLine.y2, 2))) * 10,
// actualSize: Math.round(Math.sqrt(Math.pow(newLine.x1 - newLine.x2, 2) + Math.pow(newLine.y1 - newLine.y2, 2))) * 10,
const dx = end.x - start.x const dx = end.x - start.x
const dy = end.y - start.y const dy = end.y - start.y
const length = Number(Math.sqrt(dx * dx + dy * dy).toFixed(1)) * 10 const length = Math.round(Math.sqrt(Math.pow(end.x - start.x, 2) + Math.pow(end.y - start.y, 2))) * 10
let midPoint let midPoint

View File

@ -13,14 +13,16 @@ import QContextMenu from '@/components/common/context-menu/QContextMenu'
import { useCanvasConfigInitialize } from '@/hooks/common/useCanvasConfigInitialize' import { useCanvasConfigInitialize } from '@/hooks/common/useCanvasConfigInitialize'
import { MENU } from '@/common/common' import { MENU } from '@/common/common'
import PanelBatchStatistics from '@/components/floor-plan/modal/panelBatch/PanelBatchStatistics' import PanelBatchStatistics from '@/components/floor-plan/modal/panelBatch/PanelBatchStatistics'
import { totalDisplaySelector } from '@/store/settingAtom'
export default function CanvasFrame() { export default function CanvasFrame() {
const canvasRef = useRef(null) const canvasRef = useRef(null)
const { canvas } = useCanvas('canvas') const { canvas, handleBackImageLoadToCanvas } = useCanvas('canvas')
const { canvasLoadInit, gridInit } = useCanvasConfigInitialize() const { canvasLoadInit, gridInit } = useCanvasConfigInitialize()
const currentMenu = useRecoilValue(currentMenuState) const currentMenu = useRecoilValue(currentMenuState)
const { contextMenu, handleClick } = useContextMenu() const { contextMenu, handleClick } = useContextMenu()
const { selectedPlan, modifiedPlanFlag, checkCanvasObjectEvent, resetModifiedPlans } = usePlan() const { selectedPlan, modifiedPlanFlag, checkCanvasObjectEvent, resetModifiedPlans, currentCanvasPlan } = usePlan()
const totalDisplay = useRecoilValue(totalDisplaySelector) //
useEvent() useEvent()
const loadCanvas = () => { const loadCanvas = () => {
@ -72,7 +74,8 @@ export default function CanvasFrame() {
MENU.MODULE_CIRCUIT_SETTING.BASIC_SETTING, MENU.MODULE_CIRCUIT_SETTING.BASIC_SETTING,
MENU.MODULE_CIRCUIT_SETTING.CIRCUIT_TRESTLE_SETTING, MENU.MODULE_CIRCUIT_SETTING.CIRCUIT_TRESTLE_SETTING,
MENU.MODULE_CIRCUIT_SETTING.PLAN_ORIENTATION, MENU.MODULE_CIRCUIT_SETTING.PLAN_ORIENTATION,
].includes(currentMenu) && <PanelBatchStatistics />} ].includes(currentMenu) &&
totalDisplay && <PanelBatchStatistics />}
</div> </div>
) )
} }

View File

@ -235,6 +235,7 @@ export default function CanvasMenu(props) {
<QSelectBox title={'瓦53A'} option={SelectOption} /> <QSelectBox title={'瓦53A'} option={SelectOption} />
</div> </div>
<div className="btn-from"> <div className="btn-from">
<button className="btn10"></button>
{/*<button className="btn04" onClick={() => setShowCanvasSettingModal(true)}></button>*/} {/*<button className="btn04" onClick={() => setShowCanvasSettingModal(true)}></button>*/}
<button className="btn04" onClick={handlePopup}></button> <button className="btn04" onClick={handlePopup}></button>
<button className="btn05"></button> <button className="btn05"></button>

View File

@ -1,7 +1,7 @@
'use client' 'use client'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { currentMenuState } from '@/store/canvasAtom' import { canvasState, currentMenuState } from '@/store/canvasAtom'
import { useRecoilState, useRecoilValue } from 'recoil' import { useRecoilState, useRecoilValue } from 'recoil'
import { menuTypeState, subMenusState } from '@/store/menuAtom' import { menuTypeState, subMenusState } from '@/store/menuAtom'
import useMenu from '@/hooks/common/useMenu' import useMenu from '@/hooks/common/useMenu'
@ -9,6 +9,7 @@ import { useEffect } from 'react'
export default function MenuDepth01() { export default function MenuDepth01() {
const type = useRecoilValue(menuTypeState) const type = useRecoilValue(menuTypeState)
const canvas = useRecoilValue(canvasState)
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { handleMenu } = useMenu() const { handleMenu } = useMenu()
const [currentMenu, setCurrentMenu] = useRecoilState(currentMenuState) const [currentMenu, setCurrentMenu] = useRecoilState(currentMenuState)
@ -24,6 +25,7 @@ export default function MenuDepth01() {
useEffect(() => { useEffect(() => {
handleMenu(type) handleMenu(type)
canvas.discardActiveObject()
}, [currentMenu]) }, [currentMenu])
return ( return (
<div className="canvas-depth2-inner"> <div className="canvas-depth2-inner">

View File

@ -0,0 +1,89 @@
import { useMessage } from '@/hooks/useMessage'
import { useRefFiles } from '@/hooks/common/useRefFiles'
import { usePlan } from '@/hooks/usePlan'
import WithDraggable from '@/components/common/draggable/WithDraggable'
export default function ImgLoad() {
const {
refImage,
queryRef,
setRefImage,
handleRefFile,
refFileMethod,
setRefFileMethod,
handleRefFileMethod,
mapPositionAddress,
setMapPositionAddress,
handleFileDelete,
handleMapImageDown,
} = useRefFiles()
const { currentCanvasPlan } = usePlan()
const { getMessage } = useMessage()
return (
<WithDraggable isShow={true}>
<div className={`modal-pop-wrap r`}>
<div className="modal-head">
<h1 className="title">{getMessage('common.input.file')}</h1>
{/* <button className="modal-close">닫기</button> */}
</div>
<div className="modal-body">
<div className="img-flex-box">
<span className="normal-font mr10">サイズ調整と回転</span>
<label className="toggle-btn">
<input type="checkbox" />
<span className="slider"></span>
</label>
</div>
<div className="img-load-from">
<div className="img-load-item">
<div className="d-check-radio pop">
<input type="radio" name="radio03" id="ra06" value={'1'} onChange={(e) => handleRefFileMethod(e)} checked={refFileMethod === '1'} />
<label htmlFor="ra06">ファイルを読み込む</label>
</div>
<div className="img-flex-box">
<div className="img-edit-wrap">
<label className="img-edit-btn" htmlFor="img_file">
<span className="img-edit"></span>
ファイルの追加
</label>
<input type="file" id="img_file" style={{ display: 'none' }} onChange={(e) => handleRefFile(e.target.files[0])} />
</div>
<div className="img-name-wrap">
{/* <input type="text" className="input-origin al-l" defaultValue={'IMG_Name.PNG'} readOnly />
<button className="img-check"></button> */}
{currentCanvasPlan?.bgImageName === null ? (
<input type="text" className="input-origin al-l" defaultValue={''} value={refImage ? refImage.name : ''} readOnly />
) : (
<input type="text" className="input-origin al-l" value={currentCanvasPlan?.bgImageName} readOnly />
)}
{(refImage || currentCanvasPlan?.bgImageName) && <button className="img-check" onClick={handleFileDelete}></button>}
</div>
</div>
</div>
<div className="img-load-item">
<div className="d-check-radio pop">
<input type="radio" name="radio03" id="ra06" value={'2'} onChange={(e) => handleRefFileMethod(e)} checked={refFileMethod === '2'} />
<label htmlFor="ra06">アドレスを読み込む</label>
</div>
<div className="img-flex-box for-address">
<input type="text" className="input-origin al-l mr10" placeholder={'住所入力'} />
<div className="img-edit-wrap">
<button className="img-edit-btn" onClick={handleMapImageDown}>
完了
</button>
</div>
{mapPositionAddress && <span className="check-address fail"></span>}
{/* <span className="check-address success"></span> */}
</div>
</div>
</div>
<div className="grid-btn-wrap">
<button className="btn-frame modal act">完了</button>
</div>
</div>
</div>
</WithDraggable>
)
}

View File

@ -1,20 +1,40 @@
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import WithDraggable from '@/components/common/draggable/WithDraggable' import WithDraggable from '@/components/common/draggable/WithDraggable'
import { useState } from 'react' import { useEffect, useRef, useState } from 'react'
import Orientation from '@/components/floor-plan/modal/basic/step/Orientation'
import Module from '@/components/floor-plan/modal/basic/step/Module' import Module from '@/components/floor-plan/modal/basic/step/Module'
import PitchModule from '@/components/floor-plan/modal/basic/step/pitch/PitchModule' import PitchModule from '@/components/floor-plan/modal/basic/step/pitch/PitchModule'
import PitchPlacement from '@/components/floor-plan/modal/basic/step/pitch/PitchPlacement' import PitchPlacement from '@/components/floor-plan/modal/basic/step/pitch/PitchPlacement'
import Placement from '@/components/floor-plan/modal/basic/step/Placement' import Placement from '@/components/floor-plan/modal/basic/step/Placement'
import { useRecoilState } from 'recoil' import { useRecoilValue } from 'recoil'
import { canvasSettingState } from '@/store/canvasAtom' import { canvasSettingState } from '@/store/canvasAtom'
import { usePopup } from '@/hooks/usePopup' import { usePopup } from '@/hooks/usePopup'
import { Orientation } from '@/components/floor-plan/modal/basic/step/Orientation'
import { useModuleBasicSetting } from '@/hooks/module/useModuleBasicSetting'
import { useEvent } from '@/hooks/useEvent'
export default function BasicSetting({ id, pos = { x: 50, y: 230 } }) { export default function BasicSetting({ id, pos = { x: 50, y: 230 } }) {
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { closePopup } = usePopup() const { closePopup } = usePopup()
const [tabNum, setTabNum] = useState(1) const [tabNum, setTabNum] = useState(1)
const [canvasSetting, setCanvasSetting] = useRecoilState(canvasSettingState) const canvasSetting = useRecoilValue(canvasSettingState)
const orientationRef = useRef(null)
const { initEvent } = useEvent()
const { makeModuleInstArea, manualModuleSetup, autoModuleSetup } = useModuleBasicSetting()
const handleBtnNextStep = () => {
if (tabNum === 1) {
orientationRef.current.handleNextStep()
}
setTabNum(tabNum + 1)
}
useEffect(() => {
makeModuleInstArea() //
return () => {
initEvent() //
}
}, [])
return ( return (
<WithDraggable isShow={true} pos={pos}> <WithDraggable isShow={true} pos={pos}>
<div className={`modal-pop-wrap lx-2`}> <div className={`modal-pop-wrap lx-2`}>
@ -32,7 +52,7 @@ export default function BasicSetting({ id, pos = { x: 50, y: 230 } }) {
<span className={`tab-arr ${tabNum === 3 ? 'act' : ''}`}></span> <span className={`tab-arr ${tabNum === 3 ? 'act' : ''}`}></span>
<div className={`module-tab-bx ${tabNum === 3 ? 'act' : ''}`}>{getMessage('modal.module.basic.setting.module.placement')}</div> <div className={`module-tab-bx ${tabNum === 3 ? 'act' : ''}`}>{getMessage('modal.module.basic.setting.module.placement')}</div>
</div> </div>
{tabNum === 1 && <Orientation setTabNum={setTabNum} />} {tabNum === 1 && <Orientation ref={orientationRef} tabNum={tabNum} setTabNum={setTabNum} />}
{/*배치면 초기설정 - 입력방법: 복시도 입력 || 실측값 입력*/} {/*배치면 초기설정 - 입력방법: 복시도 입력 || 실측값 입력*/}
{canvasSetting.roofSizeSet && canvasSetting.roofSizeSet != 3 && tabNum === 2 && <Module setTabNum={setTabNum} />} {canvasSetting.roofSizeSet && canvasSetting.roofSizeSet != 3 && tabNum === 2 && <Module setTabNum={setTabNum} />}
{canvasSetting.roofSizeSet && canvasSetting.roofSizeSet != 3 && tabNum === 3 && <Placement setTabNum={setTabNum} />} {canvasSetting.roofSizeSet && canvasSetting.roofSizeSet != 3 && tabNum === 3 && <Placement setTabNum={setTabNum} />}
@ -49,14 +69,18 @@ export default function BasicSetting({ id, pos = { x: 50, y: 230 } }) {
)} )}
{/*{tabNum !== 3 && <button className="btn-frame modal act mr5">{getMessage('modal.common.save')}</button>}*/} {/*{tabNum !== 3 && <button className="btn-frame modal act mr5">{getMessage('modal.common.save')}</button>}*/}
{tabNum !== 3 && ( {tabNum !== 3 && (
<button className="btn-frame modal" onClick={() => setTabNum(tabNum + 1)}> <button className="btn-frame modal" onClick={handleBtnNextStep}>
Next Next
</button> </button>
)} )}
{tabNum === 3 && ( {tabNum === 3 && (
<> <>
<button className="btn-frame modal mr5">{getMessage('modal.module.basic.setting.passivity.placement')}</button> <button className="btn-frame modal mr5" onClick={manualModuleSetup}>
<button className="btn-frame modal act">{getMessage('modal.module.basic.setting.auto.placement')}</button> {getMessage('modal.module.basic.setting.passivity.placement')}
</button>
<button className="btn-frame modal act" onClick={autoModuleSetup}>
{getMessage('modal.module.basic.setting.auto.placement')}
</button>
</> </>
)} )}
</div> </div>

View File

@ -1,22 +1,21 @@
import { useState } from 'react' import { forwardRef, useImperativeHandle, useState } from 'react'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { useOrientation } from '@/hooks/popup/useOrientation'
import { getDegreeInOrientation } from '@/util/canvas-util'
export default function Orientation({ setTabNum }) { export const Orientation = forwardRef(({ tabNum }, ref) => {
const { getMessage } = useMessage() const { getMessage } = useMessage()
const [compasDeg, setCompasDeg] = useState(0)
const { nextStep, compasDeg, setCompasDeg } = useOrientation()
const [hasAnglePassivity, setHasAnglePassivity] = useState(false) const [hasAnglePassivity, setHasAnglePassivity] = useState(false)
const getDegree = (degree) => { useImperativeHandle(ref, () => ({
if (degree % 15 === 0) return degree handleNextStep,
}))
let value = Math.floor(degree / 15) const handleNextStep = () => {
const remain = ((degree / 15) % 1).toFixed(5) nextStep()
if (remain > 0.4) {
value++
}
return value * 15
} }
return ( return (
@ -31,7 +30,7 @@ export default function Orientation({ setTabNum }) {
{Array.from({ length: 180 / 15 }).map((dot, index) => ( {Array.from({ length: 180 / 15 }).map((dot, index) => (
<div <div
key={index} key={index}
className={`circle ${getDegree(compasDeg) === 15 * (12 + index) ? 'act' : ''}`} className={`circle ${getDegreeInOrientation(compasDeg) === 15 * (12 + index) ? 'act' : ''}`}
onClick={() => setCompasDeg(15 * (12 + index))} onClick={() => setCompasDeg(15 * (12 + index))}
> >
{index === 0 && <i>180°</i>} {index === 0 && <i>180°</i>}
@ -39,13 +38,17 @@ export default function Orientation({ setTabNum }) {
</div> </div>
))} ))}
{Array.from({ length: 180 / 15 }).map((dot, index) => ( {Array.from({ length: 180 / 15 }).map((dot, index) => (
<div key={index} className={`circle ${getDegree(compasDeg) === 15 * index ? 'act' : ''}`} onClick={() => setCompasDeg(15 * index)}> <div
key={index}
className={`circle ${getDegreeInOrientation(compasDeg) === 15 * index ? 'act' : ''}`}
onClick={() => setCompasDeg(15 * index)}
>
{index === 0 && <i>0°</i>} {index === 0 && <i>0°</i>}
{index === 6 && <i>90°</i>} {index === 6 && <i>90°</i>}
</div> </div>
))} ))}
<div className="compas"> <div className="compas">
<div className="compas-arr" style={{ transform: `rotate(${getDegree(compasDeg)}deg)` }}></div> <div className="compas-arr" style={{ transform: `rotate(${getDegreeInOrientation(compasDeg)}deg)` }}></div>
</div> </div>
</div> </div>
</div> </div>
@ -62,7 +65,11 @@ export default function Orientation({ setTabNum }) {
className="input-origin block" className="input-origin block"
value={compasDeg} value={compasDeg}
readOnly={hasAnglePassivity} readOnly={hasAnglePassivity}
onChange={(e) => setCompasDeg(e.target.value !== '' ? Number.parseInt(e.target.value) : 0)} onChange={(e) =>
setCompasDeg(
e.target.value !== '' && parseInt(e.target.value) <= 360 && parseInt(e.target.value) >= 0 ? Number.parseInt(e.target.value) : 0,
)
}
/> />
</div> </div>
<span className="thin">°</span> <span className="thin">°</span>
@ -72,4 +79,4 @@ export default function Orientation({ setTabNum }) {
</div> </div>
</> </>
) )
} })

View File

@ -63,7 +63,9 @@ export default function Distance(props) {
</div> </div>
</div> </div>
<div className="grid-btn-wrap"> <div className="grid-btn-wrap">
<button className="btn-frame modal act" onClick={handleClose}>{getMessage('common.require')}</button> <button className="btn-frame modal act" onClick={handleClose}>
{getMessage('common.ok')}
</button>
</div> </div>
</div> </div>
</div> </div>

View File

@ -5,14 +5,28 @@ import { useRecoilValue } from 'recoil'
import { contextPopupPositionState } from '@/store/popupAtom' import { contextPopupPositionState } from '@/store/popupAtom'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { usePopup } from '@/hooks/usePopup' import { usePopup } from '@/hooks/usePopup'
import { useSurfaceShapeBatch } from '@/hooks/surface/useSurfaceShapeBatch'
import { FLOW_DIRECTION_TYPE, useFlowDirectionSetting } from '@/hooks/popup/useFlowDirectionSetting'
import { canvasState } from '@/store/canvasAtom'
export default function FlowDirectionSetting(props) { export default function FlowDirectionSetting(props) {
const contextPopupPosition = useRecoilValue(contextPopupPositionState) const contextPopupPosition = useRecoilValue(contextPopupPositionState)
const { id, pos = contextPopupPosition, target } = props const { id, pos = contextPopupPosition, target } = props
const canvas = useRecoilValue(canvasState)
const { getMessage } = useMessage() const { getMessage } = useMessage()
useEffect(() => {
return () => {
canvas?.discardActiveObject()
}
}, [])
const [compasDeg, setCompasDeg] = useState(null)
const [flowDirection, setFlowDirection] = useState(target.direction)
const { closePopup } = usePopup() const { closePopup } = usePopup()
const [compasDeg, setCompasDeg] = useState(360) const { changeSurfaceFlowDirection, type, setType } = useFlowDirectionSetting(id)
const orientations = [ const orientations = [
{ name: `${getMessage('commons.none')}`, value: 0 },
{ name: `${getMessage('commons.south')}`, value: 360 }, { name: `${getMessage('commons.south')}`, value: 360 },
{ name: `${getMessage('commons.south')}${getMessage('commons.east')}`, value: 315 }, { name: `${getMessage('commons.south')}${getMessage('commons.east')}`, value: 315 },
{ name: `${getMessage('commons.south')}${getMessage('commons.west')}`, value: 45 }, { name: `${getMessage('commons.south')}${getMessage('commons.west')}`, value: 45 },
@ -23,41 +37,7 @@ export default function FlowDirectionSetting(props) {
{ name: `${getMessage('commons.north')}`, value: 180 }, { name: `${getMessage('commons.north')}`, value: 180 },
] ]
const [selectedOrientation, setSelectedOrientation] = useState(orientations[0]) const [selectedOrientation, setSelectedOrientation] = useState(orientations[0])
const [type, setType] = useState('0')
useEffect(() => {
if (target?.angle === 0) {
setCompasDeg(360)
} else {
setCompasDeg(target?.angle ?? 360)
}
}, [])
useEffect(() => {
if (type === '0') {
setCompasDeg(selectedOrientation.value)
}
}, [selectedOrientation])
useEffect(() => {
if (type === '1') {
if ([15, 345, 360].includes(compasDeg)) {
setSelectedOrientation(orientations[0])
} else if ([30, 45, 60].includes(compasDeg)) {
setSelectedOrientation(orientations[2])
} else if ([75, 90, 105].includes(compasDeg)) {
setSelectedOrientation(orientations[4])
} else if ([120, 135, 150].includes(compasDeg)) {
setSelectedOrientation(orientations[6])
} else if ([165, 180, 195].includes(compasDeg)) {
setSelectedOrientation(orientations[7])
} else if ([210, 225, 240].includes(compasDeg)) {
setSelectedOrientation(orientations[5])
} else if ([255, 270, 285].includes(compasDeg)) {
setSelectedOrientation(orientations[3])
} else if ([300, 315, 330].includes(compasDeg)) {
setSelectedOrientation(orientations[1])
}
}
}, [compasDeg])
return ( return (
<WithDraggable isShow={true} pos={pos}> <WithDraggable isShow={true} pos={pos}>
<div className={`modal-pop-wrap ml mount`}> <div className={`modal-pop-wrap ml mount`}>
@ -75,13 +55,13 @@ export default function FlowDirectionSetting(props) {
<div className="object-direction-wrap"> <div className="object-direction-wrap">
<div className="plane-direction"> <div className="plane-direction">
<span className="top">{getMessage('commons.north')}</span> <span className="top">{getMessage('commons.north')}</span>
<button className="plane-btn up"></button> <button className={`plane-btn up ${flowDirection === 'north' ? 'act' : ''}`} onClick={() => setFlowDirection('north')}></button>
<span className="right">{getMessage('commons.east')}</span> <span className="right">{getMessage('commons.east')}</span>
<button className="plane-btn right"></button> <button className={`plane-btn right ${flowDirection === 'east' ? 'act' : ''}`} onClick={() => setFlowDirection('east')}></button>
<span className="bottom">{getMessage('commons.south')}</span> <span className="bottom">{getMessage('commons.south')}</span>
<button className="plane-btn down act"></button> <button className={`plane-btn down ${flowDirection === 'south' ? 'act' : ''}`} onClick={() => setFlowDirection('south')}></button>
<span className="left">{getMessage('commons.west')}</span> <span className="left">{getMessage('commons.west')}</span>
<button className="plane-btn left"></button> <button className={`plane-btn left ${flowDirection === 'west' ? 'act' : ''}`} onClick={() => setFlowDirection('west')}></button>
</div> </div>
</div> </div>
</div> </div>
@ -90,16 +70,43 @@ export default function FlowDirectionSetting(props) {
<div className="guide">{getMessage('modal.shape.flow.direction.setting.orientation.setting.info')}</div> <div className="guide">{getMessage('modal.shape.flow.direction.setting.orientation.setting.info')}</div>
<div className="mb-box"> <div className="mb-box">
<div className="d-check-radio pop mb15"> <div className="d-check-radio pop mb15">
<input type="radio" name="radio01" id="ra01" value={0} checked={type === '0'} onChange={(e) => setType(e.target.value)} /> <input
type="radio"
name="radio01"
id="ra01"
checked={type === FLOW_DIRECTION_TYPE.EIGHT_AZIMUTH}
onChange={(e) => {
setCompasDeg(0)
setType(FLOW_DIRECTION_TYPE.EIGHT_AZIMUTH)
}}
/>
<label htmlFor="ra01">{getMessage('modal.shape.flow.direction.setting.orientation.8')}</label> <label htmlFor="ra01">{getMessage('modal.shape.flow.direction.setting.orientation.8')}</label>
</div> </div>
<div className="grid-select "> <div className="grid-select ">
<QSelectBox title={''} options={orientations} value={selectedOrientation} onChange={(e) => setSelectedOrientation(e)} /> <QSelectBox
title={''}
options={orientations}
value={selectedOrientation}
onChange={(e) => {
setType(FLOW_DIRECTION_TYPE.EIGHT_AZIMUTH)
setSelectedOrientation(e)
setCompasDeg(e.value)
}}
/>
</div> </div>
</div> </div>
<div className="mb-box"> <div className="mb-box">
<div className="d-check-radio pop"> <div className="d-check-radio pop">
<input type="radio" name="radio01" id="ra02" value={1} checked={type === '1'} onChange={(e) => setType(e.target.value)} /> <input
type="radio"
name="radio01"
id="ra02"
value={1}
checked={type === FLOW_DIRECTION_TYPE.TWENTY_FOUR_AZIMUTH}
onChange={(e) => {
setType(FLOW_DIRECTION_TYPE.TWENTY_FOUR_AZIMUTH)
}}
/>
<label htmlFor="ra02">{getMessage('modal.shape.flow.direction.setting.orientation.24')}</label> <label htmlFor="ra02">{getMessage('modal.shape.flow.direction.setting.orientation.24')}</label>
</div> </div>
</div> </div>
@ -110,22 +117,27 @@ export default function FlowDirectionSetting(props) {
<div <div
key={index} key={index}
className={`circle ${compasDeg === 15 * (12 + index) ? 'act' : ''}`} className={`circle ${compasDeg === 15 * (12 + index) ? 'act' : ''}`}
onClick={() => setCompasDeg(15 * (12 + index))} onClick={() => {
> setType(FLOW_DIRECTION_TYPE.TWENTY_FOUR_AZIMUTH)
<i>{13 - index}</i> setCompasDeg(15 * (12 + index))
</div> }}
></div>
))} ))}
{Array.from({ length: 180 / 15 - 1 }).map((dot, index) => ( {Array.from({ length: 180 / 15 - 1 }).map((dot, index) => (
<div <div
key={index} key={index}
className={`circle ${compasDeg === 15 * (index + 1) ? 'act' : ''}`} className={`circle ${compasDeg === 15 * (index + 1) ? 'act' : ''}`}
onClick={() => setCompasDeg(15 * (index + 1))} onClick={() => {
> setType(FLOW_DIRECTION_TYPE.TWENTY_FOUR_AZIMUTH)
<i>{24 - index}</i> setCompasDeg(15 * (index + 1))
</div> }}
></div>
))} ))}
<div className="compas"> <div className="compas">
<div className="compas-arr" style={{ transform: `rotate(${compasDeg}deg)` }}></div> <div
className="compas-arr"
style={{ transform: `${type === FLOW_DIRECTION_TYPE.TWENTY_FOUR_AZIMUTH && `rotate(${compasDeg}deg)`}` }}
></div>
</div> </div>
</div> </div>
</div> </div>
@ -133,7 +145,9 @@ export default function FlowDirectionSetting(props) {
</div> </div>
</div> </div>
<div className="grid-btn-wrap"> <div className="grid-btn-wrap">
<button className="btn-frame modal act">{getMessage('modal.common.save')}</button> <button className="btn-frame modal act" onClick={() => changeSurfaceFlowDirection(target, flowDirection, compasDeg)}>
{getMessage('modal.common.save')}
</button>
</div> </div>
</div> </div>
</div> </div>

View File

@ -3,38 +3,57 @@ import { useRecoilValue } from 'recoil'
import { contextPopupPositionState } from '@/store/popupAtom' import { contextPopupPositionState } from '@/store/popupAtom'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { usePopup } from '@/hooks/usePopup' import { usePopup } from '@/hooks/usePopup'
import { useState } from 'react' import { useState, useEffect } from 'react'
import { useSurfaceShapeBatch } from '@/hooks/surface/useSurfaceShapeBatch'
import { useEvent } from '@/hooks/useEvent'
import { LINE_TYPE } from '@/common/common'
export default function LinePropertySetting(props) { export default function LinePropertySetting(props) {
const contextPopupPosition = useRecoilValue(contextPopupPositionState) const contextPopupPosition = useRecoilValue(contextPopupPositionState)
const { id, pos = contextPopupPosition } = props const { id, pos = contextPopupPosition, target } = props
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { closePopup } = usePopup() const { closePopup } = usePopup()
const { changeSurfaceLinePropertyEvent, changeSurfaceLineProperty, changeSurfaceLinePropertyReset } = useSurfaceShapeBatch()
const { initEvent } = useEvent()
const properties = [ const properties = [
{ name: getMessage('eaves.line'), value: 'eaves' }, { name: getMessage('eaves.line'), value: LINE_TYPE.WALLLINE.EAVES },
{ name: getMessage('ridge'), value: 'ridge' }, { name: getMessage('ridge'), value: LINE_TYPE.SUBLINE.RIDGE },
{ name: getMessage('oneside.flow.ridge'), value: 'onesideFlowRidge' }, { name: getMessage('oneside.flow.ridge'), value: LINE_TYPE.SUBLINE.ONESIDE_FLOW_RIDGE },
{ name: getMessage('gable'), value: 'gable' }, { name: getMessage('gable'), value: LINE_TYPE.WALLLINE.GABLE },
{ name: getMessage('gable.left'), value: 'gableLeft' }, { name: getMessage('gable.left'), value: LINE_TYPE.WALLLINE.GABLE_LEFT },
{ name: getMessage('gable.right'), value: 'gableRight' }, { name: getMessage('gable.right'), value: LINE_TYPE.WALLLINE.GABLE_RIGHT },
{ name: getMessage('yosemune'), value: 'yosemune' }, { name: getMessage('yosemune'), value: LINE_TYPE.SUBLINE.YOSEMUNE },
{ name: getMessage('valley'), value: 'valley' }, { name: getMessage('valley'), value: LINE_TYPE.SUBLINE.YOSEMUNE },
{ name: getMessage('l.abandon.valley'), value: 'lAbandonValley' }, { name: getMessage('l.abandon.valley'), value: LINE_TYPE.SUBLINE.L_ABANDON_VALLEY },
{ name: getMessage('mansard'), value: 'mansard' }, { name: getMessage('mansard'), value: LINE_TYPE.SUBLINE.MANSARD },
{ name: getMessage('wall.merge'), value: 'wallCollection' }, { name: getMessage('wall.merge'), value: LINE_TYPE.SUBLINE.WALL_COLLECTION },
{ name: getMessage('wall.merge.type'), value: 'wallCollectionType' }, { name: getMessage('wall.merge.type'), value: LINE_TYPE.SUBLINE.WALL_COLLECTION_TYPE },
{ name: getMessage('wall.merge.flow'), value: 'wallCollectionFlow' }, { name: getMessage('wall.merge.flow'), value: LINE_TYPE.SUBLINE.WALL_COLLECTION_FLOW },
{ name: getMessage('wall.merge.flow.left'), value: 'wallCollectionFlowLeft' }, { name: getMessage('wall.merge.flow.left'), value: LINE_TYPE.SUBLINE.WALL_COLLECTION_FLOW_LEFT },
{ name: getMessage('wall.merge.flow.right'), value: 'wallCollectionFlowRight' }, { name: getMessage('wall.merge.flow.right'), value: LINE_TYPE.SUBLINE.WALL_COLLECTION_FLOW_RIGHT },
{ name: getMessage('no.setting'), value: 'noSetting' }, { name: getMessage('no.setting'), value: LINE_TYPE.WALLLINE.DEFAULT },
] ]
const [selectedProperty, setSelectedProperty] = useState(null) const [selectedProperty, setSelectedProperty] = useState(null)
useEffect(() => {
changeSurfaceLinePropertyEvent()
return () => {
initEvent()
}
}, [])
const handleClosePopup = () => {
closePopup(id)
changeSurfaceLinePropertyReset(target)
}
return ( return (
<WithDraggable isShow={true} pos={pos}> <WithDraggable isShow={true} pos={pos}>
<div className={`modal-pop-wrap r mount`}> <div className={`modal-pop-wrap r mount`}>
<div className="modal-head"> <div className="modal-head">
<h1 className="title">{getMessage('contextmenu.line.property.edit')} </h1> <h1 className="title">{getMessage('contextmenu.line.property.edit')} </h1>
<button className="modal-close" onClick={() => closePopup(id)}> <button className="modal-close" onClick={() => handleClosePopup()}>
닫기 닫기
</button> </button>
</div> </div>
@ -66,7 +85,9 @@ export default function LinePropertySetting(props) {
</div> </div>
</div> </div>
<div className="grid-btn-wrap"> <div className="grid-btn-wrap">
<button className="btn-frame modal act">{getMessage('modal.common.save')}</button> <button className="btn-frame modal act" onClick={() => changeSurfaceLineProperty(selectedProperty, target)}>
{getMessage('modal.common.save')}
</button>
</div> </div>
</div> </div>
</div> </div>

View File

@ -8,8 +8,6 @@ import { useMessage } from '@/hooks/useMessage'
import { useAxios } from '@/hooks/useAxios' import { useAxios } from '@/hooks/useAxios'
import { useSwal } from '@/hooks/useSwal' import { useSwal } from '@/hooks/useSwal'
import { usePopup } from '@/hooks/usePopup' import { usePopup } from '@/hooks/usePopup'
import useRefFiles from '@/hooks/common/useRefFiles'
import { usePlan } from '@/hooks/usePlan'
import SizeGuide from '@/components/floor-plan/modal/placementShape/SizeGuide' import SizeGuide from '@/components/floor-plan/modal/placementShape/SizeGuide'
import MaterialGuide from '@/components/floor-plan/modal/placementShape/MaterialGuide' import MaterialGuide from '@/components/floor-plan/modal/placementShape/MaterialGuide'
@ -24,20 +22,6 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, set
const [canvasSetting, setCanvasSetting] = useRecoilState(canvasSettingState) const [canvasSetting, setCanvasSetting] = useRecoilState(canvasSettingState)
const { closePopup } = usePopup() const { closePopup } = usePopup()
const [basicSetting, setBasicSettings] = useRecoilState(basicSettingState) const [basicSetting, setBasicSettings] = useRecoilState(basicSettingState)
const {
refImage,
queryRef,
setRefImage,
handleRefFile,
refFileMethod,
setRefFileMethod,
handleRefFileMethod,
mapPositionAddress,
setMapPositionAddress,
handleFileDelete,
handleMapImageDown,
} = useRefFiles()
const { currentCanvasPlan } = usePlan()
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { get, post } = useAxios() const { get, post } = useAxios()
@ -503,90 +487,6 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, set
</div> </div>
</td> </td>
</tr> </tr>
<tr>
<th>{getMessage('common.input.file')}</th>
<td>
<div className="pop-form-radio mb10">
<div className="d-check-radio pop">
<input
type="radio"
name="radio03"
id="ra06"
value={'1'}
onChange={(e) => handleRefFileMethod(e)}
checked={refFileMethod === '1'}
/>
<label htmlFor="ra06">ファイルを読み込む</label>
</div>
<div className="d-check-radio pop">
<input
type="radio"
name="radio03"
id="ra07"
value={'2'}
onChange={(e) => handleRefFileMethod(e)}
checked={refFileMethod === '2'}
/>
<label htmlFor="ra07">アドレスを読み込む</label>
</div>
</div>
{/* 파일 불러오기 */}
{refFileMethod === '1' && (
<div className="flex-box">
<div className="img-edit-wrap">
<label className="img-edit-btn" htmlFor="img_file">
<span className="img-edit"></span>
{getMessage('common.input.file.load')}
</label>
<input type="file" id="img_file" style={{ display: 'none' }} onChange={(e) => handleRefFile(e.target.files[0])} />
</div>
<div className="img-name-wrap">
{currentCanvasPlan?.bgImageName === null ? (
<input type="text" className="input-origin al-l" defaultValue={''} value={refImage ? refImage.name : ''} readOnly />
) : (
<input type="text" className="input-origin al-l" value={currentCanvasPlan?.bgImageName} readOnly />
)}
{(refImage || currentCanvasPlan?.bgImageName) && <button className="img-check" onClick={handleFileDelete}></button>}
</div>
</div>
)}
{/* 주소 불러오기 */}
{refFileMethod === '2' && (
<div className="flex-box for-address">
<input
type="text"
className="input-origin al-l mr10"
placeholder={'住所入力'}
ref={queryRef}
value={mapPositionAddress}
onChange={(e) => setMapPositionAddress(e.target.value)}
/>
<div className="img-edit-wrap mr5">
<button className="img-edit-btn" onClick={handleMapImageDown}>
完了
</button>
</div>
{mapPositionAddress && <span className="check-address fail"></span>}
{/* <span className="check-address success"></span> */}
</div>
)}
{/* <div className="flex-box">
<div className="img-edit-wrap">
<label className="img-edit-btn" htmlFor="img_file">
<span className="img-edit"></span>
{getMessage('common.input.file.load')}
</label>
<input type="file" id="img_file" style={{ display: 'none' }} onChange={(e) => handleRefFile(e.target.files[0])} />
</div>
<div className="img-name-wrap">
<input type="text" className="input-origin al-l" defaultValue={''} value={refImage ? refImage.name : ''} readOnly />
{refImage && <button className="img-check" onClick={() => setRefImage(null)}></button>}
</div>
</div> */}
</td>
</tr>
</tbody> </tbody>
</table> </table>
</div> </div>

View File

@ -1,4 +1,3 @@
import { useRecoilValue } from 'recoil'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import React, { useEffect, useState } from 'react' import React, { useEffect, useState } from 'react'
import DimensionLineSetting from '@/components/floor-plan/modal/setting01/dimensionLine/DimensionLineSetting' import DimensionLineSetting from '@/components/floor-plan/modal/setting01/dimensionLine/DimensionLineSetting'
@ -6,55 +5,35 @@ import { usePopup } from '@/hooks/usePopup'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
import FontSetting from '@/components/common/font/FontSetting' import FontSetting from '@/components/common/font/FontSetting'
import PlanSizeSetting from '@/components/floor-plan/modal/setting01/planSize/PlanSizeSetting' import PlanSizeSetting from '@/components/floor-plan/modal/setting01/planSize/PlanSizeSetting'
import { dimensionLineSettingsState } from '@/store/commonUtilsAtom'
import { useCanvasSetting } from '@/hooks/option/useCanvasSetting' import { useCanvasSetting } from '@/hooks/option/useCanvasSetting'
import { useRecoilState, useRecoilValue } from 'recoil'
import { fontSelector, globalFontAtom } from '@/store/fontAtom'
export default function SecondOption() { export default function SecondOption() {
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { addPopup, closePopup, closePopups } = usePopup() const { addPopup, closePopup } = usePopup()
const [showFontSettingModal, setShowFontSettingModal] = useState(false) const [showFontSettingModal, setShowFontSettingModal] = useState(false)
const [showDimensionLineSettingModal, setShowDimensionLineSettingModal] = useState(false) const [showDimensionLineSettingModal, setShowDimensionLineSettingModal] = useState(false)
const [showPlanSizeSettingModal, setShowPlanSizeSettingModal] = useState(false) const [showPlanSizeSettingModal, setShowPlanSizeSettingModal] = useState(false)
const dimensionSettings = useRecoilValue(dimensionLineSettingsState) const [globalFont, setGlobalFont] = useRecoilState(globalFontAtom)
const [objectNo, setObjectNo] = useState('test123240912001') // const [objectNo, setObjectNo] = useState('test123240912001') //
const { settingModalFirstOptions, setSettingModalFirstOptions } = useCanvasSetting()
const { settingModalSecondOptions, setSettingModalSecondOptions } = useCanvasSetting() const { settingModalSecondOptions, setSettingModalSecondOptions } = useCanvasSetting()
const { adsorptionPointMode, setAdsorptionPointMode } = useCanvasSetting() const { adsorptionPointMode, setAdsorptionPointMode } = useCanvasSetting()
const { fetchSettings, frontSettings, onClickOption } = useCanvasSetting() const { fetchSettings, frontSettings, onClickOption } = useCanvasSetting()
const commonFont = useRecoilValue(fontSelector('commonText'))
const flowFont = useRecoilValue(fontSelector('flowText'))
const lengthFont = useRecoilValue(fontSelector('lengthText'))
const circuitNumberTextFont = useRecoilValue(fontSelector('circuitNumberText'))
const [dimensionId, setDimensionId] = useState(uuidv4())
const [fontId, setFontId] = useState(uuidv4())
const [planSizeId, setPlanSizeId] = useState(uuidv4())
// //
useEffect(() => { useEffect(() => {
console.log('SecondOption useEffect 실행')
//fetchSettings() //fetchSettings()
}, [objectNo]) }, [objectNo])
let dimensionId = null
let fontId = null
let planSizeId = null
const [pixel, setPixel] = useState(dimensionSettings.pixel)
const [color, setColor] = useState(dimensionSettings.color)
const [font, setFont] = useState(null)
const [fontSize, setFontSize] = useState(dimensionSettings.fontSize)
const [fontColor, setFontColor] = useState(dimensionSettings.fontColor)
useEffect(() => {
dimensionId = uuidv4()
fontId = uuidv4()
planSizeId = uuidv4()
}, [])
const dimensionProps = { const dimensionProps = {
color,
setColor,
pixel,
setPixel,
font,
setFont,
fontSize,
setFontSize,
fontColor,
setFontColor,
id: dimensionId, id: dimensionId,
isShow: showDimensionLineSettingModal, isShow: showDimensionLineSettingModal,
setIsShow: setShowDimensionLineSettingModal, setIsShow: setShowDimensionLineSettingModal,
@ -62,23 +41,6 @@ export default function SecondOption() {
const [horizon, setHorizon] = useState(1600) const [horizon, setHorizon] = useState(1600)
const [vertical, setVertical] = useState(1600) const [vertical, setVertical] = useState(1600)
const fontProps = {
id: fontId,
pos: { x: 745, y: 180 },
setIsShow: setShowFontSettingModal,
isConfig: true,
}
const planSizeProps = {
id: planSizeId,
horizon,
setHorizon,
vertical,
setVertical,
setIsShow: setShowPlanSizeSettingModal,
pos: { x: 1025, y: 180 },
}
const handlePopup = (type) => { const handlePopup = (type) => {
setShowPlanSizeSettingModal(false) setShowPlanSizeSettingModal(false)
setShowFontSettingModal(false) setShowFontSettingModal(false)
@ -90,6 +52,7 @@ export default function SecondOption() {
setShowDimensionLineSettingModal(false) setShowDimensionLineSettingModal(false)
fontProps.type = 'commonText' fontProps.type = 'commonText'
fontProps.id = fontId + 1 fontProps.id = fontId + 1
fontProps.font = commonFont
addPopup(fontId + 1, 2, <FontSetting {...fontProps} />, true) addPopup(fontId + 1, 2, <FontSetting {...fontProps} />, true)
break break
} }
@ -99,6 +62,7 @@ export default function SecondOption() {
setShowFontSettingModal(true) setShowFontSettingModal(true)
setShowDimensionLineSettingModal(false) setShowDimensionLineSettingModal(false)
fontProps.type = 'flowText' fontProps.type = 'flowText'
fontProps.font = flowFont
fontProps.id = fontId + 2 fontProps.id = fontId + 2
addPopup(fontId + 2, 2, <FontSetting {...fontProps} />, true) addPopup(fontId + 2, 2, <FontSetting {...fontProps} />, true)
break break
@ -107,9 +71,9 @@ export default function SecondOption() {
case 'font3': { case 'font3': {
// //
setShowFontSettingModal(true) setShowFontSettingModal(true)
setShowDimensionLineSettingModal(false) setShowDimensionLineSettingModal(false)
fontProps.type = 'lengthText' fontProps.type = 'lengthText'
fontProps.font = lengthFont
fontProps.id = fontId + 3 fontProps.id = fontId + 3
addPopup(fontId + 3, 2, <FontSetting {...fontProps} />, true) addPopup(fontId + 3, 2, <FontSetting {...fontProps} />, true)
break break
@ -120,6 +84,7 @@ export default function SecondOption() {
setShowFontSettingModal(true) setShowFontSettingModal(true)
setShowDimensionLineSettingModal(false) setShowDimensionLineSettingModal(false)
fontProps.type = 'circuitNumberText' fontProps.type = 'circuitNumberText'
fontProps.font = circuitNumberTextFont
fontProps.id = fontId fontProps.id = fontId
addPopup(fontId, 2, <FontSetting {...fontProps} />, true) addPopup(fontId, 2, <FontSetting {...fontProps} />, true)
break break
@ -129,6 +94,12 @@ export default function SecondOption() {
// //
if (!showDimensionLineSettingModal) { if (!showDimensionLineSettingModal) {
setShowDimensionLineSettingModal(true) setShowDimensionLineSettingModal(true)
fontProps.font = {
fontFamily: '',
fontColor: '',
fontSize: '',
fontWeight: '',
}
addPopup(dimensionId, 2, <DimensionLineSetting {...dimensionProps} />, true) addPopup(dimensionId, 2, <DimensionLineSetting {...dimensionProps} />, true)
} else { } else {
setShowDimensionLineSettingModal(false) setShowDimensionLineSettingModal(false)
@ -147,6 +118,38 @@ export default function SecondOption() {
} }
} }
const handleFontSave = (font) => {
setGlobalFont((prev) => {
return {
...prev,
[fontProps.type]: {
fontFamily: font.fontFamily,
fontWeight: font.fontWeight,
fontSize: font.fontSize,
fontColor: font.fontColor,
},
}
})
}
const fontProps = {
id: fontId,
pos: { x: 745, y: 180 },
setIsShow: setShowFontSettingModal,
onSave: handleFontSave,
isConfig: true,
}
const planSizeProps = {
id: planSizeId,
horizon,
setHorizon,
vertical,
setVertical,
setIsShow: setShowPlanSizeSettingModal,
pos: { x: 1025, y: 180 },
}
return ( return (
<> <>
<div className="modal-check-btn-wrap"> <div className="modal-check-btn-wrap">

View File

@ -8,68 +8,57 @@ import QSelectBox from '@/components/common/select/QSelectBox'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { dimensionLineSettingsState } from '@/store/commonUtilsAtom' import { dimensionLineSettingsState } from '@/store/commonUtilsAtom'
import { useRecoilState } from 'recoil' import { useRecoilState } from 'recoil'
import { globalFontAtom } from '@/store/fontAtom'
/*
color: 치수선
fontColor: 글꼴
fontSize: 치수선 치수
pixel: 치수선 두깨
*/
export default function DimensionLineSetting(props) { export default function DimensionLineSetting(props) {
const { const { isShow, setIsShow, id, pos = { x: 985, y: 180 } } = props
color,
setColor,
font,
setFont,
fontColor,
setFontColor,
fontSize,
setFontSize,
pixel,
setPixel,
isShow,
setIsShow,
id,
pos = { x: 985, y: 180 },
} = props
const { addPopup, closePopup, closePopups } = usePopup() const { addPopup, closePopup, closePopups } = usePopup()
const pixels = Array.from({ length: 5 }).map((_, index) => { const pixels = Array.from({ length: 5 }).map((_, index) => {
return { name: index + 1, value: index + 1 } return { id: index, name: index + 1, value: index + 1 }
}) })
const [originColor, setOriginColor] = useState(color) const [dimensionLineSettings, setDimensionLineSettings] = useRecoilState(dimensionLineSettingsState)
const [originFont, setOriginFont] = useState(font) const [globalFont, setGlobalFont] = useRecoilState(globalFontAtom)
const [originFontColor, setOriginFontColor] = useState(fontColor) const [originPixel, setOriginPixel] = useState(dimensionLineSettings.pixel)
const [originFontSize, setOriginFontSize] = useState(fontSize) const [originColor, setOriginColor] = useState(dimensionLineSettings.color)
const [originPixel, setOriginPixel] = useState(pixel) const [originFont, setOriginFont] = useState(globalFont.dimensionLineText.fontFamily)
const [originFontColor, setOriginFontColor] = useState(globalFont.dimensionLineText.fontColor)
const [originFontSize, setOriginFontSize] = useState(globalFont.dimensionLineText.fontSize)
const [originFontWeight, setOriginFontWeight] = useState(globalFont.dimensionLineText.fontWeight)
const [fontModalId, setFontModalId] = useState(uuidv4()) const [fontModalId, setFontModalId] = useState(uuidv4())
const [colorModalId, setColorModalId] = useState(uuidv4())
const [colorModalId, setColorModalId] = useState(uuidv4())
const [showColorPickerModal, setShowColorPickerModal] = useState(false) const [showColorPickerModal, setShowColorPickerModal] = useState(false)
const [showFontModal, setShowFontModal] = useState(false) const [showFontModal, setShowFontModal] = useState(false)
const { getMessage } = useMessage() const { getMessage } = useMessage()
const [dimensionLineSettings, setDimensionLineSettings] = useRecoilState(dimensionLineSettingsState)
useEffect(() => { useEffect(() => {
console.log(2, isShow) if (originPixel) {
if (pixel) { setOriginPixel(pixels?.filter((data) => data.value === originPixel)[0])
setOriginPixel(pixels?.filter((data) => data.value === pixel)[0])
} }
setIsShow(true) setIsShow(true)
}, []) }, [])
useEffect(() => { useEffect(() => {
console.log(1, isShow)
if (!isShow) { if (!isShow) {
closePopups([fontModalId, colorModalId]) closePopups([fontModalId, colorModalId])
} }
}, [isShow]) }, [isShow])
const handleFontSave = (font) => {
setOriginFont(font.fontFamily)
setOriginFontSize(font.fontSize)
setOriginFontColor(font.fontColor)
setOriginFontWeight(font.fontWeight)
}
const handleColorSave = () => {}
const colorPickerProps = { const colorPickerProps = {
isShow: showColorPickerModal, isShow: showColorPickerModal,
setIsShow: setShowColorPickerModal, setIsShow: setShowColorPickerModal,
color: originColor, color: originColor,
setColor: setOriginColor, setColor: setOriginColor,
id: colorModalId, id: colorModalId,
isConfig: true,
pos: { pos: {
x: 495, x: 495,
y: 180, y: 180,
@ -79,14 +68,13 @@ export default function DimensionLineSetting(props) {
const fontProps = { const fontProps = {
isShow: showFontModal, isShow: showFontModal,
setIsShow: setShowFontModal, setIsShow: setShowFontModal,
color: originColor, font: {
setColor: setOriginColor, fontFamily: originFont,
font: originFont, fontSize: originFontSize,
setFont: setOriginFont, fontColor: originFontColor,
fontColor: 'black', fontWeight: originFontWeight,
setFontColor: setOriginFontColor, },
fontSize: originFontSize, onSave: handleFontSave,
setFontSize: setOriginFontSize,
isConfig: true, isConfig: true,
id: fontModalId, id: fontModalId,
pos: { pos: {
@ -106,6 +94,29 @@ export default function DimensionLineSetting(props) {
} }
} }
const onSave = () => {
setGlobalFont((prev) => {
return {
...prev,
dimensionLineText: {
fontFamily: originFont,
fontWeight: originFontWeight,
fontSize: originFontSize,
fontColor: originFontColor,
},
}
})
setDimensionLineSettings((prev) => {
return {
...prev,
pixel: originPixel?.value,
color: originColor,
}
})
setIsShow(false)
closePopups([fontModalId, colorModalId, id])
}
return ( return (
<WithDraggable isShow={true} pos={pos}> <WithDraggable isShow={true} pos={pos}>
<div className={`modal-pop-wrap xxxm mount`}> <div className={`modal-pop-wrap xxxm mount`}>
@ -149,9 +160,10 @@ export default function DimensionLineSetting(props) {
className="font" className="font"
style={{ style={{
fontFamily: originFont?.value ?? '', fontFamily: originFont?.value ?? '',
color: originFontColor?.value ?? 'black', color: originFontColor.value ?? 'black',
fontSize: originFontSize?.value ?? '12px', fontSize: originFontSize?.value ?? '12px',
fontWeight: '400', fontStyle: originFontWeight?.value.toLowerCase().includes('italic') ? 'italic' : 'normal',
fontWeight: originFontWeight?.value.toLowerCase().includes('bold') ? 'bold' : 'normal',
}} }}
> >
9,999 9,999
@ -161,7 +173,7 @@ export default function DimensionLineSetting(props) {
style={{ style={{
backgroundColor: originColor, backgroundColor: originColor,
borderColor: originColor, borderColor: originColor,
height: originPixel.value, height: originPixel?.value,
}} }}
></span> ></span>
</div> </div>
@ -169,26 +181,7 @@ export default function DimensionLineSetting(props) {
</div> </div>
</div> </div>
<div className="grid-btn-wrap"> <div className="grid-btn-wrap">
<button <button className="btn-frame modal act" onClick={onSave}>
className="btn-frame modal act"
onClick={() => {
setPixel(originPixel.value)
setColor(originColor)
setFont(originFont)
setDimensionLineSettings((prev) => {
return {
...prev,
pixel: originPixel.value,
color: originColor,
font: originFont,
fontColor: originFontColor,
fontSize: originFontSize,
}
})
setIsShow(false)
closePopups([fontModalId, colorModalId, id])
}}
>
{getMessage('modal.common.save')} {getMessage('modal.common.save')}
</button> </button>
</div> </div>

View File

@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react' import { useEffect, useState, useContext } from 'react'
import ProductItem from './ProductItem' import ProductItem from './ProductItem'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import Image from 'next/image' import Image from 'next/image'
@ -8,17 +8,16 @@ import { useRecoilValue } from 'recoil'
import { useRouter } from 'next/navigation' import { useRouter } from 'next/navigation'
import { globalLocaleStore } from '@/store/localeAtom' import { globalLocaleStore } from '@/store/localeAtom'
import { queryStringFormatter } from '@/util/common-utils' import { queryStringFormatter } from '@/util/common-utils'
import { sessionStore } from '@/store/commonAtom'
import MainSkeleton from '../ui/MainSkeleton' import MainSkeleton from '../ui/MainSkeleton'
import { SessionContext } from '@/app/SessionProvider'
export default function MainContents() { export default function MainContents() {
const { session } = useContext(SessionContext)
const { getMessage } = useMessage() const { getMessage } = useMessage()
const router = useRouter() const router = useRouter()
const globalLocaleState = useRecoilValue(globalLocaleStore) const globalLocaleState = useRecoilValue(globalLocaleStore)
const { promiseGet } = useAxios(globalLocaleState) const { promiseGet } = useAxios(globalLocaleState)
const sessionState = useRecoilValue(sessionStore)
// //
const [objectList, setObjectList] = useState([]) const [objectList, setObjectList] = useState([])
@ -36,12 +35,12 @@ export default function MainContents() {
fetchObjectList() fetchObjectList()
fetchNoticeList() fetchNoticeList()
fetchFaqList() fetchFaqList()
}, [sessionState]) }, [])
// / Sales Contact info // / Sales Contact info
const fetchObjectList = async () => { const fetchObjectList = async () => {
try { try {
const apiUrl = `/api/main-page/object/${sessionState?.storeId}/list` const apiUrl = `/api/main-page/object/${session?.storeId}/list`
await promiseGet({ await promiseGet({
url: apiUrl, url: apiUrl,
}).then((res) => { }).then((res) => {

View File

@ -173,8 +173,10 @@ export default function Stuff() {
schFromDt: dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD'), schFromDt: dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD'),
schToDt: dayjs(new Date()).format('YYYY-MM-DD'), schToDt: dayjs(new Date()).format('YYYY-MM-DD'),
startRow: (pageNo - 1) * pageSize + 1, startRow: (pageNo - 1) * pageSize + 1,
endRow: pageNo * pageSize, // endRow: pageNo * pageSize,
schSelSaleStoreId: stuffSearchParams?.schOtherSelSaleStoreId ? stuffSearchParams.schOtherSelSaleStoreId : stuffSearchParams.schSelSaleStoreId, endRow: stuffSearchParams?.endRow,
schSelSaleStoreId: stuffSearchParams?.schSelSaleStoreId ? stuffSearchParams.schSelSaleStoreId : '',
schOtherSelSaleStoreId: stuffSearchParams?.schOtherSelSaleStoreId ? stuffSearchParams.schOtherSelSaleStoreId : '',
schSortType: stuffSearchParams.schSortType, schSortType: stuffSearchParams.schSortType,
} }
@ -190,11 +192,7 @@ export default function Stuff() {
}) })
} }
//if (session.storeId === 'T01') {
fetchData() fetchData()
//} else if (stuffSearch.schSelSaleStoreId !== '') {
//fetchData()
//}
} else if (stuffSearchParams?.code === 'M') { } else if (stuffSearchParams?.code === 'M') {
const params = { const params = {
saleStoreId: session?.storeId, saleStoreId: session?.storeId,
@ -220,10 +218,6 @@ export default function Stuff() {
stuffSearchParams.endRow = 1 * pageSize stuffSearchParams.endRow = 1 * pageSize
stuffSearchParams.schSortType = defaultSortType stuffSearchParams.schSortType = defaultSortType
setPageNo(1) setPageNo(1)
setStuffSearch({
...stuffSearch,
code: 'FINISH',
})
async function fetchData() { async function fetchData() {
const apiUrl = `/api/object/list?saleStoreId=${session?.storeId}&${queryStringFormatter(stuffSearchParams)}` const apiUrl = `/api/object/list?saleStoreId=${session?.storeId}&${queryStringFormatter(stuffSearchParams)}`
@ -241,6 +235,25 @@ export default function Stuff() {
fetchData() fetchData()
} else if (stuffSearchParams?.code === 'C') { } else if (stuffSearchParams?.code === 'C') {
resetStuffRecoil() resetStuffRecoil()
} else if (stuffSearchParams?.code === 'FINISH') {
stuffSearchParams.startRow = 1
stuffSearchParams.endRow = 1 * pageSize
stuffSearchParams.schSortType = defaultSortType
setPageNo(1)
async function fetchData() {
const apiUrl = `/api/object/list?saleStoreId=${session?.storeId}&${queryStringFormatter(stuffSearchParams)}`
await get({ url: apiUrl }).then((res) => {
if (!isEmptyArray(res)) {
setGridProps({ ...gridProps, gridData: res, count: res[0].totCnt })
setTotalCount(res[0].totCnt)
} else {
setGridProps({ ...gridProps, gridData: [], count: 0 })
setTotalCount(0)
}
})
}
fetchData()
} }
}, [stuffSearchParams]) }, [stuffSearchParams])
@ -254,12 +267,15 @@ export default function Stuff() {
: stuffSearchParams.schSelSaleStoreId : stuffSearchParams.schSelSaleStoreId
setPageSize(e.target.value) setPageSize(e.target.value)
setStuffSearch({ setStuffSearch({
...stuffSearchParams, // ...stuffSearchParams,
...stuffSearch,
code: 'S',
startRow: startRow, startRow: startRow,
endRow: 1 * e.target.value, endRow: 1 * e.target.value,
}) })
setPageNo(1) setPageNo(1)
const apiUrl = `/api/object/list?saleStoreId=${session?.storeId}&${queryStringFormatter(stuffSearchParams)}` const apiUrl = `/api/object/list?saleStoreId=${session?.storeId}&${queryStringFormatter(stuffSearchParams)}`
get({ url: apiUrl }).then((res) => { get({ url: apiUrl }).then((res) => {
if (!isEmptyArray(res)) { if (!isEmptyArray(res)) {
@ -345,13 +361,13 @@ export default function Stuff() {
</div> </div>
<div className="left-unit-box"> <div className="left-unit-box">
<div className="select-box mr5" style={{ width: '110px' }}> <div className="select-box mr5" style={{ width: '110px' }}>
<select className="select-light black" name="" id="" onChange={onChangeSortType}> <select className="select-light black" onChange={onChangeSortType} defaultValue={stuffSearch.schSortType}>
<option value="R">{getMessage('stuff.search.grid.schSortTypeR')}</option> <option value="R">{getMessage('stuff.search.grid.schSortTypeR')}</option>
<option value="U">{getMessage('stuff.search.grid.schSortTypeU')}</option> <option value="U">{getMessage('stuff.search.grid.schSortTypeU')}</option>
</select> </select>
</div> </div>
<div className="select-box" style={{ width: '80px' }}> <div className="select-box" style={{ width: '80px' }}>
<select className="select-light black" name="" id="" onChange={onChangePerPage}> <select className="select-light black" onChange={onChangePerPage} defaultValue={stuffSearch.endRow}>
<option value="100">100</option> <option value="100">100</option>
<option value="200">200</option> <option value="200">200</option>
<option value="300">300</option> <option value="300">300</option>

View File

@ -18,6 +18,7 @@ import WindSelectPop from './popup/WindSelectPop'
import { useCommonCode } from '@/hooks/common/useCommonCode' import { useCommonCode } from '@/hooks/common/useCommonCode'
import StuffPlanQGrid from './StuffPlanQGrid' import StuffPlanQGrid from './StuffPlanQGrid'
import { floorPlanObjectState } from '@/store/floorPlanObjectAtom' import { floorPlanObjectState } from '@/store/floorPlanObjectAtom'
import { ManagementContext } from '@/app/management/ManagementProvider'
export default function StuffDetail() { export default function StuffDetail() {
const setFloorPlanObjectNo = useSetRecoilState(floorPlanObjectState) // const setFloorPlanObjectNo = useSetRecoilState(floorPlanObjectState) //
@ -96,7 +97,7 @@ export default function StuffDetail() {
const objectNo = searchParams.get('objectNo') //url set const objectNo = searchParams.get('objectNo') //url set
const [editMode, setEditMode] = useState('NEW') const [editMode, setEditMode] = useState('NEW')
const [detailData, setDetailData] = useState({}) const { managementState, setManagementState } = useContext(ManagementContext)
const [planGridProps, setPlanGridProps] = useState({ const [planGridProps, setPlanGridProps] = useState({
planGridData: [], planGridData: [],
@ -289,9 +290,9 @@ export default function StuffDetail() {
promiseGet({ url: `/api/object/${objectNo}/detail` }).then((res) => { promiseGet({ url: `/api/object/${objectNo}/detail` }).then((res) => {
if (res.status === 200) { if (res.status === 200) {
if (res.data != null) { if (res.data != null) {
setDetailData(res.data) setManagementState(res.data)
} else { } else {
setDetailData({}) setManagementState({})
} }
if (isNotEmptyArray(res.data.planList)) { if (isNotEmptyArray(res.data.planList)) {
setPlanGridProps({ ...planGridProps, planGridData: res.data.planList }) setPlanGridProps({ ...planGridProps, planGridData: res.data.planList })
@ -299,7 +300,7 @@ export default function StuffDetail() {
setPlanGridProps({ ...planGridProps, planGridData: [] }) setPlanGridProps({ ...planGridProps, planGridData: [] })
} }
} else { } else {
setDetailData({}) setManagementState({})
setPlanGridProps({ ...planGridProps, planGridData: [] }) setPlanGridProps({ ...planGridProps, planGridData: [] })
} }
}) })
@ -415,7 +416,7 @@ export default function StuffDetail() {
}, [commonCode]) }, [commonCode])
useEffect(() => { useEffect(() => {
if (isObjectNotEmpty(detailData)) { if (isObjectNotEmpty(managementState)) {
// API // API
get({ url: '/api/object/prefecture/list' }).then((res) => { get({ url: '/api/object/prefecture/list' }).then((res) => {
if (!isEmptyArray(res)) { if (!isEmptyArray(res)) {
@ -449,18 +450,17 @@ export default function StuffDetail() {
setFavoriteStoreList(favList) setFavoriteStoreList(favList)
setShowSaleStoreList(favList) setShowSaleStoreList(favList)
if (detailData.firstAgentId != null) { if (managementState.firstAgentId != null) {
form.setValue('saleStoreId', detailData.firstAgentId) form.setValue('saleStoreId', managementState.firstAgentId)
setSelOptions(detailData.firstAgentId) setSelOptions(managementState.firstAgentId)
} else { } else {
form.setValue('saleStoreId', detailData.saleStoreId) form.setValue('saleStoreId', managementState.saleStoreId)
setSelOptions(detailData.saleStoreId) setSelOptions(managementState.saleStoreId)
} }
// 1 2 // 1 2
let data = detailData?.firstAgentId ? detailData.firstAgentId : detailData.saleStoreId let data = managementState?.firstAgentId ? managementState.firstAgentId : managementState.saleStoreId
// url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=0&userId=${session?.userId}`
url = `/api/object/saleStore/${data}/list?firstFlg=0&userId=${session?.userId}` url = `/api/object/saleStore/${data}/list?firstFlg=0&userId=${session?.userId}`
get({ url: url }).then((res) => { get({ url: url }).then((res) => {
@ -502,71 +502,74 @@ export default function StuffDetail() {
// 1 1 // 1 1
// 2 2 1 // 2 2 1
if (detailData.saleStoreLevel === '1') { if (managementState.saleStoreLevel === '1') {
setSelOptions(detailData.saleStoreId) setSelOptions(managementState.saleStoreId)
form.setValue('saleStoreId', detailData.saleStoreId) form.setValue('saleStoreId', managementState.saleStoreId)
form.setValue('saleStoreLevel', detailData.saleStoreLevel) form.setValue('saleStoreLevel', managementState.saleStoreLevel)
} else { } else {
setOtherSelOptions(detailData.saleStoreId) setOtherSelOptions(managementState.saleStoreId)
form.setValue('otherSaleStoreId', detailData.saleStoreId) form.setValue('otherSaleStoreId', managementState.saleStoreId)
form.setValue('otherSaleStoreLevel', detailData.saleStoreLevel) form.setValue('otherSaleStoreLevel', managementState.saleStoreLevel)
form.setValue('saleStoreLevel', '1')
} }
//No. //No.
form.setValue('planReqNo', detailData.planReqNo) form.setValue('planReqNo', managementState.planReqNo)
// //
form.setValue('receiveUser', detailData.receiveUser) form.setValue('receiveUser', managementState.receiveUser)
//objectStatusId //objectStatusId
setSelectObjectStatusId(detailData.objectStatusId) setSelectObjectStatusId(managementState.objectStatusId)
form.setValue('objectStatusId', detailData.objectStatusId) form.setValue('objectStatusId', managementState.objectStatusId)
// //
form.setValue('objectName', detailData.objectName) form.setValue('objectName', managementState.objectName)
// //
setSelHonorificCode(detailData.objectNameOmit) setSelHonorificCode(managementState.objectNameOmit)
form.setValue('objectNameOmit', detailData.objectNameOmit) form.setValue('objectNameOmit', managementState.objectNameOmit)
// //
form.setValue('objectNameKana', detailData.objectNameKana) form.setValue('objectNameKana', managementState.objectNameKana)
// //
form.setValue('zipNo', detailData.zipNo) form.setValue('zipNo', managementState.zipNo)
// / // /
setPrefValue(detailData.prefId) setPrefValue(managementState.prefId)
form.setValue('prefId', detailData.prefId) form.setValue('prefId', managementState.prefId)
form.setValue('address', detailData.address) form.setValue('prefName', managementState.prefName)
form.setValue('address', managementState.address)
// //
form.setValue('areaId', detailData.areaId) form.setValue('areaId', managementState.areaId)
// //
form.setValue('standardWindSpeedId', detailData.standardWindSpeedId) form.setValue('standardWindSpeedId', managementState.standardWindSpeedId)
// //
form.setValue('verticalSnowCover', detailData.verticalSnowCover) form.setValue('verticalSnowCover', managementState.verticalSnowCover)
// coldRegionFlg 1 true // coldRegionFlg 1 true
form.setValue('coldRegionFlg', detailData.coldRegionFlg === '1' ? true : false) form.setValue('coldRegionFlg', managementState.coldRegionFlg === '1' ? true : false)
// surfaceType null // surfaceType null
// form.setValue('surfaceType', '') // form.setValue('surfaceType', '')
// form.setValue('surfaceType', '') // form.setValue('surfaceType', '')
form.setValue('surfaceType', detailData.surfaceType) form.setValue('surfaceType', managementState.surfaceType)
// saltAreaFlg 1 true // saltAreaFlg 1 true
form.setValue('saltAreaFlg', detailData.saltAreaFlg === '1' ? true : false) form.setValue('saltAreaFlg', managementState.saltAreaFlg === '1' ? true : false)
// //
form.setValue('installHeight', detailData.installHeight) form.setValue('installHeight', managementState.installHeight)
// null 0 // null 0
if (detailData.conType === null) { if (managementState.conType === null) {
form.setValue('conType', '0') form.setValue('conType', '0')
} else { } else {
form.setValue('conType', detailData.conType) form.setValue('conType', managementState.conType)
} }
// //
form.setValue('remarks', detailData.remarks) form.setValue('remarks', managementState.remarks)
}) })
} }
}, [detailData, session]) }, [managementState])
// //
const onChangeHonorificCode = (key) => { const onChangeHonorificCode = (key) => {
@ -856,6 +859,7 @@ export default function StuffDetail() {
const setPlanReqInfo = (info) => { const setPlanReqInfo = (info) => {
form.setValue('planReqNo', info.planReqNo) form.setValue('planReqNo', info.planReqNo)
form.setValue('objectStatusId', info.building) form.setValue('objectStatusId', info.building)
setSelectObjectStatusId(info.building)
form.setValue('objectName', info.planReqName) form.setValue('objectName', info.planReqName)
form.setValue('zipNo', info.zipNo) form.setValue('zipNo', info.zipNo)
form.setValue('address', info.address2) form.setValue('address', info.address2)
@ -872,8 +876,11 @@ export default function StuffDetail() {
form.setValue('standardWindSpeedId', `WL_${info.windSpeed}`) form.setValue('standardWindSpeedId', `WL_${info.windSpeed}`)
form.setValue('verticalSnowCover', info.verticalSnowCover) form.setValue('verticalSnowCover', info.verticalSnowCover)
form.setValue('surfaceType', info.surfaceType) form.setValue('surfaceType', info.surfaceType)
if (info.surfaceType === 'Ⅱ') { if (info.surfaceType === 'Ⅱ') {
form.setValue('saltAreaFlg', true) form.setValue('saltAreaFlg', true)
} else {
form.setValue('saltAreaFlg', false)
} }
form.setValue('installHeight', info.installHeight) form.setValue('installHeight', info.installHeight)
form.setValue('remarks', info.remarks) form.setValue('remarks', info.remarks)
@ -1034,7 +1041,12 @@ export default function StuffDetail() {
// //
const onSearchDesignRequestPopOpen = () => { const onSearchDesignRequestPopOpen = () => {
setShowDesignRequestButtonValid(true) const saleStoreId = form.watch('saleStoreId')
if (saleStoreId === '') {
alert(getMessage('stuff.planReqPopup.error.message2'))
} else {
setShowDesignRequestButtonValid(true)
}
} }
// //
@ -1177,9 +1189,9 @@ export default function StuffDetail() {
return alert(getMessage('stuff.detail.save.valierror2')) return alert(getMessage('stuff.detail.save.valierror2'))
} }
let detail_sort = Object.keys(detailData) let detail_sort = Object.keys(managementState)
.sort() .sort()
.reduce((obj, key) => ((obj[key] = detailData[key]), obj), {}) .reduce((obj, key) => ((obj[key] = managementState[key]), obj), {})
let params_sort = Object.keys(params) let params_sort = Object.keys(params)
.sort() .sort()
.reduce((obj, key) => ((obj[key] = params[key]), obj), {}) .reduce((obj, key) => ((obj[key] = params[key]), obj), {})
@ -1301,7 +1313,7 @@ export default function StuffDetail() {
// //
const onDelete = () => { const onDelete = () => {
const specificationConfirmDate = detailData.specificationConfirmDate const specificationConfirmDate = managementState.specificationConfirmDate
if (specificationConfirmDate != null) { if (specificationConfirmDate != null) {
alert(getMessage('stuff.detail.delete.message1')) alert(getMessage('stuff.detail.delete.message1'))
} else { } else {
@ -1726,31 +1738,33 @@ export default function StuffDetail() {
</th> </th>
<td> <td>
<div className="flx-box"> <div className="flx-box">
<div className="d-check-radio light mr10"> <div className="radio-wrap flx-box" style={{ width: '239px' }}>
<input <div className="d-check-radio light mr10">
type="radio" <input
name="surfaceType" type="radio"
value="Ⅲ・Ⅳ" name="surfaceType"
id="surfaceType0" value="Ⅲ・Ⅳ"
{...form.register('surfaceType')} id="surfaceType0"
onChange={(e) => { {...form.register('surfaceType')}
handleRadioChange(e) onChange={(e) => {
}} handleRadioChange(e)
/> }}
<label htmlFor="surfaceType0"></label> />
</div> <label htmlFor="surfaceType0"></label>
<div className="d-check-radio light mr10"> </div>
<input <div className="d-check-radio light">
type="radio" <input
name="surfaceType" type="radio"
value="Ⅱ" name="surfaceType"
id="surfaceType1" value="Ⅱ"
{...form.register('surfaceType')} id="surfaceType1"
onChange={(e) => { {...form.register('surfaceType')}
handleRadioChange(e) onChange={(e) => {
}} handleRadioChange(e)
/> }}
<label htmlFor="surfaceType1"></label> />
<label htmlFor="surfaceType1"></label>
</div>
</div> </div>
<div className="d-check-box light mr5"> <div className="d-check-box light mr5">
<input type="checkbox" id="saltAreaFlg" {...form.register('saltAreaFlg')} /> <input type="checkbox" id="saltAreaFlg" {...form.register('saltAreaFlg')} />
@ -1846,8 +1860,7 @@ export default function StuffDetail() {
<div className="flx-box"> <div className="flx-box">
<div className="product-input-wrap mr5"> <div className="product-input-wrap mr5">
<input type="text" className="product-input" readOnly value={form.watch('planReqNo') || ''} /> <input type="text" className="product-input" readOnly value={form.watch('planReqNo') || ''} />
{/* {detailData?.tempFlg === '1' && form.watch('planReqNo') ? ( */} {managementState?.tempFlg === '1' && form.watch('planReqNo') ? (
{detailData?.tempFlg === '1' && form.watch('planReqNo') ? (
<button <button
type="button" type="button"
className="product-delete" className="product-delete"
@ -1857,8 +1870,7 @@ export default function StuffDetail() {
></button> ></button>
) : null} ) : null}
</div> </div>
{/* {detailData?.tempFlg === '1' ? ( */} {managementState?.tempFlg === '1' ? (
{detailData?.tempFlg === '1' ? (
<> <>
<Button className="btn-origin grey" onPress={onSearchDesignRequestPopOpen}> <Button className="btn-origin grey" onPress={onSearchDesignRequestPopOpen}>
{getMessage('stuff.planReqPopup.title')} {getMessage('stuff.planReqPopup.title')}
@ -1964,8 +1976,8 @@ export default function StuffDetail() {
onChange={onSelectionChange} onChange={onSelectionChange}
getOptionLabel={(x) => x.saleStoreName} getOptionLabel={(x) => x.saleStoreName}
getOptionValue={(x) => x.saleStoreId} getOptionValue={(x) => x.saleStoreId}
isClearable={detailData.tempFlg === '0' ? false : session?.storeLvl === '1' ? true : false} isClearable={managementState.tempFlg === '0' ? false : session?.storeLvl === '1' ? true : false}
isDisabled={detailData.tempFlg === '0' ? true : session?.storeLvl !== '1' ? true : false} isDisabled={managementState.tempFlg === '0' ? true : session?.storeLvl !== '1' ? true : false}
value={saleStoreList.filter(function (option) { value={saleStoreList.filter(function (option) {
return option.saleStoreId === selOptions return option.saleStoreId === selOptions
})} })}
@ -1998,7 +2010,13 @@ export default function StuffDetail() {
getOptionValue={(x) => x.saleStoreId} getOptionValue={(x) => x.saleStoreId}
isClearable={false} isClearable={false}
isDisabled={ isDisabled={
detailData.tempFlg === '0' ? true : session?.storeLvl !== '1' ? true : session?.storeId !== 'T01' ? true : false managementState.tempFlg === '0'
? true
: session?.storeLvl !== '1'
? true
: session?.storeId !== 'T01'
? true
: false
} }
value={showSaleStoreList.filter(function (option) { value={showSaleStoreList.filter(function (option) {
return option.saleStoreId === selOptions return option.saleStoreId === selOptions
@ -2077,9 +2095,9 @@ export default function StuffDetail() {
getOptionLabel={(x) => x.saleStoreName} getOptionLabel={(x) => x.saleStoreName}
getOptionValue={(x) => x.saleStoreId} getOptionValue={(x) => x.saleStoreId}
isDisabled={ isDisabled={
detailData.tempFlg === '0' ? true : session?.storeLvl === '1' && form.watch('saleStoreId') != '' ? false : true managementState.tempFlg === '0' ? true : session?.storeLvl === '1' && form.watch('saleStoreId') != '' ? false : true
} }
isClearable={detailData.tempFlg === '0' ? false : session?.storeLvl === '1' ? true : false} isClearable={managementState.tempFlg === '0' ? false : session?.storeLvl === '1' ? true : false}
value={otherSaleStoreList.filter(function (option) { value={otherSaleStoreList.filter(function (option) {
return option.saleStoreId === otherSelOptions return option.saleStoreId === otherSelOptions
})} })}
@ -2240,32 +2258,35 @@ export default function StuffDetail() {
</th> </th>
<td> <td>
<div className="flx-box"> <div className="flx-box">
<div className="d-check-radio light mr10"> <div className="radio-wrap flx-box" style={{ width: '239px' }}>
<input <div className="d-check-radio light mr10">
type="radio" <input
name="surfaceType" type="radio"
value="Ⅲ・Ⅳ" name="surfaceType"
id="surfaceType0" value="Ⅲ・Ⅳ"
{...form.register('surfaceType')} id="surfaceType0"
onChange={(e) => { {...form.register('surfaceType')}
handleRadioChange(e) onChange={(e) => {
}} handleRadioChange(e)
/> }}
<label htmlFor="surfaceType0"></label> />
</div> <label htmlFor="surfaceType0"></label>
<div className="d-check-radio light mr10"> </div>
<input <div className="d-check-radio light">
type="radio" <input
name="surfaceType" type="radio"
value="Ⅱ" name="surfaceType"
id="surfaceType1" value="Ⅱ"
{...form.register('surfaceType')} id="surfaceType1"
onChange={(e) => { {...form.register('surfaceType')}
handleRadioChange(e) onChange={(e) => {
}} handleRadioChange(e)
/> }}
<label htmlFor="surfaceType1"></label> />
<label htmlFor="surfaceType1"></label>
</div>
</div> </div>
<div className="d-check-box light mr5"> <div className="d-check-box light mr5">
<input type="checkbox" id="saltAreaFlg" {...form.register('saltAreaFlg')} /> <input type="checkbox" id="saltAreaFlg" {...form.register('saltAreaFlg')} />
<label htmlFor="saltAreaFlg">{getMessage('stuff.detail.saltAreaFlg')}</label> <label htmlFor="saltAreaFlg">{getMessage('stuff.detail.saltAreaFlg')}</label>
@ -2329,8 +2350,7 @@ export default function StuffDetail() {
</table> </table>
</div> </div>
</div> </div>
{/* {detailData?.tempFlg === '0' ? ( */} {managementState?.tempFlg === '0' ? (
{detailData?.tempFlg === '0' ? (
<> <>
{/* 진짜R 플랜시작 */} {/* 진짜R 플랜시작 */}
<div className="table-box-title-wrap"> <div className="table-box-title-wrap">
@ -2339,7 +2359,7 @@ export default function StuffDetail() {
<ul className="info-wrap"> <ul className="info-wrap">
<li> <li>
{getMessage('stuff.detail.planList.cnt')} {getMessage('stuff.detail.planList.cnt')}
<span className="red"> {detailData.planList?.length}</span> <span className="red"> {managementState.planList?.length}</span>
</li> </li>
</ul> </ul>
</div> </div>

View File

@ -1,40 +1,21 @@
'use client' 'use client'
import React, { useState, useEffect } from 'react' import { useContext } from 'react'
import { useAxios } from '@/hooks/useAxios'
import { useRouter, useSearchParams } from 'next/navigation'
import { globalLocaleStore } from '@/store/localeAtom'
import { useRecoilValue } from 'recoil'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { ManagementContext } from '@/app/management/ManagementProvider'
export default function StuffHeader() { export default function StuffHeader() {
const { getMessage } = useMessage() const { getMessage } = useMessage()
const router = useRouter()
const searchParams = useSearchParams()
const objectNo = searchParams.get('objectNo') //url set
const globalLocaleState = useRecoilValue(globalLocaleStore)
const { get } = useAxios(globalLocaleState)
const [headerData, setHeaderData] = useState({})
useEffect(() => { const { managementState } = useContext(ManagementContext)
get({ url: `/api/object/${objectNo}/detail` }).then((res) => {
if (res != null && res != '') {
setHeaderData(res)
} else {
alert(getMessage('stuff.detail.header.message1'))
router.push('/management/stuff')
}
})
}, [objectNo])
// //
const copyObjectNo = async (objectNo) => { const copyObjectNo = async (objectNo) => {
await navigator.clipboard.writeText(objectNo) await navigator.clipboard.writeText(objectNo)
alert(getMessage('stuff.detail.header.message2')) alert(getMessage('stuff.detail.header.successCopy'))
try { try {
} catch (error) { } catch (error) {
alert(getMessage('stuff.detail.header.message3')) alert(getMessage('stuff.detail.header.failCopy'))
} }
} }
@ -43,31 +24,31 @@ export default function StuffHeader() {
<div className="sub-table-box"> <div className="sub-table-box">
<div className="info-title">{getMessage('stuff.detail.header.objectNo')}</div> <div className="info-title">{getMessage('stuff.detail.header.objectNo')}</div>
<div className="info-inner"> <div className="info-inner">
{headerData.objectNo}{' '} {managementState?.objectNo}{' '}
<button <button
className="copy-ico" className="copy-ico"
onClick={() => { onClick={() => {
copyObjectNo(headerData.objectNo) copyObjectNo(managementState?.objectNo)
}} }}
></button> ></button>
</div> </div>
</div> </div>
<div className="sub-table-box"> <div className="sub-table-box">
<div className="info-title">{getMessage('stuff.detail.header.specificationConfirmDate')}</div> <div className="info-title">{getMessage('stuff.detail.header.specificationConfirmDate')}</div>
<div className="info-inner">{headerData.specificationConfirmDate}</div> <div className="info-inner">{managementState?.specificationConfirmDate}</div>
</div> </div>
<div className="sub-table-box"> <div className="sub-table-box">
<div className="info-title">{getMessage('stuff.detail.header.lastEditDatetime')}</div> <div className="info-title">{getMessage('stuff.detail.header.lastEditDatetime')}</div>
<div className="info-inner"> <div className="info-inner">
{headerData?.lastEditDatetime ? `${dayjs(headerData.lastEditDatetime).format('YYYY.MM.DD HH:mm:ss')}` : ''}{' '} {managementState?.lastEditDatetime ? `${dayjs(managementState.lastEditDatetime).format('YYYY.MM.DD HH:mm:ss')}` : ''}{' '}
{headerData?.lastEditUserName ? `(${headerData.lastEditUserName})` : null} {managementState?.lastEditUserName ? `(${managementState.lastEditUserName})` : null}
</div> </div>
</div> </div>
<div className="sub-table-box"> <div className="sub-table-box">
<div className="info-title">{getMessage('stuff.detail.header.createDatetime')}</div> <div className="info-title">{getMessage('stuff.detail.header.createDatetime')}</div>
<div className="info-inner"> <div className="info-inner">
{headerData?.createDatetime ? `${dayjs(headerData.lastEditDatetime).format('YYYY.MM.DD')}` : ''}{' '} {managementState?.createDatetime ? `${dayjs(managementState.lastEditDatetime).format('YYYY.MM.DD')}` : ''}{' '}
{headerData?.createUserName ? `(${headerData.createUserName})` : null} {managementState?.createUserName ? `(${managementState.createUserName})` : null}
</div> </div>
</div> </div>
</div> </div>

View File

@ -80,7 +80,8 @@ export default function StuffSearchCondition() {
schAddress: address ? address : '', schAddress: address ? address : '',
schObjectName: objectName ? objectName : '', schObjectName: objectName ? objectName : '',
schDispCompanyName: dispCompanyName ? dispCompanyName : '', schDispCompanyName: dispCompanyName ? dispCompanyName : '',
schSelSaleStoreId: stuffSearch?.schOtherSelSaleStoreId ? stuffSearch.schOtherSelSaleStoreId : stuffSearch.schSelSaleStoreId, schSelSaleStoreId: stuffSearch?.schSelSaleStoreId ? stuffSearch.schSelSaleStoreId : '',
schOtherSelSaleStoreId: stuffSearch?.schOtherSelSaleStoreId ? stuffSearch.schOtherSelSaleStoreId : '',
schReceiveUser: receiveUser ? receiveUser : '', schReceiveUser: receiveUser ? receiveUser : '',
schDateType: dateType, schDateType: dateType,
schFromDt: dayjs(startDate).format('YYYY-MM-DD'), schFromDt: dayjs(startDate).format('YYYY-MM-DD'),
@ -90,6 +91,24 @@ export default function StuffSearchCondition() {
endRow: stuffSearch?.endRow ? stuffSearch.endRow : 100, endRow: stuffSearch?.endRow ? stuffSearch.endRow : 100,
schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'R', schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'R',
}) })
} else if (stuffSearch.code === 'FINISH') {
setStuffSearch({
schObjectNo: objectNo,
schSaleStoreName: saleStoreName,
schAddress: address,
schObjectName: objectName,
schDispCompanyName: dispCompanyName,
schSelSaleStoreId: schSelSaleStoreId,
schOtherSelSaleStoreId: otherSaleStoreId,
schReceiveUser: receiveUser,
schDateType: dateType,
schFromDt: dayjs(startDate).format('YYYY-MM-DD'),
schToDt: dayjs(endDate).format('YYYY-MM-DD'),
code: 'E',
startRow: 1,
endRow: 100,
schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'R',
})
} else { } else {
setStuffSearch({ setStuffSearch({
schObjectNo: objectNo, schObjectNo: objectNo,
@ -97,7 +116,8 @@ export default function StuffSearchCondition() {
schAddress: address, schAddress: address,
schObjectName: objectName, schObjectName: objectName,
schDispCompanyName: dispCompanyName, schDispCompanyName: dispCompanyName,
schSelSaleStoreId: stuffSearch?.schOtherSelSaleStoreId ? stuffSearch.schOtherSelSaleStoreId : stuffSearch.schSelSaleStoreId, schSelSaleStoreId: stuffSearch?.schSelSaleStoreId,
schOtherSelSaleStoreId: stuffSearch?.schOtherSelSaleStoreId,
schReceiveUser: receiveUser, schReceiveUser: receiveUser,
schDateType: dateType, schDateType: dateType,
schFromDt: dayjs(startDate).format('YYYY-MM-DD'), schFromDt: dayjs(startDate).format('YYYY-MM-DD'),
@ -119,7 +139,7 @@ export default function StuffSearchCondition() {
objectNameRef.current.value = '' objectNameRef.current.value = ''
dispCompanyNameRef.current.value = '' dispCompanyNameRef.current.value = ''
receiveUserRef.current.value = '' receiveUserRef.current.value = ''
stuffSearch.schDateType = 'U'
setObjectNo('') setObjectNo('')
setAddress('') setAddress('')
setobjectName('') setobjectName('')
@ -131,10 +151,10 @@ export default function StuffSearchCondition() {
setEndDate(dayjs(new Date()).format('YYYY-MM-DD')) setEndDate(dayjs(new Date()).format('YYYY-MM-DD'))
if (session?.storeId === 'T01') { if (session?.storeId === 'T01') {
setSchSelSaleStoreId('') setSchSelSaleStoreId('')
setOtherSaleStoreId('')
handleClear1() // handleClear1() //
resetStuffRecoil() resetStuffRecoil()
setStuffSearch({ setStuffSearch({
...stuffSearch,
schSelSaleStoreId: '', schSelSaleStoreId: '',
schOtherSelSaleStoreId: '', schOtherSelSaleStoreId: '',
}) })
@ -184,30 +204,24 @@ export default function StuffSearchCondition() {
setSchSelSaleStoreList(allList) setSchSelSaleStoreList(allList)
setFavoriteStoreList(favList) setFavoriteStoreList(favList)
setShowSaleStoreList(favList) setShowSaleStoreList(favList)
// setSchSelSaleStoreId(session?.storeId) if (stuffSearch.schSelSaleStoreId != '') {
setStuffSearch({ setSchSelSaleStoreId(stuffSearch.schSelSaleStoreId)
...stuffSearch, url = `/api/object/saleStore/${stuffSearch.schSelSaleStoreId}/list?firstFlg=1&userId=${session?.userId}`
code: 'S', get({ url: url }).then((res) => {
// schSelSaleStoreId: session?.storeId, if (!isEmptyArray(res)) {
}) res.map((row) => {
row.value = row.saleStoreId
row.label = row.saleStoreName
})
//T01 2 1 storeId otherList = res.filter((row) => row.saleStoreLevel !== '1')
// setOtherSaleStoreList(otherList)
// url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=0&userId=${session?.userId}` setOtherSaleStoreId(stuffSearch.schOtherSelSaleStoreId)
} else {
// get({ url: url }).then((res) => { setOtherSaleStoreList([])
// if (!isEmptyArray(res)) { }
// res.map((row) => { })
// row.value = row.saleStoreId }
// row.label = row.saleStoreName
// })
// otherList = res
// setOtherSaleStoreList(otherList)
// } else {
// setOtherSaleStoreList([])
// }
// })
} else { } else {
if (session?.storeLvl === '1') { if (session?.storeLvl === '1') {
allList = res allList = res
@ -221,11 +235,9 @@ export default function StuffSearchCondition() {
setOtherSaleStoreList(otherList) setOtherSaleStoreList(otherList)
setStuffSearch({ if (stuffSearch.schOtherSelSaleStoreId != '') {
...stuffSearch, setOtherSaleStoreId(stuffSearch.schOtherSelSaleStoreId)
code: 'S', }
schSelSaleStoreId: allList[0].saleStoreId,
})
} else { } else {
//10X22, 201X112 2 //10X22, 201X112 2
//2 34 202X217 //2 34 202X217
@ -241,7 +253,8 @@ export default function StuffSearchCondition() {
setStuffSearch({ setStuffSearch({
...stuffSearch, ...stuffSearch,
code: 'S', code: 'S',
schSelSaleStoreId: otherList[0].saleStoreId, schSelSaleStoreId: res[0].saleStoreId,
schOtherSelSaleStoreId: otherList[0].saleStoreId,
}) })
} }
} }
@ -267,7 +280,7 @@ export default function StuffSearchCondition() {
const onInputChange = (key) => { const onInputChange = (key) => {
if (key !== '') { if (key !== '') {
setShowSaleStoreList(schSelSaleStoreList) setShowSaleStoreList(schSelSaleStoreList)
setOtherSaleStoreList([]) // setOtherSaleStoreList([])
} else { } else {
setShowSaleStoreList(favoriteStoreList) setShowSaleStoreList(favoriteStoreList)
} }
@ -298,8 +311,9 @@ export default function StuffSearchCondition() {
}) })
} else { } else {
//X //X
//
setSchSelSaleStoreId('') setSchSelSaleStoreId('')
stuffSearch.schSelSaleStoreId = '' // stuffSearch.schSelSaleStoreId = ''
//2 //2
setOtherSaleStoreList([]) setOtherSaleStoreList([])
@ -311,12 +325,26 @@ export default function StuffSearchCondition() {
if (isObjectNotEmpty(key)) { if (isObjectNotEmpty(key)) {
setOtherSaleStoreId(key.saleStoreId) setOtherSaleStoreId(key.saleStoreId)
stuffSearch.schOtherSelSaleStoreId = key.saleStoreId stuffSearch.schOtherSelSaleStoreId = key.saleStoreId
//2 1
stuffSearch.schSelSaleStoreId = schSelSaleStoreId
} else { } else {
//X 1 //X 1
setOtherSaleStoreId('') if (session.storeLvl === '1') {
setSchSelSaleStoreId(schSelSaleStoreId) if (stuffSearch.schOtherSelSaleStoreId === '') {
stuffSearch.schOtherSelSaleStoreId = '' //
stuffSearch.schSelSaleStoreId = schSelSaleStoreId // stuffSearch.schSelSaleStoreId = ''
setSchSelSaleStoreId(session.storeId)
} else {
//
setOtherSaleStoreId('')
}
} else {
setOtherSaleStoreId('')
setSchSelSaleStoreId(schSelSaleStoreId)
stuffSearch.schOtherSelSaleStoreId = ''
stuffSearch.schSelSaleStoreId = schSelSaleStoreId
}
} }
} }
@ -500,7 +528,9 @@ export default function StuffSearchCondition() {
} else if (stuffSearch?.code === 'E' && schSelSaleStoreId !== '') { } else if (stuffSearch?.code === 'E' && schSelSaleStoreId !== '') {
return option.saleStoreId === schSelSaleStoreId return option.saleStoreId === schSelSaleStoreId
} else { } else {
if (stuffSearch?.schSelSaleStoreId !== '') { if (stuffSearch?.code === 'FINISH') {
return option.saleStoreId === schSelSaleStoreId
} else if (stuffSearch?.schSelSaleStoreId !== '') {
return option.saleStoreId === stuffSearch.schSelSaleStoreId return option.saleStoreId === stuffSearch.schSelSaleStoreId
} else { } else {
return false return false
@ -531,7 +561,9 @@ export default function StuffSearchCondition() {
} else if (stuffSearch?.code === 'E' && schSelSaleStoreId !== '') { } else if (stuffSearch?.code === 'E' && schSelSaleStoreId !== '') {
return option.saleStoreId === schSelSaleStoreId return option.saleStoreId === schSelSaleStoreId
} else { } else {
if (stuffSearch?.schSelSaleStoreId !== '') { if (stuffSearch?.code === 'FINISH') {
return option.saleStoreId === schSelSaleStoreId
} else if (stuffSearch?.schSelSaleStoreId !== '') {
return option.saleStoreId === stuffSearch.schSelSaleStoreId return option.saleStoreId === stuffSearch.schSelSaleStoreId
} else { } else {
return false return false
@ -606,10 +638,11 @@ export default function StuffSearchCondition() {
type="radio" type="radio"
name="radio_ptype" name="radio_ptype"
id="radio_u" id="radio_u"
checked={dateType === 'U' ? true : false} checked={stuffSearch.schDateType === 'U' ? true : false}
value={'U'} value={'U'}
onChange={(e) => { onChange={(e) => {
setDateType(e.target.value) setDateType(e.target.value)
stuffSearch.schDateType = e.target.value
}} }}
/> />
<label htmlFor="radio_u">{getMessage('stuff.search.schDateTypeU')}</label> <label htmlFor="radio_u">{getMessage('stuff.search.schDateTypeU')}</label>
@ -619,10 +652,11 @@ export default function StuffSearchCondition() {
type="radio" type="radio"
name="radio_ptype" name="radio_ptype"
id="radio_r" id="radio_r"
checked={dateType === 'R' ? true : false} checked={stuffSearch.schDateType === 'R' ? true : false}
value={'R'} value={'R'}
onChange={(e) => { onChange={(e) => {
setDateType(e.target.value) setDateType(e.target.value)
stuffSearch.schDateType = e.target.value
}} }}
/> />
<label htmlFor="radio_r">{getMessage('stuff.search.schDateTypeR')}</label> <label htmlFor="radio_r">{getMessage('stuff.search.schDateTypeR')}</label>

View File

@ -98,8 +98,8 @@ export default function PlanRequestPop(props) {
schPlanReqName: schPlanReqName, schPlanReqName: schPlanReqName,
schPlanStatCd: schPlanStatCd, schPlanStatCd: schPlanStatCd,
schDateGbn: schDateGbn, schDateGbn: schDateGbn,
schStartDt: dayjs(startDate).format('YYYY-MM-DD'), schStartDt: startDate ? dayjs(startDate).format('YYYY-MM-DD') : '',
schEndDt: dayjs(endDate).format('YYYY-MM-DD'), schEndDt: endDate ? dayjs(endDate).format('YYYY-MM-DD') : '',
startRow: type === 'S' ? (1 - 1) * pageSize + 1 : (page - 1) * pageSize + 1, startRow: type === 'S' ? (1 - 1) * pageSize + 1 : (page - 1) * pageSize + 1,
endRow: type === 'S' ? 1 * pageSize : page * pageSize, endRow: type === 'S' ? 1 * pageSize : page * pageSize,
} }
@ -226,7 +226,7 @@ export default function PlanRequestPop(props) {
], ],
}) })
// //
const getSelectedRowdata = (data) => { const getSelectedRowdata = (data) => {
if (isNotEmptyArray(data)) { if (isNotEmptyArray(data)) {
setPlanReqObject(data[0]) setPlanReqObject(data[0])
@ -257,6 +257,16 @@ export default function PlanRequestPop(props) {
const handleKeyUp = (e) => { const handleKeyUp = (e) => {
let input = e.target let input = e.target
input.value = input.value.replace(/[^0-9]/g, '') input.value = input.value.replace(/[^0-9]/g, '')
if (e.key === 'Enter') {
onSubmit(pageNo, 'S')
}
}
//
const handleByOnKeyUp = (e) => {
if (e.key === 'Enter') {
onSubmit(pageNo, 'S')
}
} }
return ( return (
@ -324,6 +334,7 @@ export default function PlanRequestPop(props) {
onChange={(e) => { onChange={(e) => {
setSchTitle(e.target.value) setSchTitle(e.target.value)
}} }}
onKeyUp={handleByOnKeyUp}
/> />
</div> </div>
</td> </td>
@ -337,6 +348,7 @@ export default function PlanRequestPop(props) {
onChange={(e) => { onChange={(e) => {
setSchAddress(e.target.value) setSchAddress(e.target.value)
}} }}
onKeyUp={handleByOnKeyUp}
/> />
</div> </div>
</td> </td>
@ -352,6 +364,7 @@ export default function PlanRequestPop(props) {
onChange={(e) => { onChange={(e) => {
setSchSaleStoreName(e.target.value) setSchSaleStoreName(e.target.value)
}} }}
onKeyUp={handleByOnKeyUp}
/> />
</div> </div>
</td> </td>
@ -365,6 +378,7 @@ export default function PlanRequestPop(props) {
onChange={(e) => { onChange={(e) => {
setSchPlanReqName(e.target.value) setSchPlanReqName(e.target.value)
}} }}
onKeyUp={handleByOnKeyUp}
/> />
</div> </div>
</td> </td>
@ -440,7 +454,7 @@ export default function PlanRequestPop(props) {
<div className="design-request-count"> <div className="design-request-count">
<div className="design-request-grid-tit">Plan List</div> <div className="design-request-grid-tit">Plan List</div>
<div className="select-wrap"> <div className="select-wrap">
<select className="select-light" name="" id="" onChange={onChangePerPage}> <select className="select-light" onChange={onChangePerPage}>
<option>20</option> <option>20</option>
<option>40</option> <option>40</option>
<option>60</option> <option>60</option>

View File

@ -1,16 +1,14 @@
import { useEffect } from 'react' import { useEffect } from 'react'
import { useRecoilValue, useRecoilState } from 'recoil' import { useRecoilState, useRecoilValue } from 'recoil'
import { wordDisplaySelector } from '@/store/settingAtom' import { wordDisplaySelector } from '@/store/settingAtom'
import { useEvent } from '@/hooks/useEvent' import { useEvent } from '@/hooks/useEvent'
import { checkLineOrientation, getDistance } from '@/util/canvas-util' import { checkLineOrientation, getDistance } from '@/util/canvas-util'
import { dimensionLineSettingsState } from '@/store/commonUtilsAtom' import { commonUtilsState, dimensionLineSettingsState } from '@/store/commonUtilsAtom'
import { fontSelector } from '@/store/fontAtom' import { fontSelector } from '@/store/fontAtom'
import { canvasState } from '@/store/canvasAtom' import { canvasState } from '@/store/canvasAtom'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
import { usePopup } from '@/hooks/usePopup' import { usePopup } from '@/hooks/usePopup'
import Distance from '@/components/floor-plan/modal/distance/Distance' import Distance from '@/components/floor-plan/modal/distance/Distance'
import { commonUtilsState } from '@/store/commonUtilsAtom'
import { center, point } from '@turf/turf'
export function useCommonUtils() { export function useCommonUtils() {
const canvas = useRecoilValue(canvasState) const canvas = useRecoilValue(canvasState)
@ -39,6 +37,7 @@ export function useCommonUtils() {
commonTextKeyEvent() commonTextKeyEvent()
addCanvasMouseEventListener('mouse:down', (event) => { addCanvasMouseEventListener('mouse:down', (event) => {
const pointer = canvas?.getPointer(event.e) const pointer = canvas?.getPointer(event.e)
textbox = new fabric.Textbox('', { textbox = new fabric.Textbox('', {
left: pointer.x, left: pointer.x,
top: pointer.y, top: pointer.y,
@ -49,7 +48,8 @@ export function useCommonUtils() {
fill: commonTextFont.fontColor.value, fill: commonTextFont.fontColor.value,
fontFamily: commonTextFont.fontFamily.value, fontFamily: commonTextFont.fontFamily.value,
fontSize: commonTextFont.fontSize.value, fontSize: commonTextFont.fontSize.value,
fontStyle: commonTextFont.fontWeight.value, fontStyle: commonTextFont.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal',
fontWeight: commonTextFont.fontWeight.value.toLowerCase().includes('bold') ? 'bold' : 'normal',
selectable: true, selectable: true,
lockMovementX: true, lockMovementX: true,
lockMovementY: true, lockMovementY: true,
@ -262,7 +262,8 @@ export function useCommonUtils() {
fill: dimensionLineTextFont.fontColor.value, fill: dimensionLineTextFont.fontColor.value,
fontSize: dimensionLineTextFont.fontSize.value, fontSize: dimensionLineTextFont.fontSize.value,
fontFamily: dimensionLineTextFont.fontFamily.value, fontFamily: dimensionLineTextFont.fontFamily.value,
fontStyle: dimensionLineTextFont.fontWeight.value, fontStyle: dimensionLineTextFont.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal',
fontWeight: dimensionLineTextFont.fontWeight.value.toLowerCase().includes('bold') ? 'bold' : 'normal',
selectable: true, selectable: true,
textAlign: 'center', textAlign: 'center',
originX: 'center', originX: 'center',
@ -710,7 +711,7 @@ export function useCommonUtils() {
canvas?.remove(centerLine, ...extendLine, ...arrows, textObj) canvas?.remove(centerLine, ...extendLine, ...arrows, textObj)
const reGroup = new fabric.Group(reGroupObj, { const reGroup = new fabric.Group(reGroupObj, {
name: 'dimensionLine', name: 'dimensionGroup',
selectable: true, selectable: true,
lineDirection: originLineDirection, lineDirection: originLineDirection,
groupId: id, groupId: id,

View File

@ -17,8 +17,8 @@ export function useFont() {
textObjs.forEach((obj) => { textObjs.forEach((obj) => {
obj.set({ obj.set({
fontFamily: commonText.fontFamily.value, fontFamily: commonText.fontFamily.value,
fontWeight: lengthText.fontWeight.value.toLowerCase().includes('bold') ? 'bold' : 'normal', fontWeight: commonText.fontWeight.value.toLowerCase().includes('bold') ? 'bold' : 'normal',
fontStyle: lengthText.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal', fontStyle: commonText.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal',
fontSize: commonText.fontSize.value, fontSize: commonText.fontSize.value,
fill: commonText.fontColor.value, fill: commonText.fontColor.value,
}) })
@ -33,8 +33,8 @@ export function useFont() {
textObjs.forEach((obj) => { textObjs.forEach((obj) => {
obj.set({ obj.set({
fontFamily: dimensionLineText.fontFamily.value, fontFamily: dimensionLineText.fontFamily.value,
fontWeight: lengthText.fontWeight.value.toLowerCase().includes('bold') ? 'bold' : 'normal', fontWeight: dimensionLineText.fontWeight.value.toLowerCase().includes('bold') ? 'bold' : 'normal',
fontStyle: lengthText.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal', fontStyle: dimensionLineText.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal',
fontSize: dimensionLineText.fontSize.value, fontSize: dimensionLineText.fontSize.value,
fill: dimensionLineText.fontColor.value, fill: dimensionLineText.fontColor.value,
}) })
@ -49,8 +49,8 @@ export function useFont() {
textObjs.forEach((obj) => { textObjs.forEach((obj) => {
obj.set({ obj.set({
fontFamily: flowText.fontFamily.value, fontFamily: flowText.fontFamily.value,
fontWeight: lengthText.fontWeight.value.toLowerCase().includes('bold') ? 'bold' : 'normal', fontWeight: flowText.fontWeight.value.toLowerCase().includes('bold') ? 'bold' : 'normal',
fontStyle: lengthText.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal', fontStyle: flowText.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal',
fontSize: flowText.fontSize.value, fontSize: flowText.fontSize.value,
fill: flowText.fontColor.value, fill: flowText.fontColor.value,
}) })

View File

@ -7,7 +7,7 @@ import { convertDwgToPng } from '@/lib/cadAction'
import { useAxios } from '../useAxios' import { useAxios } from '../useAxios'
import { currentCanvasPlanState } from '@/store/canvasAtom' import { currentCanvasPlanState } from '@/store/canvasAtom'
export default function useRefFiles() { export function useRefFiles() {
const converterUrl = process.env.NEXT_PUBLIC_CONVERTER_API_URL const converterUrl = process.env.NEXT_PUBLIC_CONVERTER_API_URL
const [refImage, setRefImage] = useState(null) const [refImage, setRefImage] = useState(null)
const [refFileMethod, setRefFileMethod] = useState('1') const [refFileMethod, setRefFileMethod] = useState('1')
@ -54,20 +54,20 @@ export default function useRefFiles() {
const res = await get({ url: `http://localhost:3000/api/html2canvas?q=${queryRef.current.value}&fileNm=${uuidv4()}&zoom=20` }) const res = await get({ url: `http://localhost:3000/api/html2canvas?q=${queryRef.current.value}&fileNm=${uuidv4()}&zoom=20` })
console.log('🚀 ~ handleMapImageDown ~ res:', res) console.log('🚀 ~ handleMapImageDown ~ res:', res)
setCurrentCanvasPlan((prev) => ({ ...prev, bgFileName: res.fileNm, mapPositionAddress: queryRef.current.value })) setCurrentCanvasPlan((prev) => ({ ...prev, bgImageName: res.fileNm, mapPositionAddress: queryRef.current.value }))
} }
/** /**
* 현재 플랜이 변경되면 플랜 상태 저장 * 현재 플랜이 변경되면 플랜 상태 저장
*/ */
useEffect(() => { // useEffect(() => {
const handleCurrentPlan = async () => { // const handleCurrentPlan = async () => {
await promisePut({ url: '/api/canvas-management/canvas-statuses', data: currentCanvasPlan }).then((res) => { // await promisePut({ url: '/api/canvas-management/canvas-statuses', data: currentCanvasPlan }).then((res) => {
console.log('🚀 ~ awaitpromisePut ~ res:', res) // console.log('🚀 ~ awaitpromisePut ~ res:', res)
}) // })
} // }
handleCurrentPlan() // handleCurrentPlan()
}, [currentCanvasPlan]) // }, [currentCanvasPlan])
/** /**
* RefFile이 캐드 도면 파일일 경우 변환하여 이미지로 저장 * RefFile이 캐드 도면 파일일 경우 변환하여 이미지로 저장

View File

@ -19,37 +19,18 @@ const defaultEstimateData = {
charger: '', //담당자 charger: '', //담당자
objectName: '', //안건명 objectName: '', //안건명
objectNameOmit: '', //경칭코드 objectNameOmit: '', //경칭코드
estimateType: 'YJOD', //주문분류 estimateType: '', //주문분류
remarks: '', //비고 remarks: '', //비고
estimateOption: '', //견적특이사항 estimateOption: '', //견적특이사항
//아이템에 필요없는거 빼기 itemList: [],
itemList: [
// {
// amount: '',
// fileUploadFlg: '',
// itemChangeFlg: '',
// itemGroup: '',
// itemId: '', //키값??
// itemName: '',
// itemNo: '',
// moduleFlg: '',
// objectNo: '',
// pkgMaterialFlg: '',
// planNo: '',
// pnowW: '',
// salePrice: '',
// saleTotPrice: '',
// specification: '',
// unit: '',
// },
],
fileList: [], fileList: [],
fileFlg: '0', //후일 자료 제출 (체크 1 노체크 0) fileFlg: '0', //후일 자료 제출 (체크 1 노체크 0)
priceCd: '',
} }
// Helper functions // Helper functions
const updateItemInList = (itemList, itemId, updates) => { const updateItemInList = (itemList, dispOrder, updates) => {
return itemList.map((item) => (item.itemId === itemId ? { ...item, ...updates } : item)) return itemList.map((item) => (item.dispOrder === dispOrder ? { ...item, ...updates } : item))
} }
export const useEstimateController = (planNo) => { export const useEstimateController = (planNo) => {
@ -88,34 +69,32 @@ export const useEstimateController = (planNo) => {
} }
} }
const updateItem = (itemId, updates) => { const updateItem = (dispOrder, updates) => {
setState({ setState({
itemList: updateItemInList(state.itemList, itemId, updates), itemList: updateItemInList(state.itemList, dispOrder, updates),
}) })
} }
const addItem = () => { const addItem = () => {
const newItemId = Math.max(...state.itemList.map((item) => item.itemId)) + 1 const newItemDispOrder = Math.max(...state.itemList.map((item) => item.dispOrder)) + 1
setState({ setState({
itemList: [ itemList: [
...state.itemList, ...state.itemList,
{ {
itemId: newItemId, objectNo: objectRecoil.floorPlanObjectNo,
amount: '', planNo: planNo,
fileUploadFlg: '', dispOrder: newItemDispOrder.toString(),
itemChangeFlg: '', itemId: '', //제품번호
itemGroup: '',
itemName: '',
itemNo: '', itemNo: '',
moduleFlg: '', itemName: '', //형명
objectNo: '', amount: '', //수량
pkgMaterialFlg: '', unitPrice: '0',
planNo: '', unit: '', //단위
pnowW: '', salePrice: '0', //단가
salePrice: '', saleTotPrice: '0', //금액(부가세별도)
saleTotPrice: '', itemChangeFlg: '1', //추가시 체인지플래그 1로
specification: '', partAdd: '1', //NEW 체인지 플래그
unit: '', delFlg: '0', //삭제 플래그 0 삭제하면 1
}, },
], ],
}) })
@ -125,53 +104,83 @@ export const useEstimateController = (planNo) => {
setEstimateData({ ...state, userId: session.userId, sapSalesStoreCd: session.custCd }) setEstimateData({ ...state, userId: session.userId, sapSalesStoreCd: session.custCd })
}, [state]) }, [state])
// 첨부파일 다운로드
const handleEstimateFileDownload = async (originFile) => {
const options = { responseType: 'blob' }
await promisePost({ url: `/api/file/fileDownload`, data: originFile, option: options })
.then((resultData) => {
if (resultData) {
const blob = new Blob([resultData.data], { type: resultData.headers['content-type'] || 'application/octet-stream' })
const fileUrl = window.URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = fileUrl
link.download = originFile.faileName
document.body.appendChild(link)
link.click()
link.remove()
window.URL.revokeObjectURL(fileUrl)
}
})
.catch((error) => {
console.log('::FileDownLoad Error::', error)
alert('File does not exist.')
})
}
//견적서 저장 //견적서 저장
const handleEstimateSubmit = async () => { const handleEstimateSubmit = async () => {
//0. 필수체크 //0. 필수체크
let flag = true let flag = true
console.log('::담긴 estimateData:::', estimateData) console.log('::담긴 estimateData:::', estimateData)
// console.log('첨부파일:::::', estimateData.fileList)
//첨부파일을 첨부안했는데
//아이템 fileUploadFlg가1(첨부파일 필수)이 1개라도 있는데 후일 자료 제출(fileFlg) 체크안했으면(0) alert 저장안돼 //아이템 fileUploadFlg가1(첨부파일 필수)이 1개라도 있는데 후일 자료 제출(fileFlg) 체크안했으면(0) alert 저장안돼
if (estimateData.itemList.length > 1) { if (estimateData.fileList.length < 1) {
estimateData.itemList.map((row) => { if (estimateData.itemList.length > 1) {
if (row.fileUploadFlg === '1') { estimateData.itemList.map((row) => {
if (estimateData.fileFlg === '0') { if (row.fileUploadFlg === '1') {
alert(getMessage('estimate.detail.save.requiredMsg')) if (estimateData.fileFlg === '0') {
flag = false alert(getMessage('estimate.detail.save.requiredMsg'))
flag = false
}
} }
} })
}) }
} }
if (flag) { if (flag) {
//1. 첨부파일 저장 //1. 첨부파일 저장
const formData = new FormData() const formData = new FormData()
console.log('첨부파일:!!!', estimateData.fileList)
formData.append('file', estimateData.fileList) formData.append('file', estimateData.fileList)
formData.append('objectNo', estimateData.objectNo) formData.append('objectNo', estimateData.objectNo)
formData.append('planNo', estimateData.planNo) formData.append('planNo', estimateData.planNo)
formData.append('category', '10') formData.append('category', '10')
formData.append('userId', estimateData.userId) formData.append('userId', estimateData.userId)
for (const value of formData.values()) {
console.log('formData::', value)
}
await promisePost({ url: '/api/file/fileUpload', data: formData }).then((res) => { await post({ url: '/api/file/fileUpload', data: formData })
console.log('파일저장결과::::::::::', res)
})
//2. 상세데이터 저장 //2. 상세데이터 저장
console.log('상세저장시작!!')
return return
try { await promisePost({ url: `${ESTIMATE_API_ENDPOINT}/save-estimate`, data: estimateData }).then((res) => {
const result = await promisePost({ if (res) {
url: ESTIMATE_API_ENDPOINT, alert(getMessage('estimate.detail.save.alertMsg'))
data: estimateData, }
}) })
alert(getMessage('estimate.detail.save.alertMsg'))
return result // try {
} catch (error) { // const result = await promisePost({
console.error('Failed to submit estimate:', error) // url: ESTIMATE_API_ENDPOINT,
throw error // data: estimateData,
} // })
// alert(getMessage('estimate.detail.save.alertMsg'))
// return result
// } catch (error) {
// console.error('Failed to submit estimate:', error)
// throw error
// }
} }
} }
@ -182,5 +191,6 @@ export const useEstimateController = (planNo) => {
addItem, addItem,
handleEstimateSubmit, handleEstimateSubmit,
fetchSetting, fetchSetting,
handleEstimateFileDownload,
} }
} }

View File

@ -0,0 +1,666 @@
import { useEffect, useState } from 'react'
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
import { canvasState } from '@/store/canvasAtom'
import { rectToPolygon, setSurfaceShapePattern } from '@/util/canvas-util'
import { roofDisplaySelector } from '@/store/settingAtom'
import offsetPolygon from '@/util/qpolygon-utils'
import { QPolygon } from '@/components/fabric/QPolygon'
import { moduleSetupSurfaceState, moduleIsSetupState } from '@/store/canvasAtom'
import { useEvent } from '@/hooks/useEvent'
import { POLYGON_TYPE, BATCH_TYPE } from '@/common/common'
import * as turf from '@turf/turf'
export function useModuleBasicSetting() {
const canvas = useRecoilValue(canvasState)
const roofDisplay = useRecoilValue(roofDisplaySelector)
const [moduleSetupSurface, setModuleSetupSurface] = useRecoilState(moduleSetupSurfaceState)
const [moduleIsSetup, setModuleIsSetup] = useRecoilState(moduleIsSetupState)
const { addTargetMouseEventListener, addCanvasMouseEventListener, initEvent } = useEvent()
let selectedModuleInstSurfaceArray = []
const makeModuleInstArea = () => {
//지붕 객체 반환
const roofs = canvas.getObjects().filter((obj) => obj.name === 'roof')
if (!roofs) {
return
}
roofs.forEach((roof) => {
setSurfaceShapePattern(roof, roofDisplay.column, true) //패턴 변경
const offsetPoints = offsetPolygon(roof.points, -20) //안쪽 offset
//모듈설치영역?? 생성
const setupSurface = new QPolygon(offsetPoints, {
stroke: 'red',
fill: 'transparent',
strokeDashArray: [10, 4],
strokeWidth: 1,
lockMovementX: true,
lockMovementY: true,
lockRotation: true,
lockScalingX: true,
lockScalingY: true,
selectable: true,
parentId: roof.id, //가대 폴리곤의 임시 인덱스를 넣어줌
name: POLYGON_TYPE.MODULE_SETUP_SURFACE,
flowDirection: roof.direction,
})
canvas.add(setupSurface)
//지붕면 선택 금지
roof.set({
selectable: false,
})
//모듈설치면 클릭이벤트
addTargetMouseEventListener('mousedown', setupSurface, function () {
toggleSelection(setupSurface)
})
})
}
//설치 범위 지정 클릭 이벤트
const toggleSelection = (setupSurface) => {
const isExist = selectedModuleInstSurfaceArray.some((obj) => obj.parentId === setupSurface.parentId)
//최초 선택일때
if (!isExist) {
//기본 선택이랑 스트로크 굵기가 같으면 선택 안됨으로 봄
setupSurface.set({
...setupSurface,
strokeWidth: 3,
strokeDashArray: [0],
fill: 'transparent',
})
canvas.discardActiveObject() // 객체의 활성 상태 해제
//중복으로 들어가는걸 방지하기 위한 코드
canvas?.renderAll()
selectedModuleInstSurfaceArray.push(setupSurface)
} else {
//선택후 재선택하면 선택안됨으로 변경
setupSurface.set({
...setupSurface,
fill: 'transparent',
strokeDashArray: [10, 4],
strokeWidth: 1,
})
canvas.discardActiveObject() // 객체의 활성 상태 해제
//폴리곤에 커스텀 인덱스를 가지고 해당 배열 인덱스를 찾아 삭제함
const removeIndex = setupSurface.parentId
const removeArrayIndex = selectedModuleInstSurfaceArray.findIndex((obj) => obj.parentId === removeIndex)
selectedModuleInstSurfaceArray.splice(removeArrayIndex, 1)
}
canvas?.renderAll()
setModuleSetupSurface([...selectedModuleInstSurfaceArray])
}
/**
* trestle에서 영역을 가져와 mouse:move 이벤트로 해당 영역에 진입했을때 booleanPointInPolygon 진입여부를 확인
* 확인 셀을 이동시킴
*/
const manualModuleSetup = () => {
const moduleSetupSurfaces = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE) //모듈설치면를 가져옴
const batchObjects = canvas
?.getObjects()
.filter(
(obj) =>
obj.name === BATCH_TYPE.OPENING ||
obj.name === BATCH_TYPE.TRIANGLE_DORMER ||
obj.name === BATCH_TYPE.PENTAGON_DORMER ||
obj.name === BATCH_TYPE.SHADOW,
) //도머s 객체
if (moduleSetupSurfaces.length !== 0) {
let tempModule
let manualDrawModules = moduleIsSetup // 앞에서 자동으로 했을때 추가됨
let inside = false
let turfPolygon
let flowDirection
let trestlePolygon
addCanvasMouseEventListener('mouse:move', (e) => {
//마우스 이벤트 삭제 후 재추가
const mousePoint = canvas.getPointer(e.e)
for (let i = 0; i < moduleSetupSurfaces.length; i++) {
turfPolygon = polygonToTurfPolygon(moduleSetupSurfaces[i])
trestlePolygon = moduleSetupSurfaces[i]
flowDirection = moduleSetupSurfaces[i].flowDirection //도형의 방향
let width = flowDirection === 'south' || flowDirection === 'north' ? 172 : 113
let height = flowDirection === 'south' || flowDirection === 'north' ? 113 : 172
const points = [
{ x: mousePoint.x - width / 2, y: mousePoint.y - height / 2 },
{ x: mousePoint.x + width / 2, y: mousePoint.y - height / 2 },
{ x: mousePoint.x + width / 2, y: mousePoint.y + height / 2 },
{ x: mousePoint.x - width / 2, y: mousePoint.y + height / 2 },
]
const turfPoints = coordToTurfPolygon(points)
if (turf.booleanWithin(turfPoints, turfPolygon)) {
let isDrawing = false
if (isDrawing) return
canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'tempModule')) //움직일때 일단 지워가면서 움직임
tempModule = new fabric.Rect({
fill: 'white',
stroke: 'black',
strokeWidth: 1,
width: width,
height: height,
left: mousePoint.x - width / 2,
top: mousePoint.y - height / 2,
selectable: false,
lockMovementX: true,
lockMovementY: true,
lockRotation: true,
lockScalingX: true,
lockScalingY: true,
opacity: 0.8,
name: 'tempModule',
parentId: moduleSetupSurfaces[i].parentId,
})
canvas?.add(tempModule) //움직여가면서 추가됨
/**
* 스냅기능
*/
let snapDistance = 10
let cellSnapDistance = 20
const trestleLeft = moduleSetupSurfaces[i].left
const trestleTop = moduleSetupSurfaces[i].top
const trestleRight = trestleLeft + moduleSetupSurfaces[i].width * moduleSetupSurfaces[i].scaleX
const trestleBottom = trestleTop + moduleSetupSurfaces[i].height * moduleSetupSurfaces[i].scaleY
const bigCenterY = (trestleTop + trestleTop + moduleSetupSurfaces[i].height) / 2
// 작은 폴리곤의 경계 좌표 계산
const smallLeft = tempModule.left
const smallTop = tempModule.top
const smallRight = smallLeft + tempModule.width * tempModule.scaleX
const smallBottom = smallTop + tempModule.height * tempModule.scaleY
const smallCenterX = smallLeft + (tempModule.width * tempModule.scaleX) / 2
const smallCenterY = smallTop + (tempModule.height * tempModule.scaleX) / 2
/**
* 미리 깔아놓은 셀이 있을때 셀에 흡착됨
*/
if (manualDrawModules) {
manualDrawModules.forEach((cell) => {
const holdCellLeft = cell.left
const holdCellTop = cell.top
const holdCellRight = holdCellLeft + cell.width * cell.scaleX
const holdCellBottom = holdCellTop + cell.height * cell.scaleY
const holdCellCenterX = holdCellLeft + (cell.width * cell.scaleX) / 2
const holdCellCenterY = holdCellTop + (cell.height * cell.scaleY) / 2
//설치된 셀에 좌측에 스냅
if (Math.abs(smallRight - holdCellLeft) < snapDistance) {
tempModule.left = holdCellLeft - width - 0.5
}
//설치된 셀에 우측에 스냅
if (Math.abs(smallLeft - holdCellRight) < snapDistance) {
tempModule.left = holdCellRight + 0.5
}
//설치된 셀에 위쪽에 스냅
if (Math.abs(smallBottom - holdCellTop) < snapDistance) {
tempModule.top = holdCellTop - height - 0.5
}
//설치된 셀에 밑쪽에 스냅
if (Math.abs(smallTop - holdCellBottom) < snapDistance) {
tempModule.top = holdCellBottom + 0.5
}
//가운데 -> 가운데
if (Math.abs(smallCenterX - holdCellCenterX) < cellSnapDistance) {
tempModule.left = holdCellCenterX - width / 2
}
//왼쪽 -> 가운데
if (Math.abs(smallLeft - holdCellCenterX) < cellSnapDistance) {
tempModule.left = holdCellCenterX
}
// 오른쪽 -> 가운데
if (Math.abs(smallRight - holdCellCenterX) < cellSnapDistance) {
tempModule.left = holdCellCenterX - width
}
//세로 가운데 -> 가운데
if (Math.abs(smallCenterY - holdCellCenterY) < cellSnapDistance) {
tempModule.top = holdCellCenterY - height / 2
}
//위쪽 -> 가운데
if (Math.abs(smallTop - holdCellCenterY) < cellSnapDistance) {
tempModule.top = holdCellCenterY
}
//아랫쪽 -> 가운데
if (Math.abs(smallBottom - holdCellCenterY) < cellSnapDistance) {
tempModule.top = holdCellCenterY - height
}
})
}
// 위쪽 변에 스냅
if (Math.abs(smallTop - trestleTop) < snapDistance) {
tempModule.top = trestleTop
}
// 아래쪽 변에 스냅
if (Math.abs(smallTop + tempModule.height * tempModule.scaleY - (trestleTop + moduleSetupSurfaces[i].height)) < snapDistance) {
tempModule.top = trestleTop + moduleSetupSurfaces[i].height - tempModule.height * tempModule.scaleY
}
// 왼쪽변에 스냅
if (Math.abs(smallLeft - trestleLeft) < snapDistance) {
tempModule.left = trestleLeft
}
//오른쪽 변에 스냅
if (Math.abs(smallRight - trestleRight) < snapDistance) {
tempModule.left = trestleRight - tempModule.width * tempModule.scaleX
}
if (flowDirection === 'south' || flowDirection === 'north') {
// 모듈왼쪽이 세로중앙선에 붙게 스냅
if (Math.abs(smallLeft - (trestleLeft + moduleSetupSurfaces[i].width / 2)) < snapDistance) {
tempModule.left = trestleLeft + moduleSetupSurfaces[i].width / 2
}
// 모듈이 가운데가 세로중앙선에 붙게 스냅
if (Math.abs(smallCenterX - (trestleLeft + moduleSetupSurfaces[i].width / 2)) < snapDistance) {
tempModule.left = trestleLeft + moduleSetupSurfaces[i].width / 2 - (tempModule.width * tempModule.scaleX) / 2
}
// 모듈오른쪽이 세로중앙선에 붙게 스냅
if (Math.abs(smallRight - (trestleLeft + moduleSetupSurfaces[i].width / 2)) < snapDistance) {
tempModule.left = trestleLeft + moduleSetupSurfaces[i].width / 2 - tempModule.width * tempModule.scaleX
}
} else {
// 모듈이 가로중앙선에 스냅
if (Math.abs(smallTop + tempModule.height / 2 - bigCenterY) < snapDistance) {
tempModule.top = bigCenterY - tempModule.height / 2
}
if (Math.abs(smallTop - (trestleTop + moduleSetupSurfaces[i].height / 2)) < snapDistance) {
tempModule.top = trestleTop + moduleSetupSurfaces[i].height / 2
}
// 모듈 밑면이 가로중앙선에 스냅
if (Math.abs(smallBottom - (trestleTop + moduleSetupSurfaces[i].height / 2)) < snapDistance) {
tempModule.top = trestleTop + moduleSetupSurfaces[i].height / 2 - tempModule.height * tempModule.scaleY
}
}
tempModule.setCoords()
canvas?.renderAll()
inside = true
break
} else {
inside = false
}
}
if (!inside) {
canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'tempModule'))
canvas?.renderAll()
}
})
addCanvasMouseEventListener('mouse:up', (e) => {
let isIntersection = true
if (!inside) return
if (tempModule) {
const rectPoints = [
{ x: tempModule.left + 0.5, y: tempModule.top + 0.5 },
{ x: tempModule.left + 0.5 + tempModule.width * tempModule.scaleX, y: tempModule.top + 0.5 },
{
x: tempModule.left + tempModule.width * tempModule.scaleX + 0.5,
y: tempModule.top + tempModule.height * tempModule.scaleY + 0.5,
},
{ x: tempModule.left + 0.5, y: tempModule.top + tempModule.height * tempModule.scaleY + 0.5 },
]
tempModule.set({ points: rectPoints })
const tempTurfModule = polygonToTurfPolygon(tempModule)
//도머 객체를 가져옴
if (batchObjects) {
batchObjects.forEach((object) => {
let dormerTurfPolygon
if (object.type === 'group') {
//도머는 그룹형태임
dormerTurfPolygon = batchObjectGroupToTurfPolygon(object)
} else {
//개구, 그림자
dormerTurfPolygon = polygonToTurfPolygon(rectToPolygon(object))
}
const intersection = turf.intersect(turf.featureCollection([dormerTurfPolygon, tempTurfModule])) //겹치는지 확인
//겹치면 안됨
if (intersection) {
alert('도머위에 모듈을 올릴 수 없습니다.')
isIntersection = false
}
})
}
if (!isIntersection) return
tempModule.setCoords() //좌표 재정렬
if (turf.booleanWithin(tempTurfModule, turfPolygon)) {
//마우스 클릭시 set으로 해당 위치에 셀을 넣음
const isOverlap = manualDrawModules.some((module) => turf.booleanOverlap(tempTurfModule, polygonToTurfPolygon(module))) //겹치는지 확인
if (!isOverlap) {
//안겹치면 넣는다
tempModule.setCoords()
tempModule.set({ name: 'module', fill: '#BFFD9F' })
manualDrawModules.push(tempModule) //모듈배열에 추가
//해당 모듈에 프로퍼티로 넣는다
trestlePolygon.set({
modules: manualDrawModules,
})
} else {
alert('셀끼리 겹치면 안되죠?')
}
} else {
alert('나갔죠?!!')
}
}
})
}
}
//자동 모듈 설치(그리드 방식)
const autoModuleSetup = () => {
initEvent()
const moduleSetupSurfaces = moduleSetupSurface //선택 설치면
const notSelectedTrestlePolygons = canvas
?.getObjects()
.filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE && !moduleSetupSurfaces.includes(obj)) //설치면이 아닌것
const batchObjects = canvas
?.getObjects()
.filter(
(obj) =>
obj.name === BATCH_TYPE.OPENING ||
obj.name === BATCH_TYPE.TRIANGLE_DORMER ||
obj.name === BATCH_TYPE.PENTAGON_DORMER ||
obj.name === BATCH_TYPE.SHADOW,
) //도머s 객체
if (moduleSetupSurfaces.length === 0) {
alert('선택된 모듈 설치면이 없습니다.')
return
}
if (moduleIsSetup.length > 0) {
alert('기존 모듈은 제거됩니다.')
}
notSelectedTrestlePolygons.forEach((obj) => {
if (obj.modules) {
obj.modules.forEach((module) => {
canvas?.remove(module)
})
obj.modules = []
}
})
const moduleSetupArray = []
moduleSetupSurfaces.forEach((moduleSetupSurface, index) => {
moduleSetupSurface.fire('mousedown')
let maxLengthLine = moduleSetupSurface.lines.reduce((acc, cur) => {
return acc.length > cur.length ? acc : cur
})
const turfModuleSetupSurface = polygonToTurfPolygon(moduleSetupSurface) //폴리곤을 turf 객체로 변환
const containsBatchObjects = batchObjects.filter((batchObject) => {
let convertBatchObject
if (batchObject.type === 'group') {
//도머는 그룹형태임
convertBatchObject = batchObjectGroupToTurfPolygon(batchObject)
} else {
//개구, 그림자
batchObject.set({
points: rectToPolygon(batchObject),
})
canvas?.renderAll() // set된걸 바로 적용하기 위해
convertBatchObject = polygonToTurfPolygon(batchObject) //rect를 폴리곤으로 변환 -> turf 폴리곤으로 변환
}
// 폴리곤 안에 도머 폴리곤이 포함되어있는지 확인해서 반환하는 로직
return turf.booleanContains(turfModuleSetupSurface, convertBatchObject) || turf.booleanWithin(convertBatchObject, turfModuleSetupSurface)
})
let difference = turfModuleSetupSurface //기본 객체(면형상)
if (containsBatchObjects.length > 0) {
//turf로 도머를 제외시키는 로직
for (let i = 0; i < containsBatchObjects.length; i++) {
let convertBatchObject
if (containsBatchObjects[i].type === 'group') {
convertBatchObject = batchObjectGroupToTurfPolygon(containsBatchObjects[i])
} else {
convertBatchObject = polygonToTurfPolygon(containsBatchObjects[i])
}
if (i === 0) {
difference = turf.difference(turf.featureCollection([turfModuleSetupSurface, convertBatchObject])) //한 면에 도머가 1개일때
} else {
if (difference) {
difference = turf.difference(turf.featureCollection([difference, convertBatchObject])) //한면에 도머가 여러개일때 계속 제외시킴
}
}
}
}
const bbox = turf.bbox(difference)
let width = maxLengthLine.flowDirection === 'right' || maxLengthLine.flowDirection === 'left' ? 172.2 : 113.4
let height = maxLengthLine.flowDirection === 'right' || maxLengthLine.flowDirection === 'left' ? 113.4 : 172.2
//배치면때는 방향쪽으로 패널이 넓게 누워져야함
if (moduleSetupSurface.flowDirection !== undefined) {
width = moduleSetupSurface.flowDirection === 'south' || moduleSetupSurface.flowDirection === 'north' ? 172.2 : 113.4
height = moduleSetupSurface.flowDirection === 'south' || moduleSetupSurface.flowDirection === 'north' ? 113.4 : 172.2
}
const cols = Math.floor((bbox[2] - bbox[0]) / width)
const rows = Math.floor((bbox[3] - bbox[1]) / height)
for (let col = 0; col <= cols; col++) {
for (let row = 0; row <= rows; row++) {
let x = 0,
y = 0,
square = [],
margin = 0
if (moduleSetupSurface.flowDirection !== undefined) {
//배치면 처림 방향이 정해져있는 경우
if (moduleSetupSurface.flowDirection === 'south' || moduleSetupSurface.flowDirection === 'north') {
//남,북
margin = (bbox[2] - bbox[0] - cols * width) / 2 //박스 끝에서 박스 시작값을 빼고 width와 계산된 cols를 곱한값을 뺀뒤 나누기 2 하면 가운데 배치됨
if (moduleSetupSurface.flowDirection === 'south') {
//남쪽
x = col === 0 ? moduleSetupSurface.left + margin : bbox[0] + col * width + margin //상하 위치 기준이면 좌우 가운데 정렬한다
y = bbox[3] - row * height
} else {
//북쪽
x = col === 0 ? moduleSetupSurface.left + margin : bbox[0] + col * width + margin
y = bbox[1] + row * height
}
} else if (moduleSetupSurface.flowDirection === 'east' || moduleSetupSurface.flowDirection === 'west') {
//동쪽
margin = (bbox[3] - bbox[1] - rows * height) / 2
if (moduleSetupSurface.flowDirection === 'east') {
x = bbox[2] - col * width
y = rows === 0 ? moduleSetupSurface.top + margin : bbox[1] + row * height + margin //좌우 위치 기준이면 상하 가운데 정렬한다
} else {
x = bbox[0] + col * width
y = rows === 0 ? moduleSetupSurface.top + margin : bbox[1] + row * height + margin
}
}
} else {
//방향이 없는 경우 ex) 템플릿
x = bbox[0] + col * width
y = bbox[1] + row * height
}
square = [
[x, y],
[x + width, y],
[x + width, y + height],
[x, y + height],
[x, y],
]
const squarePolygon = turf.polygon([square])
const disjointFromTrestle =
turf.booleanContains(turfModuleSetupSurface, squarePolygon) || turf.booleanWithin(squarePolygon, turfModuleSetupSurface)
if (disjointFromTrestle) {
let turfCoordnates = squarePolygon.geometry.coordinates[0].slice(0, -1)
const points = turfCoordnates.map((coord) => ({ x: coord[0], y: coord[1] }))
if (containsBatchObjects.length > 0) {
let convertBatchObject
//도머가 있으면 적용되는 로직
const isDisjoint = containsBatchObjects.every((batchObject) => {
if (batchObject.type === 'group') {
convertBatchObject = batchObjectGroupToTurfPolygon(batchObject)
} else {
convertBatchObject = polygonToTurfPolygon(batchObject)
}
return turf.booleanDisjoint(squarePolygon, convertBatchObject) //도머가 여러개일수있으므로 겹치는게 있다면...
})
console.log('isDisjoint', isDisjoint)
if (isDisjoint) {
const tempModule = new QPolygon(points, {
fill: '#BFFD9F',
stroke: 'black',
selectable: true, // 선택 가능하게 설정
lockMovementX: false, // X 축 이동 잠금
lockMovementY: false, // Y 축 이동 잠금
lockRotation: false, // 회전 잠금
lockScalingX: false, // X 축 크기 조정 잠금
lockScalingY: false, // Y 축 크기 조정 잠금
opacity: 0.8,
parentId: moduleSetupSurface.parentId,
})
canvas?.add(tempModule)
moduleSetupArray.push(tempModule)
}
} else {
//도머가 없을땐 그냥 그림
const tempModule = new QPolygon(points, {
fill: '#BFFD9F',
stroke: 'black',
selectable: true, // 선택 가능하게 설정
lockMovementX: true, // X 축 이동 잠금
lockMovementY: true, // Y 축 이동 잠금
lockRotation: true, // 회전 잠금
lockScalingX: true, // X 축 크기 조정 잠금
lockScalingY: true, // Y 축 크기 조정 잠금
opacity: 0.8,
parentId: moduleSetupSurface.parentId,
lineCol: col,
lineRow: row,
})
canvas?.add(tempModule)
moduleSetupArray.push(tempModule)
}
}
}
}
// let drawRoofCells
// if (maxLengthLine.flowDirection === 'right' || maxLengthLine.flowDirection === 'left') {
// drawRoofCells = trestle.fillCell({ width: 113.4, height: 172.2, padding: 0 })
// trestle.flowDirection = 'south'
// } else {
// drawRoofCells = trestle.fillCell({ width: 172.2, height: 113.4, padding: 0 })
// trestle.flowDirection = 'east'
// }
// drawRoofCells.forEach((cell) => {
// moduleSetupArray.push(cell)
// })
/**
* 추후에 가대까지 완료하면 그룹시켜버림
*/
// const groupCellObj = canvas
// ?.getObjects()
// .filter(
// (obj) =>
// obj?.parentId === trestle.parentId ||
// obj?.id === trestle.parentId ||
// (obj?.name === 'arrow' && obj?.parent.id === trestle.parentId) ||
// (obj?.name === 'flowDirectionText' && obj?.parent.parent.id === trestle.parentId),
// )
// console.log('groupCellObj', groupCellObj)
// canvas?.add(
// new fabric.Group(groupCellObj, {
// name: 'cellGroup',
// originX: 'center',
// originY: 'center',
// }),
// )
moduleSetupSurface.set({ modules: moduleSetupArray })
})
setModuleIsSetup(moduleSetupArray)
}
const coordToTurfPolygon = (points) => {
const coordinates = points.map((point) => [point.x, point.y])
coordinates.push(coordinates[0])
return turf.polygon([coordinates])
}
const polygonToTurfPolygon = (object) => {
let coordinates
coordinates = object.points.map((point) => [point.x, point.y])
coordinates.push(coordinates[0])
return turf.polygon(
[coordinates],
{},
{
parentId: object.parentId,
},
)
}
function batchObjectGroupToTurfPolygon(group) {
const polygons = group.getObjects().filter((obj) => obj.type === 'QPolygon')
let allPoints = []
polygons.forEach((obj) => allPoints.push(...obj.get('points')))
const points = turf.featureCollection(allPoints.map((point) => turf.point([point.x, point.y])))
const hull = turf.concave(points, { tolerance: 0.1 })
return hull
}
return {
makeModuleInstArea,
manualModuleSetup,
autoModuleSetup,
}
}

View File

@ -283,7 +283,7 @@ export function useCanvasSetting() {
optionName = ['roof', POLYGON_TYPE.ROOF] optionName = ['roof', POLYGON_TYPE.ROOF]
break break
case 'wordDisplay': //문자 표시 case 'wordDisplay': //문자 표시
optionName = ['6'] optionName = ['commonText']
break break
case 'circuitNumDisplay': //회로번호 표시 case 'circuitNumDisplay': //회로번호 표시
optionName = ['7'] optionName = ['7']
@ -313,6 +313,8 @@ export function useCanvasSetting() {
//obj.set({ visible: !obj.visible }) //obj.set({ visible: !obj.visible })
}) })
canvas.renderAll()
// console.log( // console.log(
// 'optionName', // 'optionName',
// optionName, // optionName,

View File

@ -40,7 +40,7 @@ export function useFirstOption() {
optionName = ['roof', POLYGON_TYPE.ROOF] optionName = ['roof', POLYGON_TYPE.ROOF]
break break
case 'wordDisplay': //문자 표시 case 'wordDisplay': //문자 표시
optionName = ['6'] optionName = ['commonText']
break break
case 'circuitNumDisplay': //회로번호 표시 case 'circuitNumDisplay': //회로번호 표시
optionName = ['7'] optionName = ['7']

View File

@ -0,0 +1,30 @@
import { canvasState } from '@/store/canvasAtom'
import { useRecoilValue } from 'recoil'
import { usePolygon } from '@/hooks/usePolygon'
import { useState } from 'react'
import { usePopup } from '@/hooks/usePopup'
export const FLOW_DIRECTION_TYPE = {
EIGHT_AZIMUTH: 'eightAzimuth',
TWENTY_FOUR_AZIMUTH: 'twentyFourAzimuth',
}
export function useFlowDirectionSetting(id) {
const canvas = useRecoilValue(canvasState)
const { drawDirectionArrow } = usePolygon()
const { closePopup } = usePopup()
const [type, setType] = useState(FLOW_DIRECTION_TYPE.EIGHT_AZIMUTH)
const changeSurfaceFlowDirection = (roof, direction, orientation) => {
roof.set({
direction: direction,
surfaceCompass: orientation,
})
drawDirectionArrow(roof)
canvas?.renderAll()
closePopup(id)
}
return { changeSurfaceFlowDirection, type, setType }
}

View File

@ -0,0 +1,25 @@
import { useRecoilState, useRecoilValue } from 'recoil'
import { canvasState } from '@/store/canvasAtom'
import { usePolygon } from '@/hooks/usePolygon'
import { POLYGON_TYPE } from '@/common/common'
import { compasDegAtom } from '@/store/orientationAtom'
// 모듈,회로 구성 탭 기본설정 > 방위설정 탭
export function useOrientation() {
const canvas = useRecoilValue(canvasState)
const [compasDeg, setCompasDeg] = useRecoilState(compasDegAtom)
const { drawDirectionArrow } = usePolygon()
const nextStep = () => {
const roofs = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
roofs.forEach((roof) => {
roof.set({
moduleCompass: compasDeg,
})
drawDirectionArrow(roof)
})
}
return { nextStep, compasDeg, setCompasDeg }
}

View File

@ -1,6 +1,6 @@
import { useEffect, useRef } from 'react' import { useEffect, useRef } from 'react'
import { distanceBetweenPoints } from '@/util/canvas-util' import { distanceBetweenPoints } from '@/util/canvas-util'
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil' import { useRecoilState, useRecoilValue, useResetRecoilState, useSetRecoilState } from 'recoil'
import { import {
adsorptionPointAddModeState, adsorptionPointAddModeState,
adsorptionPointModeState, adsorptionPointModeState,
@ -65,6 +65,7 @@ export function useOuterLineWall(id, propertiesId) {
const [arrow1, setArrow1] = useRecoilState(outerLineArrow1State) const [arrow1, setArrow1] = useRecoilState(outerLineArrow1State)
const [arrow2, setArrow2] = useRecoilState(outerLineArrow2State) const [arrow2, setArrow2] = useRecoilState(outerLineArrow2State)
const [points, setPoints] = useRecoilState(outerLinePointsState) const [points, setPoints] = useRecoilState(outerLinePointsState)
const resetPoints = useResetRecoilState(outerLinePointsState)
const [type, setType] = useRecoilState(outerLineTypeState) const [type, setType] = useRecoilState(outerLineTypeState)
const [angle1, setAngle1] = useRecoilState(outerLineAngle1State) const [angle1, setAngle1] = useRecoilState(outerLineAngle1State)
const [angle2, setAngle2] = useRecoilState(outerLineAngle2State) const [angle2, setAngle2] = useRecoilState(outerLineAngle2State)
@ -88,6 +89,13 @@ export function useOuterLineWall(id, propertiesId) {
clear() clear()
return () => { return () => {
initEvent() initEvent()
canvas
.getObjects()
.filter((obj) => obj.name === 'startPoint')
.forEach((obj) => {
canvas.remove(obj)
})
} }
}, [verticalHorizontalMode, points, adsorptionPointAddMode, adsorptionPointMode, adsorptionRange, interval, tempGridMode]) }, [verticalHorizontalMode, points, adsorptionPointAddMode, adsorptionPointMode, adsorptionRange, interval, tempGridMode])

View File

@ -107,18 +107,16 @@ export function useRoofAllocationSetting(id) {
} }
}) })
}) })
if (currentObject) { if (currentObject && currentObject.name && ['auxiliaryLine', 'ridge', 'hip'].includes(currentObject.name)) {
console.log(currentObject.name) currentObject.set({
if (currentObject.name.toLowerCase().includes('line')) { strokeWidth: 4,
currentObject.set({ stroke: '#EA10AC',
strokeWidth: 4, })
stroke: '#EA10AC',
})
}
} }
}, [currentObject]) }, [currentObject])
useEffect(() => { useEffect(() => {
// canvas.getObjects().filter((obj) => obj.type === 'QLine')
const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF) const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
if (roofBases.length === 0) { if (roofBases.length === 0) {
swalFire({ text: '할당할 지붕이 없습니다.' }) swalFire({ text: '할당할 지붕이 없습니다.' })
@ -126,10 +124,6 @@ export function useRoofAllocationSetting(id) {
} }
}, []) }, [])
useEffect(() => {
console.log('editingLines', editingLines)
}, [editingLines])
const onAddRoofMaterial = () => { const onAddRoofMaterial = () => {
setValues([...values, selectedRoofMaterial]) setValues([...values, selectedRoofMaterial])
} }
@ -163,7 +157,7 @@ export function useRoofAllocationSetting(id) {
const checkInnerLines = () => { const checkInnerLines = () => {
const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF) // roofPolygon.innerLines const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF) // roofPolygon.innerLines
let result = false let result = false
console.log(roofBases)
roofBases.forEach((roof) => { roofBases.forEach((roof) => {
roof.innerLines.forEach((line) => { roof.innerLines.forEach((line) => {
if (!line.attributes.actualSize || line.attributes?.actualSize === 0) { if (!line.attributes.actualSize || line.attributes?.actualSize === 0) {
@ -182,7 +176,7 @@ export function useRoofAllocationSetting(id) {
} }
const apply = () => { const apply = () => {
const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF) const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF && !obj.isFixed)
const wallLines = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.WALL) const wallLines = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.WALL)
roofBases.forEach((roofBase) => { roofBases.forEach((roofBase) => {
try { try {
@ -205,6 +199,10 @@ export function useRoofAllocationSetting(id) {
const roofs = canvas.getObjects().filter((obj) => obj.name === 'roof') const roofs = canvas.getObjects().filter((obj) => obj.name === 'roof')
roofs.forEach((roof) => { roofs.forEach((roof) => {
if (roof.isFixed) return
roof.set({
isFixed: true,
})
setSurfaceShapePattern(roof, roofDisplay.column) setSurfaceShapePattern(roof, roofDisplay.column)
drawDirectionArrow(roof) drawDirectionArrow(roof)
}) })

View File

@ -167,8 +167,6 @@ export function useRoofShapeSetting(id) {
] ]
const handleSave = () => { const handleSave = () => {
//기존 wallLine 삭제
let outerLines let outerLines
let direction let direction
@ -379,20 +377,20 @@ export function useRoofShapeSetting(id) {
} }
// 기존 wallLine, roofBase 제거 // 기존 wallLine, roofBase 제거
canvas /*canvas
.getObjects() .getObjects()
.filter((obj) => obj.name === POLYGON_TYPE.WALL) .filter((obj) => obj.name === POLYGON_TYPE.WALL)
.forEach((line) => { .forEach((line) => {
canvas.remove(line) canvas.remove(line)
}) })*/
canvas /*canvas
.getObjects() .getObjects()
.filter((obj) => obj.name === POLYGON_TYPE.ROOF) .filter((obj) => obj.name === POLYGON_TYPE.ROOF)
.forEach((obj) => { .forEach((obj) => {
canvas.remove(...obj.innerLines) canvas.remove(...obj.innerLines)
canvas.remove(obj) canvas.remove(obj)
}) })*/
const polygon = addPolygonByLines(outerLines, { name: POLYGON_TYPE.WALL, direction }) const polygon = addPolygonByLines(outerLines, { name: POLYGON_TYPE.WALL, direction })
polygon.lines = [...outerLines] polygon.lines = [...outerLines]
@ -402,7 +400,6 @@ export function useRoofShapeSetting(id) {
canvas?.renderAll() canvas?.renderAll()
roof.drawHelpLine() roof.drawHelpLine()
// setShowRoofShapeSettingModal(false)
isFixRef.current = true isFixRef.current = true
closePopup(id) closePopup(id)
} }

View File

@ -13,6 +13,8 @@ import { usePopup } from '@/hooks/usePopup'
import { roofDisplaySelector } from '@/store/settingAtom' import { roofDisplaySelector } from '@/store/settingAtom'
import { usePolygon } from '@/hooks/usePolygon' import { usePolygon } from '@/hooks/usePolygon'
import { fontSelector } from '@/store/fontAtom' import { fontSelector } from '@/store/fontAtom'
import { slopeSelector } from '@/store/commonAtom'
import { QLine } from '@/components/fabric/QLine'
export function useSurfaceShapeBatch() { export function useSurfaceShapeBatch() {
const { getMessage } = useMessage() const { getMessage } = useMessage()
@ -22,6 +24,7 @@ export function useSurfaceShapeBatch() {
const canvas = useRecoilValue(canvasState) const canvas = useRecoilValue(canvasState)
const globalPitch = useRecoilValue(globalPitchState) const globalPitch = useRecoilValue(globalPitchState)
const roofDisplay = useRecoilValue(roofDisplaySelector) const roofDisplay = useRecoilValue(roofDisplaySelector)
const slope = useRecoilValue(slopeSelector(globalPitch))
const { swalFire } = useSwal() const { swalFire } = useSwal()
const { addCanvasMouseEventListener, initEvent } = useEvent() const { addCanvasMouseEventListener, initEvent } = useEvent()
const { closePopup } = usePopup() const { closePopup } = usePopup()
@ -126,6 +129,7 @@ export function useSurfaceShapeBatch() {
addCanvasMouseEventListener('mouse:down', (e) => { addCanvasMouseEventListener('mouse:down', (e) => {
isDrawing = false isDrawing = false
obj.set('name', POLYGON_TYPE.ROOF) obj.set('name', POLYGON_TYPE.ROOF)
obj.set('surfaceId', surfaceId)
initEvent() initEvent()
setSurfaceShapePattern(obj, roofDisplay.column) setSurfaceShapePattern(obj, roofDisplay.column)
closePopup(id) closePopup(id)
@ -580,18 +584,19 @@ export function useSurfaceShapeBatch() {
text: '배치면 내용을 전부 삭제하시겠습니까?', text: '배치면 내용을 전부 삭제하시겠습니까?',
type: 'confirm', type: 'confirm',
confirmFn: () => { confirmFn: () => {
canvas?.getObjects().forEach((obj) => { // canvas?.getObjects().forEach((obj) => {
if ( // if (
obj.name === POLYGON_TYPE.ROOF || // obj.name === POLYGON_TYPE.ROOF ||
obj.name === BATCH_TYPE.OPENING || // obj.name === BATCH_TYPE.OPENING ||
obj.name === BATCH_TYPE.SHADOW || // obj.name === BATCH_TYPE.SHADOW ||
obj.name === BATCH_TYPE.TRIANGLE_DORMER || // obj.name === BATCH_TYPE.TRIANGLE_DORMER ||
obj.name === BATCH_TYPE.PENTAGON_DORMER || // obj.name === BATCH_TYPE.PENTAGON_DORMER ||
obj.name === 'lengthText' // obj.name === 'lengthText'
) { // ) {
canvas?.remove(obj) // canvas?.remove(obj)
} // }
}) // })
canvas.clear()
swalFire({ text: '삭제 완료 되었습니다.' }) swalFire({ text: '삭제 완료 되었습니다.' })
}, },
// denyFn: () => { // denyFn: () => {
@ -823,10 +828,113 @@ export function useSurfaceShapeBatch() {
canvas.renderAll() canvas.renderAll()
} }
const changeSurfaceLinePropertyEvent = () => {
let tmpLines = []
const roof = canvas.getActiveObject()
if (roof) {
roof.set({
selectable: false,
})
roof.lines.forEach((obj, index) => {
const tmpLine = new QLine([obj.x1, obj.y1, obj.x2, obj.y2], {
...obj,
stroke: 'rgb(3, 255, 0)',
strokeWidth: 8,
selectable: true,
name: 'lineProperty',
lineIndex: index,
})
tmpLines.push(tmpLine)
canvas.add(tmpLine)
})
addCanvasMouseEventListener('mouse:down', (e) => {
const selectedLine = e.target
if (selectedLine && selectedLine.name !== 'roof') {
tmpLines.forEach((line) => {
line.set({
stroke: 'rgb(3, 255, 0)',
name: 'lineProperty',
})
})
selectedLine.set({
stroke: 'red',
name: 'selectedLineProperty',
})
} else {
tmpLines.forEach((line) => {
line.set({
stroke: 'rgb(3, 255, 0)',
name: 'lineProperty',
})
})
}
})
canvas.renderAll()
}
canvas.discardActiveObject()
}
const changeSurfaceLineProperty = (property, roof) => {
if (!property) {
swalFire({ text: getMessage('modal.line.property.change'), icon: 'error' })
return
}
const selectedLine = canvas.getActiveObjects()[0] //배열로 떨어짐 한개만 선택가능
if (selectedLine && selectedLine.name === 'selectedLineProperty') {
swalFire({
text: getMessage('modal.line.property.change.confirm'),
type: 'confirm',
confirmFn: () => {
const lineIndex = selectedLine.lineIndex
roof.lines[lineIndex].attributes = {
...roof.lines[lineIndex].attributes,
type: property.value,
}
canvas.renderAll()
},
})
} else {
swalFire({ text: getMessage('modal.line.property.change.unselect'), icon: 'error' })
}
}
const changeSurfaceLinePropertyReset = (roof) => {
const lines = canvas.getObjects().filter((obj) => obj.name === 'lineProperty' || obj.name === 'selectedLineProperty')
lines.forEach((line) => {
canvas.remove(line)
})
if (roof) {
roof.set({
selectable: true,
})
}
canvas?.renderAll()
}
const changeSurfaceFlowDirection = (roof, direction, orientation) => {
roof.set({
direction: direction,
})
drawDirectionArrow(roof)
canvas?.renderAll()
}
return { return {
applySurfaceShape, applySurfaceShape,
deleteAllSurfacesAndObjects, deleteAllSurfacesAndObjects,
moveSurfaceShapeBatch, moveSurfaceShapeBatch,
resizeSurfaceShapeBatch, resizeSurfaceShapeBatch,
changeSurfaceLinePropertyEvent,
changeSurfaceLineProperty,
changeSurfaceLinePropertyReset,
} }
} }

View File

@ -109,11 +109,24 @@ export function useCanvas(id) {
OBJECT_PROTOTYPE.forEach((type) => { OBJECT_PROTOTYPE.forEach((type) => {
type.toObject = function (propertiesToInclude) { type.toObject = function (propertiesToInclude) {
let source = {} let source = {}
for (let key in this) { for (let key in this) {
if (typeof this[key] !== 'function' && SAVE_KEY.includes(key)) { if (typeof this[key] !== 'function' && SAVE_KEY.includes(key)) {
source.key = this[key] source.key = this[key]
} }
} }
//QLine에 커스텀 어트리뷰트 넣기
if (this.type === 'QLine') {
if (this.attributes) {
this.attributes.type = this.attributes.type || 'default'
source.attributes = {
...this.attributes,
type: this.attributes.type,
}
}
}
return fabric.util.object.extend(this.callSuper('toObject', propertiesToInclude), source) return fabric.util.object.extend(this.callSuper('toObject', propertiesToInclude), source)
} }
}) })

View File

@ -225,7 +225,9 @@ export function useCanvasEvent() {
if (deselected?.length > 0) { if (deselected?.length > 0) {
deselected.forEach((obj) => { deselected.forEach((obj) => {
if (obj.type === 'QPolygon') { if (obj.type === 'QPolygon') {
obj.set({ stroke: 'black' }) if (obj.name !== 'moduleSetupSurface') {
obj.set({ stroke: 'black' })
}
} }
}) })
} }

View File

@ -24,7 +24,6 @@ import { useCommonUtils } from './common/useCommonUtils'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { useCanvasEvent } from '@/hooks/useCanvasEvent' import { useCanvasEvent } from '@/hooks/useCanvasEvent'
import { contextMenuListState, contextMenuState } from '@/store/contextMenu' import { contextMenuListState, contextMenuState } from '@/store/contextMenu'
import ImageSizeSetting from '@/components/floor-plan/modal/image/ImageSizeSetting'
import PanelEdit from '@/components/floor-plan/modal/module/PanelEdit' import PanelEdit from '@/components/floor-plan/modal/module/PanelEdit'
import DimensionLineSetting from '@/components/floor-plan/modal/dimensionLine/DimensionLineSetting' import DimensionLineSetting from '@/components/floor-plan/modal/dimensionLine/DimensionLineSetting'
import ColumnRemove from '@/components/floor-plan/modal/module/column/ColumnRemove' import ColumnRemove from '@/components/floor-plan/modal/module/column/ColumnRemove'
@ -34,6 +33,7 @@ import RowInsert from '@/components/floor-plan/modal/module/row/RowInsert'
import CircuitNumberEdit from '@/components/floor-plan/modal/module/CircuitNumberEdit' import CircuitNumberEdit from '@/components/floor-plan/modal/module/CircuitNumberEdit'
import { useObjectBatch } from '@/hooks/object/useObjectBatch' import { useObjectBatch } from '@/hooks/object/useObjectBatch'
import { useSurfaceShapeBatch } from '@/hooks/surface/useSurfaceShapeBatch' import { useSurfaceShapeBatch } from '@/hooks/surface/useSurfaceShapeBatch'
import { fontSelector, globalFontAtom } from '@/store/fontAtom'
export function useContextMenu() { export function useContextMenu() {
const currentMenu = useRecoilValue(currentMenuState) // 현재 메뉴 const currentMenu = useRecoilValue(currentMenuState) // 현재 메뉴
@ -52,6 +52,9 @@ export function useContextMenu() {
const { handleZoomClear } = useCanvasEvent() const { handleZoomClear } = useCanvasEvent()
const { moveObjectBatch } = useObjectBatch({}) const { moveObjectBatch } = useObjectBatch({})
const { moveSurfaceShapeBatch } = useSurfaceShapeBatch() const { moveSurfaceShapeBatch } = useSurfaceShapeBatch()
const [globalFont, setGlobalFont] = useRecoilState(globalFontAtom)
const commonTextFont = useRecoilValue(fontSelector('commonText'))
const currentMenuSetting = () => { const currentMenuSetting = () => {
switch (currentMenu) { switch (currentMenu) {
case MENU.PLAN_DRAWING: case MENU.PLAN_DRAWING:
@ -95,11 +98,6 @@ export function useContextMenu() {
case MENU.ROOF_COVERING.DEFAULT: case MENU.ROOF_COVERING.DEFAULT:
setContextMenu([ setContextMenu([
[ [
{
id: 'refresh',
name: getMessage('refresh'),
fn: () => handleZoomClear(),
},
{ {
id: 'roofMaterialPlacement', id: 'roofMaterialPlacement',
name: getMessage('contextmenu.roof.material.placement'), name: getMessage('contextmenu.roof.material.placement'),
@ -121,11 +119,6 @@ export function useContextMenu() {
name: getMessage('contextmenu.wallline.remove'), name: getMessage('contextmenu.wallline.remove'),
fn: () => deleteOuterLineObject(), fn: () => deleteOuterLineObject(),
}, },
{
id: 'imageSizeEdit',
name: getMessage('modal.image.size.setting'),
component: <ImageSizeSetting id={popupId} />,
},
], ],
[ [
{ {
@ -176,6 +169,7 @@ export function useContextMenu() {
{ {
id: 'sizeEdit', id: 'sizeEdit',
name: getMessage('contextmenu.size.edit'), name: getMessage('contextmenu.size.edit'),
component: <SizeSetting id={popupId} target={currentObject} />,
}, },
{ {
id: 'remove', id: 'remove',
@ -192,16 +186,12 @@ export function useContextMenu() {
shortcut: ['c', 'C'], shortcut: ['c', 'C'],
name: `${getMessage('contextmenu.copy')}(C)`, name: `${getMessage('contextmenu.copy')}(C)`,
}, },
{
id: 'imageSizeEdit',
name: getMessage('modal.image.size.setting'),
component: <ImageSizeSetting id={popupId} />,
},
], ],
[ [
{ {
id: 'roofMaterialEdit', id: 'roofMaterialEdit',
name: getMessage('contextmenu.roof.material.edit'), name: getMessage('contextmenu.roof.material.edit'),
component: <RoofAllocationSetting id={popupId} />,
}, },
{ {
id: 'linePropertyEdit', id: 'linePropertyEdit',
@ -211,6 +201,7 @@ export function useContextMenu() {
{ {
id: 'flowDirectionEdit', id: 'flowDirectionEdit',
name: getMessage('contextmenu.flow.direction.edit'), name: getMessage('contextmenu.flow.direction.edit'),
component: <FlowDirectionSetting id={popupId} target={currentObject} />,
}, },
], ],
]) ])
@ -257,8 +248,10 @@ export function useContextMenu() {
}, [currentContextMenu]) }, [currentContextMenu])
useEffect(() => { useEffect(() => {
console.log('currentObject', currentObject) console.log(currentObject)
if (currentObject?.name) { if (currentObject?.name) {
console.log('object', currentObject)
switch (currentObject.name) { switch (currentObject.name) {
case 'triangleDormer': case 'triangleDormer':
case 'pentagonDormer': case 'pentagonDormer':
@ -336,7 +329,7 @@ export function useContextMenu() {
{ {
id: 'linePropertyEdit', id: 'linePropertyEdit',
name: getMessage('contextmenu.line.property.edit'), name: getMessage('contextmenu.line.property.edit'),
component: <LinePropertySetting id={popupId} />, component: <LinePropertySetting id={popupId} target={currentObject} />,
}, },
{ {
id: 'flowDirectionEdit', id: 'flowDirectionEdit',
@ -423,7 +416,26 @@ export function useContextMenu() {
{ {
id: 'commonTextFontSetting', id: 'commonTextFontSetting',
name: getMessage('contextmenu.font.setting'), name: getMessage('contextmenu.font.setting'),
component: <FontSetting id={popupId} type={'commonText'} />, component: (
<FontSetting
id={popupId}
type={'commonText'}
font={commonTextFont}
onSave={(font) => {
setGlobalFont((prev) => {
return {
...prev,
commonText: {
fontFamily: font.fontFamily,
fontWeight: font.fontWeight,
fontSize: font.fontSize,
fontColor: font.fontColor,
},
}
})
}}
/>
),
}, },
{ {
id: 'commonTextEdit', id: 'commonTextEdit',

View File

@ -219,6 +219,12 @@ export function useEvent() {
mouseEventListeners.current.length = 0 // 배열 초기화 mouseEventListeners.current.length = 0 // 배열 초기화
} }
const addTargetMouseEventListener = (eventType, target, handler) => {
target.off(eventType)
target.on(eventType, handler)
mouseEventListeners.current.push({ eventType, handler })
}
/** /**
* document 이벤트의 경우 함수를 통해서만 등록 * document 이벤트의 경우 함수를 통해서만 등록
* @param eventType * @param eventType
@ -264,6 +270,7 @@ export function useEvent() {
return { return {
addDocumentEventListener, addDocumentEventListener,
addCanvasMouseEventListener, addCanvasMouseEventListener,
addTargetMouseEventListener,
removeAllMouseEventListeners, removeAllMouseEventListeners,
removeAllDocumentEventListeners, removeAllDocumentEventListeners,
removeDocumentEvent, removeDocumentEvent,

View File

@ -57,6 +57,7 @@ export function usePlan() {
*/ */
const currentCanvasData = (mode = '') => { const currentCanvasData = (mode = '') => {
removeMouseLines() removeMouseLines()
canvas.discardActiveObject()
if (mode === 'save') { if (mode === 'save') {
const groups = canvas.getObjects().filter((obj) => obj.type === 'group') const groups = canvas.getObjects().filter((obj) => obj.type === 'group')

View File

@ -1,7 +1,7 @@
import { ANGLE_TYPE, canvasState, currentAngleTypeSelector, fontFamilyState, fontSizeState, pitchTextSelector } from '@/store/canvasAtom' import { ANGLE_TYPE, canvasState, currentAngleTypeSelector, fontFamilyState, fontSizeState, pitchTextSelector } from '@/store/canvasAtom'
import { useRecoilValue } from 'recoil' import { useRecoilValue } from 'recoil'
import { fabric } from 'fabric' import { fabric } from 'fabric'
import { getDegreeByChon, getDirectionByPoint, isPointOnLine } from '@/util/canvas-util' import { findAndRemoveClosestPoint, getDegreeByChon, getDegreeInOrientation, getDirectionByPoint, isPointOnLine } from '@/util/canvas-util'
import { QPolygon } from '@/components/fabric/QPolygon' import { QPolygon } from '@/components/fabric/QPolygon'
import { isSamePoint, removeDuplicatePolygons } from '@/util/qpolygon-utils' import { isSamePoint, removeDuplicatePolygons } from '@/util/qpolygon-utils'
import { flowDisplaySelector } from '@/store/settingAtom' import { flowDisplaySelector } from '@/store/settingAtom'
@ -86,8 +86,8 @@ export const usePolygon = () => {
parentDirection: line.direction, parentDirection: line.direction,
parentDegree: degree, parentDegree: degree,
parentId: polygon.id, parentId: polygon.id,
planeSize, planeSize: planeSize ?? length,
actualSize, actualSize: actualSize ?? length,
editable: false, editable: false,
selectable: true, selectable: true,
lockRotation: true, lockRotation: true,
@ -181,7 +181,7 @@ export const usePolygon = () => {
polygon.canvas polygon.canvas
.getObjects() .getObjects()
.filter((obj) => obj.name === 'flowText' && obj.parent === polygon.arrow) .filter((obj) => obj.name === 'flowText' && obj.parentId === polygon.arrow?.id)
.forEach((obj) => polygon.canvas.remove(obj)) .forEach((obj) => polygon.canvas.remove(obj))
let arrow = null let arrow = null
@ -265,6 +265,8 @@ export const usePolygon = () => {
direction: direction, direction: direction,
parent: polygon, parent: polygon,
stickeyPoint: stickeyPoint, stickeyPoint: stickeyPoint,
surfaceCompass: polygon.surfaceCompass,
moduleCompass: polygon.moduleCompass,
visible: isFlowDisplay, visible: isFlowDisplay,
pitch: polygon.pitch, pitch: polygon.pitch,
parentId: polygon.id, parentId: polygon.id,
@ -274,7 +276,177 @@ export const usePolygon = () => {
polygon.arrow = arrow polygon.arrow = arrow
polygon.canvas.add(arrow) polygon.canvas.add(arrow)
polygon.canvas.renderAll() polygon.canvas.renderAll()
drawDirectionStringToArrow(polygon.canvas, 0) drawDirectionStringToArrow2(polygon)
// drawDirectionStringToArrow()
}
//arrow의 compass 값으로 방향 글자 설정 필요
const drawDirectionStringToArrow2 = (polygon) => {
const { direction, surfaceCompass, moduleCompass, arrow } = polygon
if (moduleCompass === null || moduleCompass === undefined) {
const textObj = new fabric.Text(`${currentAngleType === ANGLE_TYPE.SLOPE ? arrow.pitch : getDegreeByChon(arrow.pitch)}${pitchText}`, {
fontFamily: flowFontOptions.fontFamily.value,
fontWeight: flowFontOptions.fontWeight.value.toLowerCase().includes('bold') ? 'bold' : 'normal',
fontStyle: flowFontOptions.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal',
fontSize: flowFontOptions.fontSize.value,
fill: flowFontOptions.fontColor.value,
originX: 'center',
originY: 'center',
pitch: arrow.pitch,
name: 'flowText',
selectable: false,
left: arrow.stickeyPoint.x,
top: arrow.stickeyPoint.y,
parent: arrow,
parentId: arrow.id,
visible: isFlowDisplay,
})
polygon.canvas.add(textObj)
return
}
let text = ''
const compassType = (375 - getDegreeInOrientation(moduleCompass)) / 15
if ([1, 25].includes(compassType)) {
direction === 'north' ? (text = '北') : direction === 'south' ? (text = '南') : direction === 'west' ? (text = '西') : (text = '東')
} else if ([2, 3].includes(compassType)) {
direction === 'north'
? (text = '北北東')
: direction === 'south'
? (text = '南南西')
: direction === 'west'
? (text = '西北西')
: (text = '東南東')
} else if ([4].includes(compassType)) {
direction === 'north' ? (text = '北東') : direction === 'south' ? (text = '南西') : direction === 'west' ? (text = '北西') : (text = '南東')
} else if ([5, 6].includes(compassType)) {
direction === 'north'
? (text = '東北東')
: direction === 'south'
? (text = '西南西')
: direction === 'west'
? (text = '北北西')
: (text = '南南東')
} else if ([7].includes(compassType)) {
direction === 'north' ? (text = '東') : direction === 'south' ? (text = '西') : direction === 'west' ? (text = '北') : (text = '南')
} else if ([8, 9].includes(compassType)) {
direction === 'north'
? (text = '東南東')
: direction === 'south'
? (text = '西北西')
: direction === 'west'
? (text = '北北東')
: (text = '南南西')
} else if ([10].includes(compassType)) {
direction === 'north' ? (text = '南東') : direction === 'south' ? (text = '北西') : direction === 'west' ? (text = '南西') : (text = '北東')
} else if ([11, 12].includes(compassType)) {
direction === 'north'
? (text = '南南東')
: direction === 'south'
? (text = '北北西')
: direction === 'west'
? (text = '東北東')
: (text = '西南西')
} else if ([13].includes(compassType)) {
direction === 'north' ? (text = '南') : direction === 'south' ? (text = '北') : direction === 'west' ? (text = '東') : (text = '西')
} else if ([14, 15].includes(compassType)) {
direction === 'north'
? (text = '南南西')
: direction === 'south'
? (text = '北北東')
: direction === 'west'
? (text = '東南東')
: (text = '西北西')
} else if ([16].includes(compassType)) {
direction === 'north' ? (text = '南西') : direction === 'south' ? (text = '北東') : direction === 'west' ? (text = '南東') : (text = '北西')
} else if ([17, 18].includes(compassType)) {
direction === 'north'
? (text = '西南西')
: direction === 'south'
? (text = '東北東')
: direction === 'west'
? (text = '南南東')
: (text = '北北西')
} else if ([19].includes(compassType)) {
direction === 'north' ? (text = '西') : direction === 'south' ? (text = '東') : direction === 'west' ? (text = '南') : (text = '北')
} else if ([20, 21].includes(compassType)) {
direction === 'north'
? (text = '西北西')
: direction === 'south'
? (text = '東南東')
: direction === 'west'
? (text = '南南西')
: (text = '北北東')
} else if ([22].includes(compassType)) {
direction === 'north' ? (text = '北西') : direction === 'south' ? (text = '南東') : direction === 'west' ? (text = '南西') : (text = '北東')
} else if ([23, 24].includes(compassType)) {
direction === 'north'
? (text = '北北西')
: direction === 'south'
? (text = '南南東')
: direction === 'west'
? (text = '西南西')
: (text = '東北東')
}
// 東,西,南,北
if ([360].includes(surfaceCompass)) {
text = '南'
} else if ([345, 330].includes(surfaceCompass)) {
text = '南南東'
} else if ([315].includes(surfaceCompass)) {
text = '南東'
} else if ([300, 285].includes(surfaceCompass)) {
text = '東南東'
} else if ([270].includes(surfaceCompass)) {
text = '東'
} else if ([255, 240].includes(surfaceCompass)) {
text = '東北東'
} else if ([225].includes(surfaceCompass)) {
text = '北東'
} else if ([210, 195].includes(surfaceCompass)) {
text = '北北東'
} else if ([180].includes(surfaceCompass)) {
text = '北'
} else if ([165, 150].includes(surfaceCompass)) {
text = '北北西'
} else if ([135].includes(surfaceCompass)) {
text = '北西'
} else if ([120, 105].includes(surfaceCompass)) {
text = '西北西'
} else if ([90].includes(surfaceCompass)) {
text = '西'
} else if ([75, 60].includes(surfaceCompass)) {
text = '西南西'
} else if ([45].includes(surfaceCompass)) {
text = '西南'
} else if ([30, 15].includes(surfaceCompass)) {
text = '西西南'
}
const textObj = new fabric.Text(`${text} (${currentAngleType === ANGLE_TYPE.SLOPE ? arrow.pitch : getDegreeByChon(arrow.pitch)}${pitchText})`, {
fontFamily: flowFontOptions.fontFamily.value,
fontWeight: flowFontOptions.fontWeight.value.toLowerCase().includes('bold') ? 'bold' : 'normal',
fontStyle: flowFontOptions.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal',
fontSize: flowFontOptions.fontSize.value,
fill: flowFontOptions.fontColor.value,
pitch: arrow.pitch,
originX: 'center',
originY: 'center',
name: 'flowText',
originText: text,
selectable: false,
left: arrow.stickeyPoint.x,
top: arrow.stickeyPoint.y,
parent: arrow,
parentId: arrow.id,
visible: isFlowDisplay,
})
polygon.canvas.add(textObj)
} }
/** /**
@ -473,10 +645,11 @@ export const usePolygon = () => {
const textStr = `${txt} (${currentAngleType === ANGLE_TYPE.SLOPE ? arrow.pitch : getDegreeByChon(arrow.pitch)}${pitchText})` const textStr = `${txt} (${currentAngleType === ANGLE_TYPE.SLOPE ? arrow.pitch : getDegreeByChon(arrow.pitch)}${pitchText})`
const text = new fabric.Text(`${textStr}`, { const text = new fabric.Text(`${textStr}`, {
fontFamily: flowFontOptions.fontFamily.value,
fontWeight: flowFontOptions.fontWeight.value.toLowerCase().includes('bold') ? 'bold' : 'normal',
fontStyle: flowFontOptions.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal',
fontSize: flowFontOptions.fontSize.value, fontSize: flowFontOptions.fontSize.value,
fill: flowFontOptions.fontColor.value, fill: flowFontOptions.fontColor.value,
fontFamily: flowFontOptions.fontFamily.value,
fontWeight: flowFontOptions.fontWeight.value,
pitch: arrow.pitch, pitch: arrow.pitch,
originX: 'center', originX: 'center',
originY: 'center', originY: 'center',
@ -496,62 +669,159 @@ export const usePolygon = () => {
const splitPolygonWithLines = (polygon) => { const splitPolygonWithLines = (polygon) => {
polygon.set({ visible: false }) polygon.set({ visible: false })
let innerLines = [...polygon.innerLines] let innerLines = [...polygon.innerLines]
canvas.renderAll()
let polygonLines = [...polygon.lines] let polygonLines = [...polygon.lines]
const roofs = [] const roofs = []
let delIndexs = [] //polygonLines를 순회하며 innerLines와 교차하는 점을 line의 속성에 배열로 저장한다.
let newLines = [] polygonLines.forEach((line) => {
let startPoint // 시작점
let endPoint // 끝점
if (line.x1 < line.x2) {
startPoint = { x: line.x1, y: line.y1 }
endPoint = { x: line.x2, y: line.y2 }
} else if (line.x1 > line.x2) {
startPoint = { x: line.x2, y: line.y2 }
endPoint = { x: line.x1, y: line.y1 }
} else {
if (line.y1 < line.y2) {
startPoint = { x: line.x1, y: line.y1 }
endPoint = { x: line.x2, y: line.y2 }
} else {
startPoint = { x: line.x2, y: line.y2 }
endPoint = { x: line.x1, y: line.y1 }
}
}
polygonLines.forEach((line, index) => { line.startPoint = startPoint
line.tempIndex = index line.endPoint = endPoint
})
innerLines.forEach((line) => {
let startPoint // 시작점
let endPoint // 끝점
if (line.x1 < line.x2) {
startPoint = { x: line.x1, y: line.y1 }
endPoint = { x: line.x2, y: line.y2 }
} else if (line.x1 > line.x2) {
startPoint = { x: line.x2, y: line.y2 }
endPoint = { x: line.x1, y: line.y1 }
} else {
if (line.y1 < line.y2) {
startPoint = { x: line.x1, y: line.y1 }
endPoint = { x: line.x2, y: line.y2 }
} else {
startPoint = { x: line.x2, y: line.y2 }
endPoint = { x: line.x1, y: line.y1 }
}
}
line.startPoint = startPoint
line.endPoint = endPoint
})
polygonLines.forEach((line) => {
line.set({ strokeWidth: 10 })
canvas.add(line)
})
canvas.renderAll()
polygonLines.forEach((line) => {
const intersections = []
innerLines.forEach((innerLine) => { innerLines.forEach((innerLine) => {
let newLine1, newLine2
if (isPointOnLine(line, innerLine.startPoint)) { if (isPointOnLine(line, innerLine.startPoint)) {
// 해당 line을 startPoint로 나눈 line2개를 canvas에 추가 하고 기존 line을 제거한다. if (isSamePoint(line.startPoint, innerLine.startPoint) || isSamePoint(line.endPoint, innerLine.startPoint)) {
newLine1 = new QLine([line.x1, line.y1, innerLine.startPoint.x, innerLine.startPoint.y], { return
fontSize: polygon.fontSize,
stroke: 'black',
strokeWidth: 3,
})
newLine2 = new QLine([innerLine.startPoint.x, innerLine.startPoint.y, line.x2, line.y2], {
fontSize: polygon.fontSize,
stroke: 'black',
strokeWidth: 3,
})
delIndexs.push(polygonLines.indexOf(line))
canvas.remove(polygonLines[polygonLines.indexOf(line)])
if (newLine1.length / 10 > 10) {
newLines.push(newLine1)
}
if (newLine2.length / 10 > 10) {
newLines.push(newLine2)
} }
intersections.push(innerLine.startPoint)
} }
if (isPointOnLine(line, innerLine.endPoint)) { if (isPointOnLine(line, innerLine.endPoint)) {
newLine1 = new QLine([line.x1, line.y1, innerLine.endPoint.x, innerLine.endPoint.y], { if (isSamePoint(line.startPoint, innerLine.endPoint) || isSamePoint(line.endPoint, innerLine.endPoint)) {
fontSize: polygon.fontSize, return
stroke: 'black',
strokeWidth: 3,
})
newLine2 = new QLine([innerLine.endPoint.x, innerLine.endPoint.y, line.x2, line.y2], {
fontSize: polygon.fontSize,
stroke: 'black',
strokeWidth: 3,
})
delIndexs.push(polygonLines.indexOf(line))
canvas.remove(polygonLines[polygonLines.indexOf(line)])
if (newLine1.length / 10 > 10) {
newLines.push(newLine1)
}
if (newLine2.length / 10 > 10) {
newLines.push(newLine2)
} }
intersections.push(innerLine.endPoint)
} }
}) })
line.set({ intersections })
}) })
polygonLines = polygonLines.filter((line) => !delIndexs.includes(line.tempIndex))
const divideLines = polygonLines.filter((line) => line.intersections.length > 0)
let newLines = []
divideLines.forEach((line) => {
const { intersections, startPoint, endPoint } = line
if (intersections.length === 1) {
// 한 점만 교차하는 경우
const newLinePoint1 = [line.x1, line.y1, intersections[0].x, intersections[0].y]
const newLinePoint2 = [intersections[0].x, intersections[0].y, line.x2, line.y2]
const newLine1 = new QLine(newLinePoint1, {
stroke: 'blue',
strokeWidth: 3,
fontSize: polygon.fontSize,
attributes: line.attributes,
})
const newLine2 = new QLine(newLinePoint2, {
stroke: 'blue',
strokeWidth: 3,
fontSize: polygon.fontSize,
attributes: line.attributes,
})
newLine1.attributes = {
...line.attributes,
planeSize: Math.round(Math.sqrt(Math.pow(newLine1.x1 - newLine1.x2, 2) + Math.pow(newLine1.y1 - newLine1.y2, 2))) * 10,
actualSize: Math.round(Math.sqrt(Math.pow(newLine1.x1 - newLine1.x2, 2) + Math.pow(newLine1.y1 - newLine1.y2, 2))) * 10,
}
newLine2.attributes = {
...line.attributes,
planeSize: Math.round(Math.sqrt(Math.pow(newLine2.x1 - newLine2.x2, 2) + Math.pow(newLine2.y1 - newLine2.y2, 2))) * 10,
actualSize: Math.round(Math.sqrt(Math.pow(newLine2.x1 - newLine2.x2, 2) + Math.pow(newLine2.y1 - newLine2.y2, 2))) * 10,
}
newLines.push(newLine1)
newLines.push(newLine2)
} else {
// 두 점 이상 교차하는 경우
//1. intersections중에 startPoint와 가장 가까운 점을 찾는다.
//2. 가장 가까운 점을 기준으로 line을 나눈다.
let currentPoint = startPoint
while (intersections.length !== 0) {
const minDistancePoint = findAndRemoveClosestPoint(currentPoint, intersections)
const newLinePoint = [currentPoint.x, currentPoint.y, minDistancePoint.x, minDistancePoint.y]
const newLine = new QLine(newLinePoint, {
stroke: 'blue',
strokeWidth: 3,
fontSize: polygon.fontSize,
attributes: line.attributes,
})
newLine.attributes = {
...line.attributes,
planeSize: Math.round(Math.sqrt(Math.pow(newLine.x1 - newLine.x2, 2) + Math.pow(newLine.y1 - newLine.y2, 2))) * 10,
actualSize: Math.round(Math.sqrt(Math.pow(newLine.x1 - newLine.x2, 2) + Math.pow(newLine.y1 - newLine.y2, 2))) * 10,
}
newLines.push(newLine)
currentPoint = minDistancePoint
}
const newLinePoint = [currentPoint.x, currentPoint.y, endPoint.x, endPoint.y]
const newLine = new QLine(newLinePoint, {
stroke: 'blue',
strokeWidth: 3,
fontSize: polygon.fontSize,
attributes: line.attributes,
})
newLine.attributes = {
...line.attributes,
planeSize: Math.round(Math.sqrt(Math.pow(newLine.x1 - newLine.x2, 2) + Math.pow(newLine.y1 - newLine.y2, 2))) * 10,
actualSize: Math.round(Math.sqrt(Math.pow(newLine.x1 - newLine.x2, 2) + Math.pow(newLine.y1 - newLine.y2, 2))) * 10,
}
newLines.push(newLine)
}
})
//polygonLines에서 divideLines를 제거하고 newLines를 추가한다.
polygonLines = polygonLines.filter((line) => !divideLines.includes(line))
polygonLines = [...polygonLines, ...newLines] polygonLines = [...polygonLines, ...newLines]
const allLines = [...polygonLines, ...innerLines] const allLines = [...polygonLines, ...innerLines]
@ -583,6 +853,9 @@ export const usePolygon = () => {
}) })
polygonLines.forEach((line) => { polygonLines.forEach((line) => {
line.set({ strokeWidth: 5, stroke: 'green' })
canvas.add(line)
canvas.renderAll()
const startPoint = line.startPoint // 시작점 const startPoint = line.startPoint // 시작점
let arrivalPoint = line.endPoint // 도착점 let arrivalPoint = line.endPoint // 도착점
@ -646,17 +919,18 @@ export const usePolygon = () => {
roofPoints.push(currentPoint) roofPoints.push(currentPoint)
cnt++ cnt++
if (cnt > 100) { if (cnt > 100) {
throw new Error('무한루프') break
} }
} }
roofs.push(roofPoints) roofs.push(roofPoints)
canvas.remove(line)
canvas.renderAll()
}) })
const newRoofs = removeDuplicatePolygons(roofs) const newRoofs = removeDuplicatePolygons(roofs.filter((roof) => roof.length < 100))
newRoofs.forEach((roofPoint, index) => { newRoofs.forEach((roofPoint, index) => {
let defense, pitch let defense, pitch
const polygonLines = [...polygon.lines]
let representLines = [] let representLines = []
let representLine let representLine
@ -728,7 +1002,7 @@ export const usePolygon = () => {
//allLines중 생성된 roof와 관련있는 line을 찾는다. //allLines중 생성된 roof와 관련있는 line을 찾는다.
roof.lines = [...polygon.lines, ...polygon.innerLines].filter((line) => { roof.lines = [...polygonLines, ...polygon.innerLines].filter((line) => {
let startFlag = false let startFlag = false
let endFlag = false let endFlag = false
const startPoint = line.startPoint const startPoint = line.startPoint

View File

@ -1,22 +0,0 @@
'use server'
import path from 'path'
import multer from 'multer'
export const upload = (files) => {
console.log(files)
const storage = multer.diskStorage({
destination: (req, file, callback) => {
const extension = path.extname(file.originalname)
const basename = path.basename(file.originalname, extension)
callback(null, `/public/upload/${basename}-${Date.now()}${extension}`)
},
filename: (req, file, callback) => {
callback(null, `${file.fieldname}-${Date.now()}${path.extname(file.originalname)}`)
}
})
const test = multer({
storage: storage
}).array(files.name, 5)
console.log(test)
}

View File

@ -1,41 +0,0 @@
'use server'
import { getSession } from './authActions'
const { PrismaClient } = require('@prisma/client')
const prisma = new PrismaClient()
export async function getUserByIdAndPassword({ userId, password }) {
return prisma.m_USER.findFirst({
where: {
USER_ID: userId,
PASSWORD: password,
},
})
}
export async function getUser(userId) {
return prisma.m_USER.findUnique({
where: {
user_id: userId,
},
})
}
export async function getUsers() {
return prisma.m_USER.findMany({
where: {
// USER_ID: 'daiwajoho01',
USER_ID: { in: ['daiwajoho01', 'daiwajoho', 'daiwabutsuryu'] },
},
})
}
export async function checkSession() {
const session = await getSession()
return {
session,
}
}

View File

@ -616,9 +616,9 @@
"stuff.planReqPopup.title": "設計依頼のインポート", "stuff.planReqPopup.title": "設計依頼のインポート",
"stuff.temp.subTitle": "商品情報", "stuff.temp.subTitle": "商品情報",
"stuff.temp.subTitle2": "作図", "stuff.temp.subTitle2": "作図",
"stuff.detail.header.message1": "存在しないものです。", "stuff.detail.header.notExistObjectNo": "存在しないものです。",
"stuff.detail.header.message2": "商品番号がコピーされました。", "stuff.detail.header.successCopy": "商品番号がコピーされました。",
"stuff.detail.header.message3": "存在しないものです。", "stuff.detail.header.failCopy": "存在しないものです。",
"stuff.detail.header.objectNo": "商品番号のコピーに失敗しました。", "stuff.detail.header.objectNo": "商品番号のコピーに失敗しました。",
"stuff.detail.header.specificationConfirmDate": "仕様拡張日", "stuff.detail.header.specificationConfirmDate": "仕様拡張日",
"stuff.detail.header.lastEditDatetime": "更新日時", "stuff.detail.header.lastEditDatetime": "更新日時",
@ -694,7 +694,8 @@
"stuff.planReqPopup.search.period": "期間検索", "stuff.planReqPopup.search.period": "期間検索",
"stuff.planReqPopup.search.schDateGbnS": "提出日", "stuff.planReqPopup.search.schDateGbnS": "提出日",
"stuff.planReqPopup.search.schDateGbnR": "受付日", "stuff.planReqPopup.search.schDateGbnR": "受付日",
"stuff.planReqPopup.error.message1": "設計依頼を選択してください。", "stuff.planReqPopup.error.message1": "設計依頼を選択してください.",
"stuff.planReqPopup.error.message2": "販売店を選択してください.",
"stuff.search.title": "物件状況", "stuff.search.title": "物件状況",
"stuff.search.btn1": "新規 物件 登録", "stuff.search.btn1": "新規 物件 登録",
"stuff.search.btn2": "照会", "stuff.search.btn2": "照会",
@ -830,10 +831,10 @@
"estimate.detail.header.fileList2": "添付ファイル一覧", "estimate.detail.header.fileList2": "添付ファイル一覧",
"estimate.detail.header.specialEstimate": "見積もりの具体的な", "estimate.detail.header.specialEstimate": "見積もりの具体的な",
"estimate.detail.header.specialEstimateProductInfo": "製品情報", "estimate.detail.header.specialEstimateProductInfo": "製品情報",
"estimate.detail.sepcialEstimateProductInfo.totPcs": "数量 (PCS)", "estimate.detail.sepcialEstimateProductInfo.totAmount": "数量 (PCS)",
"estimate.detail.sepcialEstimateProductInfo.vol": "容量 (Kw)", "estimate.detail.sepcialEstimateProductInfo.totVolKw": "容量 (Kw)",
"estimate.detail.sepcialEstimateProductInfo.netAmt": "供給価格", "estimate.detail.sepcialEstimateProductInfo.supplyPrice": "供給価格",
"estimate.detail.sepcialEstimateProductInfo.vat": "付加価値税 (10%)", "estimate.detail.sepcialEstimateProductInfo.vatPrice": "付加価値税 (10%)",
"estimate.detail.sepcialEstimateProductInfo.totPrice": "総額", "estimate.detail.sepcialEstimateProductInfo.totPrice": "総額",
"estimate.detail.sepcialEstimateProductInfo.pkgUnitPrice": "住宅PKG単価 (W)", "estimate.detail.sepcialEstimateProductInfo.pkgUnitPrice": "住宅PKG単価 (W)",
"estimate.detail.sepcialEstimateProductInfo.pkgWeight": "PKG容量 (Kw)", "estimate.detail.sepcialEstimateProductInfo.pkgWeight": "PKG容量 (Kw)",
@ -847,15 +848,15 @@
"estimate.detail.showPrice.description2": "追加, 変更資材", "estimate.detail.showPrice.description2": "追加, 変更資材",
"estimate.detail.showPrice.description3": "添付必須", "estimate.detail.showPrice.description3": "添付必須",
"estimate.detail.showPrice.description4": "クリックして製品の特異性を確認する", "estimate.detail.showPrice.description4": "クリックして製品の特異性を確認する",
"estimate.detail.showPrice.btn2": "製品を追加", "estimate.detail.showPrice.addItem": "製品を追加",
"estimate.detail.showPrice.btn3": "製品削除", "estimate.detail.showPrice.delItem": "製品削除",
"estimate.detail.itemTableHeader.col1": "アイテム", "estimate.detail.itemTableHeader.dispOrder": "アイテム",
"estimate.detail.itemTableHeader.col2": "品番", "estimate.detail.itemTableHeader.itemId": "品番",
"estimate.detail.itemTableHeader.col3": "型板", "estimate.detail.itemTableHeader.itemNo": "型板",
"estimate.detail.itemTableHeader.col4": "数量", "estimate.detail.itemTableHeader.amount": "数量",
"estimate.detail.itemTableHeader.col5": "単位", "estimate.detail.itemTableHeader.unit": "単位",
"estimate.detail.itemTableHeader.col6": "単価", "estimate.detail.itemTableHeader.salePrice": "単価",
"estimate.detail.itemTableHeader.col7": "金額 (税別別)", "estimate.detail.itemTableHeader.saleTotPrice": "金額 (税別別)",
"estimate.detail.docPopup.title": "ドキュメントダウンロードオプションの設定", "estimate.detail.docPopup.title": "ドキュメントダウンロードオプションの設定",
"estimate.detail.docPopup.explane": "ダウンロードする文書のオプションを選択したら、 [文書のダウンロード]ボタンをクリックします.", "estimate.detail.docPopup.explane": "ダウンロードする文書のオプションを選択したら、 [文書のダウンロード]ボタンをクリックします.",
"estimate.detail.docPopup.schUnitPriceFlg": "ダウンロードファイル", "estimate.detail.docPopup.schUnitPriceFlg": "ダウンロードファイル",
@ -874,6 +875,8 @@
"estimate.detail.docPopup.schDrawingFlg.schDrawingFlg1": "含まない", "estimate.detail.docPopup.schDrawingFlg.schDrawingFlg1": "含まない",
"estimate.detail.docPopup.close": "閉じる", "estimate.detail.docPopup.close": "閉じる",
"estimate.detail.docPopup.docDownload": "文書のダウンロード", "estimate.detail.docPopup.docDownload": "文書のダウンロード",
"estimate.detail.productFeaturesPopup.title": "製品特異事項",
"estimate.detail.productFeaturesPopup.close": "閉じる",
"estimate.detail.save.alertMsg": "保存されている見積書で製品を変更した場合、図面や回路には反映されません.", "estimate.detail.save.alertMsg": "保存されている見積書で製品を変更した場合、図面や回路には反映されません.",
"estimate.detail.save.requiredMsg": "ファイル添付が必須のアイテムがあります。ファイルを添付するか、後日添付をチェックしてください.", "estimate.detail.save.requiredMsg": "ファイル添付が必須のアイテムがあります。ファイルを添付するか、後日添付をチェックしてください.",
"estimate.detail.reset.confirmMsg": "保存した見積書情報が初期化され、図面情報が反映されます。本当に初期化しますか?", "estimate.detail.reset.confirmMsg": "保存した見積書情報が初期化され、図面情報が反映されます。本当に初期化しますか?",

View File

@ -400,6 +400,9 @@
"modal.module.circuit.number.edit": "모듈 일괄 회로 번호 변경", "modal.module.circuit.number.edit": "모듈 일괄 회로 번호 변경",
"modal.module.circuit.number.edit.info": "회로 번호를 입력해주세요.", "modal.module.circuit.number.edit.info": "회로 번호를 입력해주세요.",
"modal.module.circuit.number": "회로 번호", "modal.module.circuit.number": "회로 번호",
"modal.line.property.change": "변경할 속성을 선택해 주세요.",
"modal.line.property.change.unselect": "변경할 라인을 선택해 주세요.",
"modal.line.property.change.confirm": "속성을 변경하시겠습니까?",
"common.message.no.data": "No data", "common.message.no.data": "No data",
"common.message.no.dataDown": "No data to download", "common.message.no.dataDown": "No data to download",
"common.message.noData": "No data to display", "common.message.noData": "No data to display",
@ -495,6 +498,7 @@
"commons.east": "동", "commons.east": "동",
"commons.south": "남", "commons.south": "남",
"commons.north": "북", "commons.north": "북",
"commons.none": "선택안함",
"font.style.normal": "보통", "font.style.normal": "보통",
"font.style.italic": "기울임꼴", "font.style.italic": "기울임꼴",
"font.style.bold": "굵게", "font.style.bold": "굵게",
@ -622,9 +626,9 @@
"stuff.planReqPopup.title": "설계의뢰 불러오기", "stuff.planReqPopup.title": "설계의뢰 불러오기",
"stuff.temp.subTitle": "물건정보", "stuff.temp.subTitle": "물건정보",
"stuff.temp.subTitle2": "도면작성", "stuff.temp.subTitle2": "도면작성",
"stuff.detail.header.message1": "존재하지 않는 물건입니다.", "stuff.detail.header.notExistObjectNo": "존재하지 않는 물건입니다.",
"stuff.detail.header.message2": "물건번호가 복사되었습니다.", "stuff.detail.header.successCopy": "물건번호가 복사되었습니다.",
"stuff.detail.header.message3": "물건번호 복사에 실패했습니다.", "stuff.detail.header.failCopy": "물건번호 복사에 실패했습니다.",
"stuff.detail.header.objectNo": "물건번호", "stuff.detail.header.objectNo": "물건번호",
"stuff.detail.header.specificationConfirmDate": "사양확장일", "stuff.detail.header.specificationConfirmDate": "사양확장일",
"stuff.detail.header.lastEditDatetime": "갱신일시", "stuff.detail.header.lastEditDatetime": "갱신일시",
@ -701,6 +705,7 @@
"stuff.planReqPopup.search.schDateGbnS": "제출일", "stuff.planReqPopup.search.schDateGbnS": "제출일",
"stuff.planReqPopup.search.schDateGbnR": "접수일", "stuff.planReqPopup.search.schDateGbnR": "접수일",
"stuff.planReqPopup.error.message1": "설계의뢰를 선택해주세요.", "stuff.planReqPopup.error.message1": "설계의뢰를 선택해주세요.",
"stuff.planReqPopup.error.message2": "판매점을 선택해주세요.",
"stuff.search.title": "물건현황", "stuff.search.title": "물건현황",
"stuff.search.btn1": "신규 물건 등록", "stuff.search.btn1": "신규 물건 등록",
"stuff.search.btn2": "조회", "stuff.search.btn2": "조회",
@ -836,10 +841,10 @@
"estimate.detail.header.fileList2": "첨부파일 목록", "estimate.detail.header.fileList2": "첨부파일 목록",
"estimate.detail.header.specialEstimate": "견적특이사항", "estimate.detail.header.specialEstimate": "견적특이사항",
"estimate.detail.header.specialEstimateProductInfo": "제품정보", "estimate.detail.header.specialEstimateProductInfo": "제품정보",
"estimate.detail.sepcialEstimateProductInfo.totPcs": "수량 (PCS)", "estimate.detail.sepcialEstimateProductInfo.totAmount": "수량 (PCS)",
"estimate.detail.sepcialEstimateProductInfo.vol": "용량 (Kw)", "estimate.detail.sepcialEstimateProductInfo.totVolKw": "용량 (Kw)",
"estimate.detail.sepcialEstimateProductInfo.netAmt": "공급가액", "estimate.detail.sepcialEstimateProductInfo.supplyPrice": "공급가액",
"estimate.detail.sepcialEstimateProductInfo.vat": "부가세 (10)", "estimate.detail.sepcialEstimateProductInfo.vatPrice": "부가세 (10)",
"estimate.detail.sepcialEstimateProductInfo.totPrice": "총액", "estimate.detail.sepcialEstimateProductInfo.totPrice": "총액",
"estimate.detail.sepcialEstimateProductInfo.pkgUnitPrice": "주택PKG단가 (W)", "estimate.detail.sepcialEstimateProductInfo.pkgUnitPrice": "주택PKG단가 (W)",
"estimate.detail.sepcialEstimateProductInfo.pkgWeight": "PKG 용량 (Kw)", "estimate.detail.sepcialEstimateProductInfo.pkgWeight": "PKG 용량 (Kw)",
@ -853,15 +858,15 @@
"estimate.detail.showPrice.description2": "추가, 변경 자재", "estimate.detail.showPrice.description2": "추가, 변경 자재",
"estimate.detail.showPrice.description3": "첨부필수", "estimate.detail.showPrice.description3": "첨부필수",
"estimate.detail.showPrice.description4": "클릭하여 제품 특이사항 확인", "estimate.detail.showPrice.description4": "클릭하여 제품 특이사항 확인",
"estimate.detail.showPrice.btn2": "제품추가", "estimate.detail.showPrice.addItem": "제품추가",
"estimate.detail.showPrice.btn3": "제품삭제", "estimate.detail.showPrice.delItem": "제품삭제",
"estimate.detail.itemTableHeader.col1": "Item", "estimate.detail.itemTableHeader.dispOrder": "Item",
"estimate.detail.itemTableHeader.col2": "품번", "estimate.detail.itemTableHeader.itemId": "품번",
"estimate.detail.itemTableHeader.col3": "형명", "estimate.detail.itemTableHeader.itemNo": "형명",
"estimate.detail.itemTableHeader.col4": "수량", "estimate.detail.itemTableHeader.amount": "수량",
"estimate.detail.itemTableHeader.col5": "단위", "estimate.detail.itemTableHeader.unit": "단위",
"estimate.detail.itemTableHeader.col6": "단가", "estimate.detail.itemTableHeader.salePrice": "단가",
"estimate.detail.itemTableHeader.col7": "금액(부가세별도)", "estimate.detail.itemTableHeader.saleTotPrice": "금액(부가세별도)",
"estimate.detail.docPopup.title": "문서다운로드 옵션설정", "estimate.detail.docPopup.title": "문서다운로드 옵션설정",
"estimate.detail.docPopup.explane": "다운로드할 문서 옵션을 선택한 후 문서 다운로드 버튼을 클릭합니다.", "estimate.detail.docPopup.explane": "다운로드할 문서 옵션을 선택한 후 문서 다운로드 버튼을 클릭합니다.",
"estimate.detail.docPopup.schUnitPriceFlg": "다운로드 파일", "estimate.detail.docPopup.schUnitPriceFlg": "다운로드 파일",
@ -880,6 +885,8 @@
"estimate.detail.docPopup.schDrawingFlg.schDrawingFlg1": "미포함", "estimate.detail.docPopup.schDrawingFlg.schDrawingFlg1": "미포함",
"estimate.detail.docPopup.close": "닫기", "estimate.detail.docPopup.close": "닫기",
"estimate.detail.docPopup.docDownload": "문서 다운로드", "estimate.detail.docPopup.docDownload": "문서 다운로드",
"estimate.detail.productFeaturesPopup.title": "제품특이사항",
"estimate.detail.productFeaturesPopup.close": "닫기",
"estimate.detail.save.alertMsg": "저장되었습니다. 견적서에서 제품을 변경할 경우, 도면 및 회로에 반영되지 않습니다.", "estimate.detail.save.alertMsg": "저장되었습니다. 견적서에서 제품을 변경할 경우, 도면 및 회로에 반영되지 않습니다.",
"estimate.detail.save.requiredMsg": "파일첨부가 필수인 아이템이 있습니다. 파일을 첨부하거나 후일첨부를 체크해주십시오.", "estimate.detail.save.requiredMsg": "파일첨부가 필수인 아이템이 있습니다. 파일을 첨부하거나 후일첨부를 체크해주십시오.",
"estimate.detail.reset.confirmMsg": "저장된 견적서 정보가 초기화되고, 도면정보가 반영됩니다. 정말로 초기화 하시겠습니까?", "estimate.detail.reset.confirmMsg": "저장된 견적서 정보가 초기화되고, 도면정보가 반영됩니다. 정말로 초기화 하시겠습니까?",

View File

@ -363,3 +363,24 @@ export const showAngleUnitSelector = selector({
return roofAngleSet === 'slope' ? '寸' : '°' return roofAngleSet === 'slope' ? '寸' : '°'
}, },
}) })
export const moduleSetupSurfaceState = atom({
key: 'moduleSetupSurfaceState',
default: [],
dangerouslyAllowMutability: true,
})
export const moduleInstSurfaceSelector = selector({
key: 'moduleInstSurfaceSelector',
get: ({ get, parentId }) => {
const moduleSetupSurfaceStateValue = get(moduleSetupSurfaceState)
return moduleSetupSurfaceStateValue.filter((obj) => obj.parentId === parentId)
},
})
//셀 그린 이후에 생성하는 state
export const moduleIsSetupState = atom({
key: 'moduleIsSetupState',
default: [],
dangerouslyAllowMutability: true,
})

View File

@ -11,10 +11,6 @@ export const dimensionLineSettingsState = atom({
default: { default: {
pixel: 1, pixel: 1,
color: '#000000', color: '#000000',
font: 'Arial',
fontColor: '#000000',
fontSize: 15,
fontStyle: 'normal',
}, },
}) })

View File

@ -0,0 +1,6 @@
import { atom } from 'recoil'
export const compasDegAtom = atom({
key: 'compasDegAtom',
default: 0,
})

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -792,7 +792,7 @@ export const rectToPolygon = (rect) => {
} }
//면형상 선택 클릭시 지붕 패턴 입히기 //면형상 선택 클릭시 지붕 패턴 입히기
export function setSurfaceShapePattern(polygon, mode = 'onlyBorder') { export function setSurfaceShapePattern(polygon, mode = 'onlyBorder', trestleMode = false) {
const ratio = window.devicePixelRatio || 1 const ratio = window.devicePixelRatio || 1
let width = 265 / 10 let width = 265 / 10
@ -820,6 +820,12 @@ export function setSurfaceShapePattern(polygon, mode = 'onlyBorder') {
ctx.lineWidth = mode === 'allPainted' ? 1 : 0.4 ctx.lineWidth = mode === 'allPainted' ? 1 : 0.4
ctx.fillStyle = mode === 'allPainted' ? 'rgba(0, 159, 64, 0.7)' : 'white' ctx.fillStyle = mode === 'allPainted' ? 'rgba(0, 159, 64, 0.7)' : 'white'
if (trestleMode) {
ctx.strokeStyle = 'black'
ctx.lineWidth = 0.2
ctx.fillStyle = 'rgba(0, 0, 0, 0.1)'
}
if (polygon.direction === 'east' || polygon.direction === 'west') { if (polygon.direction === 'east' || polygon.direction === 'west') {
offset = roofStyle === 1 ? 0 : patternSize.height / 2 offset = roofStyle === 1 ? 0 : patternSize.height / 2
for (let col = 0; col <= cols; col++) { for (let col = 0; col <= cols; col++) {
@ -830,7 +836,7 @@ export function setSurfaceShapePattern(polygon, mode = 'onlyBorder') {
ctx.moveTo(x, yStart) // 선 시작점 ctx.moveTo(x, yStart) // 선 시작점
ctx.lineTo(x, yEnd) // 선 끝점 ctx.lineTo(x, yEnd) // 선 끝점
ctx.stroke() ctx.stroke()
if (mode === 'allPainted') { if (mode === 'allPainted' || trestleMode) {
ctx.fillRect(x, yStart, patternSize.width, yEnd - yStart) ctx.fillRect(x, yStart, patternSize.width, yEnd - yStart)
} }
@ -842,7 +848,7 @@ export function setSurfaceShapePattern(polygon, mode = 'onlyBorder') {
ctx.moveTo(xStart, y) // 선 시작점 ctx.moveTo(xStart, y) // 선 시작점
ctx.lineTo(xEnd, y) // 선 끝점 ctx.lineTo(xEnd, y) // 선 끝점
ctx.stroke() ctx.stroke()
if (mode === 'allPainted') { if (mode === 'allPainted' || trestleMode) {
ctx.fillRect(xStart, y, xEnd - xStart, patternSize.height) ctx.fillRect(xStart, y, xEnd - xStart, patternSize.height)
} }
} }
@ -855,7 +861,7 @@ export function setSurfaceShapePattern(polygon, mode = 'onlyBorder') {
ctx.moveTo(0, y) // 선 시작점 ctx.moveTo(0, y) // 선 시작점
ctx.lineTo(patternSourceCanvas.width, y) // 선 끝점 ctx.lineTo(patternSourceCanvas.width, y) // 선 끝점
ctx.stroke() ctx.stroke()
if (mode === 'allPainted') { if (mode === 'allPainted' || trestleMode) {
ctx.fillRect(0, y, patternSourceCanvas.width, patternSize.height) ctx.fillRect(0, y, patternSourceCanvas.width, patternSize.height)
} }
@ -868,7 +874,7 @@ export function setSurfaceShapePattern(polygon, mode = 'onlyBorder') {
ctx.moveTo(x, yStart) // 선 시작점 ctx.moveTo(x, yStart) // 선 시작점
ctx.lineTo(x, yEnd) // 선 끝점 ctx.lineTo(x, yEnd) // 선 끝점
ctx.stroke() ctx.stroke()
if (mode === 'allPainted') { if (mode === 'allPainted' || trestleMode) {
ctx.fillRect(x, yStart, patternSize.width, yEnd - yStart) ctx.fillRect(x, yStart, patternSize.width, yEnd - yStart)
} }
} }
@ -954,3 +960,44 @@ export const getAllRelatedObjects = (id, canvas) => {
return result return result
} }
// 모듈,회로 구성에서 사용하는 degree 범위 별 값
export const getDegreeInOrientation = (degree) => {
if (degree >= 352) {
return 0
}
if (degree % 15 === 0) return degree
let value = Math.floor(degree / 15)
const remain = ((degree / 15) % 1).toFixed(5)
if (remain > 0.4) {
value++
}
return value * 15
}
export function findAndRemoveClosestPoint(targetPoint, points) {
if (points.length === 0) {
return null
}
let closestPoint = points[0]
let closestDistance = distanceBetweenPoints(targetPoint, points[0])
let closestIndex = 0
for (let i = 1; i < points.length; i++) {
const distance = distanceBetweenPoints(targetPoint, points[i])
if (distance < closestDistance) {
closestDistance = distance
closestPoint = points[i]
closestIndex = i
}
}
// Remove the closest point from the array
points.splice(closestIndex, 1)
return closestPoint
}

View File

@ -2443,6 +2443,10 @@ const changeGableRoof = (currentRoof, canvas) => {
hip2.attributes.planeSize = Math.round(Math.sqrt(Math.pow(hip2.x1 - hip2.x2, 2) + Math.pow(hip2.y1 - hip2.y2, 2))) * 10 hip2.attributes.planeSize = Math.round(Math.sqrt(Math.pow(hip2.x1 - hip2.x2, 2) + Math.pow(hip2.y1 - hip2.y2, 2))) * 10
hip2.attributes.actualSize = Math.round(Math.sqrt(Math.pow(hip2.attributes.planeSize, 2) + Math.pow(hip2Height, 2))) hip2.attributes.actualSize = Math.round(Math.sqrt(Math.pow(hip2.attributes.planeSize, 2) + Math.pow(hip2Height, 2)))
// roof.innerLines.push(hip2) // roof.innerLines.push(hip2)
hip1.set({ visible: false })
hip1.setViewLengthText(false)
hip2.set({ visible: false })
hip2.setViewLengthText(false)
canvas?.renderAll() canvas?.renderAll()
} }
} }
@ -2925,6 +2929,13 @@ const changeJerkInHeadRoof = (currentRoof, canvas) => {
hip2.attributes.actualSize = Math.round(Math.sqrt(Math.pow(hip2.attributes.planeSize, 2) + Math.pow(hip2Height, 2))) hip2.attributes.actualSize = Math.round(Math.sqrt(Math.pow(hip2.attributes.planeSize, 2) + Math.pow(hip2Height, 2)))
canvas?.add(hip2) canvas?.add(hip2)
// roof.innerLines.push(hip2) // roof.innerLines.push(hip2)
hip1.set({ visible: false })
hip1.setViewLengthText(false)
gable3.set({ visible: false })
gable3.setViewLengthText(false)
hip2.set({ visible: false })
hip2.setViewLengthText(false)
} }
} }
} }