Merge branch 'dev' into feature/test-jy
# Conflicts: # src/util/qpolygon-utils.js
This commit is contained in:
commit
9752925cd1
@ -28,3 +28,4 @@ Allpainted : allPainted
|
||||
치수선: dimensionLine
|
||||
복도치수: planeSize
|
||||
실제치수: actualSize
|
||||
모듈설치면: moduleSetupSurface
|
||||
4
public/static/images/canvas/side_icon10.svg
Normal file
4
public/static/images/canvas/side_icon10.svg
Normal 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 |
20
src/app/management/ManagementProvider.js
Normal file
20
src/app/management/ManagementProvider.js
Normal 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
|
||||
7
src/app/management/layout.js
Normal file
7
src/app/management/layout.js
Normal file
@ -0,0 +1,7 @@
|
||||
'use client'
|
||||
|
||||
import ManagementProvider from './ManagementProvider'
|
||||
|
||||
export default function ManagementLayout({ children }) {
|
||||
return <ManagementProvider>{children}</ManagementProvider>
|
||||
}
|
||||
@ -61,6 +61,8 @@ export const LINE_TYPE = {
|
||||
DEFAULT: 'default',
|
||||
EAVES: 'eaves',
|
||||
GABLE: 'gable',
|
||||
GABLE_LEFT: 'gableLeft', //케라바 왼쪽
|
||||
GABLE_RIGHT: 'gableRight', //케라바 오른쪽
|
||||
WALL: 'wall',
|
||||
HIPANDGABLE: 'hipAndGable',
|
||||
JERKINHEAD: 'jerkinhead',
|
||||
@ -74,30 +76,20 @@ export const LINE_TYPE = {
|
||||
HIP: 'hip',
|
||||
RIDGE: 'ridge',
|
||||
GABLE: 'gable',
|
||||
VALLEY: 'valley',
|
||||
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 = {
|
||||
OPENING: 'opening',
|
||||
@ -119,6 +111,7 @@ export const POLYGON_TYPE = {
|
||||
ROOF: 'roof',
|
||||
WALL: 'wall',
|
||||
TRESTLE: 'trestle',
|
||||
MODULE_SETUP_SURFACE: 'moduleSetupSurface',
|
||||
}
|
||||
|
||||
export const SAVE_KEY = [
|
||||
@ -161,6 +154,13 @@ export const SAVE_KEY = [
|
||||
'groupId',
|
||||
'planeSize',
|
||||
'actualSize',
|
||||
'surfaceId',
|
||||
'lines',
|
||||
'offset',
|
||||
'arrow',
|
||||
'surfaceCompass',
|
||||
'moduleCompass',
|
||||
'isFixed',
|
||||
]
|
||||
|
||||
export const OBJECT_PROTOTYPE = [fabric.Line.prototype, fabric.Polygon.prototype, fabric.Triangle.prototype]
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
'use client'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { useEffect, useState, useContext } from 'react'
|
||||
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||
import { sessionStore } from '@/store/commonAtom'
|
||||
import { useAxios } from '@/hooks/useAxios'
|
||||
import { globalLocaleStore } from '@/store/localeAtom'
|
||||
import MainContents from './main/MainContents'
|
||||
@ -12,8 +11,10 @@ import { stuffSearchState } from '@/store/stuffAtom'
|
||||
import '@/styles/contents.scss'
|
||||
import ChangePasswordPop from './main/ChangePasswordPop'
|
||||
import { searchState } from '@/store/boardAtom'
|
||||
import { SessionContext } from '@/app/SessionProvider'
|
||||
|
||||
export default function MainPage() {
|
||||
const sessionState = useRecoilValue(sessionStore)
|
||||
const { session } = useContext(SessionContext)
|
||||
|
||||
const globalLocaleState = useRecoilValue(globalLocaleStore)
|
||||
|
||||
@ -33,14 +34,14 @@ export default function MainPage() {
|
||||
const [searchForm, setSearchForm] = useRecoilState(searchState)
|
||||
|
||||
useEffect(() => {
|
||||
if (sessionState.pwdInitYn === 'Y') {
|
||||
if (session.pwdInitYn === 'Y') {
|
||||
fetchObjectList()
|
||||
}
|
||||
}, [sessionState])
|
||||
}, [session])
|
||||
|
||||
const fetchObjectList = async () => {
|
||||
try {
|
||||
const apiUrl = `/api/main-page/object/${sessionState?.storeId}/list`
|
||||
const apiUrl = `/api/main-page/object/${session?.storeId}/list`
|
||||
await promiseGet({
|
||||
url: apiUrl,
|
||||
}).then((res) => {
|
||||
@ -95,7 +96,7 @@ export default function MainPage() {
|
||||
|
||||
return (
|
||||
<>
|
||||
{(sessionState?.pwdInitYn !== 'N' && (
|
||||
{(session?.pwdInitYn !== 'N' && (
|
||||
<>
|
||||
<div className="background-bord"></div>
|
||||
<div className="main-contents">
|
||||
|
||||
@ -83,7 +83,7 @@ export default function Login() {
|
||||
///////////////////////////////////////////////////////////
|
||||
// 임시 로그인 처리
|
||||
setSession({
|
||||
userId: 'NEW016610',
|
||||
userId: 'NEW0166102',
|
||||
saleStoreId: null,
|
||||
name: null,
|
||||
mail: null,
|
||||
@ -101,7 +101,7 @@ export default function Login() {
|
||||
custCd: '100000',
|
||||
})
|
||||
setSessionState({
|
||||
userId: 'NEW016610',
|
||||
userId: 'NEW0166102',
|
||||
saleStoreId: null,
|
||||
name: null,
|
||||
mail: null,
|
||||
|
||||
@ -3,76 +3,69 @@ import QSelectBox from '@/components/common/select/QSelectBox'
|
||||
import { usePopup } from '@/hooks/usePopup'
|
||||
import { useState } from 'react'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||
import { fontSelector, globalFontAtom } from '@/store/fontAtom'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { contextPopupPositionState } from '@/store/popupAtom'
|
||||
|
||||
const fonts = [
|
||||
{ name: 'MS PGothic', value: 'MS PGothic' },
|
||||
{ name: '@Yu Gothic', value: '@Yu Gothic' },
|
||||
{ name: 'Yu Gothic', value: 'Yu Gothic' },
|
||||
{ name: '@Yu Gothic UI', value: '@Yu Gothic UI' },
|
||||
{ name: 'Yu Gothic UI', value: 'Yu Gothic UI' },
|
||||
{ id: 1, name: 'MS PGothic', value: 'MS PGothic' },
|
||||
{ id: 2, name: '@Yu Gothic', value: '@Yu Gothic' },
|
||||
{ id: 3, name: 'Yu Gothic', value: 'Yu Gothic' },
|
||||
{ id: 4, name: '@Yu Gothic UI', value: '@Yu Gothic UI' },
|
||||
{ id: 5, name: 'Yu Gothic UI', value: 'Yu Gothic UI' },
|
||||
3,
|
||||
]
|
||||
|
||||
const fontSizes = [
|
||||
...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) => {
|
||||
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 },
|
||||
{ name: 48, value: 48 },
|
||||
{ name: 72, value: 72 },
|
||||
{ id: 36, name: 36, value: 36 },
|
||||
{ id: 48, name: 48, value: 48 },
|
||||
{ id: 72, name: 72, value: 72 },
|
||||
]
|
||||
|
||||
export default function FontSetting(props) {
|
||||
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 { closePopup } = usePopup()
|
||||
const [globalFont, setGlobalFont] = useRecoilState(globalFontAtom)
|
||||
const currentFont = useRecoilValue(fontSelector(type))
|
||||
|
||||
const [selectedFont, setSelectedFont] = useState(currentFont.fontFamily)
|
||||
const [selectedFontWeight, setSelectedFontWeight] = useState(currentFont.fontWeight)
|
||||
const [selectedFontSize, setSelectedFontSize] = useState(currentFont.fontSize)
|
||||
const [selectedFontColor, setSelectedFontColor] = useState(currentFont.fontColor)
|
||||
const [selectedFont, setSelectedFont] = useState(font.fontFamily)
|
||||
const [selectedFontWeight, setSelectedFontWeight] = useState(font.fontWeight)
|
||||
const [selectedFontSize, setSelectedFontSize] = useState(font.fontSize)
|
||||
const [selectedFontColor, setSelectedFontColor] = useState(font.fontColor)
|
||||
const fontOptions = [
|
||||
{ name: getMessage('font.style.normal'), value: 'normal' },
|
||||
{ name: getMessage('font.style.italic'), value: 'italic' },
|
||||
{ id: 'normal', name: getMessage('font.style.normal'), value: 'normal' },
|
||||
{ id: 'italic', name: getMessage('font.style.italic'), value: 'italic' },
|
||||
{
|
||||
id: 'bold',
|
||||
name: getMessage('font.style.bold'),
|
||||
value: 'bold',
|
||||
},
|
||||
{ name: getMessage('font.style.bold.italic'), value: 'boldAndItalic' },
|
||||
{ id: 'boldAndItalic', name: getMessage('font.style.bold.italic'), value: 'boldAndItalic' },
|
||||
]
|
||||
const fontColors = [
|
||||
{ name: getMessage('color.black'), value: 'black' },
|
||||
{ name: getMessage('color.red'), value: 'red' },
|
||||
{ name: getMessage('color.blue'), value: 'blue' },
|
||||
{ name: getMessage('color.gray'), value: 'gray' },
|
||||
{ name: getMessage('color.yellow'), value: 'yellow' },
|
||||
{ name: getMessage('color.green'), value: 'green' },
|
||||
{ name: getMessage('color.pink'), value: 'pink' },
|
||||
{ name: getMessage('color.gold'), value: 'gold' },
|
||||
{ name: getMessage('color.darkblue'), value: 'darkblue' },
|
||||
{ id: 'black', name: getMessage('color.black'), value: 'black' },
|
||||
{ id: 'red', name: getMessage('color.red'), value: 'red' },
|
||||
{ id: 'blue', name: getMessage('color.blue'), value: 'blue' },
|
||||
{ id: 'gray', name: getMessage('color.gray'), value: 'gray' },
|
||||
{ id: 'yellow', name: getMessage('color.yellow'), value: 'yellow' },
|
||||
{ id: 'green', name: getMessage('color.green'), value: 'green' },
|
||||
{ id: 'pink', name: getMessage('color.pink'), value: 'pink' },
|
||||
{ id: 'gold', name: getMessage('color.gold'), value: 'gold' },
|
||||
{ id: 'darkblue', name: getMessage('color.darkblue'), value: 'darkblue' },
|
||||
]
|
||||
const handleSaveBtn = () => {
|
||||
setGlobalFont((prev) => {
|
||||
return {
|
||||
...prev,
|
||||
[type]: {
|
||||
fontFamily: selectedFont,
|
||||
fontSize: selectedFontSize,
|
||||
fontColor: selectedFontColor,
|
||||
fontWeight: selectedFontWeight,
|
||||
},
|
||||
}
|
||||
onSave({
|
||||
fontFamily: selectedFont,
|
||||
fontSize: selectedFontSize,
|
||||
fontColor: selectedFontColor,
|
||||
fontWeight: selectedFontWeight,
|
||||
})
|
||||
if (setIsShow) setIsShow(false)
|
||||
closePopup(id)
|
||||
closePopup(id, isConfig)
|
||||
}
|
||||
|
||||
return (
|
||||
@ -125,7 +118,8 @@ export default function FontSetting(props) {
|
||||
style={{
|
||||
fontFamily: selectedFont?.value ?? '',
|
||||
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',
|
||||
}}
|
||||
>
|
||||
|
||||
@ -18,8 +18,8 @@ export default function QSelectBox({ title = '', options, onChange, value, disab
|
||||
<div className={`sort-select ${openSelect ? 'active' : ''}`} onClick={disabled ? () => {} : () => setOpenSelect(!openSelect)}>
|
||||
<p>{selected}</p>
|
||||
<ul className="select-item-wrap">
|
||||
{options?.map((option) => (
|
||||
<li key={option.id} className="select-item" onClick={() => handleClickSelectOption(option)}>
|
||||
{options?.map((option, index) => (
|
||||
<li key={option.id || index} className="select-item" onClick={() => handleClickSelectOption(option)}>
|
||||
<button key={option.id + 'btn'}>{option.name}</button>
|
||||
</li>
|
||||
))}
|
||||
|
||||
@ -15,9 +15,12 @@ import { useCommonCode } from '@/hooks/common/useCommonCode'
|
||||
import { useEstimateController } from '@/hooks/floorPlan/estimate/useEstimateController'
|
||||
import { SessionContext } from '@/app/SessionProvider'
|
||||
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 }) {
|
||||
const [itemChangeYn, setItemChangeYn] = useState(false)
|
||||
const { session } = useContext(SessionContext)
|
||||
const [objectNo, setObjectNo] = useState('') //물건번호
|
||||
const [planNo, setPlanNo] = useState('') //플랜번호
|
||||
@ -27,15 +30,24 @@ export default function Estimate({ params }) {
|
||||
|
||||
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 [displayItemList, setDisplayItemList] = useState([])
|
||||
|
||||
//공통코드
|
||||
const { findCommonCode } = useCommonCode()
|
||||
const [honorificCodeList, setHonorificCodeList] = useState([]) //경칭 공통코드
|
||||
|
||||
const [storePriceList, setStorePriceList] = useState([]) //가격표시 option
|
||||
|
||||
const [tempPriceCd, setTempPriceCd] = useState('')
|
||||
|
||||
const [startDate, setStartDate] = useState(new Date())
|
||||
const singleDatePickerProps = {
|
||||
startDate,
|
||||
@ -45,9 +57,7 @@ export default function Estimate({ params }) {
|
||||
const objectRecoil = useRecoilValue(floorPlanObjectState)
|
||||
|
||||
//견적서 상세데이터
|
||||
const { state, setState } = useEstimateController(params.pid)
|
||||
|
||||
const [itemList, setItemList] = useState([])
|
||||
const { state, setState, addItem, handleEstimateFileDownload } = useEstimateController(params.pid)
|
||||
|
||||
//견적특이사항 List
|
||||
const [specialNoteList, setSpecialNoteList] = useState([])
|
||||
@ -75,6 +85,17 @@ export default function Estimate({ params }) {
|
||||
if (code1 != null) {
|
||||
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(() => {
|
||||
@ -154,50 +175,175 @@ export default function Estimate({ params }) {
|
||||
await promisePost({ url: 'api/file/fileDelete', data: delParams }).then((res) => {
|
||||
if (res.status === 204) {
|
||||
setOriginFiles(originFiles.filter((file) => file.objectNo === objectNo && file.no !== no))
|
||||
setState({
|
||||
fileList: originFiles.filter((file) => file.objectNo === objectNo && file.no !== no),
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
//아이템 목록
|
||||
//가격표시 option 최초세팅
|
||||
useEffect(() => {
|
||||
if (isNotEmptyArray(state.itemList)) {
|
||||
setItemList(state.itemList)
|
||||
}
|
||||
}, [state?.itemList])
|
||||
|
||||
//가격표시 option 세팅
|
||||
useEffect(() => {
|
||||
const param = {
|
||||
saleStoreId: session.storeId,
|
||||
sapSalesStoreCd: session.custCd,
|
||||
docTpCd: state?.estimateType,
|
||||
}
|
||||
const apiUrl = `/api/estimate/price/store-price-list?${queryStringFormatter(param)}`
|
||||
get({ url: apiUrl }).then((res) => {
|
||||
if (isNotEmptyArray(res?.data)) {
|
||||
setStorePriceList(res.data)
|
||||
if (state.estimateType !== '') {
|
||||
const param = {
|
||||
saleStoreId: session.storeId,
|
||||
sapSalesStoreCd: session.custCd,
|
||||
docTpCd: state?.estimateType,
|
||||
}
|
||||
})
|
||||
|
||||
const apiUrl = `/api/estimate/price/store-price-list?${queryStringFormatter(param)}`
|
||||
get({ url: apiUrl }).then((res) => {
|
||||
if (isNotEmptyArray(res?.data)) {
|
||||
setStorePriceList(res.data)
|
||||
}
|
||||
})
|
||||
}
|
||||
}, [state?.estimateType])
|
||||
|
||||
//가격표시 option 변경시
|
||||
useEffect(() => {
|
||||
if (tempPriceCd !== '') {
|
||||
const param = {
|
||||
saleStoreId: session.storeId,
|
||||
sapSalesStoreCd: session.custCd,
|
||||
docTpCd: tempPriceCd,
|
||||
}
|
||||
|
||||
const apiUrl = `/api/estimate/price/store-price-list?${queryStringFormatter(param)}`
|
||||
get({ url: apiUrl }).then((res) => {
|
||||
if (isNotEmptyArray(res?.data)) {
|
||||
setStorePriceList(res.data)
|
||||
}
|
||||
})
|
||||
}
|
||||
}, [tempPriceCd])
|
||||
|
||||
//Pricing 버튼
|
||||
const handlePricing = async (priceCd) => {
|
||||
const param = {
|
||||
saleStoreId: session.storeId,
|
||||
sapSalesStoreCd: session.custCd,
|
||||
docTpCd: state.estimateType,
|
||||
priceCd: priceCd,
|
||||
itemIdList: [], //아이템
|
||||
priceCd: session.storeLvl === '1' ? tempPriceCd : priceCd,
|
||||
itemIdList: state.itemList, //아이템 최초정보로 호출
|
||||
}
|
||||
|
||||
console.log('param::', param)
|
||||
return
|
||||
// console.log('프라이싱파람::', param)
|
||||
await promisePost({ url: '/api/estimate/price/item-price-list', data: param }).then((res) => {
|
||||
console.log('프라이싱결과::::::', res)
|
||||
//아이템쪽 다 새로고침............SUCK!!!
|
||||
if (res) {
|
||||
if (res.status === 200) {
|
||||
const data = res.data
|
||||
if (data.result.code === 200) {
|
||||
if (isNotEmptyArray(data.data2)) {
|
||||
//아이템쪽 다 새로고침............
|
||||
//성공후..
|
||||
//기존itemList랑 프라이싱결과랑 비교해서 단가만 업뎃 서로 갯수가 안맞을 수 있음 없는 itemId면 unitPrice 0으로
|
||||
//itemId로 비교해서 단가정보만 업데이트
|
||||
setState({
|
||||
priceCd: session.storeLvl === '1' ? tempPriceCd : priceCd,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
//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 (
|
||||
<div className="sub-content estimate">
|
||||
<div className="sub-content-inner">
|
||||
@ -249,7 +395,7 @@ export default function Estimate({ params }) {
|
||||
<tr>
|
||||
{/* 1차 판매점명 */}
|
||||
<th>{getMessage('estimate.detail.saleStoreId')}</th>
|
||||
<td></td>
|
||||
<td>{state?.firstSaleStoreName}</td>
|
||||
{/* 견적일 */}
|
||||
<th>
|
||||
{getMessage('estimate.detail.estimateDate')} <span className="important">*</span>
|
||||
@ -263,7 +409,7 @@ export default function Estimate({ params }) {
|
||||
<tr>
|
||||
{/* 2차 판매점명 */}
|
||||
<th>{getMessage('estimate.detail.otherSaleStoreId')}</th>
|
||||
<td></td>
|
||||
<td>{state?.agencySaleStoreName}</td>
|
||||
{/* 담당자 */}
|
||||
<th>
|
||||
{getMessage('estimate.detail.receiveUser')} <span className="important">*</span>
|
||||
@ -386,8 +532,8 @@ export default function Estimate({ params }) {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={`form-flex-wrap ${style}`}>
|
||||
<div className="input-wrap mr5" style={{ width: '610px' }}>
|
||||
<div className={`form-flex-wrap ${style}`} key={uuidv4()}>
|
||||
<div className="input-wrap mr5" style={{ width: '610px' }} key={`roof${index}`}>
|
||||
<input type="text" className="input-light" defaultValue={roofList} readOnly />
|
||||
</div>
|
||||
<div className="input-wrap" style={{ width: '200px' }}>
|
||||
@ -472,10 +618,17 @@ export default function Estimate({ params }) {
|
||||
{originFiles.length > 0 &&
|
||||
originFiles.map((originFile) => {
|
||||
return (
|
||||
<li className="file-item">
|
||||
<span>
|
||||
<li className="file-item" key={uuidv4()}>
|
||||
<span onClick={() => handleEstimateFileDownload(originFile)}>
|
||||
{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>
|
||||
</li>
|
||||
)
|
||||
@ -505,39 +658,41 @@ export default function Estimate({ params }) {
|
||||
<div className="estimate-check-inner">
|
||||
<div className="special-note-check-wrap">
|
||||
{/* SpecialNoteList반복문 */}
|
||||
{specialNoteList.map((row) => {
|
||||
return (
|
||||
<div
|
||||
className="special-note-check-item"
|
||||
onClick={(event) => {
|
||||
settingShowContent(row.code, event)
|
||||
}}
|
||||
>
|
||||
<div className="d-check-box light">
|
||||
<input
|
||||
type="checkbox"
|
||||
id={row.code}
|
||||
checked={!!row.text}
|
||||
disabled={row.code === 'ATTR001' ? true : false}
|
||||
onChange={(event) => {
|
||||
setSpecialNoteList((specialNote) =>
|
||||
specialNote.map((temp) => (temp.code === row.code ? { ...temp, text: !temp.text } : temp)),
|
||||
)
|
||||
settingShowContent(row.code, event)
|
||||
}}
|
||||
/>
|
||||
<label htmlFor={row.code}>{row.codeNm}</label>
|
||||
{specialNoteList.length > 0 &&
|
||||
specialNoteList.map((row) => {
|
||||
return (
|
||||
<div
|
||||
key={uuidv4()}
|
||||
className="special-note-check-item"
|
||||
onClick={(event) => {
|
||||
settingShowContent(row.code, event)
|
||||
}}
|
||||
>
|
||||
<div className="d-check-box light">
|
||||
<input
|
||||
type="checkbox"
|
||||
id={row.code}
|
||||
checked={!!row.text}
|
||||
disabled={row.code === 'ATTR001' ? true : false}
|
||||
onChange={(event) => {
|
||||
setSpecialNoteList((specialNote) =>
|
||||
specialNote.map((temp) => (temp.code === row.code ? { ...temp, text: !temp.text } : temp)),
|
||||
)
|
||||
settingShowContent(row.code, event)
|
||||
}}
|
||||
/>
|
||||
<label htmlFor={row.code}>{row.codeNm}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
{/* 견적특이사항 선택한 내용 영역시작 */}
|
||||
<div className="calculation-estimate">
|
||||
{specialNoteList.map((row) => {
|
||||
if (row.code === showContentCode) {
|
||||
return (
|
||||
<dl>
|
||||
<dl key={uuidv4()}>
|
||||
<dt>{row.codeNm}</dt>
|
||||
<dd dangerouslySetInnerHTML={{ __html: row.remarks }}></dd>
|
||||
</dl>
|
||||
@ -561,24 +716,24 @@ export default function Estimate({ params }) {
|
||||
<div className="esimate-wrap">
|
||||
<div className="estimate-list-wrap one">
|
||||
<div className="estimate-box">
|
||||
<div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.totPcs')}</div>
|
||||
<div className="estimate-name blue">74</div>
|
||||
<div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.totAmount')}</div>
|
||||
<div className="estimate-name blue">{convertNumberToPriceDecimal(state?.totAmount)}</div>
|
||||
</div>
|
||||
<div className="estimate-box">
|
||||
<div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.vol')}</div>
|
||||
<div className="estimate-name blue">8300</div>
|
||||
<div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.totVolKw')}</div>
|
||||
<div className="estimate-name blue">{convertNumberToPriceDecimal(state?.totVolKw)}</div>
|
||||
</div>
|
||||
<div className="estimate-box">
|
||||
<div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.netAmt')}</div>
|
||||
<div className="estimate-name blue">6,798,900</div>
|
||||
<div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.supplyPrice')}</div>
|
||||
<div className="estimate-name blue">{convertNumberToPriceDecimal(state?.supplyPrice)}</div>
|
||||
</div>
|
||||
<div className="estimate-box">
|
||||
<div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.vat')}</div>
|
||||
<div className="estimate-name blue">679,890</div>
|
||||
<div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.vatPrice')}</div>
|
||||
<div className="estimate-name blue">{convertNumberToPriceDecimal(state?.vatPrice)}</div>
|
||||
</div>
|
||||
<div className="estimate-box">
|
||||
<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>
|
||||
@ -620,15 +775,16 @@ export default function Estimate({ params }) {
|
||||
<div className="select-wrap">
|
||||
{session?.storeLvl === '1' ? (
|
||||
<select
|
||||
key={uuidv4()}
|
||||
className="select-light"
|
||||
onChange={(e) => {
|
||||
setState({ priceCd: e.target.value })
|
||||
setTempPriceCd(e.target.value)
|
||||
}}
|
||||
>
|
||||
{storePriceList.length > 0 && storePriceList.map((row) => <option value={row.priceCd}>{row.priceNm}</option>)}
|
||||
</select>
|
||||
) : (
|
||||
<select className="select-light">
|
||||
<select key={uuidv4()} className="select-light">
|
||||
<option value="UNIT_PRICE">{getMessage('estimate.detail.header.unitPrice')}</option>
|
||||
</select>
|
||||
)}
|
||||
@ -636,7 +792,6 @@ export default function Estimate({ params }) {
|
||||
<button
|
||||
className="btn-origin grey ml5"
|
||||
onClick={() => {
|
||||
// console.log('priceCd::', state.priceCd)
|
||||
handlePricing(state.priceCd)
|
||||
}}
|
||||
>
|
||||
@ -663,13 +818,13 @@ export default function Estimate({ params }) {
|
||||
</li>
|
||||
</ul>
|
||||
<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>
|
||||
{getMessage('estimate.detail.showPrice.btn2')}
|
||||
{getMessage('estimate.detail.showPrice.addItem')}
|
||||
</button>
|
||||
<button className="btn-origin grey">
|
||||
<button className="btn-origin grey" type="button" onClick={removeItem}>
|
||||
<span className="minus"></span>
|
||||
{getMessage('estimate.detail.showPrice.btn3')}
|
||||
{getMessage('estimate.detail.showPrice.delItem')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -696,59 +851,120 @@ export default function Estimate({ params }) {
|
||||
<label htmlFor="ch97"></label>
|
||||
</div>
|
||||
</th>
|
||||
<th>{getMessage('estimate.detail.itemTableHeader.col1')}</th>
|
||||
<th>{getMessage('estimate.detail.itemTableHeader.col2')}</th>
|
||||
<th>{getMessage('estimate.detail.itemTableHeader.col3')}</th>
|
||||
<th>{getMessage('estimate.detail.itemTableHeader.col4')}</th>
|
||||
<th>{getMessage('estimate.detail.itemTableHeader.col5')}</th>
|
||||
<th>{getMessage('estimate.detail.itemTableHeader.col6')}</th>
|
||||
<th>{getMessage('estimate.detail.itemTableHeader.col7')}</th>
|
||||
<th>{getMessage('estimate.detail.itemTableHeader.dispOrder')}</th>
|
||||
<th>{getMessage('estimate.detail.itemTableHeader.itemId')}</th>
|
||||
<th>{getMessage('estimate.detail.itemTableHeader.itemNo')}</th>
|
||||
<th>{getMessage('estimate.detail.itemTableHeader.amount')}</th>
|
||||
<th>{getMessage('estimate.detail.itemTableHeader.unit')}</th>
|
||||
<th>{getMessage('estimate.detail.itemTableHeader.salePrice')}</th>
|
||||
<th>{getMessage('estimate.detail.itemTableHeader.saleTotPrice')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td className="al-c">
|
||||
<div className="d-check-box light no-text">
|
||||
<input type="checkbox" id="ch98" />
|
||||
<label htmlFor="ch98"></label>
|
||||
</div>
|
||||
</td>
|
||||
<td className="al-r">100</td>
|
||||
<td>
|
||||
<div className="form-flex-wrap">
|
||||
<div className="select-wrap mr5">{/* <Select /> */}</div>
|
||||
<div className="btn-area">
|
||||
<span className="tb_ico change_check"></span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div className="form-flex-wrap">
|
||||
<div className="name">HNW-MC4-CHN30</div>
|
||||
<div className="icon-wrap">
|
||||
<span className="tb_ico file_check"></span>
|
||||
<button className="grid-tip"></button>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div className="input-wrap" style={{ width: '100%' }}>
|
||||
<input type="text" className="input-light al-r" defaultValue={'20'} />
|
||||
</div>
|
||||
</td>
|
||||
<td>セット</td>
|
||||
<td>
|
||||
<div className="form-flex-wrap">
|
||||
<div className="input-wrap mr5">
|
||||
<input type="text" className="input-light al-r" defaultValue={'278,050'} />
|
||||
</div>
|
||||
<div className="btn-area">
|
||||
<span className="tb_ico open_check"></span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td className="al-r">5,561,000</td>
|
||||
</tr>
|
||||
{state?.itemList.length > 0 &&
|
||||
state.itemList.map((item, index) => {
|
||||
return (
|
||||
<tr key={uuidv4()}>
|
||||
<td className="al-c">
|
||||
<div className="d-check-box light no-text">
|
||||
<input
|
||||
type="checkbox"
|
||||
id={item?.dispOrder}
|
||||
onChange={() => onChangeSelect(item.dispOrder)}
|
||||
checked={selection.has(item.dispOrder) ? true : false}
|
||||
/>
|
||||
<label htmlFor={item?.dispOrder}></label>
|
||||
</div>
|
||||
</td>
|
||||
<td className="al-r">{item?.dispOrder * 100}</td>
|
||||
<td>
|
||||
<div className="form-flex-wrap">
|
||||
<div className="select-wrap mr5">
|
||||
<Select
|
||||
id="long-value-select1"
|
||||
instanceId="long-value-select1"
|
||||
className="react-select-custom"
|
||||
classNamePrefix="custom"
|
||||
placeholder="Select"
|
||||
options={displayItemList}
|
||||
onChange={(e) => {
|
||||
if (isObjectNotEmpty(e)) {
|
||||
onChangeDisplayItem(e.itemId, item.dispOrder)
|
||||
}
|
||||
}}
|
||||
getOptionLabel={(x) => x.itemName}
|
||||
getOptionValue={(x) => x.itemId}
|
||||
isClearable={true}
|
||||
isDisabled={false}
|
||||
value={displayItemList.filter(function (option) {
|
||||
return option.itemId === item.itemId
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
{/* {item?.partAdd === '1' && ( */}
|
||||
{item?.itemChangeFlg === '1' && (
|
||||
<div className="btn-area">
|
||||
<span className="tb_ico change_check"></span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div className="form-flex-wrap">
|
||||
<div className="name">{item?.itemNo}</div>
|
||||
<div className="icon-wrap">
|
||||
{item?.fileUploadFlg === '1' && <span className="tb_ico file_check"></span>}
|
||||
{item?.specialNoteCd && (
|
||||
<button
|
||||
type="button"
|
||||
className="grid-tip"
|
||||
onClick={() => {
|
||||
setProductFeaturesPopupOpen(true)
|
||||
setShowProductFeatureData(item?.specialNoteCd)
|
||||
}}
|
||||
></button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div className="input-wrap" style={{ width: '100%' }}>
|
||||
<input
|
||||
type="text"
|
||||
className="input-light al-r"
|
||||
defaultValue={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>
|
||||
</table>
|
||||
</div>
|
||||
@ -758,6 +974,13 @@ export default function Estimate({ params }) {
|
||||
</div>
|
||||
{/* 기본정보끝 */}
|
||||
</div>
|
||||
{productFeaturesPopupOpen && (
|
||||
<ProductFeaturesPop
|
||||
specialNoteList={specialNoteList}
|
||||
showProductFeatureData={showProductFeatureData}
|
||||
setProductFeaturesPopupOpen={setProductFeaturesPopupOpen}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
67
src/components/estimate/popup/ProductFeaturesPop.jsx
Normal file
67
src/components/estimate/popup/ProductFeaturesPop.jsx
Normal file
@ -0,0 +1,67 @@
|
||||
'use client'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
export default function ProductFeaturesPop({ specialNoteList, showProductFeatureData, setProductFeaturesPopupOpen }) {
|
||||
const [showSpecialNoteList, setShowSpecialNoteList] = useState([])
|
||||
const { getMessage } = useMessage()
|
||||
|
||||
useEffect(() => {
|
||||
let pushData = []
|
||||
specialNoteList.map((row) => {
|
||||
let option = showProductFeatureData.split('、')
|
||||
option.map((row2) => {
|
||||
if (row.code === row2) {
|
||||
pushData.push(row)
|
||||
}
|
||||
})
|
||||
})
|
||||
setShowSpecialNoteList(pushData)
|
||||
}, [specialNoteList])
|
||||
|
||||
return (
|
||||
<div className="modal-popup">
|
||||
<div className="modal-dialog">
|
||||
<div className="modal-content">
|
||||
<div className="modal-header">
|
||||
<h1 className="title">{getMessage('estimate.detail.productFeaturesPopup.title')}</h1>
|
||||
<button
|
||||
type="button"
|
||||
className="modal-close"
|
||||
onClick={() => {
|
||||
setProductFeaturesPopupOpen(false)
|
||||
}}
|
||||
>
|
||||
{getMessage('estimate.detail.productFeaturesPopup.close')}
|
||||
</button>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<div className="modal-body-inner border">
|
||||
<div className="calculation-estimate usemodal">
|
||||
{showSpecialNoteList.length > 0 &&
|
||||
showSpecialNoteList.map((row) => {
|
||||
return (
|
||||
<dl>
|
||||
<dt>{row.codeNm}</dt>
|
||||
<dd dangerouslySetInnerHTML={{ __html: row.remarks }}></dd>
|
||||
</dl>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
<div className="footer-btn-wrap">
|
||||
<button
|
||||
type="button"
|
||||
className="btn-origin navy"
|
||||
onClick={() => {
|
||||
setProductFeaturesPopupOpen(false)
|
||||
}}
|
||||
>
|
||||
{getMessage('estimate.detail.productFeaturesPopup.close')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -131,15 +131,14 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
||||
|
||||
this.on('selected', () => {
|
||||
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', () => {
|
||||
// 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) => {
|
||||
this.canvas.remove(child)
|
||||
})
|
||||
@ -167,6 +166,10 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
||||
},
|
||||
|
||||
initLines() {
|
||||
// if (this.lines.length > 0) {
|
||||
// return
|
||||
// }
|
||||
|
||||
this.lines = []
|
||||
|
||||
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() {
|
||||
// drawHelpLineInHexagon(this, pitch)
|
||||
@ -263,9 +272,11 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
||||
this.texts = []
|
||||
points.forEach((start, i) => {
|
||||
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 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
|
||||
|
||||
|
||||
@ -13,14 +13,16 @@ import QContextMenu from '@/components/common/context-menu/QContextMenu'
|
||||
import { useCanvasConfigInitialize } from '@/hooks/common/useCanvasConfigInitialize'
|
||||
import { MENU } from '@/common/common'
|
||||
import PanelBatchStatistics from '@/components/floor-plan/modal/panelBatch/PanelBatchStatistics'
|
||||
import { totalDisplaySelector } from '@/store/settingAtom'
|
||||
|
||||
export default function CanvasFrame() {
|
||||
const canvasRef = useRef(null)
|
||||
const { canvas } = useCanvas('canvas')
|
||||
const { canvas, handleBackImageLoadToCanvas } = useCanvas('canvas')
|
||||
const { canvasLoadInit, gridInit } = useCanvasConfigInitialize()
|
||||
const currentMenu = useRecoilValue(currentMenuState)
|
||||
const { contextMenu, handleClick } = useContextMenu()
|
||||
const { selectedPlan, modifiedPlanFlag, checkCanvasObjectEvent, resetModifiedPlans } = usePlan()
|
||||
const { selectedPlan, modifiedPlanFlag, checkCanvasObjectEvent, resetModifiedPlans, currentCanvasPlan } = usePlan()
|
||||
const totalDisplay = useRecoilValue(totalDisplaySelector) // 집계표 표시 여부
|
||||
useEvent()
|
||||
|
||||
const loadCanvas = () => {
|
||||
@ -72,7 +74,8 @@ export default function CanvasFrame() {
|
||||
MENU.MODULE_CIRCUIT_SETTING.BASIC_SETTING,
|
||||
MENU.MODULE_CIRCUIT_SETTING.CIRCUIT_TRESTLE_SETTING,
|
||||
MENU.MODULE_CIRCUIT_SETTING.PLAN_ORIENTATION,
|
||||
].includes(currentMenu) && <PanelBatchStatistics />}
|
||||
].includes(currentMenu) &&
|
||||
totalDisplay && <PanelBatchStatistics />}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -235,6 +235,7 @@ export default function CanvasMenu(props) {
|
||||
<QSelectBox title={'瓦53A'} option={SelectOption} />
|
||||
</div>
|
||||
<div className="btn-from">
|
||||
<button className="btn10"></button>
|
||||
{/*<button className="btn04" onClick={() => setShowCanvasSettingModal(true)}></button>*/}
|
||||
<button className="btn04" onClick={handlePopup}></button>
|
||||
<button className="btn05"></button>
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
'use client'
|
||||
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { currentMenuState } from '@/store/canvasAtom'
|
||||
import { canvasState, currentMenuState } from '@/store/canvasAtom'
|
||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||
import { menuTypeState, subMenusState } from '@/store/menuAtom'
|
||||
import useMenu from '@/hooks/common/useMenu'
|
||||
@ -9,6 +9,7 @@ import { useEffect } from 'react'
|
||||
|
||||
export default function MenuDepth01() {
|
||||
const type = useRecoilValue(menuTypeState)
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
const { getMessage } = useMessage()
|
||||
const { handleMenu } = useMenu()
|
||||
const [currentMenu, setCurrentMenu] = useRecoilState(currentMenuState)
|
||||
@ -24,6 +25,7 @@ export default function MenuDepth01() {
|
||||
|
||||
useEffect(() => {
|
||||
handleMenu(type)
|
||||
canvas.discardActiveObject()
|
||||
}, [currentMenu])
|
||||
return (
|
||||
<div className="canvas-depth2-inner">
|
||||
|
||||
89
src/components/floor-plan/modal/ImgLoad.jsx
Normal file
89
src/components/floor-plan/modal/ImgLoad.jsx
Normal 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>
|
||||
)
|
||||
}
|
||||
@ -1,20 +1,40 @@
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import WithDraggable from '@/components/common/draggable/WithDraggable'
|
||||
import { useState } from 'react'
|
||||
import Orientation from '@/components/floor-plan/modal/basic/step/Orientation'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import Module from '@/components/floor-plan/modal/basic/step/Module'
|
||||
import PitchModule from '@/components/floor-plan/modal/basic/step/pitch/PitchModule'
|
||||
import PitchPlacement from '@/components/floor-plan/modal/basic/step/pitch/PitchPlacement'
|
||||
import Placement from '@/components/floor-plan/modal/basic/step/Placement'
|
||||
import { useRecoilState } from 'recoil'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { canvasSettingState } from '@/store/canvasAtom'
|
||||
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 } }) {
|
||||
const { getMessage } = useMessage()
|
||||
const { closePopup } = usePopup()
|
||||
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 (
|
||||
<WithDraggable isShow={true} pos={pos}>
|
||||
<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>
|
||||
<div className={`module-tab-bx ${tabNum === 3 ? 'act' : ''}`}>{getMessage('modal.module.basic.setting.module.placement')}</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 === 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" onClick={() => setTabNum(tabNum + 1)}>
|
||||
<button className="btn-frame modal" onClick={handleBtnNextStep}>
|
||||
Next
|
||||
</button>
|
||||
)}
|
||||
{tabNum === 3 && (
|
||||
<>
|
||||
<button className="btn-frame modal mr5">{getMessage('modal.module.basic.setting.passivity.placement')}</button>
|
||||
<button className="btn-frame modal act">{getMessage('modal.module.basic.setting.auto.placement')}</button>
|
||||
<button className="btn-frame modal mr5" onClick={manualModuleSetup}>
|
||||
{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>
|
||||
|
||||
@ -1,22 +1,21 @@
|
||||
import { useState } from 'react'
|
||||
import { forwardRef, useImperativeHandle, useState } from 'react'
|
||||
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 [compasDeg, setCompasDeg] = useState(0)
|
||||
|
||||
const { nextStep, compasDeg, setCompasDeg } = useOrientation()
|
||||
|
||||
const [hasAnglePassivity, setHasAnglePassivity] = useState(false)
|
||||
|
||||
const getDegree = (degree) => {
|
||||
if (degree % 15 === 0) return degree
|
||||
useImperativeHandle(ref, () => ({
|
||||
handleNextStep,
|
||||
}))
|
||||
|
||||
let value = Math.floor(degree / 15)
|
||||
const remain = ((degree / 15) % 1).toFixed(5)
|
||||
|
||||
if (remain > 0.4) {
|
||||
value++
|
||||
}
|
||||
|
||||
return value * 15
|
||||
const handleNextStep = () => {
|
||||
nextStep()
|
||||
}
|
||||
|
||||
return (
|
||||
@ -31,7 +30,7 @@ export default function Orientation({ setTabNum }) {
|
||||
{Array.from({ length: 180 / 15 }).map((dot, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={`circle ${getDegree(compasDeg) === 15 * (12 + index) ? 'act' : ''}`}
|
||||
className={`circle ${getDegreeInOrientation(compasDeg) === 15 * (12 + index) ? 'act' : ''}`}
|
||||
onClick={() => setCompasDeg(15 * (12 + index))}
|
||||
>
|
||||
{index === 0 && <i>180°</i>}
|
||||
@ -39,13 +38,17 @@ export default function Orientation({ setTabNum }) {
|
||||
</div>
|
||||
))}
|
||||
{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 === 6 && <i>90°</i>}
|
||||
</div>
|
||||
))}
|
||||
<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>
|
||||
@ -62,7 +65,11 @@ export default function Orientation({ setTabNum }) {
|
||||
className="input-origin block"
|
||||
value={compasDeg}
|
||||
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>
|
||||
<span className="thin">°</span>
|
||||
@ -72,4 +79,4 @@ export default function Orientation({ setTabNum }) {
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
@ -63,7 +63,9 @@ export default function Distance(props) {
|
||||
</div>
|
||||
</div>
|
||||
<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>
|
||||
|
||||
@ -5,14 +5,28 @@ import { useRecoilValue } from 'recoil'
|
||||
import { contextPopupPositionState } from '@/store/popupAtom'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
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) {
|
||||
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
||||
const { id, pos = contextPopupPosition, target } = props
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
const { getMessage } = useMessage()
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
canvas?.discardActiveObject()
|
||||
}
|
||||
}, [])
|
||||
|
||||
const [compasDeg, setCompasDeg] = useState(null)
|
||||
const [flowDirection, setFlowDirection] = useState(target.direction)
|
||||
const { closePopup } = usePopup()
|
||||
const [compasDeg, setCompasDeg] = useState(360)
|
||||
const { changeSurfaceFlowDirection, type, setType } = useFlowDirectionSetting(id)
|
||||
|
||||
const orientations = [
|
||||
{ name: `${getMessage('commons.none')}`, value: 0 },
|
||||
{ name: `${getMessage('commons.south')}`, value: 360 },
|
||||
{ name: `${getMessage('commons.south')}${getMessage('commons.east')}`, value: 315 },
|
||||
{ name: `${getMessage('commons.south')}${getMessage('commons.west')}`, value: 45 },
|
||||
@ -23,41 +37,7 @@ export default function FlowDirectionSetting(props) {
|
||||
{ name: `${getMessage('commons.north')}`, value: 180 },
|
||||
]
|
||||
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 (
|
||||
<WithDraggable isShow={true} pos={pos}>
|
||||
<div className={`modal-pop-wrap ml mount`}>
|
||||
@ -75,13 +55,13 @@ export default function FlowDirectionSetting(props) {
|
||||
<div className="object-direction-wrap">
|
||||
<div className="plane-direction">
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<button className="plane-btn left"></button>
|
||||
<button className={`plane-btn left ${flowDirection === 'west' ? 'act' : ''}`} onClick={() => setFlowDirection('west')}></button>
|
||||
</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="mb-box">
|
||||
<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>
|
||||
</div>
|
||||
<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 className="mb-box">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
@ -110,22 +117,27 @@ export default function FlowDirectionSetting(props) {
|
||||
<div
|
||||
key={index}
|
||||
className={`circle ${compasDeg === 15 * (12 + index) ? 'act' : ''}`}
|
||||
onClick={() => setCompasDeg(15 * (12 + index))}
|
||||
>
|
||||
<i>{13 - index}</i>
|
||||
</div>
|
||||
onClick={() => {
|
||||
setType(FLOW_DIRECTION_TYPE.TWENTY_FOUR_AZIMUTH)
|
||||
setCompasDeg(15 * (12 + index))
|
||||
}}
|
||||
></div>
|
||||
))}
|
||||
{Array.from({ length: 180 / 15 - 1 }).map((dot, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={`circle ${compasDeg === 15 * (index + 1) ? 'act' : ''}`}
|
||||
onClick={() => setCompasDeg(15 * (index + 1))}
|
||||
>
|
||||
<i>{24 - index}</i>
|
||||
</div>
|
||||
onClick={() => {
|
||||
setType(FLOW_DIRECTION_TYPE.TWENTY_FOUR_AZIMUTH)
|
||||
setCompasDeg(15 * (index + 1))
|
||||
}}
|
||||
></div>
|
||||
))}
|
||||
<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>
|
||||
@ -133,7 +145,9 @@ export default function FlowDirectionSetting(props) {
|
||||
</div>
|
||||
</div>
|
||||
<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>
|
||||
|
||||
@ -3,38 +3,57 @@ import { useRecoilValue } from 'recoil'
|
||||
import { contextPopupPositionState } from '@/store/popupAtom'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
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) {
|
||||
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
||||
const { id, pos = contextPopupPosition } = props
|
||||
const { id, pos = contextPopupPosition, target } = props
|
||||
const { getMessage } = useMessage()
|
||||
const { closePopup } = usePopup()
|
||||
const { changeSurfaceLinePropertyEvent, changeSurfaceLineProperty, changeSurfaceLinePropertyReset } = useSurfaceShapeBatch()
|
||||
const { initEvent } = useEvent()
|
||||
|
||||
const properties = [
|
||||
{ name: getMessage('eaves.line'), value: 'eaves' },
|
||||
{ name: getMessage('ridge'), value: 'ridge' },
|
||||
{ name: getMessage('oneside.flow.ridge'), value: 'onesideFlowRidge' },
|
||||
{ name: getMessage('gable'), value: 'gable' },
|
||||
{ name: getMessage('gable.left'), value: 'gableLeft' },
|
||||
{ name: getMessage('gable.right'), value: 'gableRight' },
|
||||
{ name: getMessage('yosemune'), value: 'yosemune' },
|
||||
{ name: getMessage('valley'), value: 'valley' },
|
||||
{ name: getMessage('l.abandon.valley'), value: 'lAbandonValley' },
|
||||
{ name: getMessage('mansard'), value: 'mansard' },
|
||||
{ name: getMessage('wall.merge'), value: 'wallCollection' },
|
||||
{ name: getMessage('wall.merge.type'), value: 'wallCollectionType' },
|
||||
{ name: getMessage('wall.merge.flow'), value: 'wallCollectionFlow' },
|
||||
{ name: getMessage('wall.merge.flow.left'), value: 'wallCollectionFlowLeft' },
|
||||
{ name: getMessage('wall.merge.flow.right'), value: 'wallCollectionFlowRight' },
|
||||
{ name: getMessage('no.setting'), value: 'noSetting' },
|
||||
{ name: getMessage('eaves.line'), value: LINE_TYPE.WALLLINE.EAVES },
|
||||
{ name: getMessage('ridge'), value: LINE_TYPE.SUBLINE.RIDGE },
|
||||
{ name: getMessage('oneside.flow.ridge'), value: LINE_TYPE.SUBLINE.ONESIDE_FLOW_RIDGE },
|
||||
{ name: getMessage('gable'), value: LINE_TYPE.WALLLINE.GABLE },
|
||||
{ name: getMessage('gable.left'), value: LINE_TYPE.WALLLINE.GABLE_LEFT },
|
||||
{ name: getMessage('gable.right'), value: LINE_TYPE.WALLLINE.GABLE_RIGHT },
|
||||
{ name: getMessage('yosemune'), value: LINE_TYPE.SUBLINE.YOSEMUNE },
|
||||
{ name: getMessage('valley'), value: LINE_TYPE.SUBLINE.YOSEMUNE },
|
||||
{ name: getMessage('l.abandon.valley'), value: LINE_TYPE.SUBLINE.L_ABANDON_VALLEY },
|
||||
{ name: getMessage('mansard'), value: LINE_TYPE.SUBLINE.MANSARD },
|
||||
{ name: getMessage('wall.merge'), value: LINE_TYPE.SUBLINE.WALL_COLLECTION },
|
||||
{ name: getMessage('wall.merge.type'), value: LINE_TYPE.SUBLINE.WALL_COLLECTION_TYPE },
|
||||
{ name: getMessage('wall.merge.flow'), value: LINE_TYPE.SUBLINE.WALL_COLLECTION_FLOW },
|
||||
{ name: getMessage('wall.merge.flow.left'), value: LINE_TYPE.SUBLINE.WALL_COLLECTION_FLOW_LEFT },
|
||||
{ name: getMessage('wall.merge.flow.right'), value: LINE_TYPE.SUBLINE.WALL_COLLECTION_FLOW_RIGHT },
|
||||
{ name: getMessage('no.setting'), value: LINE_TYPE.WALLLINE.DEFAULT },
|
||||
]
|
||||
const [selectedProperty, setSelectedProperty] = useState(null)
|
||||
|
||||
useEffect(() => {
|
||||
changeSurfaceLinePropertyEvent()
|
||||
return () => {
|
||||
initEvent()
|
||||
}
|
||||
}, [])
|
||||
|
||||
const handleClosePopup = () => {
|
||||
closePopup(id)
|
||||
changeSurfaceLinePropertyReset(target)
|
||||
}
|
||||
|
||||
return (
|
||||
<WithDraggable isShow={true} pos={pos}>
|
||||
<div className={`modal-pop-wrap r mount`}>
|
||||
<div className="modal-head">
|
||||
<h1 className="title">{getMessage('contextmenu.line.property.edit')} </h1>
|
||||
<button className="modal-close" onClick={() => closePopup(id)}>
|
||||
<button className="modal-close" onClick={() => handleClosePopup()}>
|
||||
닫기
|
||||
</button>
|
||||
</div>
|
||||
@ -66,7 +85,9 @@ export default function LinePropertySetting(props) {
|
||||
</div>
|
||||
</div>
|
||||
<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>
|
||||
|
||||
@ -8,8 +8,6 @@ import { useMessage } from '@/hooks/useMessage'
|
||||
import { useAxios } from '@/hooks/useAxios'
|
||||
import { useSwal } from '@/hooks/useSwal'
|
||||
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 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 { closePopup } = usePopup()
|
||||
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 { get, post } = useAxios()
|
||||
@ -503,90 +487,6 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, set
|
||||
</div>
|
||||
</td>
|
||||
</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>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import DimensionLineSetting from '@/components/floor-plan/modal/setting01/dimensionLine/DimensionLineSetting'
|
||||
@ -6,55 +5,35 @@ import { usePopup } from '@/hooks/usePopup'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import FontSetting from '@/components/common/font/FontSetting'
|
||||
import PlanSizeSetting from '@/components/floor-plan/modal/setting01/planSize/PlanSizeSetting'
|
||||
import { dimensionLineSettingsState } from '@/store/commonUtilsAtom'
|
||||
import { useCanvasSetting } from '@/hooks/option/useCanvasSetting'
|
||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||
import { fontSelector, globalFontAtom } from '@/store/fontAtom'
|
||||
|
||||
export default function SecondOption() {
|
||||
const { getMessage } = useMessage()
|
||||
const { addPopup, closePopup, closePopups } = usePopup()
|
||||
const { addPopup, closePopup } = usePopup()
|
||||
const [showFontSettingModal, setShowFontSettingModal] = useState(false)
|
||||
const [showDimensionLineSettingModal, setShowDimensionLineSettingModal] = useState(false)
|
||||
const [showPlanSizeSettingModal, setShowPlanSizeSettingModal] = useState(false)
|
||||
const dimensionSettings = useRecoilValue(dimensionLineSettingsState)
|
||||
|
||||
const [globalFont, setGlobalFont] = useRecoilState(globalFontAtom)
|
||||
const [objectNo, setObjectNo] = useState('test123240912001') // 이후 삭제 필요
|
||||
const { settingModalFirstOptions, setSettingModalFirstOptions } = useCanvasSetting()
|
||||
const { settingModalSecondOptions, setSettingModalSecondOptions } = useCanvasSetting()
|
||||
const { adsorptionPointMode, setAdsorptionPointMode } = 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(() => {
|
||||
console.log('SecondOption useEffect 실행')
|
||||
//fetchSettings()
|
||||
}, [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 = {
|
||||
color,
|
||||
setColor,
|
||||
pixel,
|
||||
setPixel,
|
||||
font,
|
||||
setFont,
|
||||
fontSize,
|
||||
setFontSize,
|
||||
fontColor,
|
||||
setFontColor,
|
||||
id: dimensionId,
|
||||
isShow: showDimensionLineSettingModal,
|
||||
setIsShow: setShowDimensionLineSettingModal,
|
||||
@ -62,23 +41,6 @@ export default function SecondOption() {
|
||||
|
||||
const [horizon, setHorizon] = 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) => {
|
||||
setShowPlanSizeSettingModal(false)
|
||||
setShowFontSettingModal(false)
|
||||
@ -90,6 +52,7 @@ export default function SecondOption() {
|
||||
setShowDimensionLineSettingModal(false)
|
||||
fontProps.type = 'commonText'
|
||||
fontProps.id = fontId + 1
|
||||
fontProps.font = commonFont
|
||||
addPopup(fontId + 1, 2, <FontSetting {...fontProps} />, true)
|
||||
break
|
||||
}
|
||||
@ -99,6 +62,7 @@ export default function SecondOption() {
|
||||
setShowFontSettingModal(true)
|
||||
setShowDimensionLineSettingModal(false)
|
||||
fontProps.type = 'flowText'
|
||||
fontProps.font = flowFont
|
||||
fontProps.id = fontId + 2
|
||||
addPopup(fontId + 2, 2, <FontSetting {...fontProps} />, true)
|
||||
break
|
||||
@ -107,9 +71,9 @@ export default function SecondOption() {
|
||||
case 'font3': {
|
||||
//치수 글꼴변경
|
||||
setShowFontSettingModal(true)
|
||||
|
||||
setShowDimensionLineSettingModal(false)
|
||||
fontProps.type = 'lengthText'
|
||||
fontProps.font = lengthFont
|
||||
fontProps.id = fontId + 3
|
||||
addPopup(fontId + 3, 2, <FontSetting {...fontProps} />, true)
|
||||
break
|
||||
@ -120,6 +84,7 @@ export default function SecondOption() {
|
||||
setShowFontSettingModal(true)
|
||||
setShowDimensionLineSettingModal(false)
|
||||
fontProps.type = 'circuitNumberText'
|
||||
fontProps.font = circuitNumberTextFont
|
||||
fontProps.id = fontId
|
||||
addPopup(fontId, 2, <FontSetting {...fontProps} />, true)
|
||||
break
|
||||
@ -129,6 +94,12 @@ export default function SecondOption() {
|
||||
//치수선 설정
|
||||
if (!showDimensionLineSettingModal) {
|
||||
setShowDimensionLineSettingModal(true)
|
||||
fontProps.font = {
|
||||
fontFamily: '',
|
||||
fontColor: '',
|
||||
fontSize: '',
|
||||
fontWeight: '',
|
||||
}
|
||||
addPopup(dimensionId, 2, <DimensionLineSetting {...dimensionProps} />, true)
|
||||
} else {
|
||||
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 (
|
||||
<>
|
||||
<div className="modal-check-btn-wrap">
|
||||
|
||||
@ -8,68 +8,57 @@ import QSelectBox from '@/components/common/select/QSelectBox'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { dimensionLineSettingsState } from '@/store/commonUtilsAtom'
|
||||
import { useRecoilState } from 'recoil'
|
||||
import { globalFontAtom } from '@/store/fontAtom'
|
||||
|
||||
/*
|
||||
color: 치수선 색
|
||||
fontColor: 글꼴 색
|
||||
fontSize: 치수선 치수 색
|
||||
pixel: 치수선 두깨
|
||||
*/
|
||||
export default function DimensionLineSetting(props) {
|
||||
const {
|
||||
color,
|
||||
setColor,
|
||||
font,
|
||||
setFont,
|
||||
fontColor,
|
||||
setFontColor,
|
||||
fontSize,
|
||||
setFontSize,
|
||||
pixel,
|
||||
setPixel,
|
||||
isShow,
|
||||
setIsShow,
|
||||
id,
|
||||
pos = { x: 985, y: 180 },
|
||||
} = props
|
||||
const { isShow, setIsShow, id, pos = { x: 985, y: 180 } } = props
|
||||
const { addPopup, closePopup, closePopups } = usePopup()
|
||||
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 [originFont, setOriginFont] = useState(font)
|
||||
const [originFontColor, setOriginFontColor] = useState(fontColor)
|
||||
const [originFontSize, setOriginFontSize] = useState(fontSize)
|
||||
const [originPixel, setOriginPixel] = useState(pixel)
|
||||
const [dimensionLineSettings, setDimensionLineSettings] = useRecoilState(dimensionLineSettingsState)
|
||||
const [globalFont, setGlobalFont] = useRecoilState(globalFontAtom)
|
||||
const [originPixel, setOriginPixel] = useState(dimensionLineSettings.pixel)
|
||||
const [originColor, setOriginColor] = useState(dimensionLineSettings.color)
|
||||
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 [colorModalId, setColorModalId] = useState(uuidv4())
|
||||
|
||||
const [colorModalId, setColorModalId] = useState(uuidv4())
|
||||
const [showColorPickerModal, setShowColorPickerModal] = useState(false)
|
||||
const [showFontModal, setShowFontModal] = useState(false)
|
||||
const { getMessage } = useMessage()
|
||||
const [dimensionLineSettings, setDimensionLineSettings] = useRecoilState(dimensionLineSettingsState)
|
||||
|
||||
useEffect(() => {
|
||||
console.log(2, isShow)
|
||||
if (pixel) {
|
||||
setOriginPixel(pixels?.filter((data) => data.value === pixel)[0])
|
||||
if (originPixel) {
|
||||
setOriginPixel(pixels?.filter((data) => data.value === originPixel)[0])
|
||||
}
|
||||
setIsShow(true)
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
console.log(1, isShow)
|
||||
if (!isShow) {
|
||||
closePopups([fontModalId, colorModalId])
|
||||
}
|
||||
}, [isShow])
|
||||
|
||||
const handleFontSave = (font) => {
|
||||
setOriginFont(font.fontFamily)
|
||||
setOriginFontSize(font.fontSize)
|
||||
setOriginFontColor(font.fontColor)
|
||||
setOriginFontWeight(font.fontWeight)
|
||||
}
|
||||
const handleColorSave = () => {}
|
||||
|
||||
const colorPickerProps = {
|
||||
isShow: showColorPickerModal,
|
||||
setIsShow: setShowColorPickerModal,
|
||||
color: originColor,
|
||||
setColor: setOriginColor,
|
||||
id: colorModalId,
|
||||
isConfig: true,
|
||||
pos: {
|
||||
x: 495,
|
||||
y: 180,
|
||||
@ -79,14 +68,13 @@ export default function DimensionLineSetting(props) {
|
||||
const fontProps = {
|
||||
isShow: showFontModal,
|
||||
setIsShow: setShowFontModal,
|
||||
color: originColor,
|
||||
setColor: setOriginColor,
|
||||
font: originFont,
|
||||
setFont: setOriginFont,
|
||||
fontColor: 'black',
|
||||
setFontColor: setOriginFontColor,
|
||||
fontSize: originFontSize,
|
||||
setFontSize: setOriginFontSize,
|
||||
font: {
|
||||
fontFamily: originFont,
|
||||
fontSize: originFontSize,
|
||||
fontColor: originFontColor,
|
||||
fontWeight: originFontWeight,
|
||||
},
|
||||
onSave: handleFontSave,
|
||||
isConfig: true,
|
||||
id: fontModalId,
|
||||
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 (
|
||||
<WithDraggable isShow={true} pos={pos}>
|
||||
<div className={`modal-pop-wrap xxxm mount`}>
|
||||
@ -149,9 +160,10 @@ export default function DimensionLineSetting(props) {
|
||||
className="font"
|
||||
style={{
|
||||
fontFamily: originFont?.value ?? '',
|
||||
color: originFontColor?.value ?? 'black',
|
||||
color: originFontColor.value ?? 'black',
|
||||
fontSize: originFontSize?.value ?? '12px',
|
||||
fontWeight: '400',
|
||||
fontStyle: originFontWeight?.value.toLowerCase().includes('italic') ? 'italic' : 'normal',
|
||||
fontWeight: originFontWeight?.value.toLowerCase().includes('bold') ? 'bold' : 'normal',
|
||||
}}
|
||||
>
|
||||
9,999
|
||||
@ -161,7 +173,7 @@ export default function DimensionLineSetting(props) {
|
||||
style={{
|
||||
backgroundColor: originColor,
|
||||
borderColor: originColor,
|
||||
height: originPixel.value,
|
||||
height: originPixel?.value,
|
||||
}}
|
||||
></span>
|
||||
</div>
|
||||
@ -169,26 +181,7 @@ export default function DimensionLineSetting(props) {
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid-btn-wrap">
|
||||
<button
|
||||
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])
|
||||
}}
|
||||
>
|
||||
<button className="btn-frame modal act" onClick={onSave}>
|
||||
{getMessage('modal.common.save')}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { useEffect, useState, useContext } from 'react'
|
||||
import ProductItem from './ProductItem'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import Image from 'next/image'
|
||||
@ -8,17 +8,16 @@ import { useRecoilValue } from 'recoil'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { globalLocaleStore } from '@/store/localeAtom'
|
||||
import { queryStringFormatter } from '@/util/common-utils'
|
||||
import { sessionStore } from '@/store/commonAtom'
|
||||
import MainSkeleton from '../ui/MainSkeleton'
|
||||
import { SessionContext } from '@/app/SessionProvider'
|
||||
|
||||
export default function MainContents() {
|
||||
const { session } = useContext(SessionContext)
|
||||
const { getMessage } = useMessage()
|
||||
const router = useRouter()
|
||||
const globalLocaleState = useRecoilValue(globalLocaleStore)
|
||||
const { promiseGet } = useAxios(globalLocaleState)
|
||||
|
||||
const sessionState = useRecoilValue(sessionStore)
|
||||
|
||||
//최근 물건
|
||||
const [objectList, setObjectList] = useState([])
|
||||
|
||||
@ -36,12 +35,12 @@ export default function MainContents() {
|
||||
fetchObjectList()
|
||||
fetchNoticeList()
|
||||
fetchFaqList()
|
||||
}, [sessionState])
|
||||
}, [])
|
||||
|
||||
//최근 갱신 물건목록 / Sales Contact info 정보
|
||||
const fetchObjectList = async () => {
|
||||
try {
|
||||
const apiUrl = `/api/main-page/object/${sessionState?.storeId}/list`
|
||||
const apiUrl = `/api/main-page/object/${session?.storeId}/list`
|
||||
await promiseGet({
|
||||
url: apiUrl,
|
||||
}).then((res) => {
|
||||
|
||||
@ -173,8 +173,10 @@ export default function Stuff() {
|
||||
schFromDt: dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD'),
|
||||
schToDt: dayjs(new Date()).format('YYYY-MM-DD'),
|
||||
startRow: (pageNo - 1) * pageSize + 1,
|
||||
endRow: pageNo * pageSize,
|
||||
schSelSaleStoreId: stuffSearchParams?.schOtherSelSaleStoreId ? stuffSearchParams.schOtherSelSaleStoreId : stuffSearchParams.schSelSaleStoreId,
|
||||
// endRow: pageNo * pageSize,
|
||||
endRow: stuffSearchParams?.endRow,
|
||||
schSelSaleStoreId: stuffSearchParams?.schSelSaleStoreId ? stuffSearchParams.schSelSaleStoreId : '',
|
||||
schOtherSelSaleStoreId: stuffSearchParams?.schOtherSelSaleStoreId ? stuffSearchParams.schOtherSelSaleStoreId : '',
|
||||
schSortType: stuffSearchParams.schSortType,
|
||||
}
|
||||
|
||||
@ -190,11 +192,7 @@ export default function Stuff() {
|
||||
})
|
||||
}
|
||||
|
||||
//if (session.storeId === 'T01') {
|
||||
fetchData()
|
||||
//} else if (stuffSearch.schSelSaleStoreId !== '') {
|
||||
//fetchData()
|
||||
//}
|
||||
} else if (stuffSearchParams?.code === 'M') {
|
||||
const params = {
|
||||
saleStoreId: session?.storeId,
|
||||
@ -220,10 +218,6 @@ export default function Stuff() {
|
||||
stuffSearchParams.endRow = 1 * pageSize
|
||||
stuffSearchParams.schSortType = defaultSortType
|
||||
setPageNo(1)
|
||||
setStuffSearch({
|
||||
...stuffSearch,
|
||||
code: 'FINISH',
|
||||
})
|
||||
|
||||
async function fetchData() {
|
||||
const apiUrl = `/api/object/list?saleStoreId=${session?.storeId}&${queryStringFormatter(stuffSearchParams)}`
|
||||
@ -241,6 +235,25 @@ export default function Stuff() {
|
||||
fetchData()
|
||||
} else if (stuffSearchParams?.code === 'C') {
|
||||
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])
|
||||
|
||||
@ -254,12 +267,15 @@ export default function Stuff() {
|
||||
: stuffSearchParams.schSelSaleStoreId
|
||||
setPageSize(e.target.value)
|
||||
setStuffSearch({
|
||||
...stuffSearchParams,
|
||||
// ...stuffSearchParams,
|
||||
...stuffSearch,
|
||||
code: 'S',
|
||||
startRow: startRow,
|
||||
endRow: 1 * e.target.value,
|
||||
})
|
||||
|
||||
setPageNo(1)
|
||||
|
||||
const apiUrl = `/api/object/list?saleStoreId=${session?.storeId}&${queryStringFormatter(stuffSearchParams)}`
|
||||
get({ url: apiUrl }).then((res) => {
|
||||
if (!isEmptyArray(res)) {
|
||||
@ -345,13 +361,13 @@ export default function Stuff() {
|
||||
</div>
|
||||
<div className="left-unit-box">
|
||||
<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="U">{getMessage('stuff.search.grid.schSortTypeU')}</option>
|
||||
</select>
|
||||
</div>
|
||||
<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="200">200</option>
|
||||
<option value="300">300</option>
|
||||
|
||||
@ -18,6 +18,7 @@ import WindSelectPop from './popup/WindSelectPop'
|
||||
import { useCommonCode } from '@/hooks/common/useCommonCode'
|
||||
import StuffPlanQGrid from './StuffPlanQGrid'
|
||||
import { floorPlanObjectState } from '@/store/floorPlanObjectAtom'
|
||||
import { ManagementContext } from '@/app/management/ManagementProvider'
|
||||
|
||||
export default function StuffDetail() {
|
||||
const setFloorPlanObjectNo = useSetRecoilState(floorPlanObjectState) //견적서 화면용 물건번호리코일
|
||||
@ -96,7 +97,7 @@ export default function StuffDetail() {
|
||||
const objectNo = searchParams.get('objectNo') //url에서 물건번호 꺼내서 바로 set
|
||||
|
||||
const [editMode, setEditMode] = useState('NEW')
|
||||
const [detailData, setDetailData] = useState({})
|
||||
const { managementState, setManagementState } = useContext(ManagementContext)
|
||||
|
||||
const [planGridProps, setPlanGridProps] = useState({
|
||||
planGridData: [],
|
||||
@ -289,9 +290,9 @@ export default function StuffDetail() {
|
||||
promiseGet({ url: `/api/object/${objectNo}/detail` }).then((res) => {
|
||||
if (res.status === 200) {
|
||||
if (res.data != null) {
|
||||
setDetailData(res.data)
|
||||
setManagementState(res.data)
|
||||
} else {
|
||||
setDetailData({})
|
||||
setManagementState({})
|
||||
}
|
||||
if (isNotEmptyArray(res.data.planList)) {
|
||||
setPlanGridProps({ ...planGridProps, planGridData: res.data.planList })
|
||||
@ -299,7 +300,7 @@ export default function StuffDetail() {
|
||||
setPlanGridProps({ ...planGridProps, planGridData: [] })
|
||||
}
|
||||
} else {
|
||||
setDetailData({})
|
||||
setManagementState({})
|
||||
setPlanGridProps({ ...planGridProps, planGridData: [] })
|
||||
}
|
||||
})
|
||||
@ -415,7 +416,7 @@ export default function StuffDetail() {
|
||||
}, [commonCode])
|
||||
|
||||
useEffect(() => {
|
||||
if (isObjectNotEmpty(detailData)) {
|
||||
if (isObjectNotEmpty(managementState)) {
|
||||
// 도도부현API
|
||||
get({ url: '/api/object/prefecture/list' }).then((res) => {
|
||||
if (!isEmptyArray(res)) {
|
||||
@ -449,18 +450,17 @@ export default function StuffDetail() {
|
||||
setFavoriteStoreList(favList)
|
||||
setShowSaleStoreList(favList)
|
||||
|
||||
if (detailData.firstAgentId != null) {
|
||||
form.setValue('saleStoreId', detailData.firstAgentId)
|
||||
setSelOptions(detailData.firstAgentId)
|
||||
if (managementState.firstAgentId != null) {
|
||||
form.setValue('saleStoreId', managementState.firstAgentId)
|
||||
setSelOptions(managementState.firstAgentId)
|
||||
} else {
|
||||
form.setValue('saleStoreId', detailData.saleStoreId)
|
||||
setSelOptions(detailData.saleStoreId)
|
||||
form.setValue('saleStoreId', managementState.saleStoreId)
|
||||
setSelOptions(managementState.saleStoreId)
|
||||
}
|
||||
|
||||
//상세데이터의 1차점 아이디로 2차점 목록 조회하기
|
||||
|
||||
let data = detailData?.firstAgentId ? detailData.firstAgentId : detailData.saleStoreId
|
||||
// url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=0&userId=${session?.userId}`
|
||||
let data = managementState?.firstAgentId ? managementState.firstAgentId : managementState.saleStoreId
|
||||
url = `/api/object/saleStore/${data}/list?firstFlg=0&userId=${session?.userId}`
|
||||
|
||||
get({ url: url }).then((res) => {
|
||||
@ -502,71 +502,74 @@ export default function StuffDetail() {
|
||||
|
||||
//상세데이터가 1차점이면 1차점에 세팅
|
||||
//상세데이터가 2차점이면 2차점에 세팅하고 세션으로 1차점 세팅
|
||||
if (detailData.saleStoreLevel === '1') {
|
||||
setSelOptions(detailData.saleStoreId)
|
||||
form.setValue('saleStoreId', detailData.saleStoreId)
|
||||
form.setValue('saleStoreLevel', detailData.saleStoreLevel)
|
||||
if (managementState.saleStoreLevel === '1') {
|
||||
setSelOptions(managementState.saleStoreId)
|
||||
form.setValue('saleStoreId', managementState.saleStoreId)
|
||||
form.setValue('saleStoreLevel', managementState.saleStoreLevel)
|
||||
} else {
|
||||
setOtherSelOptions(detailData.saleStoreId)
|
||||
form.setValue('otherSaleStoreId', detailData.saleStoreId)
|
||||
form.setValue('otherSaleStoreLevel', detailData.saleStoreLevel)
|
||||
setOtherSelOptions(managementState.saleStoreId)
|
||||
form.setValue('otherSaleStoreId', managementState.saleStoreId)
|
||||
form.setValue('otherSaleStoreLevel', managementState.saleStoreLevel)
|
||||
|
||||
form.setValue('saleStoreLevel', '1')
|
||||
}
|
||||
|
||||
//설계의뢰No.
|
||||
form.setValue('planReqNo', detailData.planReqNo)
|
||||
form.setValue('planReqNo', managementState.planReqNo)
|
||||
//담당자
|
||||
form.setValue('receiveUser', detailData.receiveUser)
|
||||
form.setValue('receiveUser', managementState.receiveUser)
|
||||
|
||||
//물건구분objectStatusId
|
||||
setSelectObjectStatusId(detailData.objectStatusId)
|
||||
form.setValue('objectStatusId', detailData.objectStatusId)
|
||||
setSelectObjectStatusId(managementState.objectStatusId)
|
||||
form.setValue('objectStatusId', managementState.objectStatusId)
|
||||
|
||||
//물건명
|
||||
form.setValue('objectName', detailData.objectName)
|
||||
form.setValue('objectName', managementState.objectName)
|
||||
|
||||
//경칭코드
|
||||
setSelHonorificCode(detailData.objectNameOmit)
|
||||
form.setValue('objectNameOmit', detailData.objectNameOmit)
|
||||
setSelHonorificCode(managementState.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)
|
||||
form.setValue('prefId', detailData.prefId)
|
||||
form.setValue('address', detailData.address)
|
||||
setPrefValue(managementState.prefId)
|
||||
form.setValue('prefId', managementState.prefId)
|
||||
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
|
||||
form.setValue('coldRegionFlg', detailData.coldRegionFlg === '1' ? true : false)
|
||||
form.setValue('coldRegionFlg', managementState.coldRegionFlg === '1' ? true : false)
|
||||
|
||||
//면조도구분 surfaceType null로 내려오면 셋팅 안하고 저장할때 필수값 체크하도록
|
||||
// form.setValue('surfaceType', 'Ⅱ')
|
||||
// form.setValue('surfaceType', 'Ⅲ・Ⅳ')
|
||||
form.setValue('surfaceType', detailData.surfaceType)
|
||||
form.setValue('surfaceType', managementState.surfaceType)
|
||||
//염해지역용아이템사용 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으로 디폴트셋팅
|
||||
if (detailData.conType === null) {
|
||||
if (managementState.conType === null) {
|
||||
form.setValue('conType', '0')
|
||||
} 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) => {
|
||||
@ -856,6 +859,7 @@ export default function StuffDetail() {
|
||||
const setPlanReqInfo = (info) => {
|
||||
form.setValue('planReqNo', info.planReqNo)
|
||||
form.setValue('objectStatusId', info.building)
|
||||
setSelectObjectStatusId(info.building)
|
||||
form.setValue('objectName', info.planReqName)
|
||||
form.setValue('zipNo', info.zipNo)
|
||||
form.setValue('address', info.address2)
|
||||
@ -872,8 +876,11 @@ export default function StuffDetail() {
|
||||
form.setValue('standardWindSpeedId', `WL_${info.windSpeed}`)
|
||||
form.setValue('verticalSnowCover', info.verticalSnowCover)
|
||||
form.setValue('surfaceType', info.surfaceType)
|
||||
|
||||
if (info.surfaceType === 'Ⅱ') {
|
||||
form.setValue('saltAreaFlg', true)
|
||||
} else {
|
||||
form.setValue('saltAreaFlg', false)
|
||||
}
|
||||
form.setValue('installHeight', info.installHeight)
|
||||
form.setValue('remarks', info.remarks)
|
||||
@ -1034,7 +1041,12 @@ export default function StuffDetail() {
|
||||
|
||||
//설계의뢰 팝업 오픈
|
||||
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'))
|
||||
}
|
||||
|
||||
let detail_sort = Object.keys(detailData)
|
||||
let detail_sort = Object.keys(managementState)
|
||||
.sort()
|
||||
.reduce((obj, key) => ((obj[key] = detailData[key]), obj), {})
|
||||
.reduce((obj, key) => ((obj[key] = managementState[key]), obj), {})
|
||||
let params_sort = Object.keys(params)
|
||||
.sort()
|
||||
.reduce((obj, key) => ((obj[key] = params[key]), obj), {})
|
||||
@ -1301,7 +1313,7 @@ export default function StuffDetail() {
|
||||
|
||||
// 물건삭제
|
||||
const onDelete = () => {
|
||||
const specificationConfirmDate = detailData.specificationConfirmDate
|
||||
const specificationConfirmDate = managementState.specificationConfirmDate
|
||||
if (specificationConfirmDate != null) {
|
||||
alert(getMessage('stuff.detail.delete.message1'))
|
||||
} else {
|
||||
@ -1726,31 +1738,33 @@ export default function StuffDetail() {
|
||||
</th>
|
||||
<td>
|
||||
<div className="flx-box">
|
||||
<div className="d-check-radio light mr10">
|
||||
<input
|
||||
type="radio"
|
||||
name="surfaceType"
|
||||
value="Ⅲ・Ⅳ"
|
||||
id="surfaceType0"
|
||||
{...form.register('surfaceType')}
|
||||
onChange={(e) => {
|
||||
handleRadioChange(e)
|
||||
}}
|
||||
/>
|
||||
<label htmlFor="surfaceType0">Ⅲ・Ⅳ</label>
|
||||
</div>
|
||||
<div className="d-check-radio light mr10">
|
||||
<input
|
||||
type="radio"
|
||||
name="surfaceType"
|
||||
value="Ⅱ"
|
||||
id="surfaceType1"
|
||||
{...form.register('surfaceType')}
|
||||
onChange={(e) => {
|
||||
handleRadioChange(e)
|
||||
}}
|
||||
/>
|
||||
<label htmlFor="surfaceType1">Ⅱ</label>
|
||||
<div className="radio-wrap flx-box" style={{ width: '239px' }}>
|
||||
<div className="d-check-radio light mr10">
|
||||
<input
|
||||
type="radio"
|
||||
name="surfaceType"
|
||||
value="Ⅲ・Ⅳ"
|
||||
id="surfaceType0"
|
||||
{...form.register('surfaceType')}
|
||||
onChange={(e) => {
|
||||
handleRadioChange(e)
|
||||
}}
|
||||
/>
|
||||
<label htmlFor="surfaceType0">Ⅲ・Ⅳ</label>
|
||||
</div>
|
||||
<div className="d-check-radio light">
|
||||
<input
|
||||
type="radio"
|
||||
name="surfaceType"
|
||||
value="Ⅱ"
|
||||
id="surfaceType1"
|
||||
{...form.register('surfaceType')}
|
||||
onChange={(e) => {
|
||||
handleRadioChange(e)
|
||||
}}
|
||||
/>
|
||||
<label htmlFor="surfaceType1">Ⅱ</label>
|
||||
</div>
|
||||
</div>
|
||||
<div className="d-check-box light mr5">
|
||||
<input type="checkbox" id="saltAreaFlg" {...form.register('saltAreaFlg')} />
|
||||
@ -1846,8 +1860,7 @@ export default function StuffDetail() {
|
||||
<div className="flx-box">
|
||||
<div className="product-input-wrap mr5">
|
||||
<input type="text" className="product-input" readOnly value={form.watch('planReqNo') || ''} />
|
||||
{/* {detailData?.tempFlg === '1' && form.watch('planReqNo') ? ( */}
|
||||
{detailData?.tempFlg === '1' && form.watch('planReqNo') ? (
|
||||
{managementState?.tempFlg === '1' && form.watch('planReqNo') ? (
|
||||
<button
|
||||
type="button"
|
||||
className="product-delete"
|
||||
@ -1857,8 +1870,7 @@ export default function StuffDetail() {
|
||||
></button>
|
||||
) : null}
|
||||
</div>
|
||||
{/* {detailData?.tempFlg === '1' ? ( */}
|
||||
{detailData?.tempFlg === '1' ? (
|
||||
{managementState?.tempFlg === '1' ? (
|
||||
<>
|
||||
<Button className="btn-origin grey" onPress={onSearchDesignRequestPopOpen}>
|
||||
{getMessage('stuff.planReqPopup.title')}
|
||||
@ -1964,8 +1976,8 @@ export default function StuffDetail() {
|
||||
onChange={onSelectionChange}
|
||||
getOptionLabel={(x) => x.saleStoreName}
|
||||
getOptionValue={(x) => x.saleStoreId}
|
||||
isClearable={detailData.tempFlg === '0' ? false : session?.storeLvl === '1' ? true : false}
|
||||
isDisabled={detailData.tempFlg === '0' ? true : session?.storeLvl !== '1' ? true : false}
|
||||
isClearable={managementState.tempFlg === '0' ? false : session?.storeLvl === '1' ? true : false}
|
||||
isDisabled={managementState.tempFlg === '0' ? true : session?.storeLvl !== '1' ? true : false}
|
||||
value={saleStoreList.filter(function (option) {
|
||||
return option.saleStoreId === selOptions
|
||||
})}
|
||||
@ -1998,7 +2010,13 @@ export default function StuffDetail() {
|
||||
getOptionValue={(x) => x.saleStoreId}
|
||||
isClearable={false}
|
||||
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) {
|
||||
return option.saleStoreId === selOptions
|
||||
@ -2077,9 +2095,9 @@ export default function StuffDetail() {
|
||||
getOptionLabel={(x) => x.saleStoreName}
|
||||
getOptionValue={(x) => x.saleStoreId}
|
||||
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) {
|
||||
return option.saleStoreId === otherSelOptions
|
||||
})}
|
||||
@ -2240,32 +2258,35 @@ export default function StuffDetail() {
|
||||
</th>
|
||||
<td>
|
||||
<div className="flx-box">
|
||||
<div className="d-check-radio light mr10">
|
||||
<input
|
||||
type="radio"
|
||||
name="surfaceType"
|
||||
value="Ⅲ・Ⅳ"
|
||||
id="surfaceType0"
|
||||
{...form.register('surfaceType')}
|
||||
onChange={(e) => {
|
||||
handleRadioChange(e)
|
||||
}}
|
||||
/>
|
||||
<label htmlFor="surfaceType0">Ⅲ・Ⅳ</label>
|
||||
</div>
|
||||
<div className="d-check-radio light mr10">
|
||||
<input
|
||||
type="radio"
|
||||
name="surfaceType"
|
||||
value="Ⅱ"
|
||||
id="surfaceType1"
|
||||
{...form.register('surfaceType')}
|
||||
onChange={(e) => {
|
||||
handleRadioChange(e)
|
||||
}}
|
||||
/>
|
||||
<label htmlFor="surfaceType1">Ⅱ</label>
|
||||
<div className="radio-wrap flx-box" style={{ width: '239px' }}>
|
||||
<div className="d-check-radio light mr10">
|
||||
<input
|
||||
type="radio"
|
||||
name="surfaceType"
|
||||
value="Ⅲ・Ⅳ"
|
||||
id="surfaceType0"
|
||||
{...form.register('surfaceType')}
|
||||
onChange={(e) => {
|
||||
handleRadioChange(e)
|
||||
}}
|
||||
/>
|
||||
<label htmlFor="surfaceType0">Ⅲ・Ⅳ</label>
|
||||
</div>
|
||||
<div className="d-check-radio light">
|
||||
<input
|
||||
type="radio"
|
||||
name="surfaceType"
|
||||
value="Ⅱ"
|
||||
id="surfaceType1"
|
||||
{...form.register('surfaceType')}
|
||||
onChange={(e) => {
|
||||
handleRadioChange(e)
|
||||
}}
|
||||
/>
|
||||
<label htmlFor="surfaceType1">Ⅱ</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="d-check-box light mr5">
|
||||
<input type="checkbox" id="saltAreaFlg" {...form.register('saltAreaFlg')} />
|
||||
<label htmlFor="saltAreaFlg">{getMessage('stuff.detail.saltAreaFlg')}</label>
|
||||
@ -2329,8 +2350,7 @@ export default function StuffDetail() {
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{/* {detailData?.tempFlg === '0' ? ( */}
|
||||
{detailData?.tempFlg === '0' ? (
|
||||
{managementState?.tempFlg === '0' ? (
|
||||
<>
|
||||
{/* 진짜R 플랜시작 */}
|
||||
<div className="table-box-title-wrap">
|
||||
@ -2339,7 +2359,7 @@ export default function StuffDetail() {
|
||||
<ul className="info-wrap">
|
||||
<li>
|
||||
{getMessage('stuff.detail.planList.cnt')}
|
||||
<span className="red"> {detailData.planList?.length}</span>
|
||||
<span className="red"> {managementState.planList?.length}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@ -1,40 +1,21 @@
|
||||
'use client'
|
||||
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import { useAxios } from '@/hooks/useAxios'
|
||||
import { useRouter, useSearchParams } from 'next/navigation'
|
||||
import { globalLocaleStore } from '@/store/localeAtom'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { useContext } from 'react'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
import { ManagementContext } from '@/app/management/ManagementProvider'
|
||||
export default function StuffHeader() {
|
||||
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(() => {
|
||||
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 { managementState } = useContext(ManagementContext)
|
||||
|
||||
//물건번호 복사
|
||||
const copyObjectNo = async (objectNo) => {
|
||||
await navigator.clipboard.writeText(objectNo)
|
||||
alert(getMessage('stuff.detail.header.message2'))
|
||||
alert(getMessage('stuff.detail.header.successCopy'))
|
||||
try {
|
||||
} 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="info-title">{getMessage('stuff.detail.header.objectNo')}</div>
|
||||
<div className="info-inner">
|
||||
{headerData.objectNo}{' '}
|
||||
{managementState?.objectNo}{' '}
|
||||
<button
|
||||
className="copy-ico"
|
||||
onClick={() => {
|
||||
copyObjectNo(headerData.objectNo)
|
||||
copyObjectNo(managementState?.objectNo)
|
||||
}}
|
||||
></button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="sub-table-box">
|
||||
<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 className="sub-table-box">
|
||||
<div className="info-title">{getMessage('stuff.detail.header.lastEditDatetime')}</div>
|
||||
<div className="info-inner">
|
||||
{headerData?.lastEditDatetime ? `${dayjs(headerData.lastEditDatetime).format('YYYY.MM.DD HH:mm:ss')}` : ''}{' '}
|
||||
{headerData?.lastEditUserName ? `(${headerData.lastEditUserName})` : null}
|
||||
{managementState?.lastEditDatetime ? `${dayjs(managementState.lastEditDatetime).format('YYYY.MM.DD HH:mm:ss')}` : ''}{' '}
|
||||
{managementState?.lastEditUserName ? `(${managementState.lastEditUserName})` : null}
|
||||
</div>
|
||||
</div>
|
||||
<div className="sub-table-box">
|
||||
<div className="info-title">{getMessage('stuff.detail.header.createDatetime')}</div>
|
||||
<div className="info-inner">
|
||||
{headerData?.createDatetime ? `${dayjs(headerData.lastEditDatetime).format('YYYY.MM.DD')}` : ''}{' '}
|
||||
{headerData?.createUserName ? `(${headerData.createUserName})` : null}
|
||||
{managementState?.createDatetime ? `${dayjs(managementState.lastEditDatetime).format('YYYY.MM.DD')}` : ''}{' '}
|
||||
{managementState?.createUserName ? `(${managementState.createUserName})` : null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -80,7 +80,8 @@ export default function StuffSearchCondition() {
|
||||
schAddress: address ? address : '',
|
||||
schObjectName: objectName ? objectName : '',
|
||||
schDispCompanyName: dispCompanyName ? dispCompanyName : '',
|
||||
schSelSaleStoreId: stuffSearch?.schOtherSelSaleStoreId ? stuffSearch.schOtherSelSaleStoreId : stuffSearch.schSelSaleStoreId,
|
||||
schSelSaleStoreId: stuffSearch?.schSelSaleStoreId ? stuffSearch.schSelSaleStoreId : '',
|
||||
schOtherSelSaleStoreId: stuffSearch?.schOtherSelSaleStoreId ? stuffSearch.schOtherSelSaleStoreId : '',
|
||||
schReceiveUser: receiveUser ? receiveUser : '',
|
||||
schDateType: dateType,
|
||||
schFromDt: dayjs(startDate).format('YYYY-MM-DD'),
|
||||
@ -90,6 +91,24 @@ export default function StuffSearchCondition() {
|
||||
endRow: stuffSearch?.endRow ? stuffSearch.endRow : 100,
|
||||
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 {
|
||||
setStuffSearch({
|
||||
schObjectNo: objectNo,
|
||||
@ -97,7 +116,8 @@ export default function StuffSearchCondition() {
|
||||
schAddress: address,
|
||||
schObjectName: objectName,
|
||||
schDispCompanyName: dispCompanyName,
|
||||
schSelSaleStoreId: stuffSearch?.schOtherSelSaleStoreId ? stuffSearch.schOtherSelSaleStoreId : stuffSearch.schSelSaleStoreId,
|
||||
schSelSaleStoreId: stuffSearch?.schSelSaleStoreId,
|
||||
schOtherSelSaleStoreId: stuffSearch?.schOtherSelSaleStoreId,
|
||||
schReceiveUser: receiveUser,
|
||||
schDateType: dateType,
|
||||
schFromDt: dayjs(startDate).format('YYYY-MM-DD'),
|
||||
@ -119,7 +139,7 @@ export default function StuffSearchCondition() {
|
||||
objectNameRef.current.value = ''
|
||||
dispCompanyNameRef.current.value = ''
|
||||
receiveUserRef.current.value = ''
|
||||
|
||||
stuffSearch.schDateType = 'U'
|
||||
setObjectNo('')
|
||||
setAddress('')
|
||||
setobjectName('')
|
||||
@ -131,10 +151,10 @@ export default function StuffSearchCondition() {
|
||||
setEndDate(dayjs(new Date()).format('YYYY-MM-DD'))
|
||||
if (session?.storeId === 'T01') {
|
||||
setSchSelSaleStoreId('')
|
||||
setOtherSaleStoreId('')
|
||||
handleClear1() //판매대리점선택 자동완성 클리어
|
||||
resetStuffRecoil()
|
||||
setStuffSearch({
|
||||
...stuffSearch,
|
||||
schSelSaleStoreId: '',
|
||||
schOtherSelSaleStoreId: '',
|
||||
})
|
||||
@ -184,30 +204,24 @@ export default function StuffSearchCondition() {
|
||||
setSchSelSaleStoreList(allList)
|
||||
setFavoriteStoreList(favList)
|
||||
setShowSaleStoreList(favList)
|
||||
// setSchSelSaleStoreId(session?.storeId)
|
||||
setStuffSearch({
|
||||
...stuffSearch,
|
||||
code: 'S',
|
||||
// schSelSaleStoreId: session?.storeId,
|
||||
})
|
||||
if (stuffSearch.schSelSaleStoreId != '') {
|
||||
setSchSelSaleStoreId(stuffSearch.schSelSaleStoreId)
|
||||
url = `/api/object/saleStore/${stuffSearch.schSelSaleStoreId}/list?firstFlg=1&userId=${session?.userId}`
|
||||
get({ url: url }).then((res) => {
|
||||
if (!isEmptyArray(res)) {
|
||||
res.map((row) => {
|
||||
row.value = row.saleStoreId
|
||||
row.label = row.saleStoreName
|
||||
})
|
||||
|
||||
//T01일때 2차 판매점 호출하기 디폴트로 1차점을 본인으로 셋팅해서 세션storeId사용
|
||||
// 디폴트 셋팅 안하기로
|
||||
// url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=0&userId=${session?.userId}`
|
||||
|
||||
// get({ url: url }).then((res) => {
|
||||
// if (!isEmptyArray(res)) {
|
||||
// res.map((row) => {
|
||||
// row.value = row.saleStoreId
|
||||
// row.label = row.saleStoreName
|
||||
// })
|
||||
|
||||
// otherList = res
|
||||
// setOtherSaleStoreList(otherList)
|
||||
// } else {
|
||||
// setOtherSaleStoreList([])
|
||||
// }
|
||||
// })
|
||||
otherList = res.filter((row) => row.saleStoreLevel !== '1')
|
||||
setOtherSaleStoreList(otherList)
|
||||
setOtherSaleStoreId(stuffSearch.schOtherSelSaleStoreId)
|
||||
} else {
|
||||
setOtherSaleStoreList([])
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
if (session?.storeLvl === '1') {
|
||||
allList = res
|
||||
@ -221,11 +235,9 @@ export default function StuffSearchCondition() {
|
||||
|
||||
setOtherSaleStoreList(otherList)
|
||||
|
||||
setStuffSearch({
|
||||
...stuffSearch,
|
||||
code: 'S',
|
||||
schSelSaleStoreId: allList[0].saleStoreId,
|
||||
})
|
||||
if (stuffSearch.schOtherSelSaleStoreId != '') {
|
||||
setOtherSaleStoreId(stuffSearch.schOtherSelSaleStoreId)
|
||||
}
|
||||
} else {
|
||||
//10X22, 201X112 그냥2차점
|
||||
//2차점인데 34들고있는애 202X217
|
||||
@ -241,7 +253,8 @@ export default function StuffSearchCondition() {
|
||||
setStuffSearch({
|
||||
...stuffSearch,
|
||||
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) => {
|
||||
if (key !== '') {
|
||||
setShowSaleStoreList(schSelSaleStoreList)
|
||||
setOtherSaleStoreList([])
|
||||
// setOtherSaleStoreList([])
|
||||
} else {
|
||||
setShowSaleStoreList(favoriteStoreList)
|
||||
}
|
||||
@ -298,8 +311,9 @@ export default function StuffSearchCondition() {
|
||||
})
|
||||
} else {
|
||||
//X누름
|
||||
//화면에선 지우는데 리코일은 조회누르지 않으면 보존
|
||||
setSchSelSaleStoreId('')
|
||||
stuffSearch.schSelSaleStoreId = ''
|
||||
// stuffSearch.schSelSaleStoreId = ''
|
||||
|
||||
//2차점 판매점목록비우기
|
||||
setOtherSaleStoreList([])
|
||||
@ -311,12 +325,26 @@ export default function StuffSearchCondition() {
|
||||
if (isObjectNotEmpty(key)) {
|
||||
setOtherSaleStoreId(key.saleStoreId)
|
||||
stuffSearch.schOtherSelSaleStoreId = key.saleStoreId
|
||||
|
||||
//2차점 골랐을때 1차점 값
|
||||
stuffSearch.schSelSaleStoreId = schSelSaleStoreId
|
||||
} else {
|
||||
//X누르면 검색조건에 1차점으로 셋팅
|
||||
setOtherSaleStoreId('')
|
||||
setSchSelSaleStoreId(schSelSaleStoreId)
|
||||
stuffSearch.schOtherSelSaleStoreId = ''
|
||||
stuffSearch.schSelSaleStoreId = schSelSaleStoreId
|
||||
if (session.storeLvl === '1') {
|
||||
if (stuffSearch.schOtherSelSaleStoreId === '') {
|
||||
// 화면에선 지우는데 조회누르기 전이면 리코일은 남김
|
||||
// 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 !== '') {
|
||||
return option.saleStoreId === schSelSaleStoreId
|
||||
} else {
|
||||
if (stuffSearch?.schSelSaleStoreId !== '') {
|
||||
if (stuffSearch?.code === 'FINISH') {
|
||||
return option.saleStoreId === schSelSaleStoreId
|
||||
} else if (stuffSearch?.schSelSaleStoreId !== '') {
|
||||
return option.saleStoreId === stuffSearch.schSelSaleStoreId
|
||||
} else {
|
||||
return false
|
||||
@ -531,7 +561,9 @@ export default function StuffSearchCondition() {
|
||||
} else if (stuffSearch?.code === 'E' && schSelSaleStoreId !== '') {
|
||||
return option.saleStoreId === schSelSaleStoreId
|
||||
} else {
|
||||
if (stuffSearch?.schSelSaleStoreId !== '') {
|
||||
if (stuffSearch?.code === 'FINISH') {
|
||||
return option.saleStoreId === schSelSaleStoreId
|
||||
} else if (stuffSearch?.schSelSaleStoreId !== '') {
|
||||
return option.saleStoreId === stuffSearch.schSelSaleStoreId
|
||||
} else {
|
||||
return false
|
||||
@ -606,10 +638,11 @@ export default function StuffSearchCondition() {
|
||||
type="radio"
|
||||
name="radio_ptype"
|
||||
id="radio_u"
|
||||
checked={dateType === 'U' ? true : false}
|
||||
checked={stuffSearch.schDateType === 'U' ? true : false}
|
||||
value={'U'}
|
||||
onChange={(e) => {
|
||||
setDateType(e.target.value)
|
||||
stuffSearch.schDateType = e.target.value
|
||||
}}
|
||||
/>
|
||||
<label htmlFor="radio_u">{getMessage('stuff.search.schDateTypeU')}</label>
|
||||
@ -619,10 +652,11 @@ export default function StuffSearchCondition() {
|
||||
type="radio"
|
||||
name="radio_ptype"
|
||||
id="radio_r"
|
||||
checked={dateType === 'R' ? true : false}
|
||||
checked={stuffSearch.schDateType === 'R' ? true : false}
|
||||
value={'R'}
|
||||
onChange={(e) => {
|
||||
setDateType(e.target.value)
|
||||
stuffSearch.schDateType = e.target.value
|
||||
}}
|
||||
/>
|
||||
<label htmlFor="radio_r">{getMessage('stuff.search.schDateTypeR')}</label>
|
||||
|
||||
@ -98,8 +98,8 @@ export default function PlanRequestPop(props) {
|
||||
schPlanReqName: schPlanReqName,
|
||||
schPlanStatCd: schPlanStatCd,
|
||||
schDateGbn: schDateGbn,
|
||||
schStartDt: dayjs(startDate).format('YYYY-MM-DD'),
|
||||
schEndDt: dayjs(endDate).format('YYYY-MM-DD'),
|
||||
schStartDt: startDate ? dayjs(startDate).format('YYYY-MM-DD') : '',
|
||||
schEndDt: endDate ? dayjs(endDate).format('YYYY-MM-DD') : '',
|
||||
startRow: type === 'S' ? (1 - 1) * pageSize + 1 : (page - 1) * pageSize + 1,
|
||||
endRow: type === 'S' ? 1 * pageSize : page * pageSize,
|
||||
}
|
||||
@ -226,7 +226,7 @@ export default function PlanRequestPop(props) {
|
||||
],
|
||||
})
|
||||
|
||||
//설계의뢰 그리드에서 선택한 설계의로 정보
|
||||
//설계의뢰 그리드에서 선택한 설계의뢰 정보
|
||||
const getSelectedRowdata = (data) => {
|
||||
if (isNotEmptyArray(data)) {
|
||||
setPlanReqObject(data[0])
|
||||
@ -257,6 +257,16 @@ export default function PlanRequestPop(props) {
|
||||
const handleKeyUp = (e) => {
|
||||
let input = e.target
|
||||
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 (
|
||||
@ -324,6 +334,7 @@ export default function PlanRequestPop(props) {
|
||||
onChange={(e) => {
|
||||
setSchTitle(e.target.value)
|
||||
}}
|
||||
onKeyUp={handleByOnKeyUp}
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
@ -337,6 +348,7 @@ export default function PlanRequestPop(props) {
|
||||
onChange={(e) => {
|
||||
setSchAddress(e.target.value)
|
||||
}}
|
||||
onKeyUp={handleByOnKeyUp}
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
@ -352,6 +364,7 @@ export default function PlanRequestPop(props) {
|
||||
onChange={(e) => {
|
||||
setSchSaleStoreName(e.target.value)
|
||||
}}
|
||||
onKeyUp={handleByOnKeyUp}
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
@ -365,6 +378,7 @@ export default function PlanRequestPop(props) {
|
||||
onChange={(e) => {
|
||||
setSchPlanReqName(e.target.value)
|
||||
}}
|
||||
onKeyUp={handleByOnKeyUp}
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
@ -440,7 +454,7 @@ export default function PlanRequestPop(props) {
|
||||
<div className="design-request-count">
|
||||
<div className="design-request-grid-tit">Plan List</div>
|
||||
<div className="select-wrap">
|
||||
<select className="select-light" name="" id="" onChange={onChangePerPage}>
|
||||
<select className="select-light" onChange={onChangePerPage}>
|
||||
<option>20</option>
|
||||
<option>40</option>
|
||||
<option>60</option>
|
||||
|
||||
@ -1,16 +1,14 @@
|
||||
import { useEffect } from 'react'
|
||||
import { useRecoilValue, useRecoilState } from 'recoil'
|
||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||
import { wordDisplaySelector } from '@/store/settingAtom'
|
||||
import { useEvent } from '@/hooks/useEvent'
|
||||
import { checkLineOrientation, getDistance } from '@/util/canvas-util'
|
||||
import { dimensionLineSettingsState } from '@/store/commonUtilsAtom'
|
||||
import { commonUtilsState, dimensionLineSettingsState } from '@/store/commonUtilsAtom'
|
||||
import { fontSelector } from '@/store/fontAtom'
|
||||
import { canvasState } from '@/store/canvasAtom'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { usePopup } from '@/hooks/usePopup'
|
||||
import Distance from '@/components/floor-plan/modal/distance/Distance'
|
||||
import { commonUtilsState } from '@/store/commonUtilsAtom'
|
||||
import { center, point } from '@turf/turf'
|
||||
|
||||
export function useCommonUtils() {
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
@ -39,6 +37,7 @@ export function useCommonUtils() {
|
||||
commonTextKeyEvent()
|
||||
addCanvasMouseEventListener('mouse:down', (event) => {
|
||||
const pointer = canvas?.getPointer(event.e)
|
||||
|
||||
textbox = new fabric.Textbox('', {
|
||||
left: pointer.x,
|
||||
top: pointer.y,
|
||||
@ -49,7 +48,8 @@ export function useCommonUtils() {
|
||||
fill: commonTextFont.fontColor.value,
|
||||
fontFamily: commonTextFont.fontFamily.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,
|
||||
lockMovementX: true,
|
||||
lockMovementY: true,
|
||||
@ -262,7 +262,8 @@ export function useCommonUtils() {
|
||||
fill: dimensionLineTextFont.fontColor.value,
|
||||
fontSize: dimensionLineTextFont.fontSize.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,
|
||||
textAlign: 'center',
|
||||
originX: 'center',
|
||||
@ -710,7 +711,7 @@ export function useCommonUtils() {
|
||||
canvas?.remove(centerLine, ...extendLine, ...arrows, textObj)
|
||||
|
||||
const reGroup = new fabric.Group(reGroupObj, {
|
||||
name: 'dimensionLine',
|
||||
name: 'dimensionGroup',
|
||||
selectable: true,
|
||||
lineDirection: originLineDirection,
|
||||
groupId: id,
|
||||
|
||||
@ -17,8 +17,8 @@ export function useFont() {
|
||||
textObjs.forEach((obj) => {
|
||||
obj.set({
|
||||
fontFamily: commonText.fontFamily.value,
|
||||
fontWeight: lengthText.fontWeight.value.toLowerCase().includes('bold') ? 'bold' : 'normal',
|
||||
fontStyle: lengthText.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal',
|
||||
fontWeight: commonText.fontWeight.value.toLowerCase().includes('bold') ? 'bold' : 'normal',
|
||||
fontStyle: commonText.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal',
|
||||
fontSize: commonText.fontSize.value,
|
||||
fill: commonText.fontColor.value,
|
||||
})
|
||||
@ -33,8 +33,8 @@ export function useFont() {
|
||||
textObjs.forEach((obj) => {
|
||||
obj.set({
|
||||
fontFamily: dimensionLineText.fontFamily.value,
|
||||
fontWeight: lengthText.fontWeight.value.toLowerCase().includes('bold') ? 'bold' : 'normal',
|
||||
fontStyle: lengthText.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal',
|
||||
fontWeight: dimensionLineText.fontWeight.value.toLowerCase().includes('bold') ? 'bold' : 'normal',
|
||||
fontStyle: dimensionLineText.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal',
|
||||
fontSize: dimensionLineText.fontSize.value,
|
||||
fill: dimensionLineText.fontColor.value,
|
||||
})
|
||||
@ -49,8 +49,8 @@ export function useFont() {
|
||||
textObjs.forEach((obj) => {
|
||||
obj.set({
|
||||
fontFamily: flowText.fontFamily.value,
|
||||
fontWeight: lengthText.fontWeight.value.toLowerCase().includes('bold') ? 'bold' : 'normal',
|
||||
fontStyle: lengthText.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal',
|
||||
fontWeight: flowText.fontWeight.value.toLowerCase().includes('bold') ? 'bold' : 'normal',
|
||||
fontStyle: flowText.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal',
|
||||
fontSize: flowText.fontSize.value,
|
||||
fill: flowText.fontColor.value,
|
||||
})
|
||||
|
||||
@ -7,7 +7,7 @@ import { convertDwgToPng } from '@/lib/cadAction'
|
||||
import { useAxios } from '../useAxios'
|
||||
import { currentCanvasPlanState } from '@/store/canvasAtom'
|
||||
|
||||
export default function useRefFiles() {
|
||||
export function useRefFiles() {
|
||||
const converterUrl = process.env.NEXT_PUBLIC_CONVERTER_API_URL
|
||||
const [refImage, setRefImage] = useState(null)
|
||||
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` })
|
||||
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(() => {
|
||||
const handleCurrentPlan = async () => {
|
||||
await promisePut({ url: '/api/canvas-management/canvas-statuses', data: currentCanvasPlan }).then((res) => {
|
||||
console.log('🚀 ~ awaitpromisePut ~ res:', res)
|
||||
})
|
||||
}
|
||||
handleCurrentPlan()
|
||||
}, [currentCanvasPlan])
|
||||
// useEffect(() => {
|
||||
// const handleCurrentPlan = async () => {
|
||||
// await promisePut({ url: '/api/canvas-management/canvas-statuses', data: currentCanvasPlan }).then((res) => {
|
||||
// console.log('🚀 ~ awaitpromisePut ~ res:', res)
|
||||
// })
|
||||
// }
|
||||
// handleCurrentPlan()
|
||||
// }, [currentCanvasPlan])
|
||||
|
||||
/**
|
||||
* RefFile이 캐드 도면 파일일 경우 변환하여 이미지로 저장
|
||||
|
||||
@ -19,37 +19,18 @@ const defaultEstimateData = {
|
||||
charger: '', //담당자
|
||||
objectName: '', //안건명
|
||||
objectNameOmit: '', //경칭코드
|
||||
estimateType: 'YJOD', //주문분류
|
||||
estimateType: '', //주문분류
|
||||
remarks: '', //비고
|
||||
estimateOption: '', //견적특이사항
|
||||
//아이템에 필요없는거 빼기
|
||||
itemList: [
|
||||
// {
|
||||
// amount: '',
|
||||
// fileUploadFlg: '',
|
||||
// itemChangeFlg: '',
|
||||
// itemGroup: '',
|
||||
// itemId: '', //키값??
|
||||
// itemName: '',
|
||||
// itemNo: '',
|
||||
// moduleFlg: '',
|
||||
// objectNo: '',
|
||||
// pkgMaterialFlg: '',
|
||||
// planNo: '',
|
||||
// pnowW: '',
|
||||
// salePrice: '',
|
||||
// saleTotPrice: '',
|
||||
// specification: '',
|
||||
// unit: '',
|
||||
// },
|
||||
],
|
||||
itemList: [],
|
||||
fileList: [],
|
||||
fileFlg: '0', //후일 자료 제출 (체크 1 노체크 0)
|
||||
priceCd: '',
|
||||
}
|
||||
|
||||
// Helper functions
|
||||
const updateItemInList = (itemList, itemId, updates) => {
|
||||
return itemList.map((item) => (item.itemId === itemId ? { ...item, ...updates } : item))
|
||||
const updateItemInList = (itemList, dispOrder, updates) => {
|
||||
return itemList.map((item) => (item.dispOrder === dispOrder ? { ...item, ...updates } : item))
|
||||
}
|
||||
|
||||
export const useEstimateController = (planNo) => {
|
||||
@ -88,34 +69,32 @@ export const useEstimateController = (planNo) => {
|
||||
}
|
||||
}
|
||||
|
||||
const updateItem = (itemId, updates) => {
|
||||
const updateItem = (dispOrder, updates) => {
|
||||
setState({
|
||||
itemList: updateItemInList(state.itemList, itemId, updates),
|
||||
itemList: updateItemInList(state.itemList, dispOrder, updates),
|
||||
})
|
||||
}
|
||||
|
||||
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({
|
||||
itemList: [
|
||||
...state.itemList,
|
||||
{
|
||||
itemId: newItemId,
|
||||
amount: '',
|
||||
fileUploadFlg: '',
|
||||
itemChangeFlg: '',
|
||||
itemGroup: '',
|
||||
itemName: '',
|
||||
objectNo: objectRecoil.floorPlanObjectNo,
|
||||
planNo: planNo,
|
||||
dispOrder: newItemDispOrder.toString(),
|
||||
itemId: '', //제품번호
|
||||
itemNo: '',
|
||||
moduleFlg: '',
|
||||
objectNo: '',
|
||||
pkgMaterialFlg: '',
|
||||
planNo: '',
|
||||
pnowW: '',
|
||||
salePrice: '',
|
||||
saleTotPrice: '',
|
||||
specification: '',
|
||||
unit: '',
|
||||
itemName: '', //형명
|
||||
amount: '', //수량
|
||||
unitPrice: '0',
|
||||
unit: '', //단위
|
||||
salePrice: '0', //단가
|
||||
saleTotPrice: '0', //금액(부가세별도)
|
||||
itemChangeFlg: '1', //추가시 체인지플래그 1로
|
||||
partAdd: '1', //NEW 체인지 플래그
|
||||
delFlg: '0', //삭제 플래그 0 삭제하면 1
|
||||
},
|
||||
],
|
||||
})
|
||||
@ -125,53 +104,83 @@ export const useEstimateController = (planNo) => {
|
||||
setEstimateData({ ...state, userId: session.userId, sapSalesStoreCd: session.custCd })
|
||||
}, [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 () => {
|
||||
//0. 필수체크
|
||||
let flag = true
|
||||
console.log('::담긴 estimateData:::', estimateData)
|
||||
// console.log('첨부파일:::::', estimateData.fileList)
|
||||
//첨부파일을 첨부안했는데
|
||||
//아이템 fileUploadFlg가1(첨부파일 필수)이 1개라도 있는데 후일 자료 제출(fileFlg) 체크안했으면(0) alert 저장안돼
|
||||
if (estimateData.itemList.length > 1) {
|
||||
estimateData.itemList.map((row) => {
|
||||
if (row.fileUploadFlg === '1') {
|
||||
if (estimateData.fileFlg === '0') {
|
||||
alert(getMessage('estimate.detail.save.requiredMsg'))
|
||||
flag = false
|
||||
if (estimateData.fileList.length < 1) {
|
||||
if (estimateData.itemList.length > 1) {
|
||||
estimateData.itemList.map((row) => {
|
||||
if (row.fileUploadFlg === '1') {
|
||||
if (estimateData.fileFlg === '0') {
|
||||
alert(getMessage('estimate.detail.save.requiredMsg'))
|
||||
flag = false
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (flag) {
|
||||
//1. 첨부파일 저장
|
||||
const formData = new FormData()
|
||||
console.log('첨부파일:!!!', estimateData.fileList)
|
||||
formData.append('file', estimateData.fileList)
|
||||
formData.append('objectNo', estimateData.objectNo)
|
||||
formData.append('planNo', estimateData.planNo)
|
||||
formData.append('category', '10')
|
||||
formData.append('userId', estimateData.userId)
|
||||
for (const value of formData.values()) {
|
||||
console.log('formData::', value)
|
||||
}
|
||||
|
||||
await promisePost({ url: '/api/file/fileUpload', data: formData }).then((res) => {
|
||||
console.log('파일저장결과::::::::::', res)
|
||||
})
|
||||
await post({ url: '/api/file/fileUpload', data: formData })
|
||||
|
||||
//2. 상세데이터 저장
|
||||
|
||||
console.log('상세저장시작!!')
|
||||
return
|
||||
try {
|
||||
const result = await promisePost({
|
||||
url: ESTIMATE_API_ENDPOINT,
|
||||
data: estimateData,
|
||||
})
|
||||
alert(getMessage('estimate.detail.save.alertMsg'))
|
||||
return result
|
||||
} catch (error) {
|
||||
console.error('Failed to submit estimate:', error)
|
||||
throw error
|
||||
}
|
||||
await promisePost({ url: `${ESTIMATE_API_ENDPOINT}/save-estimate`, data: estimateData }).then((res) => {
|
||||
if (res) {
|
||||
alert(getMessage('estimate.detail.save.alertMsg'))
|
||||
}
|
||||
})
|
||||
|
||||
// try {
|
||||
// const result = await promisePost({
|
||||
// url: ESTIMATE_API_ENDPOINT,
|
||||
// data: estimateData,
|
||||
// })
|
||||
// alert(getMessage('estimate.detail.save.alertMsg'))
|
||||
// return result
|
||||
// } catch (error) {
|
||||
// console.error('Failed to submit estimate:', error)
|
||||
// throw error
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@ -182,5 +191,6 @@ export const useEstimateController = (planNo) => {
|
||||
addItem,
|
||||
handleEstimateSubmit,
|
||||
fetchSetting,
|
||||
handleEstimateFileDownload,
|
||||
}
|
||||
}
|
||||
|
||||
666
src/hooks/module/useModuleBasicSetting.js
Normal file
666
src/hooks/module/useModuleBasicSetting.js
Normal 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,
|
||||
}
|
||||
}
|
||||
@ -283,7 +283,7 @@ export function useCanvasSetting() {
|
||||
optionName = ['roof', POLYGON_TYPE.ROOF]
|
||||
break
|
||||
case 'wordDisplay': //문자 표시
|
||||
optionName = ['6']
|
||||
optionName = ['commonText']
|
||||
break
|
||||
case 'circuitNumDisplay': //회로번호 표시
|
||||
optionName = ['7']
|
||||
@ -313,6 +313,8 @@ export function useCanvasSetting() {
|
||||
//obj.set({ visible: !obj.visible })
|
||||
})
|
||||
|
||||
canvas.renderAll()
|
||||
|
||||
// console.log(
|
||||
// 'optionName',
|
||||
// optionName,
|
||||
|
||||
@ -40,7 +40,7 @@ export function useFirstOption() {
|
||||
optionName = ['roof', POLYGON_TYPE.ROOF]
|
||||
break
|
||||
case 'wordDisplay': //문자 표시
|
||||
optionName = ['6']
|
||||
optionName = ['commonText']
|
||||
break
|
||||
case 'circuitNumDisplay': //회로번호 표시
|
||||
optionName = ['7']
|
||||
|
||||
30
src/hooks/popup/useFlowDirectionSetting.js
Normal file
30
src/hooks/popup/useFlowDirectionSetting.js
Normal 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 }
|
||||
}
|
||||
25
src/hooks/popup/useOrientation.js
Normal file
25
src/hooks/popup/useOrientation.js
Normal 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 }
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
import { useEffect, useRef } from 'react'
|
||||
import { distanceBetweenPoints } from '@/util/canvas-util'
|
||||
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
|
||||
import { useRecoilState, useRecoilValue, useResetRecoilState, useSetRecoilState } from 'recoil'
|
||||
import {
|
||||
adsorptionPointAddModeState,
|
||||
adsorptionPointModeState,
|
||||
@ -65,6 +65,7 @@ export function useOuterLineWall(id, propertiesId) {
|
||||
const [arrow1, setArrow1] = useRecoilState(outerLineArrow1State)
|
||||
const [arrow2, setArrow2] = useRecoilState(outerLineArrow2State)
|
||||
const [points, setPoints] = useRecoilState(outerLinePointsState)
|
||||
const resetPoints = useResetRecoilState(outerLinePointsState)
|
||||
const [type, setType] = useRecoilState(outerLineTypeState)
|
||||
const [angle1, setAngle1] = useRecoilState(outerLineAngle1State)
|
||||
const [angle2, setAngle2] = useRecoilState(outerLineAngle2State)
|
||||
@ -88,6 +89,13 @@ export function useOuterLineWall(id, propertiesId) {
|
||||
clear()
|
||||
return () => {
|
||||
initEvent()
|
||||
|
||||
canvas
|
||||
.getObjects()
|
||||
.filter((obj) => obj.name === 'startPoint')
|
||||
.forEach((obj) => {
|
||||
canvas.remove(obj)
|
||||
})
|
||||
}
|
||||
}, [verticalHorizontalMode, points, adsorptionPointAddMode, adsorptionPointMode, adsorptionRange, interval, tempGridMode])
|
||||
|
||||
|
||||
@ -107,18 +107,16 @@ export function useRoofAllocationSetting(id) {
|
||||
}
|
||||
})
|
||||
})
|
||||
if (currentObject) {
|
||||
console.log(currentObject.name)
|
||||
if (currentObject.name.toLowerCase().includes('line')) {
|
||||
currentObject.set({
|
||||
strokeWidth: 4,
|
||||
stroke: '#EA10AC',
|
||||
})
|
||||
}
|
||||
if (currentObject && currentObject.name && ['auxiliaryLine', 'ridge', 'hip'].includes(currentObject.name)) {
|
||||
currentObject.set({
|
||||
strokeWidth: 4,
|
||||
stroke: '#EA10AC',
|
||||
})
|
||||
}
|
||||
}, [currentObject])
|
||||
|
||||
useEffect(() => {
|
||||
// canvas.getObjects().filter((obj) => obj.type === 'QLine')
|
||||
const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
|
||||
if (roofBases.length === 0) {
|
||||
swalFire({ text: '할당할 지붕이 없습니다.' })
|
||||
@ -126,10 +124,6 @@ export function useRoofAllocationSetting(id) {
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
console.log('editingLines', editingLines)
|
||||
}, [editingLines])
|
||||
|
||||
const onAddRoofMaterial = () => {
|
||||
setValues([...values, selectedRoofMaterial])
|
||||
}
|
||||
@ -163,7 +157,7 @@ export function useRoofAllocationSetting(id) {
|
||||
const checkInnerLines = () => {
|
||||
const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF) // roofPolygon.innerLines
|
||||
let result = false
|
||||
console.log(roofBases)
|
||||
|
||||
roofBases.forEach((roof) => {
|
||||
roof.innerLines.forEach((line) => {
|
||||
if (!line.attributes.actualSize || line.attributes?.actualSize === 0) {
|
||||
@ -182,7 +176,7 @@ export function useRoofAllocationSetting(id) {
|
||||
}
|
||||
|
||||
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)
|
||||
roofBases.forEach((roofBase) => {
|
||||
try {
|
||||
@ -205,6 +199,10 @@ export function useRoofAllocationSetting(id) {
|
||||
const roofs = canvas.getObjects().filter((obj) => obj.name === 'roof')
|
||||
|
||||
roofs.forEach((roof) => {
|
||||
if (roof.isFixed) return
|
||||
roof.set({
|
||||
isFixed: true,
|
||||
})
|
||||
setSurfaceShapePattern(roof, roofDisplay.column)
|
||||
drawDirectionArrow(roof)
|
||||
})
|
||||
|
||||
@ -167,8 +167,6 @@ export function useRoofShapeSetting(id) {
|
||||
]
|
||||
|
||||
const handleSave = () => {
|
||||
//기존 wallLine 삭제
|
||||
|
||||
let outerLines
|
||||
let direction
|
||||
|
||||
@ -379,20 +377,20 @@ export function useRoofShapeSetting(id) {
|
||||
}
|
||||
|
||||
// 기존 wallLine, roofBase 제거
|
||||
canvas
|
||||
/*canvas
|
||||
.getObjects()
|
||||
.filter((obj) => obj.name === POLYGON_TYPE.WALL)
|
||||
.forEach((line) => {
|
||||
canvas.remove(line)
|
||||
})
|
||||
})*/
|
||||
|
||||
canvas
|
||||
/*canvas
|
||||
.getObjects()
|
||||
.filter((obj) => obj.name === POLYGON_TYPE.ROOF)
|
||||
.forEach((obj) => {
|
||||
canvas.remove(...obj.innerLines)
|
||||
canvas.remove(obj)
|
||||
})
|
||||
})*/
|
||||
|
||||
const polygon = addPolygonByLines(outerLines, { name: POLYGON_TYPE.WALL, direction })
|
||||
polygon.lines = [...outerLines]
|
||||
@ -402,7 +400,6 @@ export function useRoofShapeSetting(id) {
|
||||
|
||||
canvas?.renderAll()
|
||||
roof.drawHelpLine()
|
||||
// setShowRoofShapeSettingModal(false)
|
||||
isFixRef.current = true
|
||||
closePopup(id)
|
||||
}
|
||||
|
||||
@ -13,6 +13,8 @@ import { usePopup } from '@/hooks/usePopup'
|
||||
import { roofDisplaySelector } from '@/store/settingAtom'
|
||||
import { usePolygon } from '@/hooks/usePolygon'
|
||||
import { fontSelector } from '@/store/fontAtom'
|
||||
import { slopeSelector } from '@/store/commonAtom'
|
||||
import { QLine } from '@/components/fabric/QLine'
|
||||
|
||||
export function useSurfaceShapeBatch() {
|
||||
const { getMessage } = useMessage()
|
||||
@ -22,6 +24,7 @@ export function useSurfaceShapeBatch() {
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
const globalPitch = useRecoilValue(globalPitchState)
|
||||
const roofDisplay = useRecoilValue(roofDisplaySelector)
|
||||
const slope = useRecoilValue(slopeSelector(globalPitch))
|
||||
const { swalFire } = useSwal()
|
||||
const { addCanvasMouseEventListener, initEvent } = useEvent()
|
||||
const { closePopup } = usePopup()
|
||||
@ -126,6 +129,7 @@ export function useSurfaceShapeBatch() {
|
||||
addCanvasMouseEventListener('mouse:down', (e) => {
|
||||
isDrawing = false
|
||||
obj.set('name', POLYGON_TYPE.ROOF)
|
||||
obj.set('surfaceId', surfaceId)
|
||||
initEvent()
|
||||
setSurfaceShapePattern(obj, roofDisplay.column)
|
||||
closePopup(id)
|
||||
@ -580,18 +584,19 @@ export function useSurfaceShapeBatch() {
|
||||
text: '배치면 내용을 전부 삭제하시겠습니까?',
|
||||
type: 'confirm',
|
||||
confirmFn: () => {
|
||||
canvas?.getObjects().forEach((obj) => {
|
||||
if (
|
||||
obj.name === POLYGON_TYPE.ROOF ||
|
||||
obj.name === BATCH_TYPE.OPENING ||
|
||||
obj.name === BATCH_TYPE.SHADOW ||
|
||||
obj.name === BATCH_TYPE.TRIANGLE_DORMER ||
|
||||
obj.name === BATCH_TYPE.PENTAGON_DORMER ||
|
||||
obj.name === 'lengthText'
|
||||
) {
|
||||
canvas?.remove(obj)
|
||||
}
|
||||
})
|
||||
// canvas?.getObjects().forEach((obj) => {
|
||||
// if (
|
||||
// obj.name === POLYGON_TYPE.ROOF ||
|
||||
// obj.name === BATCH_TYPE.OPENING ||
|
||||
// obj.name === BATCH_TYPE.SHADOW ||
|
||||
// obj.name === BATCH_TYPE.TRIANGLE_DORMER ||
|
||||
// obj.name === BATCH_TYPE.PENTAGON_DORMER ||
|
||||
// obj.name === 'lengthText'
|
||||
// ) {
|
||||
// canvas?.remove(obj)
|
||||
// }
|
||||
// })
|
||||
canvas.clear()
|
||||
swalFire({ text: '삭제 완료 되었습니다.' })
|
||||
},
|
||||
// denyFn: () => {
|
||||
@ -823,10 +828,113 @@ export function useSurfaceShapeBatch() {
|
||||
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 {
|
||||
applySurfaceShape,
|
||||
deleteAllSurfacesAndObjects,
|
||||
moveSurfaceShapeBatch,
|
||||
resizeSurfaceShapeBatch,
|
||||
|
||||
changeSurfaceLinePropertyEvent,
|
||||
changeSurfaceLineProperty,
|
||||
changeSurfaceLinePropertyReset,
|
||||
}
|
||||
}
|
||||
|
||||
@ -109,11 +109,24 @@ export function useCanvas(id) {
|
||||
OBJECT_PROTOTYPE.forEach((type) => {
|
||||
type.toObject = function (propertiesToInclude) {
|
||||
let source = {}
|
||||
|
||||
for (let key in this) {
|
||||
if (typeof this[key] !== 'function' && SAVE_KEY.includes(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)
|
||||
}
|
||||
})
|
||||
|
||||
@ -225,7 +225,9 @@ export function useCanvasEvent() {
|
||||
if (deselected?.length > 0) {
|
||||
deselected.forEach((obj) => {
|
||||
if (obj.type === 'QPolygon') {
|
||||
obj.set({ stroke: 'black' })
|
||||
if (obj.name !== 'moduleSetupSurface') {
|
||||
obj.set({ stroke: 'black' })
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -24,7 +24,6 @@ import { useCommonUtils } from './common/useCommonUtils'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { useCanvasEvent } from '@/hooks/useCanvasEvent'
|
||||
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 DimensionLineSetting from '@/components/floor-plan/modal/dimensionLine/DimensionLineSetting'
|
||||
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 { useObjectBatch } from '@/hooks/object/useObjectBatch'
|
||||
import { useSurfaceShapeBatch } from '@/hooks/surface/useSurfaceShapeBatch'
|
||||
import { fontSelector, globalFontAtom } from '@/store/fontAtom'
|
||||
|
||||
export function useContextMenu() {
|
||||
const currentMenu = useRecoilValue(currentMenuState) // 현재 메뉴
|
||||
@ -52,6 +52,9 @@ export function useContextMenu() {
|
||||
const { handleZoomClear } = useCanvasEvent()
|
||||
const { moveObjectBatch } = useObjectBatch({})
|
||||
const { moveSurfaceShapeBatch } = useSurfaceShapeBatch()
|
||||
const [globalFont, setGlobalFont] = useRecoilState(globalFontAtom)
|
||||
const commonTextFont = useRecoilValue(fontSelector('commonText'))
|
||||
|
||||
const currentMenuSetting = () => {
|
||||
switch (currentMenu) {
|
||||
case MENU.PLAN_DRAWING:
|
||||
@ -95,11 +98,6 @@ export function useContextMenu() {
|
||||
case MENU.ROOF_COVERING.DEFAULT:
|
||||
setContextMenu([
|
||||
[
|
||||
{
|
||||
id: 'refresh',
|
||||
name: getMessage('refresh'),
|
||||
fn: () => handleZoomClear(),
|
||||
},
|
||||
{
|
||||
id: 'roofMaterialPlacement',
|
||||
name: getMessage('contextmenu.roof.material.placement'),
|
||||
@ -121,11 +119,6 @@ export function useContextMenu() {
|
||||
name: getMessage('contextmenu.wallline.remove'),
|
||||
fn: () => deleteOuterLineObject(),
|
||||
},
|
||||
{
|
||||
id: 'imageSizeEdit',
|
||||
name: getMessage('modal.image.size.setting'),
|
||||
component: <ImageSizeSetting id={popupId} />,
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
@ -176,6 +169,7 @@ export function useContextMenu() {
|
||||
{
|
||||
id: 'sizeEdit',
|
||||
name: getMessage('contextmenu.size.edit'),
|
||||
component: <SizeSetting id={popupId} target={currentObject} />,
|
||||
},
|
||||
{
|
||||
id: 'remove',
|
||||
@ -192,16 +186,12 @@ export function useContextMenu() {
|
||||
shortcut: ['c', 'C'],
|
||||
name: `${getMessage('contextmenu.copy')}(C)`,
|
||||
},
|
||||
{
|
||||
id: 'imageSizeEdit',
|
||||
name: getMessage('modal.image.size.setting'),
|
||||
component: <ImageSizeSetting id={popupId} />,
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
id: 'roofMaterialEdit',
|
||||
name: getMessage('contextmenu.roof.material.edit'),
|
||||
component: <RoofAllocationSetting id={popupId} />,
|
||||
},
|
||||
{
|
||||
id: 'linePropertyEdit',
|
||||
@ -211,6 +201,7 @@ export function useContextMenu() {
|
||||
{
|
||||
id: 'flowDirectionEdit',
|
||||
name: getMessage('contextmenu.flow.direction.edit'),
|
||||
component: <FlowDirectionSetting id={popupId} target={currentObject} />,
|
||||
},
|
||||
],
|
||||
])
|
||||
@ -257,8 +248,10 @@ export function useContextMenu() {
|
||||
}, [currentContextMenu])
|
||||
|
||||
useEffect(() => {
|
||||
console.log('currentObject', currentObject)
|
||||
console.log(currentObject)
|
||||
|
||||
if (currentObject?.name) {
|
||||
console.log('object', currentObject)
|
||||
switch (currentObject.name) {
|
||||
case 'triangleDormer':
|
||||
case 'pentagonDormer':
|
||||
@ -336,7 +329,7 @@ export function useContextMenu() {
|
||||
{
|
||||
id: 'linePropertyEdit',
|
||||
name: getMessage('contextmenu.line.property.edit'),
|
||||
component: <LinePropertySetting id={popupId} />,
|
||||
component: <LinePropertySetting id={popupId} target={currentObject} />,
|
||||
},
|
||||
{
|
||||
id: 'flowDirectionEdit',
|
||||
@ -423,7 +416,26 @@ export function useContextMenu() {
|
||||
{
|
||||
id: 'commonTextFontSetting',
|
||||
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',
|
||||
|
||||
@ -219,6 +219,12 @@ export function useEvent() {
|
||||
mouseEventListeners.current.length = 0 // 배열 초기화
|
||||
}
|
||||
|
||||
const addTargetMouseEventListener = (eventType, target, handler) => {
|
||||
target.off(eventType)
|
||||
target.on(eventType, handler)
|
||||
mouseEventListeners.current.push({ eventType, handler })
|
||||
}
|
||||
|
||||
/**
|
||||
* document 이벤트의 경우 이 함수를 통해서만 등록
|
||||
* @param eventType
|
||||
@ -264,6 +270,7 @@ export function useEvent() {
|
||||
return {
|
||||
addDocumentEventListener,
|
||||
addCanvasMouseEventListener,
|
||||
addTargetMouseEventListener,
|
||||
removeAllMouseEventListeners,
|
||||
removeAllDocumentEventListeners,
|
||||
removeDocumentEvent,
|
||||
|
||||
@ -57,6 +57,7 @@ export function usePlan() {
|
||||
*/
|
||||
const currentCanvasData = (mode = '') => {
|
||||
removeMouseLines()
|
||||
canvas.discardActiveObject()
|
||||
|
||||
if (mode === 'save') {
|
||||
const groups = canvas.getObjects().filter((obj) => obj.type === 'group')
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { ANGLE_TYPE, canvasState, currentAngleTypeSelector, fontFamilyState, fontSizeState, pitchTextSelector } from '@/store/canvasAtom'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
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 { isSamePoint, removeDuplicatePolygons } from '@/util/qpolygon-utils'
|
||||
import { flowDisplaySelector } from '@/store/settingAtom'
|
||||
@ -86,8 +86,8 @@ export const usePolygon = () => {
|
||||
parentDirection: line.direction,
|
||||
parentDegree: degree,
|
||||
parentId: polygon.id,
|
||||
planeSize,
|
||||
actualSize,
|
||||
planeSize: planeSize ?? length,
|
||||
actualSize: actualSize ?? length,
|
||||
editable: false,
|
||||
selectable: true,
|
||||
lockRotation: true,
|
||||
@ -181,7 +181,7 @@ export const usePolygon = () => {
|
||||
|
||||
polygon.canvas
|
||||
.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))
|
||||
|
||||
let arrow = null
|
||||
@ -265,6 +265,8 @@ export const usePolygon = () => {
|
||||
direction: direction,
|
||||
parent: polygon,
|
||||
stickeyPoint: stickeyPoint,
|
||||
surfaceCompass: polygon.surfaceCompass,
|
||||
moduleCompass: polygon.moduleCompass,
|
||||
visible: isFlowDisplay,
|
||||
pitch: polygon.pitch,
|
||||
parentId: polygon.id,
|
||||
@ -274,7 +276,177 @@ export const usePolygon = () => {
|
||||
polygon.arrow = arrow
|
||||
polygon.canvas.add(arrow)
|
||||
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 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,
|
||||
fill: flowFontOptions.fontColor.value,
|
||||
fontFamily: flowFontOptions.fontFamily.value,
|
||||
fontWeight: flowFontOptions.fontWeight.value,
|
||||
pitch: arrow.pitch,
|
||||
originX: 'center',
|
||||
originY: 'center',
|
||||
@ -496,62 +669,159 @@ export const usePolygon = () => {
|
||||
const splitPolygonWithLines = (polygon) => {
|
||||
polygon.set({ visible: false })
|
||||
let innerLines = [...polygon.innerLines]
|
||||
|
||||
canvas.renderAll()
|
||||
let polygonLines = [...polygon.lines]
|
||||
const roofs = []
|
||||
|
||||
let delIndexs = []
|
||||
let newLines = []
|
||||
//polygonLines를 순회하며 innerLines와 교차하는 점을 line의 속성에 배열로 저장한다.
|
||||
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.tempIndex = index
|
||||
line.startPoint = startPoint
|
||||
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) => {
|
||||
let newLine1, newLine2
|
||||
if (isPointOnLine(line, innerLine.startPoint)) {
|
||||
// 해당 line을 startPoint로 나눈 line2개를 canvas에 추가 하고 기존 line을 제거한다.
|
||||
newLine1 = new QLine([line.x1, line.y1, innerLine.startPoint.x, innerLine.startPoint.y], {
|
||||
fontSize: polygon.fontSize,
|
||||
stroke: 'black',
|
||||
strokeWidth: 3,
|
||||
})
|
||||
|
||||
newLine2 = new QLine([innerLine.startPoint.x, innerLine.startPoint.y, line.x2, line.y2], {
|
||||
fontSize: polygon.fontSize,
|
||||
stroke: 'black',
|
||||
strokeWidth: 3,
|
||||
})
|
||||
delIndexs.push(polygonLines.indexOf(line))
|
||||
canvas.remove(polygonLines[polygonLines.indexOf(line)])
|
||||
if (newLine1.length / 10 > 10) {
|
||||
newLines.push(newLine1)
|
||||
}
|
||||
if (newLine2.length / 10 > 10) {
|
||||
newLines.push(newLine2)
|
||||
if (isSamePoint(line.startPoint, innerLine.startPoint) || isSamePoint(line.endPoint, innerLine.startPoint)) {
|
||||
return
|
||||
}
|
||||
intersections.push(innerLine.startPoint)
|
||||
}
|
||||
if (isPointOnLine(line, innerLine.endPoint)) {
|
||||
newLine1 = new QLine([line.x1, line.y1, innerLine.endPoint.x, innerLine.endPoint.y], {
|
||||
fontSize: polygon.fontSize,
|
||||
stroke: 'black',
|
||||
strokeWidth: 3,
|
||||
})
|
||||
|
||||
newLine2 = new QLine([innerLine.endPoint.x, innerLine.endPoint.y, line.x2, line.y2], {
|
||||
fontSize: polygon.fontSize,
|
||||
stroke: 'black',
|
||||
strokeWidth: 3,
|
||||
})
|
||||
delIndexs.push(polygonLines.indexOf(line))
|
||||
canvas.remove(polygonLines[polygonLines.indexOf(line)])
|
||||
if (newLine1.length / 10 > 10) {
|
||||
newLines.push(newLine1)
|
||||
}
|
||||
if (newLine2.length / 10 > 10) {
|
||||
newLines.push(newLine2)
|
||||
if (isSamePoint(line.startPoint, innerLine.endPoint) || isSamePoint(line.endPoint, innerLine.endPoint)) {
|
||||
return
|
||||
}
|
||||
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]
|
||||
|
||||
const allLines = [...polygonLines, ...innerLines]
|
||||
@ -583,6 +853,9 @@ export const usePolygon = () => {
|
||||
})
|
||||
|
||||
polygonLines.forEach((line) => {
|
||||
line.set({ strokeWidth: 5, stroke: 'green' })
|
||||
canvas.add(line)
|
||||
canvas.renderAll()
|
||||
const startPoint = line.startPoint // 시작점
|
||||
let arrivalPoint = line.endPoint // 도착점
|
||||
|
||||
@ -646,17 +919,18 @@ export const usePolygon = () => {
|
||||
roofPoints.push(currentPoint)
|
||||
cnt++
|
||||
if (cnt > 100) {
|
||||
throw new Error('무한루프')
|
||||
break
|
||||
}
|
||||
}
|
||||
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) => {
|
||||
let defense, pitch
|
||||
const polygonLines = [...polygon.lines]
|
||||
|
||||
let representLines = []
|
||||
let representLine
|
||||
@ -728,7 +1002,7 @@ export const usePolygon = () => {
|
||||
|
||||
//allLines중 생성된 roof와 관련있는 line을 찾는다.
|
||||
|
||||
roof.lines = [...polygon.lines, ...polygon.innerLines].filter((line) => {
|
||||
roof.lines = [...polygonLines, ...polygon.innerLines].filter((line) => {
|
||||
let startFlag = false
|
||||
let endFlag = false
|
||||
const startPoint = line.startPoint
|
||||
|
||||
@ -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)
|
||||
}
|
||||
@ -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,
|
||||
}
|
||||
}
|
||||
@ -616,9 +616,9 @@
|
||||
"stuff.planReqPopup.title": "設計依頼のインポート",
|
||||
"stuff.temp.subTitle": "商品情報",
|
||||
"stuff.temp.subTitle2": "作図",
|
||||
"stuff.detail.header.message1": "存在しないものです。",
|
||||
"stuff.detail.header.message2": "商品番号がコピーされました。",
|
||||
"stuff.detail.header.message3": "存在しないものです。",
|
||||
"stuff.detail.header.notExistObjectNo": "存在しないものです。",
|
||||
"stuff.detail.header.successCopy": "商品番号がコピーされました。",
|
||||
"stuff.detail.header.failCopy": "存在しないものです。",
|
||||
"stuff.detail.header.objectNo": "商品番号のコピーに失敗しました。",
|
||||
"stuff.detail.header.specificationConfirmDate": "仕様拡張日",
|
||||
"stuff.detail.header.lastEditDatetime": "更新日時",
|
||||
@ -694,7 +694,8 @@
|
||||
"stuff.planReqPopup.search.period": "期間検索",
|
||||
"stuff.planReqPopup.search.schDateGbnS": "提出日",
|
||||
"stuff.planReqPopup.search.schDateGbnR": "受付日",
|
||||
"stuff.planReqPopup.error.message1": "設計依頼を選択してください。",
|
||||
"stuff.planReqPopup.error.message1": "設計依頼を選択してください.",
|
||||
"stuff.planReqPopup.error.message2": "販売店を選択してください.",
|
||||
"stuff.search.title": "物件状況",
|
||||
"stuff.search.btn1": "新規 物件 登録",
|
||||
"stuff.search.btn2": "照会",
|
||||
@ -830,10 +831,10 @@
|
||||
"estimate.detail.header.fileList2": "添付ファイル一覧",
|
||||
"estimate.detail.header.specialEstimate": "見積もりの具体的な",
|
||||
"estimate.detail.header.specialEstimateProductInfo": "製品情報",
|
||||
"estimate.detail.sepcialEstimateProductInfo.totPcs": "数量 (PCS)",
|
||||
"estimate.detail.sepcialEstimateProductInfo.vol": "容量 (Kw)",
|
||||
"estimate.detail.sepcialEstimateProductInfo.netAmt": "供給価格",
|
||||
"estimate.detail.sepcialEstimateProductInfo.vat": "付加価値税 (10%)",
|
||||
"estimate.detail.sepcialEstimateProductInfo.totAmount": "数量 (PCS)",
|
||||
"estimate.detail.sepcialEstimateProductInfo.totVolKw": "容量 (Kw)",
|
||||
"estimate.detail.sepcialEstimateProductInfo.supplyPrice": "供給価格",
|
||||
"estimate.detail.sepcialEstimateProductInfo.vatPrice": "付加価値税 (10%)",
|
||||
"estimate.detail.sepcialEstimateProductInfo.totPrice": "総額",
|
||||
"estimate.detail.sepcialEstimateProductInfo.pkgUnitPrice": "住宅PKG単価 (W)",
|
||||
"estimate.detail.sepcialEstimateProductInfo.pkgWeight": "PKG容量 (Kw)",
|
||||
@ -847,15 +848,15 @@
|
||||
"estimate.detail.showPrice.description2": "追加, 変更資材",
|
||||
"estimate.detail.showPrice.description3": "添付必須",
|
||||
"estimate.detail.showPrice.description4": "クリックして製品の特異性を確認する",
|
||||
"estimate.detail.showPrice.btn2": "製品を追加",
|
||||
"estimate.detail.showPrice.btn3": "製品削除",
|
||||
"estimate.detail.itemTableHeader.col1": "アイテム",
|
||||
"estimate.detail.itemTableHeader.col2": "品番",
|
||||
"estimate.detail.itemTableHeader.col3": "型板",
|
||||
"estimate.detail.itemTableHeader.col4": "数量",
|
||||
"estimate.detail.itemTableHeader.col5": "単位",
|
||||
"estimate.detail.itemTableHeader.col6": "単価",
|
||||
"estimate.detail.itemTableHeader.col7": "金額 (税別別)",
|
||||
"estimate.detail.showPrice.addItem": "製品を追加",
|
||||
"estimate.detail.showPrice.delItem": "製品削除",
|
||||
"estimate.detail.itemTableHeader.dispOrder": "アイテム",
|
||||
"estimate.detail.itemTableHeader.itemId": "品番",
|
||||
"estimate.detail.itemTableHeader.itemNo": "型板",
|
||||
"estimate.detail.itemTableHeader.amount": "数量",
|
||||
"estimate.detail.itemTableHeader.unit": "単位",
|
||||
"estimate.detail.itemTableHeader.salePrice": "単価",
|
||||
"estimate.detail.itemTableHeader.saleTotPrice": "金額 (税別別)",
|
||||
"estimate.detail.docPopup.title": "ドキュメントダウンロードオプションの設定",
|
||||
"estimate.detail.docPopup.explane": "ダウンロードする文書のオプションを選択したら、 [文書のダウンロード]ボタンをクリックします.",
|
||||
"estimate.detail.docPopup.schUnitPriceFlg": "ダウンロードファイル",
|
||||
@ -874,6 +875,8 @@
|
||||
"estimate.detail.docPopup.schDrawingFlg.schDrawingFlg1": "含まない",
|
||||
"estimate.detail.docPopup.close": "閉じる",
|
||||
"estimate.detail.docPopup.docDownload": "文書のダウンロード",
|
||||
"estimate.detail.productFeaturesPopup.title": "製品特異事項",
|
||||
"estimate.detail.productFeaturesPopup.close": "閉じる",
|
||||
"estimate.detail.save.alertMsg": "保存されている見積書で製品を変更した場合、図面や回路には反映されません.",
|
||||
"estimate.detail.save.requiredMsg": "ファイル添付が必須のアイテムがあります。ファイルを添付するか、後日添付をチェックしてください.",
|
||||
"estimate.detail.reset.confirmMsg": "保存した見積書情報が初期化され、図面情報が反映されます。本当に初期化しますか?",
|
||||
|
||||
@ -400,6 +400,9 @@
|
||||
"modal.module.circuit.number.edit": "모듈 일괄 회로 번호 변경",
|
||||
"modal.module.circuit.number.edit.info": "회로 번호를 입력해주세요.",
|
||||
"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.dataDown": "No data to download",
|
||||
"common.message.noData": "No data to display",
|
||||
@ -495,6 +498,7 @@
|
||||
"commons.east": "동",
|
||||
"commons.south": "남",
|
||||
"commons.north": "북",
|
||||
"commons.none": "선택안함",
|
||||
"font.style.normal": "보통",
|
||||
"font.style.italic": "기울임꼴",
|
||||
"font.style.bold": "굵게",
|
||||
@ -622,9 +626,9 @@
|
||||
"stuff.planReqPopup.title": "설계의뢰 불러오기",
|
||||
"stuff.temp.subTitle": "물건정보",
|
||||
"stuff.temp.subTitle2": "도면작성",
|
||||
"stuff.detail.header.message1": "존재하지 않는 물건입니다.",
|
||||
"stuff.detail.header.message2": "물건번호가 복사되었습니다.",
|
||||
"stuff.detail.header.message3": "물건번호 복사에 실패했습니다.",
|
||||
"stuff.detail.header.notExistObjectNo": "존재하지 않는 물건입니다.",
|
||||
"stuff.detail.header.successCopy": "물건번호가 복사되었습니다.",
|
||||
"stuff.detail.header.failCopy": "물건번호 복사에 실패했습니다.",
|
||||
"stuff.detail.header.objectNo": "물건번호",
|
||||
"stuff.detail.header.specificationConfirmDate": "사양확장일",
|
||||
"stuff.detail.header.lastEditDatetime": "갱신일시",
|
||||
@ -701,6 +705,7 @@
|
||||
"stuff.planReqPopup.search.schDateGbnS": "제출일",
|
||||
"stuff.planReqPopup.search.schDateGbnR": "접수일",
|
||||
"stuff.planReqPopup.error.message1": "설계의뢰를 선택해주세요.",
|
||||
"stuff.planReqPopup.error.message2": "판매점을 선택해주세요.",
|
||||
"stuff.search.title": "물건현황",
|
||||
"stuff.search.btn1": "신규 물건 등록",
|
||||
"stuff.search.btn2": "조회",
|
||||
@ -836,10 +841,10 @@
|
||||
"estimate.detail.header.fileList2": "첨부파일 목록",
|
||||
"estimate.detail.header.specialEstimate": "견적특이사항",
|
||||
"estimate.detail.header.specialEstimateProductInfo": "제품정보",
|
||||
"estimate.detail.sepcialEstimateProductInfo.totPcs": "수량 (PCS)",
|
||||
"estimate.detail.sepcialEstimateProductInfo.vol": "용량 (Kw)",
|
||||
"estimate.detail.sepcialEstimateProductInfo.netAmt": "공급가액",
|
||||
"estimate.detail.sepcialEstimateProductInfo.vat": "부가세 (10%)",
|
||||
"estimate.detail.sepcialEstimateProductInfo.totAmount": "수량 (PCS)",
|
||||
"estimate.detail.sepcialEstimateProductInfo.totVolKw": "용량 (Kw)",
|
||||
"estimate.detail.sepcialEstimateProductInfo.supplyPrice": "공급가액",
|
||||
"estimate.detail.sepcialEstimateProductInfo.vatPrice": "부가세 (10%)",
|
||||
"estimate.detail.sepcialEstimateProductInfo.totPrice": "총액",
|
||||
"estimate.detail.sepcialEstimateProductInfo.pkgUnitPrice": "주택PKG단가 (W)",
|
||||
"estimate.detail.sepcialEstimateProductInfo.pkgWeight": "PKG 용량 (Kw)",
|
||||
@ -853,15 +858,15 @@
|
||||
"estimate.detail.showPrice.description2": "추가, 변경 자재",
|
||||
"estimate.detail.showPrice.description3": "첨부필수",
|
||||
"estimate.detail.showPrice.description4": "클릭하여 제품 특이사항 확인",
|
||||
"estimate.detail.showPrice.btn2": "제품추가",
|
||||
"estimate.detail.showPrice.btn3": "제품삭제",
|
||||
"estimate.detail.itemTableHeader.col1": "Item",
|
||||
"estimate.detail.itemTableHeader.col2": "품번",
|
||||
"estimate.detail.itemTableHeader.col3": "형명",
|
||||
"estimate.detail.itemTableHeader.col4": "수량",
|
||||
"estimate.detail.itemTableHeader.col5": "단위",
|
||||
"estimate.detail.itemTableHeader.col6": "단가",
|
||||
"estimate.detail.itemTableHeader.col7": "금액(부가세별도)",
|
||||
"estimate.detail.showPrice.addItem": "제품추가",
|
||||
"estimate.detail.showPrice.delItem": "제품삭제",
|
||||
"estimate.detail.itemTableHeader.dispOrder": "Item",
|
||||
"estimate.detail.itemTableHeader.itemId": "품번",
|
||||
"estimate.detail.itemTableHeader.itemNo": "형명",
|
||||
"estimate.detail.itemTableHeader.amount": "수량",
|
||||
"estimate.detail.itemTableHeader.unit": "단위",
|
||||
"estimate.detail.itemTableHeader.salePrice": "단가",
|
||||
"estimate.detail.itemTableHeader.saleTotPrice": "금액(부가세별도)",
|
||||
"estimate.detail.docPopup.title": "문서다운로드 옵션설정",
|
||||
"estimate.detail.docPopup.explane": "다운로드할 문서 옵션을 선택한 후 문서 다운로드 버튼을 클릭합니다.",
|
||||
"estimate.detail.docPopup.schUnitPriceFlg": "다운로드 파일",
|
||||
@ -880,6 +885,8 @@
|
||||
"estimate.detail.docPopup.schDrawingFlg.schDrawingFlg1": "미포함",
|
||||
"estimate.detail.docPopup.close": "닫기",
|
||||
"estimate.detail.docPopup.docDownload": "문서 다운로드",
|
||||
"estimate.detail.productFeaturesPopup.title": "제품특이사항",
|
||||
"estimate.detail.productFeaturesPopup.close": "닫기",
|
||||
"estimate.detail.save.alertMsg": "저장되었습니다. 견적서에서 제품을 변경할 경우, 도면 및 회로에 반영되지 않습니다.",
|
||||
"estimate.detail.save.requiredMsg": "파일첨부가 필수인 아이템이 있습니다. 파일을 첨부하거나 후일첨부를 체크해주십시오.",
|
||||
"estimate.detail.reset.confirmMsg": "저장된 견적서 정보가 초기화되고, 도면정보가 반영됩니다. 정말로 초기화 하시겠습니까?",
|
||||
|
||||
@ -363,3 +363,24 @@ export const showAngleUnitSelector = selector({
|
||||
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,
|
||||
})
|
||||
|
||||
@ -11,10 +11,6 @@ export const dimensionLineSettingsState = atom({
|
||||
default: {
|
||||
pixel: 1,
|
||||
color: '#000000',
|
||||
font: 'Arial',
|
||||
fontColor: '#000000',
|
||||
fontSize: 15,
|
||||
fontStyle: 'normal',
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
6
src/store/orientationAtom.js
Normal file
6
src/store/orientationAtom.js
Normal 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
@ -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
|
||||
|
||||
let width = 265 / 10
|
||||
@ -820,6 +820,12 @@ export function setSurfaceShapePattern(polygon, mode = 'onlyBorder') {
|
||||
ctx.lineWidth = mode === 'allPainted' ? 1 : 0.4
|
||||
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') {
|
||||
offset = roofStyle === 1 ? 0 : patternSize.height / 2
|
||||
for (let col = 0; col <= cols; col++) {
|
||||
@ -830,7 +836,7 @@ export function setSurfaceShapePattern(polygon, mode = 'onlyBorder') {
|
||||
ctx.moveTo(x, yStart) // 선 시작점
|
||||
ctx.lineTo(x, yEnd) // 선 끝점
|
||||
ctx.stroke()
|
||||
if (mode === 'allPainted') {
|
||||
if (mode === 'allPainted' || trestleMode) {
|
||||
ctx.fillRect(x, yStart, patternSize.width, yEnd - yStart)
|
||||
}
|
||||
|
||||
@ -842,7 +848,7 @@ export function setSurfaceShapePattern(polygon, mode = 'onlyBorder') {
|
||||
ctx.moveTo(xStart, y) // 선 시작점
|
||||
ctx.lineTo(xEnd, y) // 선 끝점
|
||||
ctx.stroke()
|
||||
if (mode === 'allPainted') {
|
||||
if (mode === 'allPainted' || trestleMode) {
|
||||
ctx.fillRect(xStart, y, xEnd - xStart, patternSize.height)
|
||||
}
|
||||
}
|
||||
@ -855,7 +861,7 @@ export function setSurfaceShapePattern(polygon, mode = 'onlyBorder') {
|
||||
ctx.moveTo(0, y) // 선 시작점
|
||||
ctx.lineTo(patternSourceCanvas.width, y) // 선 끝점
|
||||
ctx.stroke()
|
||||
if (mode === 'allPainted') {
|
||||
if (mode === 'allPainted' || trestleMode) {
|
||||
ctx.fillRect(0, y, patternSourceCanvas.width, patternSize.height)
|
||||
}
|
||||
|
||||
@ -868,7 +874,7 @@ export function setSurfaceShapePattern(polygon, mode = 'onlyBorder') {
|
||||
ctx.moveTo(x, yStart) // 선 시작점
|
||||
ctx.lineTo(x, yEnd) // 선 끝점
|
||||
ctx.stroke()
|
||||
if (mode === 'allPainted') {
|
||||
if (mode === 'allPainted' || trestleMode) {
|
||||
ctx.fillRect(x, yStart, patternSize.width, yEnd - yStart)
|
||||
}
|
||||
}
|
||||
@ -954,3 +960,44 @@ export const getAllRelatedObjects = (id, canvas) => {
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@ -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.actualSize = Math.round(Math.sqrt(Math.pow(hip2.attributes.planeSize, 2) + Math.pow(hip2Height, 2)))
|
||||
// roof.innerLines.push(hip2)
|
||||
hip1.set({ visible: false })
|
||||
hip1.setViewLengthText(false)
|
||||
hip2.set({ visible: false })
|
||||
hip2.setViewLengthText(false)
|
||||
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)))
|
||||
canvas?.add(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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user