Merge branch 'dev'
This commit is contained in:
commit
5b40f4ca44
@ -28,3 +28,4 @@ Allpainted : allPainted
|
||||
치수선: dimensionLine
|
||||
복도치수: planeSize
|
||||
실제치수: actualSize
|
||||
모듈설치면: moduleSetupSurface
|
||||
@ -32,6 +32,7 @@
|
||||
"react-icons": "^5.3.0",
|
||||
"react-loading-skeleton": "^3.5.0",
|
||||
"react-responsive-modal": "^6.4.2",
|
||||
"react-spinners": "^0.14.1",
|
||||
"recoil": "^0.7.7",
|
||||
"sweetalert2": "^11.14.1",
|
||||
"sweetalert2-react-content": "^5.0.7",
|
||||
|
||||
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 |
@ -1,23 +1,40 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useState } from 'react'
|
||||
import { createContext, useEffect, useState } from 'react'
|
||||
import { ErrorBoundary } from 'next/dist/client/components/error-boundary'
|
||||
import { useCommonCode } from '@/hooks/common/useCommonCode'
|
||||
import { usePlan } from '@/hooks/usePlan'
|
||||
import ServerError from './error'
|
||||
|
||||
import '@/styles/common.scss'
|
||||
import GlobalSpinner from '@/components/common/spinner/GlobalSpinner'
|
||||
|
||||
export const QcastContext = createContext({
|
||||
qcastState: {},
|
||||
setQcastState: () => {},
|
||||
isGlobalLoading: false,
|
||||
setIsGlobalLoading: () => {},
|
||||
})
|
||||
|
||||
export const QcastProvider = ({ children }) => {
|
||||
const [planSave, setPlanSave] = useState(false)
|
||||
const [isGlobalLoading, setIsGlobalLoading] = useState(false)
|
||||
const { currentCanvasPlan, modifiedPlans, checkUnsavedCanvasPlan } = usePlan()
|
||||
const { commonCode, findCommonCode } = useCommonCode()
|
||||
|
||||
const [qcastState, setQcastState] = useState({
|
||||
saleStoreId: '',
|
||||
saleStoreName: '',
|
||||
objectList: [],
|
||||
businessCharger: null,
|
||||
businessChargerMail: null,
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
const targetElement = document.getElementById('canvas')
|
||||
if (!targetElement && currentCanvasPlan?.id && planSave) {
|
||||
setPlanSave((prev) => !prev)
|
||||
checkUnsavedCanvasPlan(currentCanvasPlan.userId)
|
||||
setPlanSave((prev) => !prev)
|
||||
checkUnsavedCanvasPlan(currentCanvasPlan.userId)
|
||||
} else if (targetElement && currentCanvasPlan?.id) {
|
||||
setPlanSave(true)
|
||||
}
|
||||
@ -30,7 +47,14 @@ export const QcastProvider = ({ children }) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<ErrorBoundary fallback={<ServerError />}>{children}</ErrorBoundary>
|
||||
{isGlobalLoading && (
|
||||
<div className="fixed inset-0 bg-white z-50 flex items-center justify-center">
|
||||
<GlobalSpinner />
|
||||
</div>
|
||||
)}
|
||||
<QcastContext.Provider value={{ qcastState, setQcastState, isGlobalLoading, setIsGlobalLoading }}>
|
||||
<ErrorBoundary fallback={<ServerError />}>{children}</ErrorBoundary>
|
||||
</QcastContext.Provider>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@ import fs from 'fs/promises'
|
||||
import { NextResponse } from 'next/server'
|
||||
|
||||
export async function GET(req) {
|
||||
const path = 'public/mapImages'
|
||||
const path = 'public/plan-map-images'
|
||||
const q = req.nextUrl.searchParams.get('q')
|
||||
const fileNm = req.nextUrl.searchParams.get('fileNm')
|
||||
const zoom = req.nextUrl.searchParams.get('zoom')
|
||||
|
||||
25
src/app/api/image-upload/route.js
Normal file
25
src/app/api/image-upload/route.js
Normal file
@ -0,0 +1,25 @@
|
||||
'use server'
|
||||
|
||||
import fs from 'fs/promises'
|
||||
|
||||
import { NextResponse } from 'next/server'
|
||||
|
||||
export async function POST(req) {
|
||||
const path = 'public/plan-bg-images'
|
||||
|
||||
const formData = await req.formData()
|
||||
const file = formData.get('file')
|
||||
const arrayBuffer = await file.arrayBuffer()
|
||||
const buffer = Buffer.from(arrayBuffer)
|
||||
// const buffer = new Uint8Array(arrayBuffer)
|
||||
|
||||
try {
|
||||
await fs.readdir(path)
|
||||
} catch {
|
||||
await fs.mkdir(path)
|
||||
} finally {
|
||||
await fs.writeFile(`${path}/${file.name}`, buffer)
|
||||
}
|
||||
|
||||
return NextResponse.json({ fileNm: `${file.name}` })
|
||||
}
|
||||
34
src/app/floor-plan/EventProvider.js
Normal file
34
src/app/floor-plan/EventProvider.js
Normal file
@ -0,0 +1,34 @@
|
||||
import { useEvent } from '@/hooks/useEvent'
|
||||
import { createContext, useState } from 'react'
|
||||
|
||||
export const EventContext = createContext({})
|
||||
|
||||
const EventProvider = ({ children }) => {
|
||||
const {
|
||||
addDocumentEventListener,
|
||||
addCanvasMouseEventListener,
|
||||
addTargetMouseEventListener,
|
||||
removeAllMouseEventListeners,
|
||||
removeAllDocumentEventListeners,
|
||||
removeDocumentEvent,
|
||||
removeMouseEvent,
|
||||
removeMouseLine,
|
||||
initEvent,
|
||||
} = useEvent()
|
||||
|
||||
const [value, setValue] = useState({
|
||||
addDocumentEventListener,
|
||||
addCanvasMouseEventListener,
|
||||
addTargetMouseEventListener,
|
||||
removeAllMouseEventListeners,
|
||||
removeAllDocumentEventListeners,
|
||||
removeDocumentEvent,
|
||||
removeMouseEvent,
|
||||
removeMouseLine,
|
||||
initEvent,
|
||||
})
|
||||
|
||||
return <EventContext.Provider value={value}>{children}</EventContext.Provider>
|
||||
}
|
||||
|
||||
export default EventProvider
|
||||
@ -1,9 +1,73 @@
|
||||
'ues client'
|
||||
|
||||
import { ErrorBoundary } from 'next/dist/client/components/error-boundary'
|
||||
import ServerError from '../error'
|
||||
import { correntObjectNoState } from '@/store/settingAtom'
|
||||
import { notFound, usePathname, useSearchParams } from 'next/navigation'
|
||||
// import { ErrorBoundary } from 'next/dist/client/components/error-boundary'
|
||||
// import ServerError from '../error'
|
||||
import { createContext, useEffect, useReducer, useState } from 'react'
|
||||
import { useSetRecoilState } from 'recoil'
|
||||
|
||||
export const FloorPlanProvider = ({ children }) => {
|
||||
console.log('FloorPlanProvider')
|
||||
return <ErrorBoundary fallback={<ServerError />}>{children}</ErrorBoundary>
|
||||
const reducer = (prevState, nextState) => {
|
||||
return { ...prevState, ...nextState }
|
||||
}
|
||||
|
||||
const defaultEstimateData = {
|
||||
estimateDate: new Date(), //견적일
|
||||
charger: '', //담당자
|
||||
objectName: '', //안건명
|
||||
objectNameOmit: '', //경칭코드
|
||||
estimateType: '', //주문분류
|
||||
remarks: '', //비고
|
||||
estimateOption: '', //견적특이사항
|
||||
itemList: [],
|
||||
fileList: [],
|
||||
fileFlg: '0', //후일 자료 제출 (체크 1 노체크 0)
|
||||
priceCd: '',
|
||||
}
|
||||
|
||||
export const FloorPlanContext = createContext({
|
||||
floorPlanState: {},
|
||||
setFloorPlanState: () => {},
|
||||
estimateContextState: {},
|
||||
setEstimateContextState: () => {},
|
||||
})
|
||||
|
||||
const FloorPlanProvider = ({ children }) => {
|
||||
const pathname = usePathname()
|
||||
const setCurrentObjectNo = useSetRecoilState(correntObjectNoState)
|
||||
const searchParams = useSearchParams()
|
||||
const objectNo = searchParams.get('objectNo')
|
||||
const pid = searchParams.get('pid')
|
||||
|
||||
if (pathname === '/floor-plan') {
|
||||
if (pid === undefined || pid === '' || objectNo === undefined || objectNo === '') {
|
||||
notFound()
|
||||
}
|
||||
setCurrentObjectNo(objectNo)
|
||||
}
|
||||
|
||||
const [floorPlanState, setFloorPlanState] = useState({
|
||||
// 플랜 파일 업로드 모달 오픈 제어
|
||||
refFileModalOpen: false,
|
||||
// 플랜 회전 모드 제어
|
||||
toggleRotate: false,
|
||||
// 물건 번호
|
||||
objectNo,
|
||||
// 플랜 번호
|
||||
pid,
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
console.log('🚀 ~ FloorPlanProvider ~ floorPlanState:', floorPlanState)
|
||||
}, [floorPlanState])
|
||||
|
||||
const [estimateContextState, setEstimateContextState] = useReducer(reducer, defaultEstimateData)
|
||||
|
||||
return (
|
||||
<FloorPlanContext.Provider value={{ floorPlanState, setFloorPlanState, estimateContextState, setEstimateContextState }}>
|
||||
{children}
|
||||
</FloorPlanContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export default FloorPlanProvider
|
||||
|
||||
@ -1,17 +1,22 @@
|
||||
'use client'
|
||||
|
||||
import FloorPlanProvider from './FloorPlanProvider'
|
||||
import FloorPlan from '@/components/floor-plan/FloorPlan'
|
||||
import { FloorPlanProvider } from './FloorPlanProvider'
|
||||
import CanvasLayout from '@/components/floor-plan/CanvasLayout'
|
||||
import EventProvider from './EventProvider'
|
||||
|
||||
export default function FloorPlanLayout({ children }) {
|
||||
console.log('FloorPlanLayout')
|
||||
console.log('🚀 ~ FloorPlanLayout ~ FloorPlanLayout:')
|
||||
|
||||
return (
|
||||
<>
|
||||
<FloorPlanProvider>
|
||||
<FloorPlan>
|
||||
<CanvasLayout>{children}</CanvasLayout>
|
||||
</FloorPlan>
|
||||
</FloorPlanProvider>
|
||||
{/*<EventProvider>*/}
|
||||
<FloorPlanProvider>
|
||||
<FloorPlan>
|
||||
<CanvasLayout>{children}</CanvasLayout>
|
||||
</FloorPlan>
|
||||
</FloorPlanProvider>
|
||||
{/*</EventProvider>*/}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@ -62,16 +62,16 @@ export default async function RootLayout({ children }) {
|
||||
{headerPathname === '/login' || headerPathname === '/join' ? (
|
||||
<QcastProvider>{children}</QcastProvider>
|
||||
) : (
|
||||
<div className="wrap">
|
||||
<Header userSession={sessionProps} />
|
||||
<div className="content">
|
||||
<Dimmed />
|
||||
<QcastProvider>
|
||||
<QcastProvider>
|
||||
<div className="wrap">
|
||||
<Header userSession={sessionProps} />
|
||||
<div className="content">
|
||||
<Dimmed />
|
||||
<SessionProvider useSession={sessionProps}>{children}</SessionProvider>
|
||||
</QcastProvider>
|
||||
</div>
|
||||
<Footer />
|
||||
</div>
|
||||
<Footer />
|
||||
</div>
|
||||
</QcastProvider>
|
||||
)}
|
||||
<QModal />
|
||||
<PopupManager />
|
||||
|
||||
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 = [
|
||||
@ -162,6 +155,12 @@ export const SAVE_KEY = [
|
||||
'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,11 @@ import { stuffSearchState } from '@/store/stuffAtom'
|
||||
import '@/styles/contents.scss'
|
||||
import ChangePasswordPop from './main/ChangePasswordPop'
|
||||
import { searchState } from '@/store/boardAtom'
|
||||
import { SessionContext } from '@/app/SessionProvider'
|
||||
import { QcastContext } from '@/app/QcastProvider'
|
||||
|
||||
export default function MainPage() {
|
||||
const sessionState = useRecoilValue(sessionStore)
|
||||
const { session } = useContext(SessionContext)
|
||||
|
||||
const globalLocaleState = useRecoilValue(globalLocaleStore)
|
||||
|
||||
@ -25,22 +27,24 @@ export default function MainPage() {
|
||||
|
||||
const [searchRadioType, setSearchRadioType] = useState('object')
|
||||
|
||||
const [saleStoreId, setSaleStoreId] = useState('')
|
||||
const [saleStoreName, setSaleStoreName] = useState('')
|
||||
// const [saleStoreId, setSaleStoreId] = useState('')
|
||||
// const [saleStoreName, setSaleStoreName] = useState('')
|
||||
|
||||
const [stuffSearch, setStuffSearch] = useRecoilState(stuffSearchState)
|
||||
|
||||
const [searchForm, setSearchForm] = useRecoilState(searchState)
|
||||
|
||||
useEffect(() => {
|
||||
if (sessionState.pwdInitYn === 'Y') {
|
||||
fetchObjectList()
|
||||
}
|
||||
}, [sessionState])
|
||||
const { qcastState } = useContext(QcastContext)
|
||||
|
||||
// useEffect(() => {
|
||||
// if (session.pwdInitYn === 'Y') {
|
||||
// fetchObjectList()
|
||||
// }
|
||||
// }, [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 +99,7 @@ export default function MainPage() {
|
||||
|
||||
return (
|
||||
<>
|
||||
{(sessionState?.pwdInitYn !== 'N' && (
|
||||
{(session?.pwdInitYn !== 'N' && (
|
||||
<>
|
||||
<div className="background-bord"></div>
|
||||
<div className="main-contents">
|
||||
@ -107,7 +111,7 @@ export default function MainPage() {
|
||||
</div>
|
||||
<span className="store-arr"></span>
|
||||
<div className="store-id-name">
|
||||
{saleStoreId} / {saleStoreName}
|
||||
{qcastState?.saleStoreId} / {qcastState?.saleStoreName}
|
||||
</div>
|
||||
</div>
|
||||
<div className="main-search-wrap">
|
||||
|
||||
@ -5,7 +5,7 @@ import { useEffect, useRef, useState } from 'react'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { useMode } from '@/hooks/useMode'
|
||||
import { LINE_TYPE, Mode } from '@/common/common'
|
||||
import { Button, Input } from '@nextui-org/react'
|
||||
import { Button } from '@nextui-org/react'
|
||||
import RangeSlider from './ui/RangeSlider'
|
||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||
import {
|
||||
@ -410,10 +410,10 @@ export default function Roof2(props) {
|
||||
]
|
||||
|
||||
const rectangleType1 = [
|
||||
{ x: 100, y: 100 },
|
||||
{ x: 100, y: 600 },
|
||||
{ x: 300, y: 600 },
|
||||
{ x: 300, y: 100 },
|
||||
{ x: 500, y: 100 },
|
||||
{ x: 500, y: 800 },
|
||||
{ x: 900, y: 800 },
|
||||
{ x: 900, y: 100 },
|
||||
]
|
||||
|
||||
const rectangleType2 = [
|
||||
@ -445,11 +445,31 @@ export default function Roof2(props) {
|
||||
{ x: 113, y: 371.9 },
|
||||
{ x: 762, y: 371.9 },
|
||||
{ x: 762, y: 818.7 },
|
||||
{ x: 1468.6, y: 818.7 },
|
||||
{ x: 1468.6, y: 114.9 },
|
||||
{ x: 1478.6, y: 818.7 },
|
||||
{ x: 1478.6, y: 114.9 },
|
||||
]
|
||||
|
||||
const polygon = new QPolygon(type2, {
|
||||
const test3 = [
|
||||
{ x: 100, y: 100 },
|
||||
{ x: 100, y: 600 },
|
||||
{ x: 600, y: 600 },
|
||||
{ x: 600, y: 100 },
|
||||
{ x: 500, y: 100 },
|
||||
{ x: 500, y: 200 },
|
||||
{ x: 200, y: 200 },
|
||||
{ x: 200, y: 100 },
|
||||
]
|
||||
|
||||
const test4 = [
|
||||
{ x: 100, y: 100 },
|
||||
{ x: 100, y: 1000 },
|
||||
{ x: 1100, y: 1000 },
|
||||
{ x: 1100, y: 550 },
|
||||
{ x: 500, y: 550 },
|
||||
{ x: 500, y: 100 },
|
||||
]
|
||||
|
||||
const polygon = new QPolygon(test4, {
|
||||
fill: 'transparent',
|
||||
stroke: 'green',
|
||||
strokeWidth: 1,
|
||||
@ -1010,7 +1030,7 @@ export default function Roof2(props) {
|
||||
</>
|
||||
)}
|
||||
<ThumbnailList {...thumbnailProps} />
|
||||
<div className={'flex'}>
|
||||
{/* <div className={'flex'}>
|
||||
<p className={'m-1 p-3'}>각도 입력(0~360) 후 방향설정 클릭</p>
|
||||
<Input
|
||||
className="m-1 p-3"
|
||||
@ -1028,14 +1048,14 @@ export default function Roof2(props) {
|
||||
<Button className="m-1 p-3" onClick={setDirectionStringToArrow}>
|
||||
방향 설정
|
||||
</Button>
|
||||
</div>
|
||||
<div className="relative w-48 h-48 flex justify-center items-center border-2 border-gray-300 rounded-full">
|
||||
{/* Compass Circle */}
|
||||
</div>*/}
|
||||
{/* <div className="relative w-48 h-48 flex justify-center items-center border-2 border-gray-300 rounded-full">
|
||||
Compass Circle
|
||||
<div
|
||||
className="absolute w-full h-full border-2 border-gray-300 rounded-full flex justify-center items-center"
|
||||
style={{ rotate: `${globalCampass}deg` }}
|
||||
>
|
||||
{/* N, S, E, W Labels */}
|
||||
N, S, E, W Labels
|
||||
<div className="absolute top-2 text-lg font-bold">N</div>
|
||||
<div className="absolute bottom-2 text-lg font-bold">S</div>
|
||||
<div className="absolute right-2 text-lg font-bold" style={{ rotate: '90deg' }}>
|
||||
@ -1046,9 +1066,9 @@ export default function Roof2(props) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Compass Pointer */}
|
||||
Compass Pointer
|
||||
<div className="relative w-10 h-10">
|
||||
{/* Red Upper Triangle */}
|
||||
Red Upper Triangle
|
||||
<div
|
||||
className="absolute top-0"
|
||||
style={{
|
||||
@ -1061,7 +1081,7 @@ export default function Roof2(props) {
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>*/}
|
||||
<div className="flex justify-start my-8 mx-2 w-full">
|
||||
<canvas ref={canvasRef} id="canvas" style={{ border: '1px solid black' }} />
|
||||
{!canvas ? null : mode === Mode.DRAW_LINE ? (
|
||||
|
||||
@ -80,79 +80,32 @@ export default function Login() {
|
||||
e.preventDefault()
|
||||
const formData = new FormData(e.target)
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
// 임시 로그인 처리
|
||||
setSession({
|
||||
userId: 'NEW016610',
|
||||
saleStoreId: null,
|
||||
name: null,
|
||||
mail: null,
|
||||
tel: null,
|
||||
storeId: 'TEMP02',
|
||||
userNm: 'ㅇㅇ6610',
|
||||
userNmKana: '신규사용자 16610',
|
||||
category: '인상6610',
|
||||
telNo: '336610',
|
||||
fax: null,
|
||||
email: 't10t@naver.com',
|
||||
pwdInitYn: 'Y',
|
||||
storeLvl: '1',
|
||||
groupId: '60000',
|
||||
custCd: '100000',
|
||||
})
|
||||
setSessionState({
|
||||
userId: 'NEW016610',
|
||||
saleStoreId: null,
|
||||
name: null,
|
||||
mail: null,
|
||||
tel: null,
|
||||
storeId: 'TEMP02',
|
||||
userNm: 'ㅇㅇ6610',
|
||||
userNmKana: '신규사용자 16610',
|
||||
category: '인상6610',
|
||||
telNo: '336610',
|
||||
fax: null,
|
||||
email: 't10t@naver.com',
|
||||
pwdInitYn: 'Y',
|
||||
storeLvl: '1',
|
||||
groupId: '60000',
|
||||
custCd: '100000',
|
||||
})
|
||||
if (chkLoginId) {
|
||||
Cookies.set('chkLoginId', formData.get('id'), { expires: 7 })
|
||||
} else {
|
||||
Cookies.remove('chkLoginId')
|
||||
// 로그인 처리 시작
|
||||
const param = {
|
||||
loginId: formData.get('id'),
|
||||
pwd: formData.get('password'),
|
||||
}
|
||||
router.push('/')
|
||||
// 임시 로그인 처리 끝
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
// 로그인 처리 시작 - ** 상단 임시 로그인 추후 삭제 필요 **
|
||||
// const param = {
|
||||
// loginId: formData.get('id'),
|
||||
// pwd: formData.get('password'),
|
||||
// }
|
||||
// await promisePost({ url: '/api/login/v1.0/login', data: param })
|
||||
// .then((res) => {
|
||||
// if (res) {
|
||||
// if (res.data.result.resultCode === 'S') {
|
||||
// setSession(res.data.data)
|
||||
// setSessionState(res.data.data)
|
||||
// // ID SAVE 체크되어 있는 경우, 쿠키 저장
|
||||
// if (chkLoginId) {
|
||||
// Cookies.set('chkLoginId', formData.get('id'), { expires: 7 })
|
||||
// } else {
|
||||
// Cookies.remove('chkLoginId')
|
||||
// }
|
||||
// router.push('/')
|
||||
// } else {
|
||||
// alert(res.data.result.resultMsg)
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// .catch((error) => {
|
||||
// alert(error.response.data.message)
|
||||
// })
|
||||
await promisePost({ url: '/api/login/v1.0/login', data: param })
|
||||
.then((res) => {
|
||||
if (res) {
|
||||
if (res.data.result.resultCode === 'S') {
|
||||
setSession(res.data.data)
|
||||
setSessionState(res.data.data)
|
||||
// ID SAVE 체크되어 있는 경우, 쿠키 저장
|
||||
if (chkLoginId) {
|
||||
Cookies.set('chkLoginId', formData.get('id'), { expires: 7 })
|
||||
} else {
|
||||
Cookies.remove('chkLoginId')
|
||||
}
|
||||
router.push('/')
|
||||
} else {
|
||||
alert(res.data.result.resultMsg)
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
alert(error.response.data.message)
|
||||
})
|
||||
}
|
||||
|
||||
// 비밀번호 초기화 관련
|
||||
|
||||
@ -15,6 +15,7 @@ export default function QContextMenu(props) {
|
||||
const { tempGridMode, setTempGridMode } = useTempGrid()
|
||||
const { handleKeyup } = useContextMenu()
|
||||
const { addDocumentEventListener, removeDocumentEvent } = useEvent()
|
||||
// const { addDocumentEventListener, removeDocumentEvent } = useContext(EventContext)
|
||||
|
||||
let contextType = ''
|
||||
|
||||
|
||||
@ -1,78 +1,69 @@
|
||||
import WithDraggable from '@/components/common/draggable/WithDraggable'
|
||||
import QSelectBox from '@/components/common/select/QSelectBox'
|
||||
import { usePopup } from '@/hooks/usePopup'
|
||||
import { useState } from 'react'
|
||||
import { useEffect, 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'
|
||||
import { useCanvasSetting } from '@/hooks/option/useCanvasSetting'
|
||||
|
||||
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' },
|
||||
]
|
||||
|
||||
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(font.fontFamily)
|
||||
const [selectedFontWeight, setSelectedFontWeight] = useState(font.fontWeight)
|
||||
const [selectedFontSize, setSelectedFontSize] = useState(font.fontSize)
|
||||
const [selectedFontColor, setSelectedFontColor] = useState(font.fontColor)
|
||||
|
||||
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 fontOptions = [
|
||||
{ name: getMessage('font.style.normal'), value: 'normal' },
|
||||
{ name: getMessage('font.style.italic'), value: 'italic' },
|
||||
{
|
||||
name: getMessage('font.style.bold'),
|
||||
value: 'bold',
|
||||
},
|
||||
{ name: getMessage('font.style.bold.italic'), value: 'boldAndItalic' },
|
||||
{ id: 'normal', name: getMessage('font.style.normal'), value: 'normal' },
|
||||
{ id: 'italic', name: getMessage('font.style.italic'), value: 'italic' },
|
||||
{ id: 'bold', name: getMessage('font.style.bold'), value: 'bold' },
|
||||
{ id: 'boldAndItalic', name: getMessage('font.style.bold.italic'), value: 'boldAndItalic' },
|
||||
]
|
||||
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,
|
||||
fontWeight: selectedFontWeight,
|
||||
fontSize: selectedFontSize,
|
||||
fontColor: selectedFontColor,
|
||||
})
|
||||
if (setIsShow) setIsShow(false)
|
||||
closePopup(id)
|
||||
closePopup(id, isConfig)
|
||||
}
|
||||
|
||||
return (
|
||||
@ -123,9 +114,10 @@ export default function FontSetting(props) {
|
||||
<div className="font-ex-box">
|
||||
<span
|
||||
style={{
|
||||
fontFamily: selectedFont?.value ?? '',
|
||||
fontSize: selectedFontSize?.value ?? '12px',
|
||||
fontWeight: '400',
|
||||
fontFamily: selectedFont?.value ?? 'MS PGothic',
|
||||
fontWeight: selectedFontWeight?.value?.toLowerCase().includes('bold') ? 'bold' : 'normal',
|
||||
fontStyle: selectedFontWeight?.value?.toLowerCase().includes('italic') ? 'italic' : 'normal',
|
||||
fontSize: selectedFontSize?.value ?? 16,
|
||||
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>
|
||||
))}
|
||||
|
||||
7
src/components/common/spinner/GlobalSpinner.jsx
Normal file
7
src/components/common/spinner/GlobalSpinner.jsx
Normal file
@ -0,0 +1,7 @@
|
||||
'use client'
|
||||
|
||||
import { HashLoader } from 'react-spinners'
|
||||
|
||||
export default function GlobalSpinner() {
|
||||
return <HashLoader />
|
||||
}
|
||||
@ -69,7 +69,12 @@ export default function BoardDetailModal({ noticeNo, setOpen }) {
|
||||
</dl>
|
||||
)}
|
||||
|
||||
<div className="community_detail-inner">{boardDetail.contents}</div>
|
||||
<div
|
||||
className="community_detail-inner"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: boardDetail.contents ? boardDetail.contents.replaceAll('\n', '<br/>') : '',
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -6,36 +6,88 @@ import { useRecoilValue } from 'recoil'
|
||||
import { floorPlanObjectState } from '@/store/floorPlanObjectAtom'
|
||||
|
||||
export default function DocDownOptionPop({ planNo, setEstimatePopupOpen }) {
|
||||
// console.log('플랜번호::::::::::::', planNo)
|
||||
const { getMessage } = useMessage()
|
||||
const { promiseGet } = useAxios()
|
||||
const { promisePost } = useAxios()
|
||||
|
||||
//EXCEL, PDF 구분
|
||||
const [schDownload, setSchDownload] = useState('EXCEL')
|
||||
//다운로드 파일 EXCEL
|
||||
const [schUnitPriceFlg, setSchUnitPriceFlg] = useState('0')
|
||||
|
||||
//견적제출서 표시명
|
||||
const [schDisplayFlg, setSchSchDisplayFlg] = useState('0')
|
||||
//가대 중량표 포함
|
||||
const [schWeightFlg, setSchWeightFlg] = useState('0')
|
||||
//가대 중량표 포함(포함:1 미포함 : 0)
|
||||
const [schWeightFlg, setSchWeightFlg] = useState('1')
|
||||
//도면/시뮬레이션 파일 포함
|
||||
const [schDrawingFlg, setSchDrawingFlg] = useState('0')
|
||||
const [schDrawingFlg, setSchDrawingFlg] = useState('1')
|
||||
|
||||
// recoil 물건번호
|
||||
const objectRecoil = useRecoilValue(floorPlanObjectState)
|
||||
|
||||
//문서 다운로드
|
||||
const handleFileDown = async () => {
|
||||
// console.log('물건번호:::', objectRecoil.floorPlanObjectNo)
|
||||
// console.log('planNo::', planNo)
|
||||
// 고른 옵션값들
|
||||
//0 : 견적가 Excel 1 : 정가용Excel 2: 견적가 PDF 3 :정가용PDF
|
||||
// console.log(schUnitPriceFlg)
|
||||
// console.log(schDisplayFlg)
|
||||
// console.log(schWeightFlg)
|
||||
// console.log(schDrawingFlg)
|
||||
const url = '/api/estimate/excel-download'
|
||||
const params = {}
|
||||
let sendUnitPriceFlg
|
||||
if (schUnitPriceFlg === '0') {
|
||||
sendUnitPriceFlg = '0'
|
||||
} else if (schUnitPriceFlg === '1') {
|
||||
sendUnitPriceFlg = '1'
|
||||
} else if (schUnitPriceFlg === '2') {
|
||||
sendUnitPriceFlg = '0'
|
||||
} else {
|
||||
sendUnitPriceFlg = '1'
|
||||
}
|
||||
|
||||
//schDrawingFlg 선택값은 의미없어짐
|
||||
//가대중량표 포함, 도면/시뮬레이션 파일 포함 선택값에따라 schDrawingFlg에 |구분자로 보냄
|
||||
//SchDrawingFlg (1 : 견적서,2 : 발전시뮬레이션, 3 : 도면, 4 : 가대)
|
||||
// ex) 1|2|3|4
|
||||
let defaultSchDrawingFlg = '1'
|
||||
if (schWeightFlg === '1') {
|
||||
defaultSchDrawingFlg = defaultSchDrawingFlg.concat('|', '4')
|
||||
}
|
||||
if (schDrawingFlg === '1') {
|
||||
defaultSchDrawingFlg = defaultSchDrawingFlg.concat('|', '2', '|', '3')
|
||||
}
|
||||
|
||||
const params = {
|
||||
objectNo: objectRecoil.floorPlanObjectNo,
|
||||
planNo: planNo,
|
||||
schDownload: schDownload,
|
||||
schUnitPriceFlg: sendUnitPriceFlg,
|
||||
schDisplayFlg: schDisplayFlg,
|
||||
schWeightFlg: schWeightFlg,
|
||||
schDrawingFlg: defaultSchDrawingFlg,
|
||||
pwrGnrSimType: 'D', //default 화면에 안보여줌
|
||||
}
|
||||
|
||||
const options = { responseType: 'blob' }
|
||||
await promisePost({ url: url, data: params, option: options })
|
||||
.then((resultData) => {
|
||||
if (resultData) {
|
||||
let fileName = 'unknow'
|
||||
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
|
||||
|
||||
//서버에서 내려오는 파일명
|
||||
const contentDisposition = resultData.headers['content-disposition']
|
||||
if (contentDisposition) {
|
||||
fileName = contentDisposition.split('filename=')[1].replace(/['"]/g, '')
|
||||
}
|
||||
|
||||
link.download = fileName
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
link.remove()
|
||||
window.URL.revokeObjectURL(fileUrl)
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log('::FileDownLoad Error::', error)
|
||||
alert('File does not exist.')
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
@ -74,28 +126,30 @@ export default function DocDownOptionPop({ planNo, setEstimatePopupOpen }) {
|
||||
<div className="d-check-radio light mr10">
|
||||
<input
|
||||
type="radio"
|
||||
id="schUnitPriceFlg0"
|
||||
id="schUnitPriceExcelFlg0"
|
||||
name="schUnitPriceFlg"
|
||||
value={'0'}
|
||||
checked={schUnitPriceFlg === '0'}
|
||||
onChange={(e) => {
|
||||
setSchDownload('EXCEL')
|
||||
setSchUnitPriceFlg(e.target.value)
|
||||
}}
|
||||
/>
|
||||
<label htmlFor="schUnitPriceFlg0">{getMessage('estimate.detail.docPopup.schUnitPriceFlg.schUnitPriceFlg0')}</label>
|
||||
<label htmlFor="schUnitPriceExcelFlg0">{getMessage('estimate.detail.docPopup.schUnitPriceFlg.excelFlg0')}</label>
|
||||
</div>
|
||||
<div className="d-check-radio light mr10">
|
||||
<input
|
||||
type="radio"
|
||||
id="schUnitPriceFlg1"
|
||||
id="schUnitPriceExcelFlg1"
|
||||
name="schUnitPriceFlg"
|
||||
value={'1'}
|
||||
checked={schUnitPriceFlg === '1'}
|
||||
onChange={(e) => {
|
||||
setSchDownload('EXCEL')
|
||||
setSchUnitPriceFlg(e.target.value)
|
||||
}}
|
||||
/>
|
||||
<label htmlFor="schUnitPriceFlg1">{getMessage('estimate.detail.docPopup.schUnitPriceFlg.schUnitPriceFlg1')}</label>
|
||||
<label htmlFor="schUnitPriceExcelFlg1">{getMessage('estimate.detail.docPopup.schUnitPriceFlg.excelFlg1')}</label>
|
||||
</div>
|
||||
<div className="d-check-radio light mr10">
|
||||
<input
|
||||
@ -105,10 +159,11 @@ export default function DocDownOptionPop({ planNo, setEstimatePopupOpen }) {
|
||||
value={'2'}
|
||||
checked={schUnitPriceFlg === '2'}
|
||||
onChange={(e) => {
|
||||
setSchDownload('PDF')
|
||||
setSchUnitPriceFlg(e.target.value)
|
||||
}}
|
||||
/>
|
||||
<label htmlFor="schUnitPricePdfFlg0">{getMessage('estimate.detail.docPopup.schUnitPriceFlg.schUnitPriceFlg2')}</label>
|
||||
<label htmlFor="schUnitPricePdfFlg0">{getMessage('estimate.detail.docPopup.schUnitPriceFlg.pdfFlg0')}</label>
|
||||
</div>
|
||||
<div className="d-check-radio light ">
|
||||
<input
|
||||
@ -118,10 +173,11 @@ export default function DocDownOptionPop({ planNo, setEstimatePopupOpen }) {
|
||||
value={'3'}
|
||||
checked={schUnitPriceFlg === '3'}
|
||||
onChange={(e) => {
|
||||
setSchDownload('PDF')
|
||||
setSchUnitPriceFlg(e.target.value)
|
||||
}}
|
||||
/>
|
||||
<label htmlFor="schUnitPricePdfFlg1">{getMessage('estimate.detail.docPopup.schUnitPriceFlg.schUnitPriceFlg3')}</label>
|
||||
<label htmlFor="schUnitPricePdfFlg1">{getMessage('estimate.detail.docPopup.schUnitPriceFlg.pdfFlg1')}</label>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
@ -168,19 +224,6 @@ export default function DocDownOptionPop({ planNo, setEstimatePopupOpen }) {
|
||||
<td>
|
||||
<div className="form-flex-wrap">
|
||||
<div className="d-check-radio light mr10">
|
||||
<input
|
||||
type="radio"
|
||||
name="schWeightFlg"
|
||||
id="schWeightFlg0"
|
||||
value={'0'}
|
||||
checked={schWeightFlg === '0'}
|
||||
onChange={(e) => {
|
||||
setSchWeightFlg(e.target.value)
|
||||
}}
|
||||
/>
|
||||
<label htmlFor="schWeightFlg0">{getMessage('estimate.detail.docPopup.schWeightFlg.schWeightFlg0')}</label>
|
||||
</div>
|
||||
<div className="d-check-radio light">
|
||||
<input
|
||||
type="radio"
|
||||
name="schWeightFlg"
|
||||
@ -193,6 +236,19 @@ export default function DocDownOptionPop({ planNo, setEstimatePopupOpen }) {
|
||||
/>
|
||||
<label htmlFor="schWeightFlg1">{getMessage('estimate.detail.docPopup.schWeightFlg.schWeightFlg1')}</label>
|
||||
</div>
|
||||
<div className="d-check-radio light">
|
||||
<input
|
||||
type="radio"
|
||||
name="schWeightFlg"
|
||||
id="schWeightFlg0"
|
||||
value={'0'}
|
||||
checked={schWeightFlg === '0'}
|
||||
onChange={(e) => {
|
||||
setSchWeightFlg(e.target.value)
|
||||
}}
|
||||
/>
|
||||
<label htmlFor="schWeightFlg0">{getMessage('estimate.detail.docPopup.schWeightFlg.schWeightFlg0')}</label>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@ -201,6 +257,19 @@ export default function DocDownOptionPop({ planNo, setEstimatePopupOpen }) {
|
||||
<td>
|
||||
<div className="form-flex-wrap">
|
||||
<div className="d-check-radio light mr10">
|
||||
<input
|
||||
type="radio"
|
||||
name="schDrawingFlg"
|
||||
id="schDrawingFlg1"
|
||||
value={'1'}
|
||||
checked={schDrawingFlg === '1'}
|
||||
onChange={(e) => {
|
||||
setSchDrawingFlg(e.target.value)
|
||||
}}
|
||||
/>
|
||||
<label htmlFor="schDrawingFlg1">{getMessage('estimate.detail.docPopup.schDrawingFlg.schDrawingFlg1')}</label>
|
||||
</div>
|
||||
<div className="d-check-radio light">
|
||||
<input
|
||||
type="radio"
|
||||
name="schDrawingFlg"
|
||||
@ -213,19 +282,6 @@ export default function DocDownOptionPop({ planNo, setEstimatePopupOpen }) {
|
||||
/>
|
||||
<label htmlFor="schDrawingFlg0">{getMessage('estimate.detail.docPopup.schDrawingFlg.schDrawingFlg0')}</label>
|
||||
</div>
|
||||
<div className="d-check-radio light">
|
||||
<input
|
||||
type="radio"
|
||||
name="schDrawingFlg"
|
||||
id="schDrawingFlg01"
|
||||
value={'1'}
|
||||
checked={schDrawingFlg === '1'}
|
||||
onChange={(e) => {
|
||||
setSchDrawingFlg(e.target.value)
|
||||
}}
|
||||
/>
|
||||
<label htmlFor="schDrawingFlg01">{getMessage('estimate.detail.docPopup.schDrawingFlg.schDrawingFlg1')}</label>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
313
src/components/estimate/popup/EstimateCopyPop.jsx
Normal file
313
src/components/estimate/popup/EstimateCopyPop.jsx
Normal file
@ -0,0 +1,313 @@
|
||||
'use client'
|
||||
import { useEffect, useState, useContext, useRef } from 'react'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { useAxios } from '@/hooks/useAxios'
|
||||
import Select, { components } from 'react-select'
|
||||
import { SessionContext } from '@/app/SessionProvider'
|
||||
import { isEmptyArray, isObjectNotEmpty } from '@/util/common-utils'
|
||||
import { useEstimateController } from '@/hooks/floorPlan/estimate/useEstimateController'
|
||||
|
||||
export default function EstimateCopyPop({ planNo, setEstimateCopyPopupOpen }) {
|
||||
const { getMessage } = useMessage()
|
||||
const { get } = useAxios()
|
||||
|
||||
const { handleEstimateCopy, state } = useEstimateController(planNo)
|
||||
|
||||
const { session } = useContext(SessionContext)
|
||||
|
||||
const [saleStoreList, setSaleStoreList] = useState([]) // 판매점 리스트
|
||||
const [favoriteStoreList, setFavoriteStoreList] = useState([]) //즐겨찾기한 판매점목록
|
||||
const [showSaleStoreList, setShowSaleStoreList] = useState([]) //보여줄 판매점목록
|
||||
const [otherSaleStoreList, setOtherSaleStoreList] = useState([])
|
||||
const [originOtherSaleStoreList, setOriginOtherSaleStoreList] = useState([])
|
||||
|
||||
const [saleStoreId, setSaleStoreId] = useState('') //선택한 1차점
|
||||
const [otherSaleStoreId, setOtherSaleStoreId] = useState('') //선택한 1차점 이외
|
||||
|
||||
const [sendPlanNo, setSendPlanNo] = useState('1')
|
||||
const [copyReceiveUser, setCopyReceiveUser] = useState('')
|
||||
|
||||
const ref = useRef() //2차점 자동완성 초기화용
|
||||
|
||||
useEffect(() => {
|
||||
let url
|
||||
let firstList
|
||||
let favList
|
||||
let otherList
|
||||
if (session.storeId === 'T01') {
|
||||
url = `/api/object/saleStore/${session?.storeId}/firstList?userId=${session?.userId}`
|
||||
} else {
|
||||
if (session.storeLvl === '1') {
|
||||
url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=1&userId=${session?.userId}`
|
||||
} else {
|
||||
//T01 or 1차점만 복사버튼 노출됨으로
|
||||
// url = `/api/object/saleStore/${session?.storeId}/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
|
||||
})
|
||||
|
||||
if (session.storeId === 'T01') {
|
||||
firstList = res
|
||||
//T01을 첫번째로 정렬
|
||||
firstList.sort((a, b) => (a.saleStoreId !== 'T01') - (b.saleStoreId !== 'T01') || a.saleStoreId - b.saleStoreId)
|
||||
favList = firstList.filter((row) => row.saleStoreId === 'T01' || row.priority !== 'B')
|
||||
|
||||
setSaleStoreList(firstList)
|
||||
setFavoriteStoreList(favList)
|
||||
setShowSaleStoreList(favList)
|
||||
setSaleStoreId(session?.storeId)
|
||||
|
||||
// T01때 디폴트로 T01셋팅하고 하위 2차목록 조회하기 바꾸면(onSelectionChange..) 바꾼거로 2차목록 조회하기
|
||||
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)
|
||||
setOriginOtherSaleStoreList(otherList)
|
||||
} else {
|
||||
setOtherSaleStoreList([])
|
||||
}
|
||||
})
|
||||
} else {
|
||||
if (session.storeLvl === '1') {
|
||||
firstList = res
|
||||
favList = res.filter((row) => row.priority !== 'B')
|
||||
otherList = res.filter((row) => row.firstAgentYn === 'N')
|
||||
setSaleStoreList(firstList)
|
||||
setFavoriteStoreList(firstList)
|
||||
setShowSaleStoreList(firstList)
|
||||
setSaleStoreId(firstList[0].saleStoreId)
|
||||
|
||||
setOtherSaleStoreList(otherList)
|
||||
} else {
|
||||
//T01 or 1차점만 복사버튼 노출됨으로
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (planNo) {
|
||||
setSendPlanNo(planNo)
|
||||
}
|
||||
}, [planNo])
|
||||
|
||||
useEffect(() => {
|
||||
if (state?.charger) {
|
||||
setCopyReceiveUser(state.charger)
|
||||
}
|
||||
}, [state.charger])
|
||||
|
||||
//T01 1차점 자동완성 인풋때 목록 변환
|
||||
const onInputChange = (key) => {
|
||||
if (key !== '') {
|
||||
setShowSaleStoreList(saleStoreList)
|
||||
} else {
|
||||
setShowSaleStoreList(favoriteStoreList)
|
||||
}
|
||||
}
|
||||
|
||||
// 1차점 변경 이벤트
|
||||
const onSelectionChange = (key) => {
|
||||
if (isObjectNotEmpty(key)) {
|
||||
if (key.saleStoreId === saleStoreId) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (isObjectNotEmpty(key)) {
|
||||
setSaleStoreId(key.saleStoreId)
|
||||
const url = `/api/object/saleStore/${key.saleStoreId}/list?firstFlg=0&userId=${session?.userId}`
|
||||
let otherList
|
||||
get({ url: url }).then((res) => {
|
||||
if (!isEmptyArray(res)) {
|
||||
res.map((row) => {
|
||||
row.value = row.saleStoreId
|
||||
row.label = row.saleStoreName
|
||||
})
|
||||
|
||||
otherList = res
|
||||
setOtherSaleStoreList(otherList)
|
||||
setOtherSaleStoreId('')
|
||||
} else {
|
||||
setOtherSaleStoreId('')
|
||||
setOtherSaleStoreList([])
|
||||
}
|
||||
})
|
||||
} else {
|
||||
setSaleStoreId('')
|
||||
//otherSaleStoreId는 onSelectionChange2에서 초기화됨
|
||||
setOtherSaleStoreList(originOtherSaleStoreList)
|
||||
handleClear()
|
||||
}
|
||||
}
|
||||
|
||||
// 2차점 변경 이벤트
|
||||
const onSelectionChange2 = (key) => {
|
||||
if (isObjectNotEmpty(key)) {
|
||||
if (key.saleStoreId === otherSaleStoreId) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (isObjectNotEmpty(key)) {
|
||||
setOtherSaleStoreId(key.saleStoreId)
|
||||
} else {
|
||||
setOtherSaleStoreId('')
|
||||
}
|
||||
}
|
||||
|
||||
//2차점 자동완성 초기화
|
||||
const handleClear = () => {
|
||||
if (ref.current) {
|
||||
ref.current.clearValue()
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="modal-popup">
|
||||
<div className="modal-dialog">
|
||||
<div className="modal-content">
|
||||
<div className="modal-header">
|
||||
<h1 className="title">{getMessage('estimate.detail.estimateCopyPopup.title')}</h1>
|
||||
<button
|
||||
type="button"
|
||||
className="modal-close"
|
||||
onClick={() => {
|
||||
setEstimateCopyPopupOpen(false)
|
||||
}}
|
||||
>
|
||||
{getMessage('estimate.detail.estimateCopyPopup.close')}
|
||||
</button>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<div className="modal-body-inner">
|
||||
<div className="explane">{getMessage('estimate.detail.estimateCopyPopup.explane')}</div>
|
||||
<div className="estimate-copy-info-wrap">
|
||||
<div className="estimate-copy-info-item">
|
||||
<div className="estimate-copy-info-tit">
|
||||
{getMessage('estimate.detail.estimateCopyPopup.label.saleStoreId')} <span className="red">*</span>
|
||||
</div>
|
||||
{session.storeId === 'T01' && (
|
||||
<div className="estimate-copy-info-box">
|
||||
<div className="estimate-copy-sel">
|
||||
<Select
|
||||
id="long-value-select1"
|
||||
instanceId="long-value-select1"
|
||||
className="react-select-custom"
|
||||
classNamePrefix="custom"
|
||||
placeholder="Select"
|
||||
options={showSaleStoreList}
|
||||
onInputChange={onInputChange}
|
||||
onChange={onSelectionChange}
|
||||
getOptionLabel={(x) => x.saleStoreName}
|
||||
getOptionValue={(x) => x.saleStoreId}
|
||||
isClearable={true}
|
||||
isDisabled={false}
|
||||
value={saleStoreList.filter(function (option) {
|
||||
return option.saleStoreId === saleStoreId
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
<div className="estimate-copy-id">{saleStoreId}</div>
|
||||
</div>
|
||||
)}
|
||||
{session.storeId !== 'T01' && session.storeLvl === '1' && (
|
||||
<div className="estimate-copy-info-box">
|
||||
<div className="estimate-copy-sel">
|
||||
<Select
|
||||
id="long-value-select1"
|
||||
instanceId="long-value-select1"
|
||||
className="react-select-custom"
|
||||
classNamePrefix="custom"
|
||||
placeholder="Select"
|
||||
options={showSaleStoreList[0]}
|
||||
onChange={onSelectionChange}
|
||||
getOptionLabel={(x) => x.saleStoreName}
|
||||
getOptionValue={(x) => x.saleStoreId}
|
||||
isClearable={false}
|
||||
isDisabled={session?.storeLvl !== '1' ? true : session?.storeId !== 'T01' ? true : false}
|
||||
value={showSaleStoreList.filter(function (option) {
|
||||
return option.saleStoreId === saleStoreId
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
<div className="estimate-copy-id">{saleStoreId}</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="estimate-copy-info-item">
|
||||
<div className="estimate-copy-info-tit">{getMessage('estimate.detail.estimateCopyPopup.label.otherSaleStoreId')}</div>
|
||||
<div className="estimate-copy-info-box">
|
||||
<div className="estimate-copy-sel">
|
||||
<Select
|
||||
id="long-value-select2"
|
||||
instanceId="long-value-select2"
|
||||
className="react-select-custom"
|
||||
classNamePrefix="custom"
|
||||
placeholder="Select"
|
||||
ref={ref}
|
||||
options={otherSaleStoreList}
|
||||
onChange={onSelectionChange2}
|
||||
getOptionLabel={(x) => x.saleStoreName}
|
||||
getOptionValue={(x) => x.saleStoreId}
|
||||
isClearable={true}
|
||||
isDisabled={otherSaleStoreList.length > 0 ? false : true}
|
||||
value={otherSaleStoreList.filter(function (option) {
|
||||
return option.saleStoreId === otherSaleStoreId
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
<div className="estimate-copy-id">{otherSaleStoreId}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="estimate-copy-info-item">
|
||||
<div className="estimate-copy-info-tit">
|
||||
{getMessage('estimate.detail.estimateCopyPopup.label.receiveUser')} <span className="red">*</span>
|
||||
</div>
|
||||
<div className="input-wrap">
|
||||
<input
|
||||
type="text"
|
||||
className="input-light"
|
||||
required
|
||||
defaultValue={state?.charger}
|
||||
onChange={(e) => {
|
||||
setCopyReceiveUser(e.target.value)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="footer-btn-wrap">
|
||||
<button type="button" className="btn-origin grey mr5" onClick={() => setEstimateCopyPopupOpen(false)}>
|
||||
{getMessage('estimate.detail.estimateCopyPopup.close')}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="btn-origin navy"
|
||||
onClick={() => {
|
||||
handleEstimateCopy(sendPlanNo, copyReceiveUser, saleStoreId, otherSaleStoreId)
|
||||
}}
|
||||
>
|
||||
{getMessage('estimate.detail.estimateCopyPopup.copyBtn')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -1,13 +1,13 @@
|
||||
'use client'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
export default function ProductFeaturesPop({ specialNoteList, showProductFeatureData, setProductFeaturesPopupOpen }) {
|
||||
export default function ProductFeaturesPop({ popShowSpecialNoteList, showProductFeatureData, setProductFeaturesPopupOpen }) {
|
||||
const [showSpecialNoteList, setShowSpecialNoteList] = useState([])
|
||||
const { getMessage } = useMessage()
|
||||
|
||||
useEffect(() => {
|
||||
let pushData = []
|
||||
specialNoteList.map((row) => {
|
||||
popShowSpecialNoteList.map((row) => {
|
||||
let option = showProductFeatureData.split('、')
|
||||
option.map((row2) => {
|
||||
if (row.code === row2) {
|
||||
@ -16,7 +16,7 @@ export default function ProductFeaturesPop({ specialNoteList, showProductFeature
|
||||
})
|
||||
})
|
||||
setShowSpecialNoteList(pushData)
|
||||
}, [specialNoteList])
|
||||
}, [popShowSpecialNoteList])
|
||||
|
||||
return (
|
||||
<div className="modal-popup">
|
||||
@ -42,7 +42,8 @@ export default function ProductFeaturesPop({ specialNoteList, showProductFeature
|
||||
return (
|
||||
<dl>
|
||||
<dt>{row.codeNm}</dt>
|
||||
<dd dangerouslySetInnerHTML={{ __html: row.remarks }}></dd>
|
||||
{/* <dd dangerouslySetInnerHTML={{ __html: row.remarks }}></dd> */}
|
||||
<dd dangerouslySetInnerHTML={{ __html: row.remarks }} style={{ whiteSpace: 'pre-wrap' }}></dd>
|
||||
</dl>
|
||||
)
|
||||
})}
|
||||
|
||||
@ -1,15 +1,8 @@
|
||||
import { fabric } from 'fabric'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { QLine } from '@/components/fabric/QLine'
|
||||
import {
|
||||
distanceBetweenPoints,
|
||||
findTopTwoIndexesByDistance,
|
||||
getAllRelatedObjects,
|
||||
getDirectionByPoint,
|
||||
sortedPointLessEightPoint,
|
||||
sortedPoints,
|
||||
} from '@/util/canvas-util'
|
||||
import { calculateAngle, drawRidgeRoof, drawShedRoof, inPolygon, toGeoJSON } from '@/util/qpolygon-utils'
|
||||
import { distanceBetweenPoints, findTopTwoIndexesByDistance, getDirectionByPoint, sortedPointLessEightPoint, sortedPoints } from '@/util/canvas-util'
|
||||
import { calculateAngle, drawGabledRoof, drawRidgeRoof, drawShedRoof, inPolygon, toGeoJSON } from '@/util/qpolygon-utils'
|
||||
import * as turf from '@turf/turf'
|
||||
import { LINE_TYPE } from '@/common/common'
|
||||
|
||||
@ -38,6 +31,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
||||
this.cells = []
|
||||
this.innerLines = []
|
||||
this.children = []
|
||||
this.separatePolygon = []
|
||||
|
||||
// 소수점 전부 제거
|
||||
points.forEach((point) => {
|
||||
@ -131,15 +125,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 +160,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 +186,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)
|
||||
@ -208,7 +211,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
||||
(gableOdd.every((type) => type === LINE_TYPE.WALLLINE.EAVES) && gableEven.every((type) => gableType.includes(type))) ||
|
||||
(gableEven.every((type) => type === LINE_TYPE.WALLLINE.EAVES) && gableOdd.every((type) => gableType.includes(type)))
|
||||
) {
|
||||
console.log('박공 지붕')
|
||||
drawGabledRoof(this.id, this.canvas)
|
||||
} else if (hasShed) {
|
||||
const sheds = this.lines.filter((line) => line.attributes !== undefined && line.attributes.type === LINE_TYPE.WALLLINE.SHED)
|
||||
const areLinesParallel = function (line1, line2) {
|
||||
@ -263,9 +266,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
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useRef } from 'react'
|
||||
import { useContext, useEffect, useRef } from 'react'
|
||||
|
||||
import { useRecoilValue } from 'recoil'
|
||||
|
||||
@ -13,6 +13,9 @@ 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'
|
||||
import ImgLoad from '@/components/floor-plan/modal/ImgLoad'
|
||||
import { EventContext } from '@/app/floor-plan/EventProvider'
|
||||
|
||||
export default function CanvasFrame() {
|
||||
const canvasRef = useRef(null)
|
||||
@ -21,7 +24,10 @@ export default function CanvasFrame() {
|
||||
const currentMenu = useRecoilValue(currentMenuState)
|
||||
const { contextMenu, handleClick } = useContextMenu()
|
||||
const { selectedPlan, modifiedPlanFlag, checkCanvasObjectEvent, resetModifiedPlans, currentCanvasPlan } = usePlan()
|
||||
useEvent()
|
||||
const totalDisplay = useRecoilValue(totalDisplaySelector) // 집계표 표시 여부
|
||||
// useEvent()
|
||||
// const { initEvent } = useContext(EventContext)
|
||||
// initEvent()
|
||||
|
||||
const loadCanvas = () => {
|
||||
if (canvas) {
|
||||
@ -72,7 +78,10 @@ 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 />}
|
||||
{/* 이미지 로드 팝업 */}
|
||||
<ImgLoad />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
'use client'
|
||||
|
||||
import { useContext, useEffect, useState } from 'react'
|
||||
import { useContext, useEffect } from 'react'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import CanvasFrame from './CanvasFrame'
|
||||
import { FloorPlanContext } from '@/app/floor-plan/FloorPlanProvider'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { useSwal } from '@/hooks/useSwal'
|
||||
import { usePlan } from '@/hooks/usePlan'
|
||||
@ -14,15 +14,16 @@ export default function CanvasLayout({ children }) {
|
||||
// const { menuNumber } = props
|
||||
const { menuNumber } = useCanvasMenu()
|
||||
const { session } = useContext(SessionContext)
|
||||
const [objectNo, setObjectNo] = useState('test123240822001') // 이후 삭제 필요
|
||||
const { floorPlanState } = useContext(FloorPlanContext)
|
||||
const { objectNo, pid } = floorPlanState
|
||||
const globalLocaleState = useRecoilValue(globalLocaleStore)
|
||||
|
||||
const { getMessage } = useMessage()
|
||||
const { swalFire } = useSwal()
|
||||
const { plans, modifiedPlans, loadCanvasPlanData, handleCurrentPlan, handleAddPlan, handleDeletePlan } = usePlan()
|
||||
const { plans, initCanvasPlans, modifiedPlans, loadCanvasPlanData, handleCurrentPlan, handleAddPlan, handleDeletePlan } = usePlan()
|
||||
|
||||
useEffect(() => {
|
||||
loadCanvasPlanData(session.userId, objectNo)
|
||||
loadCanvasPlanData(session.userId, objectNo, pid)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
@ -36,14 +37,18 @@ export default function CanvasLayout({ children }) {
|
||||
onClick={() => handleCurrentPlan(session.userId, plan.id)}
|
||||
>
|
||||
<span>
|
||||
{plan.name}
|
||||
{!initCanvasPlans.some((initCanvasPlans) => initCanvasPlans.id === plan.id) && 'New '}
|
||||
{`Plan ${plan.ordering}`}
|
||||
{modifiedPlans.some((modifiedPlan) => modifiedPlan === plan.id) && ' [ M ]'}
|
||||
</span>
|
||||
<i
|
||||
className="close"
|
||||
onClick={(e) =>
|
||||
swalFire({
|
||||
text: `${plan.name} ` + getMessage('plan.message.confirm.delete'),
|
||||
text:
|
||||
(!initCanvasPlans.some((initCanvasPlans) => initCanvasPlans.id === plan.id) ? 'New ' : '') +
|
||||
`Plan ${plan.ordering} ` +
|
||||
getMessage('plan.message.confirm.delete'),
|
||||
type: 'confirm',
|
||||
confirmFn: () => {
|
||||
handleDeletePlan(e, plan.id)
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useContext, useEffect, useState } from 'react'
|
||||
|
||||
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
|
||||
|
||||
@ -33,8 +33,12 @@ import useMenu from '@/hooks/common/useMenu'
|
||||
import { MENU } from '@/common/common'
|
||||
|
||||
import { useEstimateController } from '@/hooks/floorPlan/estimate/useEstimateController'
|
||||
import { estimateState } from '@/store/floorPlanObjectAtom'
|
||||
import { estimateState, floorPlanObjectState } from '@/store/floorPlanObjectAtom'
|
||||
import DocDownOptionPop from '../estimate/popup/DocDownOptionPop'
|
||||
import { FloorPlanContext } from '@/app/floor-plan/FloorPlanProvider'
|
||||
import EstimateCopyPop from '../estimate/popup/EstimateCopyPop'
|
||||
import { pwrGnrSimTypeState } from '@/store/simulatorAtom'
|
||||
import { useAxios } from '@/hooks/useAxios'
|
||||
|
||||
export default function CanvasMenu(props) {
|
||||
const { menuNumber, setMenuNumber } = props
|
||||
@ -59,14 +63,17 @@ export default function CanvasMenu(props) {
|
||||
const { handleEstimateSubmit } = useEstimateController()
|
||||
const estimateRecoilState = useRecoilValue(estimateState)
|
||||
const [estimatePopupOpen, setEstimatePopupOpen] = useState(false)
|
||||
const [estimateCopyPopupOpen, setEstimateCopyPopupOpen] = useState(false)
|
||||
|
||||
const { getMessage } = useMessage()
|
||||
const { currentCanvasPlan, saveCanvas } = usePlan()
|
||||
const { swalFire } = useSwal()
|
||||
const { initEvent, addCanvasMouseEventListener, addDocumentEventListener } = useEvent()
|
||||
// const { initEvent, addCanvasMouseEventListener, addDocumentEventListener } = useContext(EventContext)
|
||||
const commonUtils = useRecoilValue(commonUtilsState)
|
||||
const { commonFunctions } = useCommonUtils()
|
||||
const SelectOption = [{ name: '瓦53A' }, { name: '瓦53A' }]
|
||||
const { floorPlanState, setFloorPlanState } = useContext(FloorPlanContext)
|
||||
|
||||
const onClickNav = (menu) => {
|
||||
setMenuNumber(menu.index)
|
||||
@ -161,20 +168,6 @@ export default function CanvasMenu(props) {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 견적서 복사버튼
|
||||
* (견적서 번호(estimateRecoilState.docNo)가 생성된 이후 버튼 활성화 )
|
||||
* T01관리자 계정 및 1차판매점에게만 제공
|
||||
*/
|
||||
|
||||
const handleEstimateCopy = () => {
|
||||
// console.log('estimateRecoilState::', estimateRecoilState)
|
||||
//objectNo, planNo
|
||||
console.log('복사')
|
||||
console.log('물건정보+도면+견적서를 모두 복사')
|
||||
console.log('견적서 가격은 정가를 표시')
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (globalLocale === 'ko') {
|
||||
setAppMessageState(KO)
|
||||
@ -184,7 +177,7 @@ export default function CanvasMenu(props) {
|
||||
}, [type, globalLocale])
|
||||
|
||||
useEffect(() => {
|
||||
if (['2', '3'].includes(canvasSetting?.roofSizeSet?.toString())) {
|
||||
if ([2, 3].some((num) => num === canvasSetting?.roofSizeSet)) {
|
||||
setMenuNumber(3)
|
||||
setType('surface')
|
||||
setCurrentMenu(MENU.BATCH_CANVAS.BATCH_DRAWING)
|
||||
@ -195,8 +188,60 @@ export default function CanvasMenu(props) {
|
||||
}
|
||||
}, [canvasSetting])
|
||||
|
||||
const checkMenuState = (menu) => {
|
||||
return ([2, 3].some((num) => num === canvasSetting?.roofSizeSet) && menu.index === 2) || (menuNumber === 4 && menu.index === 2)
|
||||
}
|
||||
|
||||
// 발전시물레이션 Excel/PDF 다운
|
||||
const { promisePost } = useAxios(globalLocale)
|
||||
const objectRecoil = useRecoilValue(floorPlanObjectState)
|
||||
const pwrGnrSimTypeRecoil = useRecoilValue(pwrGnrSimTypeState)
|
||||
|
||||
const { plans } = usePlan()
|
||||
const plan = plans.find((plan) => plan.isCurrent === true)
|
||||
|
||||
const handleExcelPdfFileDown = async (donwloadType, drawingFlg) => {
|
||||
const url = '/api/estimate/excel-download'
|
||||
|
||||
const params = {
|
||||
objectNo: objectRecoil.floorPlanObjectNo,
|
||||
planNo: plan?.id,
|
||||
schDownload: donwloadType,
|
||||
schDrawingFlg: drawingFlg,
|
||||
pwrGnrSimType: pwrGnrSimTypeRecoil.type,
|
||||
}
|
||||
|
||||
const options = { responseType: 'blob' }
|
||||
await promisePost({ url: url, data: params, option: options })
|
||||
.then((resultData) => {
|
||||
if (resultData) {
|
||||
let fileName = 'unknow'
|
||||
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
|
||||
|
||||
//서버에서 내려오는 파일명
|
||||
const contentDisposition = resultData.headers['content-disposition']
|
||||
if (contentDisposition) {
|
||||
fileName = contentDisposition.split('filename=')[1].replace(/['"]/g, '')
|
||||
}
|
||||
|
||||
link.download = fileName
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
link.remove()
|
||||
window.URL.revokeObjectURL(fileUrl)
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
alert('File does not exist.')
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`canvas-menu-wrap ${menuNumber === 2 || menuNumber === 3 || menuNumber === 4 ? 'active' : ''}`}>
|
||||
<div className={`canvas-menu-wrap ${[2, 3, 4].some((num) => num === menuNumber) ? 'active' : ''}`}>
|
||||
<div className="canvas-menu-inner">
|
||||
<ul className="canvas-menu-list">
|
||||
{canvasMenus.map((menu) => {
|
||||
@ -205,11 +250,12 @@ export default function CanvasMenu(props) {
|
||||
key={`canvas-menu-${menu.index}`}
|
||||
className={`canvas-menu-item ${menuNumber === menu.index ? 'active' : ''}`}
|
||||
onClick={() => {
|
||||
if (['2', '3'].includes(canvasSetting?.roofSizeSet?.toString()) && menu.index === 2) return
|
||||
if ([2, 3].some((num) => num === canvasSetting?.roofSizeSet) && menu.index === 2) return
|
||||
if (menuNumber === 4 && menu.index === 2) return
|
||||
onClickNav(menu)
|
||||
}}
|
||||
>
|
||||
<button className={['2', '3'].includes(canvasSetting?.roofSizeSet?.toString()) && menu.index === 2 ? 'no-click' : ''}>
|
||||
<button className={checkMenuState(menu) ? 'no-click' : ''}>
|
||||
<span className={`menu-icon ${menu.icon}`}></span>
|
||||
{getMessage(menu.name)}
|
||||
</button>
|
||||
@ -218,7 +264,7 @@ export default function CanvasMenu(props) {
|
||||
})}
|
||||
</ul>
|
||||
<div className="canvas-side-btn-wrap">
|
||||
{menuNumber !== 6 && menuNumber !== 5 && (
|
||||
{![5, 6].some((num) => num === menuNumber) && (
|
||||
<>
|
||||
{
|
||||
<div className={`vertical-horizontal ${verticalHorizontalMode ? 'on' : ''}`}>
|
||||
@ -235,6 +281,10 @@ export default function CanvasMenu(props) {
|
||||
<QSelectBox title={'瓦53A'} option={SelectOption} />
|
||||
</div>
|
||||
<div className="btn-from">
|
||||
<button
|
||||
className={`btn10 ${floorPlanState.refFileModalOpen && 'active'}`}
|
||||
onClick={() => setFloorPlanState({ ...floorPlanState, refFileModalOpen: true })}
|
||||
></button>
|
||||
{/*<button className="btn04" onClick={() => setShowCanvasSettingModal(true)}></button>*/}
|
||||
<button className="btn04" onClick={handlePopup}></button>
|
||||
<button className="btn05"></button>
|
||||
@ -247,7 +297,7 @@ export default function CanvasMenu(props) {
|
||||
handleZoom(false)
|
||||
}}
|
||||
></button>
|
||||
<span>{canvasZoom}%</span>
|
||||
<span onClick={handleZoomClear}>{canvasZoom}%</span>
|
||||
<button
|
||||
className="control-btn plus"
|
||||
onClick={() => {
|
||||
@ -273,7 +323,6 @@ export default function CanvasMenu(props) {
|
||||
<span className="ico ico02"></span>
|
||||
<span>{getMessage('plan.menu.estimate.save')}</span>
|
||||
</button>
|
||||
{/* {estimateRecoilState?.docNo != null && ( */}
|
||||
<button
|
||||
className="btn-frame gray ico-flx"
|
||||
onClick={() => {
|
||||
@ -283,28 +332,29 @@ export default function CanvasMenu(props) {
|
||||
<span className="ico ico03"></span>
|
||||
<span>{getMessage('plan.menu.estimate.reset')}</span>
|
||||
</button>
|
||||
{/* )} */}
|
||||
|
||||
<button
|
||||
className="btn-frame gray ico-flx"
|
||||
onClick={() => {
|
||||
handleEstimateCopy()
|
||||
}}
|
||||
>
|
||||
<span className="ico ico04"></span>
|
||||
<span>{getMessage('plan.menu.estimate.copy')}</span>
|
||||
</button>
|
||||
{estimateRecoilState?.docNo !== null && (sessionState.storeId === 'T01' || sessionState.storeLvl === '1') && (
|
||||
<button
|
||||
className="btn-frame gray ico-flx"
|
||||
onClick={() => {
|
||||
setEstimateCopyPopupOpen(true)
|
||||
}}
|
||||
>
|
||||
<span className="ico ico04"></span>
|
||||
<span>{getMessage('plan.menu.estimate.copy')}</span>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{menuNumber === 6 && (
|
||||
<>
|
||||
<div className="ico-btn-from">
|
||||
<button className="btn-frame gray ico-flx">
|
||||
<button type="button" className="btn-frame gray ico-flx" onClick={() => handleExcelPdfFileDown('EXCEL', '2')}>
|
||||
<span className="ico ico01"></span>
|
||||
<span>{getMessage('plan.menu.simulation.excel')}</span>
|
||||
</button>
|
||||
<button className="btn-frame gray ico-flx">
|
||||
<button type="button" className="btn-frame gray ico-flx" onClick={() => handleExcelPdfFileDown('PDF', '2')}>
|
||||
<span className="ico ico01"></span>
|
||||
<span>{getMessage('plan.menu.simulation.pdf')}</span>
|
||||
</button>
|
||||
@ -313,11 +363,13 @@ export default function CanvasMenu(props) {
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className={`canvas-depth2-wrap ${menuNumber === 2 || menuNumber === 3 || menuNumber === 4 ? 'active' : ''}`}>
|
||||
{(menuNumber === 2 || menuNumber === 3 || menuNumber === 4) && <MenuDepth01 />}
|
||||
<div className={`canvas-depth2-wrap ${[2, 3, 4].some((num) => num === menuNumber) ? 'active' : ''}`}>
|
||||
{[2, 3, 4].some((num) => num === menuNumber) && <MenuDepth01 />}
|
||||
</div>
|
||||
{/* 견적서(menuNumber=== 5) 상세화면인경우 문서다운로드 팝업 */}
|
||||
{estimatePopupOpen && <DocDownOptionPop planNo={estimateRecoilState?.planNo} setEstimatePopupOpen={setEstimatePopupOpen} />}
|
||||
{/* 견적서(menuNumber ===5)복사 팝업 */}
|
||||
{estimateCopyPopupOpen && <EstimateCopyPop planNo={estimateRecoilState?.planNo} setEstimateCopyPopupOpen={setEstimateCopyPopupOpen} />}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,20 +1,20 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||
import { settingModalFirstOptionsState, settingModalSecondOptionsState } from '@/store/settingAtom'
|
||||
import { useContext, useEffect } from 'react'
|
||||
//import { useRecoilState } from 'recoil'
|
||||
import CanvasMenu from '@/components/floor-plan/CanvasMenu'
|
||||
import { useCanvasMenu } from '@/hooks/common/useCanvasMenu'
|
||||
import { useCanvasSetting } from '@/hooks/option/useCanvasSetting'
|
||||
import { usePopup } from '@/hooks/usePopup'
|
||||
//import { FloorPlanContext } from '@/app/floor-plan/FloorPlanProvider'
|
||||
//import { correntObjectNoState } from '@/store/settingAtom'
|
||||
import '@/styles/contents.scss'
|
||||
|
||||
export default function FloorPlan({ children }) {
|
||||
const [settingModalFirstOptions, setSettingModalFirstOptions] = useRecoilState(settingModalFirstOptionsState)
|
||||
const [settingModalSecondOptions, setSettingModalSecondOptions] = useRecoilState(settingModalSecondOptionsState)
|
||||
const [objectNo, setObjectNo] = useState('test123240912001') // 이후 삭제 필요
|
||||
|
||||
//const { floorPlanState, setFloorPlanState } = useContext(FloorPlanContext)
|
||||
//const [correntObjectNo, setCorrentObjectNo] = useRecoilState(correntObjectNoState)
|
||||
const { closeAll } = usePopup()
|
||||
const { menuNumber, setMenuNumber } = useCanvasMenu()
|
||||
|
||||
const { fetchSettings } = useCanvasSetting()
|
||||
|
||||
const modalProps = {
|
||||
@ -23,8 +23,13 @@ export default function FloorPlan({ children }) {
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
///setCorrentObjectNo(floorPlanState.objectNo)
|
||||
//console.log('FloorPlan objectNo ', floorPlanState.objectNo, correntObjectNo)
|
||||
fetchSettings()
|
||||
}, [objectNo])
|
||||
return () => {
|
||||
closeAll()
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@ -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">
|
||||
|
||||
127
src/components/floor-plan/modal/ImgLoad.jsx
Normal file
127
src/components/floor-plan/modal/ImgLoad.jsx
Normal file
@ -0,0 +1,127 @@
|
||||
'use client'
|
||||
|
||||
import { useContext, useEffect } from 'react'
|
||||
import { FloorPlanContext } from '@/app/floor-plan/FloorPlanProvider'
|
||||
|
||||
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()
|
||||
const { floorPlanState, setFloorPlanState } = useContext(FloorPlanContext)
|
||||
|
||||
const handleModal = () => {
|
||||
setFloorPlanState({ ...floorPlanState, refFileModalOpen: false })
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
console.log('🚀 ~ ImgLoad ~ floorPlanState.refFileModalOpen:', floorPlanState.refFileModalOpen)
|
||||
console.log('🚀 ~ ImgLoad ~ currentCanvasPlan:', currentCanvasPlan)
|
||||
}, [floorPlanState.refFileModalOpen])
|
||||
|
||||
useEffect(() => {
|
||||
const refFileMethod = currentCanvasPlan?.mapPositionAddress === null ? '1' : '2'
|
||||
setRefFileMethod(refFileMethod)
|
||||
}, [currentCanvasPlan])
|
||||
|
||||
return (
|
||||
<WithDraggable isShow={floorPlanState.refFileModalOpen} pos={{ x: 1000, y: 200 }} handle=".modal">
|
||||
<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"
|
||||
checked={floorPlanState.toggleRotate}
|
||||
onChange={(e) => setFloorPlanState({ ...floorPlanState, toggleRotate: e.target.checked })}
|
||||
/>
|
||||
<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={refFileMethod === '1' ? (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" 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="ra07" value={'2'} onChange={(e) => handleRefFileMethod(e)} checked={refFileMethod === '2'} />
|
||||
<label htmlFor="ra07">アドレスを読み込む</label>
|
||||
</div>
|
||||
<div className="img-flex-box for-address">
|
||||
<input
|
||||
type="text"
|
||||
ref={queryRef}
|
||||
className="input-origin al-l mr10"
|
||||
placeholder={'住所入力'}
|
||||
value={mapPositionAddress}
|
||||
onChange={(e) => setMapPositionAddress(e.target.value)}
|
||||
/>
|
||||
<div className="img-edit-wrap">
|
||||
<button className="img-edit-btn" onClick={refFileMethod === '2' ? 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" onClick={handleModal}>
|
||||
完了
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</WithDraggable>
|
||||
)
|
||||
}
|
||||
@ -1,84 +0,0 @@
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import WithDraggable from '@/components/common/draggable/WithDraggable'
|
||||
import { usePopup } from '@/hooks/usePopup'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { contextPopupPositionState } from '@/store/popupAtom'
|
||||
import { useState } from 'react'
|
||||
|
||||
export default function AuxiliaryCopy(props) {
|
||||
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
||||
const { id, pos = contextPopupPosition } = props
|
||||
const { getMessage } = useMessage()
|
||||
const { closePopup } = usePopup()
|
||||
const [arrow1, setArrow1] = useState(null)
|
||||
const [arrow2, setArrow2] = useState(null)
|
||||
return (
|
||||
<WithDraggable isShow={true} pos={pos}>
|
||||
<div className={`modal-pop-wrap xm mount`}>
|
||||
<div className="modal-head">
|
||||
<h1 className="title">{getMessage('modal.auxiliary.copy')} </h1>
|
||||
<button className="modal-close" onClick={() => closePopup(id)}>
|
||||
닫기
|
||||
</button>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<div className="grid-option-tit">{getMessage('modal.auxiliary.copy.info')}</div>
|
||||
<div className="grid-option-wrap">
|
||||
<div className="grid-option-box">
|
||||
<div className="move-form">
|
||||
<p className="mb5">{getMessage('length')}</p>
|
||||
<div className="input-move-wrap mb5">
|
||||
<div className="input-move">
|
||||
<input type="text" className="input-origin" defaultValue={910} />
|
||||
</div>
|
||||
<span>mm</span>
|
||||
<div className="direction-move-wrap">
|
||||
<button
|
||||
className={`direction up ${arrow1 === '↑' ? 'act' : ''}`}
|
||||
onClick={() => {
|
||||
setArrow1('↑')
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' }))
|
||||
}}
|
||||
></button>
|
||||
<button
|
||||
className={`direction down ${arrow1 === '↓' ? 'act' : ''}`}
|
||||
onClick={() => {
|
||||
setArrow1('↓')
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' }))
|
||||
}}
|
||||
></button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="input-move-wrap">
|
||||
<div className="input-move">
|
||||
<input type="text" className="input-origin" defaultValue={910} />
|
||||
</div>
|
||||
<span>mm</span>
|
||||
<div className="direction-move-wrap">
|
||||
<button
|
||||
className={`direction left ${arrow2 === '←' ? 'act' : ''}`}
|
||||
onClick={() => {
|
||||
setArrow2('←')
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowLeft' }))
|
||||
}}
|
||||
></button>
|
||||
<button
|
||||
className={`direction right ${arrow2 === '→' ? 'act' : ''}`}
|
||||
onClick={() => {
|
||||
setArrow2('→')
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowRight' }))
|
||||
}}
|
||||
></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid-btn-wrap">
|
||||
<button className="btn-frame modal act">{getMessage('modal.common.save')}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</WithDraggable>
|
||||
)
|
||||
}
|
||||
@ -49,6 +49,7 @@ export default function AuxiliaryDrawing({ id, pos = { x: 50, y: 230 } }) {
|
||||
handleFix,
|
||||
buttonAct,
|
||||
setButtonAct,
|
||||
cutAuxiliary,
|
||||
} = useAuxiliaryDrawing(id)
|
||||
|
||||
const outerLineProps = {
|
||||
@ -151,6 +152,9 @@ export default function AuxiliaryDrawing({ id, pos = { x: 50, y: 230 } }) {
|
||||
<button className="btn-frame modal mr5" onClick={handleRollback}>
|
||||
{getMessage('modal.cover.outline.rollback')}
|
||||
</button>
|
||||
<button className="btn-frame modal mr5" onClick={cutAuxiliary}>
|
||||
{getMessage('contextmenu.auxiliary.cut')}
|
||||
</button>
|
||||
<button className="btn-frame modal act" onClick={() => handleFix(id)}>
|
||||
{getMessage('apply')}
|
||||
</button>
|
||||
|
||||
@ -1,37 +1,66 @@
|
||||
'use client'
|
||||
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import WithDraggable from '@/components/common/draggable/WithDraggable'
|
||||
import { usePopup } from '@/hooks/usePopup'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { contextPopupPositionState } from '@/store/popupAtom'
|
||||
import { usePopup } from '@/hooks/usePopup'
|
||||
import { useState } from 'react'
|
||||
import { currentObjectState } from '@/store/canvasAtom'
|
||||
import { useAuxiliaryDrawing } from '@/hooks/roofcover/useAuxiliaryDrawing'
|
||||
import { useSwal } from '@/hooks/useSwal'
|
||||
|
||||
export default function AuxiliaryMove(props) {
|
||||
export default function AuxiliaryEdit(props) {
|
||||
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
||||
const { id, pos = contextPopupPosition } = props
|
||||
const { id, pos = contextPopupPosition, type } = props
|
||||
const { getMessage } = useMessage()
|
||||
const { closePopup } = usePopup()
|
||||
const { move, copy } = useAuxiliaryDrawing()
|
||||
const [verticalSize, setVerticalSize] = useState('0')
|
||||
const [horizonSize, setHorizonSize] = useState('0')
|
||||
const [arrow1, setArrow1] = useState(null)
|
||||
const [arrow2, setArrow2] = useState(null)
|
||||
const currentObject = useRecoilValue(currentObjectState)
|
||||
const { swalFire } = useSwal()
|
||||
const handleSave = () => {
|
||||
if (!horizonSize || !verticalSize || !arrow1 || !arrow2) {
|
||||
swalFire({ title: '길이와 방향을 입력하세요.', type: 'alert' })
|
||||
return
|
||||
}
|
||||
if (type === 'copy') {
|
||||
if (currentObject) {
|
||||
copy(
|
||||
currentObject,
|
||||
arrow2 === '←' ? Number(horizonSize) * -1 : Number(horizonSize),
|
||||
arrow1 === '↑' ? Number(verticalSize) * -1 : Number(verticalSize),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
move(
|
||||
currentObject,
|
||||
arrow2 === '←' ? Number(horizonSize) * -1 : Number(horizonSize),
|
||||
arrow1 === '↑' ? Number(verticalSize) * -1 : Number(verticalSize),
|
||||
)
|
||||
}
|
||||
|
||||
closePopup(id)
|
||||
}
|
||||
return (
|
||||
<WithDraggable isShow={true} pos={pos}>
|
||||
<div className={`modal-pop-wrap xm mount`}>
|
||||
<div className="modal-head">
|
||||
<h1 className="title">{getMessage('modal.auxiliary.move')}</h1>
|
||||
<h1 className="title">{getMessage(type === 'copy' ? 'modal.auxiliary.copy' : 'modal.auxiliary.move')} </h1>
|
||||
<button className="modal-close" onClick={() => closePopup(id)}>
|
||||
닫기
|
||||
</button>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<div className="grid-option-tit">{getMessage('modal.auxiliary.move.info')}</div>
|
||||
<div className="grid-option-tit">{getMessage(type === 'copy' ? 'modal.auxiliary.copy.info' : 'modal.auxiliary.move.info')}</div>
|
||||
<div className="grid-option-wrap">
|
||||
<div className="grid-option-box">
|
||||
<div className="move-form">
|
||||
<p className="mb5">{getMessage('length')}</p>
|
||||
<div className="input-move-wrap mb5">
|
||||
<div className="input-move">
|
||||
<input type="text" className="input-origin" defaultValue={910} />
|
||||
<input type="text" className="input-origin" value={verticalSize} onChange={(e) => setVerticalSize(e.target.value)} />
|
||||
</div>
|
||||
<span>mm</span>
|
||||
<div className="direction-move-wrap">
|
||||
@ -53,7 +82,7 @@ export default function AuxiliaryMove(props) {
|
||||
</div>
|
||||
<div className="input-move-wrap">
|
||||
<div className="input-move">
|
||||
<input type="text" className="input-origin" defaultValue={910} />
|
||||
<input type="text" className="input-origin" value={horizonSize} onChange={(e) => setHorizonSize(e.target.value)} />
|
||||
</div>
|
||||
<span>mm</span>
|
||||
<div className="direction-move-wrap">
|
||||
@ -77,7 +106,9 @@ export default function AuxiliaryMove(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={handleSave}>
|
||||
{getMessage('modal.common.save')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -3,12 +3,14 @@ import WithDraggable from '@/components/common/draggable/WithDraggable'
|
||||
import { usePopup } from '@/hooks/usePopup'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { contextPopupPositionState } from '@/store/popupAtom'
|
||||
import { useCanvas } from '@/hooks/useCanvas'
|
||||
|
||||
export default function AuxiliarySize(props) {
|
||||
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
||||
const { id, pos = contextPopupPosition } = props
|
||||
const { getMessage } = useMessage()
|
||||
const { closePopup } = usePopup()
|
||||
const { currentObject } = useCanvas()
|
||||
return (
|
||||
<WithDraggable isShow={true} pos={pos}>
|
||||
<div className={`modal-pop-wrap xm mount`}>
|
||||
@ -26,7 +28,7 @@ export default function AuxiliarySize(props) {
|
||||
</div>
|
||||
<div className="outline-form mb15">
|
||||
<div className="input-grid mr5" style={{ flex: '1 1 auto' }}>
|
||||
<input type="text" className="input-origin block" defaultValue={100} />
|
||||
<input type="text" className="input-origin block" defaultValue={currentObject?.length} readOnly={true} />
|
||||
</div>
|
||||
<span className="thin">mm</span>
|
||||
</div>
|
||||
@ -45,14 +47,14 @@ export default function AuxiliarySize(props) {
|
||||
</div>
|
||||
<div className="outline-form mb15">
|
||||
<div className="input-grid mr5" style={{ flex: '1 1 auto' }}>
|
||||
<input type="text" className="input-origin block" defaultValue={100} />
|
||||
<input type="text" className="input-origin block" defaultValue={100} readOnly={true} />
|
||||
</div>
|
||||
<span className="thin">mm</span>
|
||||
</div>
|
||||
<div className="outline-form">
|
||||
<span style={{ width: 'auto' }}>{getMessage('length')}</span>
|
||||
<div className="input-grid mr5">
|
||||
<input type="text" className="input-origin block" defaultValue={100} />
|
||||
<input type="text" className="input-origin block" defaultValue={currentObject?.length} readOnly={true} />
|
||||
</div>
|
||||
<span className="thin">mm</span>
|
||||
</div>
|
||||
|
||||
@ -1,20 +1,47 @@
|
||||
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 { initEvent } = useContext(EventContext)
|
||||
const { makeModuleInstArea, manualModuleSetup, autoModuleSetup } = useModuleBasicSetting()
|
||||
const handleBtnNextStep = () => {
|
||||
if (tabNum === 1) {
|
||||
orientationRef.current.handleNextStep()
|
||||
}
|
||||
setTabNum(tabNum + 1)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
makeModuleInstArea() //기붕 모듈설치면 생성
|
||||
|
||||
return () => {
|
||||
initEvent() //모듈설치면 선택 이벤트 삭제
|
||||
}
|
||||
}, [])
|
||||
|
||||
const placementRef = {
|
||||
isChidori: useRef('false'),
|
||||
setupLocation: useRef('center'),
|
||||
isMaxSetup: useRef('false'),
|
||||
}
|
||||
|
||||
return (
|
||||
<WithDraggable isShow={true} pos={pos}>
|
||||
<div className={`modal-pop-wrap lx-2`}>
|
||||
@ -32,10 +59,10 @@ 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} />}
|
||||
{canvasSetting.roofSizeSet && canvasSetting.roofSizeSet != 3 && tabNum === 3 && <Placement setTabNum={setTabNum} ref={placementRef} />}
|
||||
|
||||
{/*배치면 초기설정 - 입력방법: 육지붕*/}
|
||||
{canvasSetting.roofSizeSet && canvasSetting.roofSizeSet == 3 && tabNum === 2 && <PitchModule setTabNum={setTabNum} />}
|
||||
@ -49,14 +76,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(placementRef)}>
|
||||
{getMessage('modal.module.basic.setting.auto.placement')}
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@ -32,6 +32,16 @@ export default function Module({}) {
|
||||
},
|
||||
],
|
||||
}
|
||||
const surfaceTypes = [
|
||||
{ id: 1, name: 'Ⅱ', value: 'Ⅱ' },
|
||||
{ id: 2, name: 'Ⅲ ∙ Ⅳ', value: 'Ⅲ ∙ Ⅳ' },
|
||||
]
|
||||
const fiftingHeights = Array.from({ length: 16 }).map((data, index) => {
|
||||
return { id: index, name: index + 5, value: index + 5 }
|
||||
})
|
||||
const windSpeeds = Array.from({ length: 7 }).map((data, index) => {
|
||||
return { id: index, name: index * 2 + 30, value: index * 2 + 30 }
|
||||
})
|
||||
return (
|
||||
<>
|
||||
<div className="module-table-flex-wrap mb10">
|
||||
@ -47,11 +57,13 @@ export default function Module({}) {
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
{moduleData.header.map((data) => (
|
||||
<th key={data.prop} style={{ width: data.width ? data.width : '' }}>
|
||||
{data.name}
|
||||
</th>
|
||||
))}
|
||||
{moduleData.header.map((header) => {
|
||||
return (
|
||||
<th key={header.prop} style={{ width: header.width ? header.width + 'px' : '' }}>
|
||||
{header.name}
|
||||
</th>
|
||||
)
|
||||
})}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@ -88,37 +100,50 @@ export default function Module({}) {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="module-table-box">
|
||||
<div className="module-box-tab">
|
||||
<button className="module-btn act">スレート</button>
|
||||
<button className="module-btn">平板瓦</button>
|
||||
<button className="module-btn">53A</button>
|
||||
<button className="module-btn">53A</button>
|
||||
</div>
|
||||
<div className="module-table-box none-flex">
|
||||
<div className="module-table-inner">
|
||||
<div className="module-table-tit">{getMessage('modal.module.basic.setting.module.roof.material')}: スレーツ(4寸)</div>
|
||||
<div className="module-table-tit">{getMessage('modal.module.basic.setting.module.stuff.info')}</div>
|
||||
<div className="eaves-keraba-table">
|
||||
<div className="eaves-keraba-item">
|
||||
<div className="eaves-keraba-th">{getMessage('modal.module.basic.setting.module.trestle.maker')}</div>
|
||||
<div className="eaves-keraba-th">{getMessage('modal.module.basic.setting.module.surface.type')}</div>
|
||||
<div className="eaves-keraba-td">
|
||||
<div className="grid-select">
|
||||
<QSelectBox title={'屋根技術研究所'} option={SelectOption01} />
|
||||
<div className="outline-form">
|
||||
<div className="grid-select" style={{ width: '95.77px', flex: 'none' }}>
|
||||
<QSelectBox title={'Ⅲ ∙ Ⅳ'} options={surfaceTypes} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="eaves-keraba-item">
|
||||
<div className="eaves-keraba-th">{getMessage('modal.module.basic.setting.module.construction.method')}</div>
|
||||
<div className="eaves-keraba-th">{getMessage('modal.module.basic.setting.module.fitting.height')}</div>
|
||||
<div className="eaves-keraba-td">
|
||||
<div className="grid-select">
|
||||
<QSelectBox title={'スレート金具4'} option={SelectOption01} />
|
||||
<div className="outline-form">
|
||||
<div className="grid-select mr10">
|
||||
<QSelectBox title={'13'} options={fiftingHeights} />
|
||||
</div>
|
||||
<span className="thin">mm</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="eaves-keraba-item">
|
||||
<div className="eaves-keraba-th">{getMessage('modal.module.basic.setting.module.under.roof')}</div>
|
||||
<div className="eaves-keraba-th">{getMessage('modal.module.basic.setting.module.standard.wind.speed')}</div>
|
||||
<div className="eaves-keraba-td">
|
||||
<div className="grid-select">
|
||||
<QSelectBox title={'構造溶合板 12mm以上'} option={SelectOption01} />
|
||||
<div className="outline-form">
|
||||
<div className="grid-select mr10">
|
||||
<QSelectBox title={'32'} options={windSpeeds} />
|
||||
</div>
|
||||
<span className="thin">m/s</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="eaves-keraba-item">
|
||||
<div className="eaves-keraba-th">{getMessage('modal.module.basic.setting.module.standard.snowfall.amount')}</div>
|
||||
<div className="eaves-keraba-td">
|
||||
<div className="outline-form">
|
||||
<div className="grid-select mr10">
|
||||
<input type="text" className="input-origin block" />
|
||||
</div>
|
||||
<span className="thin">mm</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -127,61 +152,93 @@ export default function Module({}) {
|
||||
</div>
|
||||
</div>
|
||||
<div className="module-table-box mb10">
|
||||
<div className="module-box-tab">
|
||||
<button className="module-btn act">屋根材1</button>
|
||||
<button className="module-btn">屋根材2</button>
|
||||
<button className="module-btn">屋根材3</button>
|
||||
<button className="module-btn">屋根材4</button>
|
||||
</div>
|
||||
<div className="module-table-inner">
|
||||
<div className="module-table-flex-wrap">
|
||||
<div className="outline-form">
|
||||
<span className="mr10">{getMessage('modal.module.basic.setting.module.cotton.classification')}</span>
|
||||
<div className="grid-select">
|
||||
<QSelectBox title={'Ⅲ ∙ Ⅳ'} option={SelectOption01} />
|
||||
<div className="module-table-flex-wrap tab2">
|
||||
<div className="module-flex-item">
|
||||
<div className="module-flex-item-tit">{getMessage('modal.module.basic.setting.module.roof.material')}:スレーツ(4寸)</div>
|
||||
<div className="eaves-keraba-table">
|
||||
<div className="eaves-keraba-item">
|
||||
<div className="eaves-keraba-th">{getMessage('modal.module.basic.setting.module.rafter.margin')}</div>
|
||||
<div className="eaves-keraba-td">
|
||||
<div className="keraba-flex">
|
||||
<div className="grid-select">
|
||||
<QSelectBox title={'455'} option={SelectOption01} />
|
||||
</div>
|
||||
<div className="outline-form">
|
||||
<span>垂木の間隔</span>
|
||||
<div className="grid-select">
|
||||
<QSelectBox title={'455'} option={SelectOption01} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="eaves-keraba-item">
|
||||
<div className="eaves-keraba-th">{getMessage('modal.module.basic.setting.module.trestle.maker')}</div>
|
||||
<div className="eaves-keraba-td">
|
||||
<div className="grid-select">
|
||||
<QSelectBox title={'屋根技術研究所'} option={SelectOption01} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="eaves-keraba-item">
|
||||
<div className="eaves-keraba-th">{getMessage('modal.module.basic.setting.module.construction.method')}</div>
|
||||
<div className="eaves-keraba-td">
|
||||
<div className="grid-select">
|
||||
<QSelectBox title={'スレート金具4'} option={SelectOption01} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="eaves-keraba-item">
|
||||
<div className="eaves-keraba-th">{getMessage('modal.module.basic.setting.module.under.roof')}</div>
|
||||
<div className="eaves-keraba-td">
|
||||
<div className="grid-select">
|
||||
<QSelectBox title={'構造用合板12mm以上 又はOSB12mm以上'} option={SelectOption01} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="outline-form">
|
||||
<span className="mr10">{getMessage('modal.module.basic.setting.module.fitting.height')}</span>
|
||||
<div className="grid-select">
|
||||
<QSelectBox title={'13'} option={SelectOption01} />
|
||||
<div className="module-flex-item non-flex">
|
||||
<div className="flex-item-btn-wrap">
|
||||
<button className="btn-frame roof blue">標準施工(I)</button>
|
||||
<button className="btn-frame roof white">多設施工</button>
|
||||
<button className="btn-frame roof">標準施工</button>
|
||||
<button className="btn-frame roof">多設施工(II)</button>
|
||||
<button className="btn-frame roof">強化施工</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="outline-form">
|
||||
<span className="mr10">{getMessage('modal.module.basic.setting.module.standard.wind.speed')}</span>
|
||||
<div className="grid-select">
|
||||
<QSelectBox title={'32'} option={SelectOption01} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="outline-form">
|
||||
<span className="mr10">{getMessage('modal.module.basic.setting.module.standard.snowfall.amount')}</span>
|
||||
<div className="grid-select">
|
||||
<QSelectBox title={'15'} option={SelectOption01} />
|
||||
<div className="grid-check-form">
|
||||
<div className="d-check-box pop">
|
||||
<input type="checkbox" id="ch01" />
|
||||
<label htmlFor="ch01">{getMessage('modal.module.basic.setting.module.eaves.bar.fitting')}</label>
|
||||
</div>
|
||||
<div className="d-check-box pop">
|
||||
<input type="checkbox" id="ch02" />
|
||||
<label htmlFor="ch02">{getMessage('modal.module.basic.setting.module.blind.metal.fitting')}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="module-table-box mb15">
|
||||
<div className="warning-guide">
|
||||
<div className="warning">
|
||||
{getMessage('modal.module.basic.setting.module.setting.info1')}
|
||||
<br />
|
||||
{getMessage('modal.module.basic.setting.module.setting.info2')}
|
||||
<br />
|
||||
<div className="module-bottom">
|
||||
<div className="module-table-box ">
|
||||
<div className="warning-guide">
|
||||
<div className="warning">
|
||||
{getMessage('modal.module.basic.setting.module.setting.info1')}
|
||||
<br />
|
||||
{getMessage('modal.module.basic.setting.module.setting.info2')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="modal-btn-wrap mb15">
|
||||
<button className="btn-frame roof blue">{getMessage('modal.module.basic.setting.module.standard.construction')}(1)</button>
|
||||
<button className="btn-frame roof white">{getMessage('modal.module.basic.setting.module.standard.construction')}</button>
|
||||
<button className="btn-frame roof">{getMessage('modal.module.basic.setting.module.enforce.construction')}</button>
|
||||
<button className="btn-frame roof">{getMessage('modal.module.basic.setting.module.multiple.construction')}</button>
|
||||
<button className="btn-frame roof blue">{getMessage('modal.module.basic.setting.module.multiple.construction')}(II)</button>
|
||||
</div>
|
||||
<div className="grid-check-form border">
|
||||
<div className="d-check-box pop">
|
||||
<input type="checkbox" id="ch01" />
|
||||
<label htmlFor="ch01">{getMessage('modal.module.basic.setting.module.eaves.bar.fitting')}</label>
|
||||
</div>
|
||||
<div className="d-check-box pop">
|
||||
<input type="checkbox" id="ch02" />
|
||||
<label htmlFor="ch02">{getMessage('modal.module.basic.setting.module.blind.metal.fitting')}</label>
|
||||
</div>
|
||||
{/* 설정 오류시 노출 */}
|
||||
<div className="reset-word">※ 施工方法が選択できません。 基準風速または基準積雪量を確認してください。</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
|
||||
@ -1,22 +1,21 @@
|
||||
import { useState } from 'react'
|
||||
import { forwardRef, useImperativeHandle, useState } from 'react'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { useOrientation } from '@/hooks/module/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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
@ -1,7 +1,12 @@
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { forwardRef, useState } from 'react'
|
||||
|
||||
export default function Placement() {
|
||||
const Placement = forwardRef((props, refs) => {
|
||||
const { getMessage } = useMessage()
|
||||
const [isChidori, setIsChidori] = useState('false')
|
||||
const [setupLocation, setSetupLocation] = useState('center')
|
||||
const [isMaxSetup, setIsMaxSetup] = useState('false')
|
||||
|
||||
const moduleData = {
|
||||
header: [
|
||||
{ type: 'check', name: '', prop: 'check', width: 70 },
|
||||
@ -24,6 +29,29 @@ export default function Placement() {
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const handleChangeChidori = (e) => {
|
||||
setIsChidori(e.target.value)
|
||||
refs.isChidori.current = e.target.value
|
||||
}
|
||||
|
||||
const handleSetupLocation = (e) => {
|
||||
setSetupLocation(e.target.value)
|
||||
refs.setupLocation.current = e.target.value
|
||||
}
|
||||
|
||||
const handleMaxSetup = (e) => {
|
||||
console.log(e.target.checked)
|
||||
|
||||
if (e.target.checked) {
|
||||
setIsMaxSetup('true')
|
||||
refs.isMaxSetup.current = 'true'
|
||||
} else {
|
||||
setIsMaxSetup('false')
|
||||
refs.isMaxSetup.current = 'false'
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="module-table-flex-wrap mb10">
|
||||
@ -96,12 +124,20 @@ export default function Placement() {
|
||||
<div className="self-item-td">
|
||||
<div className="pop-form-radio">
|
||||
<div className="d-check-radio pop">
|
||||
<input type="radio" name="radio01" id="ra01" />
|
||||
<input type="radio" name="radio01" id="ra01" checked={isChidori === 'true'} value={'true'} onChange={handleChangeChidori} />
|
||||
<label htmlFor="ra01">{getMessage('modal.module.basic.setting.module.placement.do')}</label>
|
||||
</div>
|
||||
<div className="d-check-radio pop">
|
||||
<input type="radio" name="radio01" id="ra02" />
|
||||
<label htmlFor="ra01">{getMessage('modal.module.basic.setting.module.placement.do.not')}</label>
|
||||
<input
|
||||
type="radio"
|
||||
name="radio02"
|
||||
id="ra02"
|
||||
value={'false'}
|
||||
defaultChecked
|
||||
checked={isChidori === 'false'}
|
||||
onChange={handleChangeChidori}
|
||||
/>
|
||||
<label htmlFor="ra02">{getMessage('modal.module.basic.setting.module.placement.do.not')}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -111,15 +147,36 @@ export default function Placement() {
|
||||
<div className="self-item-td">
|
||||
<div className="pop-form-radio">
|
||||
<div className="d-check-radio pop">
|
||||
<input type="radio" name="radio02" id="ra03" />
|
||||
<input
|
||||
type="radio"
|
||||
name="radio03"
|
||||
id="ra03"
|
||||
checked={setupLocation === 'center'}
|
||||
value={'center'}
|
||||
onChange={handleSetupLocation}
|
||||
/>
|
||||
<label htmlFor="ra03">{getMessage('modal.module.basic.setting.module.placement.arrangement.standard.center')}</label>
|
||||
</div>
|
||||
<div className="d-check-radio pop">
|
||||
<input type="radio" name="radio02" id="ra04" />
|
||||
<input
|
||||
type="radio"
|
||||
name="radio04"
|
||||
id="ra04"
|
||||
checked={setupLocation === 'eaves'}
|
||||
value={'eaves'}
|
||||
onChange={handleSetupLocation}
|
||||
/>
|
||||
<label htmlFor="ra04">{getMessage('modal.module.basic.setting.module.placement.arrangement.standard.eaves')}</label>
|
||||
</div>
|
||||
<div className="d-check-radio pop">
|
||||
<input type="radio" name="radio02" id="ra05" />
|
||||
<input
|
||||
type="radio"
|
||||
name="radio05"
|
||||
id="ra05"
|
||||
checked={setupLocation === 'ridge'}
|
||||
value={'ridge'}
|
||||
onChange={handleSetupLocation}
|
||||
/>
|
||||
<label htmlFor="ra05">{getMessage('modal.module.basic.setting.module.placement.arrangement.standard.ridge')}</label>
|
||||
</div>
|
||||
</div>
|
||||
@ -128,7 +185,7 @@ export default function Placement() {
|
||||
</div>
|
||||
<div className="self-table-flx">
|
||||
<div className="d-check-box pop">
|
||||
<input type="checkbox" id="ch04" />
|
||||
<input type="checkbox" id="ch04" checked={isMaxSetup === 'true'} value={'true'} onChange={handleMaxSetup} />
|
||||
<label htmlFor="ch04">{getMessage('modal.module.basic.setting.module.placement.maximum')}</label>
|
||||
</div>
|
||||
</div>
|
||||
@ -137,4 +194,6 @@ export default function Placement() {
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
export default Placement
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -6,17 +6,27 @@ 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/contextpopup/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()
|
||||
const { closePopup } = usePopup()
|
||||
const [compasDeg, setCompasDeg] = useState(360)
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
canvas?.discardActiveObject()
|
||||
}
|
||||
}, [])
|
||||
|
||||
const [compasDeg, setCompasDeg] = useState(null)
|
||||
const [flowDirection, setFlowDirection] = useState(target.direction)
|
||||
const { changeSurfaceFlowDirection } = useSurfaceShapeBatch()
|
||||
const { closePopup } = usePopup()
|
||||
const { changeSurfaceFlowDirection, type, setType } = useFlowDirectionSetting(id)
|
||||
|
||||
const orientations = [
|
||||
// { name: `${getMessage('commons.none')}`, value: 0 },
|
||||
{ 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 },
|
||||
@ -27,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`}>
|
||||
@ -94,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>
|
||||
@ -114,18 +117,27 @@ export default function FlowDirectionSetting(props) {
|
||||
<div
|
||||
key={index}
|
||||
className={`circle ${compasDeg === 15 * (12 + index) ? 'act' : ''}`}
|
||||
onClick={() => setCompasDeg(15 * (12 + index))}
|
||||
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))}
|
||||
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,7 @@ export default function FlowDirectionSetting(props) {
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid-btn-wrap">
|
||||
<button className="btn-frame modal act" onClick={() => changeSurfaceFlowDirection(target, flowDirection, selectedOrientation)}>
|
||||
<button className="btn-frame modal act" onClick={() => changeSurfaceFlowDirection(target, flowDirection, compasDeg)}>
|
||||
{getMessage('modal.common.save')}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@ -1,42 +0,0 @@
|
||||
import WithDraggable from '@/components/common/draggable/WithDraggable'
|
||||
import { useState } from 'react'
|
||||
import { usePopup } from '@/hooks/usePopup'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { contextPopupPositionState } from '@/store/popupAtom'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
|
||||
export default function ImageSizeSetting(props) {
|
||||
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
||||
const { id, pos = contextPopupPosition, size, setSize } = props
|
||||
const [sizeValue, setSizeValue] = useState(100)
|
||||
const { getMessage } = useMessage()
|
||||
const { closePopup } = usePopup()
|
||||
|
||||
return (
|
||||
<WithDraggable isShow={true} pos={pos}>
|
||||
<div className={`modal-pop-wrap xxxm mount`}>
|
||||
<div className="modal-head">
|
||||
<h1 className="title">{getMessage('modal.image.size.setting')} </h1>
|
||||
<button className="modal-close" onClick={() => closePopup(id)}>
|
||||
닫기
|
||||
</button>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<div className="range-wrap">
|
||||
<input
|
||||
type="range"
|
||||
id="size"
|
||||
name="volume"
|
||||
min="20"
|
||||
max="200"
|
||||
step={10}
|
||||
value={sizeValue}
|
||||
onChange={(e) => setSizeValue(e.target.value)}
|
||||
/>
|
||||
<label htmlFor="size">{sizeValue}%</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</WithDraggable>
|
||||
)
|
||||
}
|
||||
@ -3,51 +3,58 @@ import { useRecoilValue } from 'recoil'
|
||||
import { contextPopupPositionState } from '@/store/popupAtom'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { usePopup } from '@/hooks/usePopup'
|
||||
import { useState, useEffect } from 'react'
|
||||
import { useEffect, useState } 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, target } = props
|
||||
const { getMessage } = useMessage()
|
||||
const { closePopup } = usePopup()
|
||||
const { changeSurfaceLinePropertyEvent, changeSurfaceLineProperty } = useSurfaceShapeBatch()
|
||||
const { changeSurfaceLinePropertyEvent, changeSurfaceLineProperty, changeSurfaceLinePropertyReset } = useSurfaceShapeBatch()
|
||||
const { initEvent } = useEvent()
|
||||
// const { initEvent } = useContext(EventContext)
|
||||
|
||||
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(target)
|
||||
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>
|
||||
@ -79,7 +86,7 @@ export default function LinePropertySetting(props) {
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid-btn-wrap">
|
||||
<button className="btn-frame modal act" onClick={() => changeSurfaceLineProperty(selectedProperty)}>
|
||||
<button className="btn-frame modal act" onClick={() => changeSurfaceLineProperty(selectedProperty, target)}>
|
||||
{getMessage('modal.common.save')}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@ -5,11 +5,12 @@ import { useMessage } from '@/hooks/useMessage'
|
||||
import WithDraggable from '@/components/common/draggable/WithDraggable'
|
||||
import { usePopup } from '@/hooks/usePopup'
|
||||
import { contextPopupPositionState } from '@/store/popupAtom'
|
||||
import { useRef, useState, useEffect } from 'react'
|
||||
import { useRef, useState, useEffect, useContext } from 'react'
|
||||
import { useObjectBatch } from '@/hooks/object/useObjectBatch'
|
||||
import { useEvent } from '@/hooks/useEvent'
|
||||
import { BATCH_TYPE, POLYGON_TYPE } from '@/common/common'
|
||||
import { useSurfaceShapeBatch } from '@/hooks/surface/useSurfaceShapeBatch'
|
||||
import { EventContext } from '@/app/floor-plan/EventProvider'
|
||||
|
||||
export default function SizeSetting(props) {
|
||||
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
||||
@ -22,11 +23,12 @@ export default function SizeSetting(props) {
|
||||
const widthRef = useRef(null)
|
||||
const heightRef = useRef(null)
|
||||
|
||||
const { initEvent } = useEvent()
|
||||
// const { initEvent } = useEvent()
|
||||
// const { initEvent } = useContext(EventContext)
|
||||
|
||||
useEffect(() => {
|
||||
initEvent()
|
||||
}, [])
|
||||
// useEffect(() => {
|
||||
// initEvent()
|
||||
// }, [])
|
||||
|
||||
const handleReSizeObject = () => {
|
||||
const width = widthRef.current.value
|
||||
|
||||
@ -82,7 +82,7 @@ const PentagonDormer = forwardRef((props, refs) => {
|
||||
</div>
|
||||
</div>
|
||||
<div className="discrimination-box">
|
||||
<div className="discrimination-tit">方向の選択</div>
|
||||
<div className="discrimination-tit">{getMessage('modal.object.setting.direction.select')}</div>
|
||||
<div className="object-direction-wrap">
|
||||
<div className="plane-direction">
|
||||
<span className="top">{getMessage('commons.north')}</span>
|
||||
|
||||
@ -60,7 +60,7 @@ const TriangleDormer = forwardRef((props, refs) => {
|
||||
</div>
|
||||
</div>
|
||||
<div className="discrimination-box">
|
||||
<div className="discrimination-tit">方向の選択</div>
|
||||
<div className="discrimination-tit">{getMessage('modal.object.setting.direction.select')}</div>
|
||||
<div className="object-direction-wrap">
|
||||
<div className="plane-direction">
|
||||
<span className="top">{getMessage('commons.north')}</span>
|
||||
|
||||
@ -8,13 +8,10 @@ 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'
|
||||
import WithDraggable from '@/components/common/draggable/WithDraggable'
|
||||
import { SessionContext } from '@/app/SessionProvider'
|
||||
|
||||
export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, setShowPlaceShapeModal }) {
|
||||
const [objectNo, setObjectNo] = useState('test123241008001') // 후에 삭제 필요
|
||||
@ -24,20 +21,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 +486,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,19 +1,46 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { useCanvasSetting } from '@/hooks/option/useCanvasSetting'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { POLYGON_TYPE } from '@/common/common'
|
||||
import { setSurfaceShapePattern } from '@/util/canvas-util'
|
||||
|
||||
export default function FirstOption() {
|
||||
const [objectNo, setObjectNo] = useState('test123240912001') // 이후 삭제 필요
|
||||
const { settingModalFirstOptions, setSettingModalFirstOptions } = useCanvasSetting()
|
||||
const { settingModalSecondOptions, setSettingModalSecondOptions } = useCanvasSetting()
|
||||
const { getMessage } = useMessage()
|
||||
|
||||
const { fetchSettings, frontSettings, onClickOption } = useCanvasSetting()
|
||||
const { canvas, settingModalFirstOptions, setSettingModalFirstOptions } = useCanvasSetting()
|
||||
const { option1, option2, dimensionDisplay } = settingModalFirstOptions
|
||||
|
||||
// 데이터를 최초 한 번만 조회
|
||||
useEffect(() => {
|
||||
console.log('FirstOption useEffect 실행')
|
||||
}, [objectNo])
|
||||
}, [])
|
||||
|
||||
const onClickOption = async (item) => {
|
||||
//치수 표시(단 건 선택)
|
||||
if (item.column === 'corridorDimension' || item.column === 'realDimension' || item.column === 'noneDimension') {
|
||||
const options = settingModalFirstOptions?.dimensionDisplay.map((option) => {
|
||||
option.selected = option.id === item.id
|
||||
return option
|
||||
})
|
||||
|
||||
//화면 표시(단 건 선택)
|
||||
} else if (item.column === 'onlyBorder' || item.column === 'lineHatch' || item.column === 'allPainted') {
|
||||
const options2 = settingModalFirstOptions?.option2.map((option2) => {
|
||||
option2.selected = option2.id === item.id
|
||||
return option2
|
||||
})
|
||||
|
||||
const polygons = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
|
||||
|
||||
polygons.forEach((polygon) => {
|
||||
setSurfaceShapePattern(polygon, item.column)
|
||||
})
|
||||
//디스플레이 설정 표시(단 건 선택)
|
||||
} else {
|
||||
item.selected = !item.selected
|
||||
}
|
||||
|
||||
setSettingModalFirstOptions({ ...settingModalFirstOptions, option1, option2, dimensionDisplay, fontFlag: true })
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@ -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,80 +5,44 @@ 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 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())
|
||||
|
||||
const [objectNo, setObjectNo] = useState('test123240912001') // 이후 삭제 필요
|
||||
const { settingModalFirstOptions, setSettingModalFirstOptions } = useCanvasSetting()
|
||||
const { settingModalSecondOptions, setSettingModalSecondOptions } = useCanvasSetting()
|
||||
const { adsorptionPointMode, setAdsorptionPointMode } = useCanvasSetting()
|
||||
const { fetchSettings, frontSettings, onClickOption } = useCanvasSetting()
|
||||
const {
|
||||
fetchSettings,
|
||||
planSizeSettingMode,
|
||||
setPlanSizeSettingMode,
|
||||
settingModalSecondOptions,
|
||||
setSettingModalSecondOptions,
|
||||
adsorptionPointMode,
|
||||
setAdsorptionPointMode,
|
||||
setAdsorptionRange,
|
||||
} = useCanvasSetting()
|
||||
const { option3, option4 } = settingModalSecondOptions
|
||||
|
||||
// 데이터를 최초 한 번만 조회
|
||||
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,
|
||||
}
|
||||
|
||||
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) => {
|
||||
setShowDimensionLineSettingModal(false)
|
||||
setShowPlanSizeSettingModal(false)
|
||||
setShowFontSettingModal(false)
|
||||
|
||||
@ -89,6 +52,7 @@ export default function SecondOption() {
|
||||
setShowFontSettingModal(true)
|
||||
setShowDimensionLineSettingModal(false)
|
||||
fontProps.type = 'commonText'
|
||||
fontProps.font = commonFont
|
||||
fontProps.id = fontId + 1
|
||||
addPopup(fontId + 1, 2, <FontSetting {...fontProps} />, true)
|
||||
break
|
||||
@ -99,6 +63,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 +72,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 +85,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 +95,12 @@ export default function SecondOption() {
|
||||
//치수선 설정
|
||||
if (!showDimensionLineSettingModal) {
|
||||
setShowDimensionLineSettingModal(true)
|
||||
fontProps.font = {
|
||||
fontFamily: '',
|
||||
fontWeight: '',
|
||||
fontSize: '',
|
||||
fontColor: '',
|
||||
}
|
||||
addPopup(dimensionId, 2, <DimensionLineSetting {...dimensionProps} />, true)
|
||||
} else {
|
||||
setShowDimensionLineSettingModal(false)
|
||||
@ -139,14 +111,84 @@ export default function SecondOption() {
|
||||
|
||||
case 'planSize': {
|
||||
//도면크기 설정
|
||||
setShowPlanSizeSettingModal(true)
|
||||
setShowDimensionLineSettingModal(false)
|
||||
addPopup(planSizeId, 2, <PlanSizeSetting {...planSizeProps} />, true)
|
||||
if (!showPlanSizeSettingModal) {
|
||||
fetchSettings() //화면 오픈 시 데이터 조회
|
||||
setShowPlanSizeSettingModal(true)
|
||||
addPopup(planSizeId, 2, <PlanSizeSetting {...planSizeProps} />, true)
|
||||
} else {
|
||||
setShowPlanSizeSettingModal(false)
|
||||
closePopup(planSizeId, true)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const handleFontSave = (font) => {
|
||||
setGlobalFont((prev) => {
|
||||
return {
|
||||
...prev,
|
||||
[fontProps.type]: {
|
||||
// fontFamily: font.fontFamily.value,
|
||||
// fontWeight: font.fontWeight.value,
|
||||
// fontSize: font.fontSize.value,
|
||||
// fontColor: font.fontColor.value,
|
||||
fontFamily: font.fontFamily,
|
||||
fontWeight: font.fontWeight,
|
||||
fontSize: font.fontSize,
|
||||
fontColor: font.fontColor,
|
||||
},
|
||||
fontFlag: true,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const fontProps = {
|
||||
id: fontId,
|
||||
pos: { x: 745, y: 180 },
|
||||
setIsShow: setShowFontSettingModal,
|
||||
onSave: handleFontSave,
|
||||
isConfig: true,
|
||||
}
|
||||
|
||||
const dimensionProps = {
|
||||
id: dimensionId,
|
||||
isShow: showDimensionLineSettingModal,
|
||||
setIsShow: setShowDimensionLineSettingModal,
|
||||
}
|
||||
|
||||
const planSizeProps = {
|
||||
id: planSizeId,
|
||||
horizon: planSizeSettingMode.originHorizon,
|
||||
vertical: planSizeSettingMode.originVertical,
|
||||
isShow: showPlanSizeSettingModal,
|
||||
setIsShow: setShowPlanSizeSettingModal,
|
||||
pos: { x: 1025, y: 180 },
|
||||
}
|
||||
|
||||
const onClickOption = async (item) => {
|
||||
//흡착범위 설정(단 건 선택)
|
||||
if (
|
||||
item.column === 'adsorpRangeSmall' ||
|
||||
item.column === 'adsorpRangeSmallSemi' ||
|
||||
item.column === 'adsorpRangeMedium' ||
|
||||
item.column === 'adsorpRangeLarge'
|
||||
) {
|
||||
// option4에서 한 개만 선택 가능하도록 처리
|
||||
//const updatedOption4 = option4.map((option) => (option.id === item.id ? { ...option, selected: true } : { ...option, selected: false }))
|
||||
const options = settingModalSecondOptions?.option4.map((option4) => {
|
||||
option4.selected = option4.id === item.id
|
||||
return option4
|
||||
})
|
||||
|
||||
setSettingModalSecondOptions({ ...settingModalSecondOptions, option3, option4, fontFlag: true })
|
||||
} else if (item === 'adsorpPoint') {
|
||||
setAdsorptionPointMode({ ...adsorptionPointMode, adsorptionPoint: !adsorptionPointMode.adsorptionPoint, fontFlag: true })
|
||||
}
|
||||
//setAdsorptionRange(item.range) //사용여부 확인 필요
|
||||
setAdsorptionRange(50)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="modal-check-btn-wrap">
|
||||
@ -181,12 +223,12 @@ export default function SecondOption() {
|
||||
</button>
|
||||
<button
|
||||
className="adsorption-point act"
|
||||
onClick={(e) => {
|
||||
setAdsorptionPointMode(!adsorptionPointMode)
|
||||
onClick={() => {
|
||||
onClickOption('adsorpPoint')
|
||||
}}
|
||||
>
|
||||
<span>{getMessage('modal.canvas.setting.font.plan.absorption.point')}</span>
|
||||
<i>{adsorptionPointMode ? 'ON' : 'OFF'}</i>
|
||||
<i>{adsorptionPointMode.adsorptionPoint ? 'ON' : 'OFF'}</i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -8,68 +8,110 @@ 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'
|
||||
import { useCanvasSetting } from '@/hooks/option/useCanvasSetting'
|
||||
|
||||
const fonts = [
|
||||
{ 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 { id: index + 8, name: index + 8, value: index + 8 }
|
||||
}),
|
||||
...Array.from({ length: 9 }).map((_, index) => {
|
||||
return { id: (index + 6) * 2, name: (index + 6) * 2, value: (index + 6) * 2 }
|
||||
}),
|
||||
{ id: 36, name: 36, value: 36 },
|
||||
{ id: 48, name: 48, value: 48 },
|
||||
{ id: 72, name: 72, value: 72 },
|
||||
]
|
||||
|
||||
/*
|
||||
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 [originPixel, setOriginPixel] = useState(dimensionLineSettings.pixel)
|
||||
//const [originColor, setOriginColor] = useState(dimensionLineSettings.color)
|
||||
//const [globalFont, setGlobalFont] = useRecoilState(globalFontAtom)
|
||||
|
||||
const [fontModalId, setFontModalId] = 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)
|
||||
|
||||
const { globalFont, setGlobalFont, dimensionLineSettings, setDimensionLineSettings } = useCanvasSetting()
|
||||
|
||||
const [originFont, setOriginFont] = useState(globalFont.dimensionLineText.fontFamily)
|
||||
const [originFontWeight, setOriginFontWeight] = useState(globalFont.dimensionLineText.fontWeight)
|
||||
const [originFontSize, setOriginFontSize] = useState(globalFont.dimensionLineText.fontSize)
|
||||
const [originFontColor, setOriginFontColor] = useState(globalFont.dimensionLineText.fontColor)
|
||||
|
||||
const [originPixel, setOriginPixel] = useState(dimensionLineSettings.pixel)
|
||||
const [originColor, setOriginColor] = useState(dimensionLineSettings.color)
|
||||
|
||||
const fontOptions = [
|
||||
{ id: 'normal', name: getMessage('font.style.normal'), value: 'normal' },
|
||||
{ id: 'italic', name: getMessage('font.style.italic'), value: 'italic' },
|
||||
{ id: 'bold', name: getMessage('font.style.bold'), value: 'bold' },
|
||||
{ id: 'boldAndItalic', name: getMessage('font.style.bold.italic'), value: 'boldAndItalic' },
|
||||
]
|
||||
const fontColors = [
|
||||
{ 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' },
|
||||
]
|
||||
|
||||
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 (originPixel.name) {
|
||||
setOriginPixel(originPixel)
|
||||
}
|
||||
}, [originPixel])
|
||||
|
||||
useEffect(() => {
|
||||
if (!isShow) {
|
||||
closePopups([fontModalId, colorModalId])
|
||||
}
|
||||
}, [isShow])
|
||||
|
||||
const handleFontSave = (font) => {
|
||||
setOriginFont(font.fontFamily)
|
||||
setOriginFontWeight(font.fontWeight)
|
||||
setOriginFontSize(font.fontSize)
|
||||
setOriginFontColor(font.fontColor)
|
||||
}
|
||||
const handleColorSave = () => {}
|
||||
|
||||
const colorPickerProps = {
|
||||
isShow: showColorPickerModal,
|
||||
setIsShow: setShowColorPickerModal,
|
||||
color: originColor,
|
||||
setColor: setOriginColor,
|
||||
id: colorModalId,
|
||||
isConfig: true,
|
||||
pos: {
|
||||
x: 495,
|
||||
y: 180,
|
||||
@ -79,14 +121,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,
|
||||
fontWeight: originFontWeight,
|
||||
fontSize: originFontSize,
|
||||
fontColor: originFontColor,
|
||||
},
|
||||
onSave: handleFontSave,
|
||||
isConfig: true,
|
||||
id: fontModalId,
|
||||
pos: {
|
||||
@ -106,6 +147,30 @@ export default function DimensionLineSetting(props) {
|
||||
}
|
||||
}
|
||||
|
||||
const onSave = () => {
|
||||
setGlobalFont((prev) => {
|
||||
return {
|
||||
...prev,
|
||||
dimensionLineText: {
|
||||
fontFamily: originFont,
|
||||
fontWeight: originFontWeight,
|
||||
fontSize: originFontSize,
|
||||
fontColor: originFontColor,
|
||||
},
|
||||
fontFlag: true,
|
||||
}
|
||||
})
|
||||
setDimensionLineSettings((prev) => {
|
||||
return {
|
||||
...prev,
|
||||
pixel: originPixel.name,
|
||||
color: originColor,
|
||||
}
|
||||
})
|
||||
setIsShow(false)
|
||||
closePopups([fontModalId, colorModalId, id])
|
||||
}
|
||||
|
||||
return (
|
||||
<WithDraggable isShow={true} pos={pos}>
|
||||
<div className={`modal-pop-wrap xxxm mount`}>
|
||||
@ -114,8 +179,8 @@ export default function DimensionLineSetting(props) {
|
||||
<button
|
||||
className="modal-close"
|
||||
onClick={() => {
|
||||
closePopups([fontModalId, colorModalId, id])
|
||||
setIsShow(false)
|
||||
closePopups([fontModalId, colorModalId, id])
|
||||
}}
|
||||
>
|
||||
닫기
|
||||
@ -149,9 +214,10 @@ export default function DimensionLineSetting(props) {
|
||||
className="font"
|
||||
style={{
|
||||
fontFamily: originFont?.value ?? '',
|
||||
color: originFontColor?.value ?? 'black',
|
||||
fontWeight: originFontWeight?.value?.toLowerCase().includes('bold') ? 'bold' : 'normal',
|
||||
fontStyle: originFontWeight?.value?.toLowerCase().includes('italic') ? 'italic' : 'normal',
|
||||
fontSize: originFontSize?.value ?? '12px',
|
||||
fontWeight: '400',
|
||||
color: originFontColor?.value ?? 'black',
|
||||
}}
|
||||
>
|
||||
9,999
|
||||
@ -161,7 +227,7 @@ export default function DimensionLineSetting(props) {
|
||||
style={{
|
||||
backgroundColor: originColor,
|
||||
borderColor: originColor,
|
||||
height: originPixel.value,
|
||||
height: originPixel.name,
|
||||
}}
|
||||
></span>
|
||||
</div>
|
||||
@ -169,26 +235,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,17 +1,54 @@
|
||||
import { useState } from 'react'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { usePopup } from '@/hooks/usePopup'
|
||||
import WithDraggable from '@/components/common/draggable/WithDraggable'
|
||||
import { canvasState } from '@/store/canvasAtom'
|
||||
import { useCanvasSetting } from '@/hooks/option/useCanvasSetting'
|
||||
import { onlyNumberInputChange } from '@/util/input-utils'
|
||||
|
||||
export default function PlanSizeSetting(props) {
|
||||
const { horizon, setHorizon, vertical, setVertical, id, pos = { x: 985, y: 180 }, setIsShow } = props
|
||||
const { setIsShow, horizon, vertical, id, pos = { x: 985, y: 180 } } = props
|
||||
const { closePopup } = usePopup()
|
||||
const { getMessage } = useMessage()
|
||||
const [originHorizon, setOriginHorizon] = useState(horizon)
|
||||
const [originVertical, setOriginVertical] = useState(vertical)
|
||||
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
const { planSizeSettingMode, setPlanSizeSettingMode } = useCanvasSetting()
|
||||
|
||||
// 데이터를 최초 한 번만 조회
|
||||
useEffect(() => {
|
||||
console.log('PlanSizeSetting useEffect 실행')
|
||||
}, [])
|
||||
|
||||
const onSave = () => {
|
||||
setPlanSizeSettingMode((prev) => {
|
||||
return {
|
||||
...prev,
|
||||
originHorizon: Number(planSizeSettingMode.originHorizon),
|
||||
originVertical: Number(planSizeSettingMode.originVertical),
|
||||
flag: true,
|
||||
}
|
||||
})
|
||||
|
||||
canvas.setWidth(planSizeSettingMode.originHorizon)
|
||||
canvas.setHeight(planSizeSettingMode.originVertical)
|
||||
canvas.renderAll()
|
||||
|
||||
setIsShow(false)
|
||||
closePopup(id, true)
|
||||
}
|
||||
|
||||
const changeInput = (value, e) => {
|
||||
const { name } = e.target
|
||||
console.log('name', name, value)
|
||||
setPlanSizeSettingMode((prev) => {
|
||||
return {
|
||||
...prev,
|
||||
[name]: Number(value),
|
||||
flag: false,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<WithDraggable isShow={true} pos={pos}>
|
||||
@ -33,7 +70,15 @@ export default function PlanSizeSetting(props) {
|
||||
<div className="outline-form mb10">
|
||||
<span style={{ width: 'auto' }}>{getMessage('common.horizon')}</span>
|
||||
<div className="input-grid mr5" style={{ width: '90px' }}>
|
||||
<input type="text" className="input-origin block" value={originHorizon} onChange={(e) => setOriginHorizon(Number(e.target.value))} />
|
||||
<input
|
||||
type="text"
|
||||
className="input-origin block"
|
||||
name={`originHorizon`}
|
||||
value={planSizeSettingMode.originHorizon}
|
||||
//onChange={(e) => setPlanSizeSettingMode({ ...planSizeSettingMode, originHorizon: Number(e.target.value), flag: false })}
|
||||
//onFocus={(e) => (originHorizon.current.value = '')}
|
||||
onChange={(e) => onlyNumberInputChange(e, changeInput)}
|
||||
/>
|
||||
</div>
|
||||
<span className="thin">mm</span>
|
||||
</div>
|
||||
@ -43,26 +88,18 @@ export default function PlanSizeSetting(props) {
|
||||
<input
|
||||
type="text"
|
||||
className="input-origin block"
|
||||
value={originVertical}
|
||||
onChange={(e) => setOriginVertical(Number(e.target.value))}
|
||||
name={`originVertical`}
|
||||
value={planSizeSettingMode.originVertical}
|
||||
//onChange={(e) => setPlanSizeSettingMode({ ...planSizeSettingMode, originVertical: Number(e.target.value), flag: false })}
|
||||
//onFocus={(e) => (originVertical.current.value = '')}
|
||||
onChange={(e) => onlyNumberInputChange(e, changeInput)}
|
||||
/>
|
||||
</div>
|
||||
<span className="thin">mm</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid-btn-wrap">
|
||||
<button
|
||||
className="btn-frame modal act"
|
||||
onClick={() => {
|
||||
setHorizon(originHorizon)
|
||||
setVertical(originVertical)
|
||||
setIsShow(false)
|
||||
closePopup(id)
|
||||
canvas.setWidth(originHorizon)
|
||||
canvas.setHeight(originVertical)
|
||||
canvas.renderAll()
|
||||
}}
|
||||
>
|
||||
<button className="btn-frame modal act" onClick={onSave}>
|
||||
{getMessage('modal.common.save')}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@ -5,6 +5,7 @@ import { useEvent } from '@/hooks/useEvent'
|
||||
export default function Offset({ length1Ref, arrow1Ref, currentWallLineRef }) {
|
||||
const { getMessage } = useMessage()
|
||||
const { addDocumentEventListener, initEvent } = useEvent()
|
||||
// const { addDocumentEventListener, initEvent } = useContext(EventContext)
|
||||
|
||||
useEffect(() => {
|
||||
addDocumentEventListener('keydown', document, keyDown)
|
||||
|
||||
@ -5,6 +5,7 @@ import { useEvent } from '@/hooks/useEvent'
|
||||
export default forwardRef(function WallLine({ length1Ref, length2Ref, arrow1Ref, arrow2Ref, radioTypeRef, currentWallLineRef }, ref) {
|
||||
const { getMessage } = useMessage()
|
||||
const { addDocumentEventListener, initEvent } = useEvent()
|
||||
// const { addDocumentEventListener, initEvent } = useContext(EventContext)
|
||||
const [type, setType] = useState(1)
|
||||
const [arrow1, setArrow1] = useState('up')
|
||||
const [arrow2, setArrow2] = useState('up')
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
'use client'
|
||||
import { Fragment, useCallback, useEffect, useState } from 'react'
|
||||
import { Fragment, useCallback, useContext, useEffect, useState } from 'react'
|
||||
|
||||
import Link from 'next/link'
|
||||
import { usePathname } from 'next/navigation'
|
||||
|
||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||
import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil'
|
||||
import { dimmedStore, sessionStore } from '@/store/commonAtom'
|
||||
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
@ -16,6 +16,9 @@ import UserInfoModal from '@/components/myInfo/UserInfoModal'
|
||||
import { useAxios } from '@/hooks/useAxios'
|
||||
import { globalLocaleStore } from '@/store/localeAtom'
|
||||
|
||||
import { stuffSearchState } from '@/store/stuffAtom'
|
||||
import { QcastContext } from '@/app/QcastProvider'
|
||||
|
||||
export const ToggleonMouse = (e, act, target) => {
|
||||
const listWrap = e.target.closest(target)
|
||||
const ListItem = Array.from(listWrap.childNodes)
|
||||
@ -34,6 +37,8 @@ export const ToggleonMouse = (e, act, target) => {
|
||||
export default function Header(props) {
|
||||
const [userInfoModal, setUserInfoModal] = useState(false)
|
||||
|
||||
const [stuffSearch, setStuffSearch] = useRecoilState(stuffSearchState)
|
||||
|
||||
const { userSession } = props
|
||||
const [sessionState, setSessionState] = useRecoilState(sessionStore)
|
||||
const { getMessage } = useMessage()
|
||||
@ -43,6 +48,8 @@ export default function Header(props) {
|
||||
// }
|
||||
const [selected, setSelected] = useState('')
|
||||
|
||||
const { isGlobalLoading } = useContext(QcastContext)
|
||||
|
||||
const dimmedState = useRecoilValue(dimmedStore)
|
||||
const isDimmed = dimmedState ? 'opacity-50 bg-black' : ''
|
||||
|
||||
@ -160,45 +167,56 @@ export default function Header(props) {
|
||||
}
|
||||
|
||||
return (
|
||||
!(pathName.includes('login') || pathName.includes('join') || sessionState.pwdInitYn === 'N') && (
|
||||
<header className={isDimmed}>
|
||||
<div className="header-inner">
|
||||
<div className="header-right">
|
||||
<h1 className="logo">
|
||||
<Link href={'/'}></Link>
|
||||
</h1>
|
||||
<nav>
|
||||
<ul className="nav-list ">{getMenuTemplate(menus)}</ul>
|
||||
</nav>
|
||||
</div>
|
||||
<div className="header-left">
|
||||
<div className="profile-box">
|
||||
<Link
|
||||
href="#"
|
||||
onClick={() => {
|
||||
setUserInfoModal(true)
|
||||
}}
|
||||
>
|
||||
<button className="profile">{userSession.userNm}</button>
|
||||
</Link>
|
||||
{userInfoModal && <UserInfoModal userId={sessionState.userId} userInfoModal={userInfoModal} setUserInfoModal={setUserInfoModal} />}
|
||||
<>
|
||||
{!isGlobalLoading && !(pathName.includes('login') || pathName.includes('join') || sessionState.pwdInitYn === 'N') && (
|
||||
<header className={isDimmed}>
|
||||
<div className="header-inner">
|
||||
<div className="header-right">
|
||||
<h1 className="logo">
|
||||
<Link href={'/'}></Link>
|
||||
</h1>
|
||||
<nav>
|
||||
<ul className="nav-list ">{getMenuTemplate(menus)}</ul>
|
||||
</nav>
|
||||
</div>
|
||||
<div className="sign-out-box">
|
||||
<button className="sign-out" onClick={() => logout()}>
|
||||
{getMessage('header.logout')}
|
||||
</button>
|
||||
</div>
|
||||
<div className="select-box">
|
||||
<QSelectBox options={SelectOptions} onChange={onChangeSelect} />
|
||||
</div>
|
||||
<div className="btn-wrap">
|
||||
<button className="btn-frame small dark" onClick={() => navPage()}>
|
||||
{getMessage('header.go')}
|
||||
</button>
|
||||
<div className="header-left">
|
||||
<div className="profile-box">
|
||||
<Link
|
||||
href="#"
|
||||
onClick={() => {
|
||||
setUserInfoModal(true)
|
||||
}}
|
||||
>
|
||||
<button className="profile">{userSession.userNm}</button>
|
||||
</Link>
|
||||
{userInfoModal && <UserInfoModal userId={sessionState.userId} userInfoModal={userInfoModal} setUserInfoModal={setUserInfoModal} />}
|
||||
</div>
|
||||
<div className="sign-out-box">
|
||||
<button
|
||||
className="sign-out"
|
||||
onClick={() => {
|
||||
setStuffSearch({
|
||||
...stuffSearch,
|
||||
code: 'DELETE',
|
||||
})
|
||||
logout()
|
||||
}}
|
||||
>
|
||||
{getMessage('header.logout')}
|
||||
</button>
|
||||
</div>
|
||||
<div className="select-box">
|
||||
<QSelectBox options={SelectOptions} onChange={onChangeSelect} />
|
||||
</div>
|
||||
<div className="btn-wrap">
|
||||
<button className="btn-frame small dark" onClick={() => navPage()}>
|
||||
{getMessage('header.go')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
)
|
||||
</header>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
'use client'
|
||||
|
||||
import { useEffect, useState, useContext } from 'react'
|
||||
import ProductItem from './ProductItem'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import Image from 'next/image'
|
||||
@ -8,19 +10,20 @@ 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'
|
||||
import { useMainContentsController } from '@/hooks/main/useMainContentsController'
|
||||
import { QcastContext } from '@/app/QcastProvider'
|
||||
|
||||
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([])
|
||||
// const [objectList, setObjectList] = useState([])
|
||||
|
||||
//공지사항
|
||||
const [recentNoticeList, setRecentNoticeList] = useState([])
|
||||
@ -29,36 +32,42 @@ export default function MainContents() {
|
||||
const [recentFaqList, setRecentFaqList] = useState([])
|
||||
|
||||
//Sales Contact info
|
||||
const [businessCharger, setBusinessCharger] = useState(null)
|
||||
const [businessChargerMail, setBusinessChargerMail] = useState(null)
|
||||
// const [businessCharger, setBusinessCharger] = useState(null)
|
||||
// const [businessChargerMail, setBusinessChargerMail] = useState(null)
|
||||
|
||||
const { qcastState } = useContext(QcastContext)
|
||||
const { fetchObjectList, initObjectList } = useMainContentsController()
|
||||
|
||||
useEffect(() => {
|
||||
fetchObjectList()
|
||||
fetchNoticeList()
|
||||
fetchFaqList()
|
||||
}, [sessionState])
|
||||
return () => {
|
||||
initObjectList()
|
||||
}
|
||||
}, [])
|
||||
|
||||
//최근 갱신 물건목록 / Sales Contact info 정보
|
||||
const fetchObjectList = async () => {
|
||||
try {
|
||||
const apiUrl = `/api/main-page/object/${sessionState?.storeId}/list`
|
||||
await promiseGet({
|
||||
url: apiUrl,
|
||||
}).then((res) => {
|
||||
if (res.status === 200) {
|
||||
setObjectList(res.data.objectList)
|
||||
setBusinessCharger(res.data.businessCharger)
|
||||
setBusinessChargerMail(res.data.businessChargerMail)
|
||||
} else {
|
||||
setObjectList([])
|
||||
setBusinessCharger(null)
|
||||
setBusinessChargerMail(null)
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('MAIN API fetching error:', error)
|
||||
}
|
||||
}
|
||||
// const fetchObjectList = async () => {
|
||||
// try {
|
||||
// const apiUrl = `/api/main-page/object/${session?.storeId}/list`
|
||||
// await promiseGet({
|
||||
// url: apiUrl,
|
||||
// }).then((res) => {
|
||||
// if (res.status === 200) {
|
||||
// setObjectList(res.data.objectList)
|
||||
// setBusinessCharger(res.data.businessCharger)
|
||||
// setBusinessChargerMail(res.data.businessChargerMail)
|
||||
// } else {
|
||||
// setObjectList([])
|
||||
// setBusinessCharger(null)
|
||||
// setBusinessChargerMail(null)
|
||||
// }
|
||||
// })
|
||||
// } catch (error) {
|
||||
// console.error('MAIN API fetching error:', error)
|
||||
// }
|
||||
// }
|
||||
|
||||
//공지사항 호출
|
||||
const fetchNoticeList = async () => {
|
||||
@ -110,9 +119,9 @@ export default function MainContents() {
|
||||
<div className="main-product-list-wrap">
|
||||
<div className="main-product-list">
|
||||
<ProductItem num={1} name={getMessage('main.content.objectList')}>
|
||||
{objectList.length > 0 ? (
|
||||
{qcastState?.objectList.length > 0 ? (
|
||||
<ul className="recently-list">
|
||||
{objectList.map((row) => {
|
||||
{qcastState?.objectList.map((row) => {
|
||||
return (
|
||||
<li
|
||||
key={row.objectNo}
|
||||
@ -191,7 +200,7 @@ export default function MainContents() {
|
||||
<div className="icon-box">
|
||||
<Image src="/static/images/main/user.svg" alt="react" width={20} height={20} />
|
||||
</div>
|
||||
{(businessCharger && <div className="infor-data">{businessCharger}</div>) || (
|
||||
{(qcastState?.businessCharger && <div className="infor-data">{qcastState?.businessCharger}</div>) || (
|
||||
<div className="infor-data pre">{getMessage('main.content.noBusiness')}</div>
|
||||
)}
|
||||
</li>
|
||||
@ -199,7 +208,7 @@ export default function MainContents() {
|
||||
<div className="icon-box">
|
||||
<Image src="/static/images/main/mail.svg" alt="react" width={20} height={20} />
|
||||
</div>
|
||||
{(businessChargerMail && <div className="infor-data pre">{businessChargerMail}</div>) || (
|
||||
{(qcastState?.businessChargerMail && <div className="infor-data pre">{qcastState?.businessChargerMail}</div>) || (
|
||||
<div className="infor-data pre">{getMessage('main.content.noBusiness')}</div>
|
||||
)}
|
||||
</li>
|
||||
|
||||
@ -16,7 +16,11 @@ import JA from '@/locales/ja.json'
|
||||
import QPagination from '../common/pagination/QPagination'
|
||||
import { SessionContext } from '@/app/SessionProvider'
|
||||
|
||||
import { QcastContext } from '@/app/QcastProvider'
|
||||
|
||||
export default function Stuff() {
|
||||
const { setIsGlobalLoading } = useContext(QcastContext)
|
||||
|
||||
const resetStuffRecoil = useResetRecoilState(stuffSearchState)
|
||||
const { session } = useContext(SessionContext)
|
||||
const setAppMessageState = useSetRecoilState(appMessageStore)
|
||||
@ -39,14 +43,15 @@ export default function Stuff() {
|
||||
const copyNo = async (value) => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(value)
|
||||
alert(getMessage('stuff.detail.header.message2'))
|
||||
alert(getMessage('stuff.detail.header.successCopy'))
|
||||
} catch (error) {
|
||||
alert(getMessage('stuff.detail.header.message3'))
|
||||
alert(getMessage('stuff.detail.header.failCopy'))
|
||||
}
|
||||
}
|
||||
|
||||
//물건번호 복사버튼 옆에 영역
|
||||
const onDoubleClick = (data) => {
|
||||
setIsGlobalLoading(true)
|
||||
if (data.tempFlg === '0') {
|
||||
router.push(`${pathname}/detail?objectNo=${data.objectNo.toString()}`, { scroll: false })
|
||||
} else {
|
||||
@ -149,6 +154,7 @@ export default function Stuff() {
|
||||
} else {
|
||||
//T 면 임시 R은 진짜
|
||||
if (event.data.objectNo) {
|
||||
setIsGlobalLoading(true)
|
||||
if (event.data.tempFlg === '0') {
|
||||
router.push(`${pathname}/detail?objectNo=${event.data.objectNo.toString()}`, { scroll: false })
|
||||
} else {
|
||||
@ -172,12 +178,14 @@ export default function Stuff() {
|
||||
schDateType: stuffSearchParams.schDateType,
|
||||
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,
|
||||
startRow: (stuffSearch.pageNo - 1) * stuffSearchParams.pageSize + 1,
|
||||
endRow: stuffSearchParams?.endRow,
|
||||
schSelSaleStoreId: stuffSearchParams?.schSelSaleStoreId ? stuffSearchParams.schSelSaleStoreId : '',
|
||||
schOtherSelSaleStoreId: stuffSearchParams?.schOtherSelSaleStoreId ? stuffSearchParams.schOtherSelSaleStoreId : '',
|
||||
schSortType: stuffSearchParams.schSortType,
|
||||
pageNo: stuffSearchParams?.pageNo ? stuffSearchParams.pageNo : 1,
|
||||
pageSize: stuffSearchParams?.pageSize ? stuffSearchParams.pageSize : 100,
|
||||
}
|
||||
|
||||
async function fetchData() {
|
||||
const apiUrl = `/api/object/list?${queryStringFormatter(params)}`
|
||||
await get({
|
||||
@ -186,15 +194,21 @@ export default function Stuff() {
|
||||
if (!isEmptyArray(res)) {
|
||||
setGridProps({ ...gridProps, gridData: res, count: res[0].totCnt })
|
||||
setTotalCount(res[0].totCnt)
|
||||
} else {
|
||||
setGridProps({ ...gridProps, gridData: [], count: 0 })
|
||||
setTotalCount(0)
|
||||
|
||||
setPageNo(1)
|
||||
setPageSize(stuffSearchParams.pageSize)
|
||||
stuffSearchParams.pageNo = 1
|
||||
stuffSearchParams.startRow = 1
|
||||
stuffSearchParams.endRow = 1 * stuffSearchParams.pageSize
|
||||
}
|
||||
|
||||
setIsGlobalLoading(false)
|
||||
})
|
||||
}
|
||||
|
||||
//if (session.storeId === 'T01') {
|
||||
fetchData()
|
||||
//} else if (stuffSearch.schSelSaleStoreId !== '') {
|
||||
//fetchData()
|
||||
//}
|
||||
} else if (stuffSearchParams?.code === 'M') {
|
||||
const params = {
|
||||
saleStoreId: session?.storeId,
|
||||
@ -211,19 +225,47 @@ export default function Stuff() {
|
||||
endRow: pageNo * pageSize,
|
||||
schSelSaleStoreId: stuffSearchParams?.schOtherSelSaleStoreId ? stuffSearchParams.schOtherSelSaleStoreId : stuffSearchParams.schSelSaleStoreId,
|
||||
schSortType: 'R',
|
||||
code: 'S',
|
||||
pageNo: 1,
|
||||
pageSize: 100,
|
||||
}
|
||||
setStuffSearch({
|
||||
...params,
|
||||
})
|
||||
} else if (stuffSearchParams?.code === 'E') {
|
||||
stuffSearchParams.startRow = (stuffSearch.pageNo - 1) * stuffSearchParams.pageSize + 1
|
||||
stuffSearchParams.endRow = stuffSearchParams.pageNo * stuffSearchParams.pageSize
|
||||
stuffSearchParams.schSortType = defaultSortType
|
||||
stuffSearchParams.pageNo = stuffSearchParams.pageNo
|
||||
|
||||
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)
|
||||
|
||||
setPageNo(1)
|
||||
setPageSize(stuffSearchParams.pageSize)
|
||||
stuffSearchParams.pageNo = 1
|
||||
stuffSearchParams.startRow = 1
|
||||
stuffSearchParams.endRow = 1 * stuffSearchParams.pageSize
|
||||
}
|
||||
|
||||
setIsGlobalLoading(false)
|
||||
})
|
||||
}
|
||||
fetchData()
|
||||
} else if (stuffSearchParams?.code === 'C') {
|
||||
resetStuffRecoil()
|
||||
} else if (stuffSearchParams?.code === 'FINISH') {
|
||||
stuffSearchParams.startRow = 1
|
||||
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)}`
|
||||
@ -237,10 +279,32 @@ export default function Stuff() {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fetchData()
|
||||
} else if (stuffSearchParams?.code === 'C') {
|
||||
resetStuffRecoil()
|
||||
} else if (stuffSearchParams?.code === 'DELETE') {
|
||||
const newParams = {
|
||||
saleStoreId: session.storeId,
|
||||
schObjectNo: '',
|
||||
schAddress: '',
|
||||
schObjectName: '',
|
||||
schSaleStoreName: '',
|
||||
schReceiveUser: '',
|
||||
schDispCompanyName: '',
|
||||
schDateType: 'U',
|
||||
schFromDt: dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD'),
|
||||
schToDt: dayjs(new Date()).format('YYYY-MM-DD'),
|
||||
startRow: 1,
|
||||
endRow: 100,
|
||||
schSelSaleStoreId: '',
|
||||
schOtherSelSaleStoreId: '',
|
||||
schSortType: 'R',
|
||||
code: 'S',
|
||||
pageNo: 1,
|
||||
pageSize: 100,
|
||||
}
|
||||
|
||||
setStuffSearch({
|
||||
...newParams,
|
||||
})
|
||||
}
|
||||
}, [stuffSearchParams])
|
||||
|
||||
@ -249,59 +313,46 @@ export default function Stuff() {
|
||||
let startRow = (1 - 1) * e.target.value + 1
|
||||
stuffSearchParams.startRow = startRow
|
||||
stuffSearchParams.endRow = 1 * e.target.value
|
||||
stuffSearchParams.schSelSaleStoreId = stuffSearchParams?.schOtherSelSaleStoreId
|
||||
? stuffSearchParams.schOtherSelSaleStoreId
|
||||
: stuffSearchParams.schSelSaleStoreId
|
||||
stuffSearchParams.schSelSaleStoreId = stuffSearchParams.schSelSaleStoreId
|
||||
stuffSearchParams.schOtherSelSaleStoreId = stuffSearchParams.schOtherSelSaleStoreId
|
||||
stuffSearchParams.pageNo = startRow
|
||||
stuffSearchParams.pageSize = 1 * e.target.value
|
||||
setPageSize(e.target.value)
|
||||
setStuffSearch({
|
||||
...stuffSearchParams,
|
||||
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)) {
|
||||
setGridProps({ ...gridProps, gridData: res, count: res[0].totCnt })
|
||||
setTotalCount(res[0].totCnt)
|
||||
} else {
|
||||
setGridProps({ ...gridProps, gridData: [], count: 0 })
|
||||
setTotalCount(0)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
//최근 등록일 수정일 정렬 이벤트
|
||||
const onChangeSortType = (e) => {
|
||||
let startRow = (1 - 1) * pageSize + 1
|
||||
stuffSearchParams.startRow = startRow
|
||||
stuffSearchParams.endRow = 1 * pageSize
|
||||
|
||||
stuffSearchParams.schSortType = e.target.value
|
||||
stuffSearchParams.schSelSaleStoreId = stuffSearchParams?.schOtherSelSaleStoreId
|
||||
? stuffSearchParams.schOtherSelSaleStoreId
|
||||
: stuffSearchParams.schSelSaleStoreId
|
||||
setDefaultSortType(e.target.value)
|
||||
setStuffSearch({
|
||||
...stuffSearch,
|
||||
code: 'S',
|
||||
startRow: startRow,
|
||||
endRow: 1 * pageSize,
|
||||
endRow: 1 * e.target.value,
|
||||
pageSize: e.target.value,
|
||||
})
|
||||
|
||||
setPageNo(1)
|
||||
}
|
||||
|
||||
//최근 등록일 수정일 정렬 이벤트
|
||||
const onChangeSortType = (e) => {
|
||||
// let startRow = (stuffSearchParams.pageNo - 1) * pageSize + 1
|
||||
let startRow = (1 - 1) * stuffSearchParams.pageSize + 1
|
||||
stuffSearchParams.startRow = startRow
|
||||
stuffSearchParams.endRow = startRow * stuffSearchParams.pageSize
|
||||
stuffSearchParams.schSelSaleStoreId = stuffSearchParams.schSelSaleStoreId
|
||||
stuffSearchParams.schOtherSelSaleStoreId = stuffSearchParams.schOtherSelSaleStoreId
|
||||
stuffSearchParams.pageNo = startRow
|
||||
stuffSearchParams.pageSize = 1 * stuffSearchParams.pageSize
|
||||
setPageSize(stuffSearchParams.pageSize)
|
||||
stuffSearchParams.schSortType = e.target.value
|
||||
setDefaultSortType(e.target.value)
|
||||
|
||||
setStuffSearch({
|
||||
...stuffSearch,
|
||||
code: 'S',
|
||||
startRow: startRow,
|
||||
endRow: startRow * stuffSearchParams.pageSize,
|
||||
pageSize: stuffSearchParams.pageSize,
|
||||
schSortType: e.target.value,
|
||||
})
|
||||
|
||||
setPageNo(1)
|
||||
const apiUrl = `/api/object/list?saleStoreId=${session?.storeId}&${queryStringFormatter(stuffSearchParams)}`
|
||||
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)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
@ -315,19 +366,24 @@ export default function Stuff() {
|
||||
// 페이징 현재페이지 변경
|
||||
const handleChangePage = (page) => {
|
||||
stuffSearchParams.code = 'S'
|
||||
stuffSearchParams.schSelSaleStoreId = stuffSearchParams?.schOtherSelSaleStoreId
|
||||
? stuffSearchParams.schOtherSelSaleStoreId
|
||||
: stuffSearchParams.schSelSaleStoreId
|
||||
stuffSearchParams.schSelSaleStoreId = stuffSearchParams.schSelSaleStoreId
|
||||
stuffSearchParams.schOtherSelSaleStoreId = stuffSearchParams.schOtherSelSaleStoreId
|
||||
|
||||
setStuffSearch({
|
||||
...stuffSearch,
|
||||
code: 'S',
|
||||
startRow: (page - 1) * pageSize + 1,
|
||||
endRow: page * pageSize,
|
||||
endRow: page * stuffSearchParams?.pageSize,
|
||||
pageNo: page,
|
||||
})
|
||||
|
||||
setPageNo(page)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setIsGlobalLoading(true)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* 퍼블시작 */}
|
||||
@ -345,13 +401,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} value={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} value={stuffSearch.pageSize}>
|
||||
<option value="100">100</option>
|
||||
<option value="200">200</option>
|
||||
<option value="300">300</option>
|
||||
@ -363,7 +419,13 @@ export default function Stuff() {
|
||||
<div className="q-grid">
|
||||
<StuffQGrid {...gridProps} getCellDoubleClicked={getCellDoubleClicked} gridRef={gridRef} />
|
||||
<div className="pagination-wrap">
|
||||
<QPagination pageNo={pageNo} pageSize={pageSize} pagePerBlock={10} totalCount={totalCount} handleChangePage={handleChangePage} />
|
||||
<QPagination
|
||||
pageNo={stuffSearch.pageNo}
|
||||
pageSize={stuffSearch.pageSize}
|
||||
pagePerBlock={10}
|
||||
totalCount={totalCount}
|
||||
handleChangePage={handleChangePage}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -10,7 +10,7 @@ import { globalLocaleStore } from '@/store/localeAtom'
|
||||
import { isEmptyArray, isNotEmptyArray, isObjectNotEmpty } from '@/util/common-utils'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { useForm } from 'react-hook-form'
|
||||
import { useRecoilValue, useSetRecoilState } from 'recoil'
|
||||
import { useRecoilValue, useSetRecoilState, useResetRecoilState, useRecoilState } from 'recoil'
|
||||
import { SessionContext } from '@/app/SessionProvider'
|
||||
import FindAddressPop from './popup/FindAddressPop'
|
||||
import PlanRequestPop from './popup/PlanRequestPop'
|
||||
@ -18,10 +18,21 @@ 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'
|
||||
import DocDownOptionPop from '../estimate/popup/DocDownOptionPop'
|
||||
import { stuffSearchState } from '@/store/stuffAtom'
|
||||
import { QcastContext } from '@/app/QcastProvider'
|
||||
export default function StuffDetail() {
|
||||
const { setIsGlobalLoading } = useContext(QcastContext)
|
||||
const resetStuffRecoil = useResetRecoilState(stuffSearchState)
|
||||
const stuffSearchParams = useRecoilValue(stuffSearchState)
|
||||
|
||||
const setFloorPlanObjectNo = useSetRecoilState(floorPlanObjectState) //견적서 화면용 물건번호리코일
|
||||
|
||||
const [estimatePopupOpen, setEstimatePopupOpen] = useState(false)
|
||||
|
||||
const [popPlanNo, setPopPlanNo] = useState('1') //default 1
|
||||
|
||||
//공통코드
|
||||
const { commonCode, findCommonCode } = useCommonCode()
|
||||
const [selOptions, setSelOptions] = useState('') //선택한 1차점
|
||||
@ -96,7 +107,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: [],
|
||||
@ -131,6 +142,16 @@ export default function StuffDetail() {
|
||||
headerName: getMessage('stuff.detail.planGridHeader.capacity'),
|
||||
width: 120,
|
||||
cellStyle: { justifyContent: 'flex-end' /* 우측정렬*/ },
|
||||
cellRenderer: (params) => {
|
||||
let origin = params.value
|
||||
let capacity
|
||||
if (origin) {
|
||||
capacity = origin / 1000
|
||||
return capacity.toFixed(3)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'roofMaterialIdMulti',
|
||||
@ -159,8 +180,8 @@ export default function StuffDetail() {
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'constructSpecification',
|
||||
headerName: getMessage('stuff.detail.planGridHeader.constructSpecification'),
|
||||
field: 'constructSpecificationMulti',
|
||||
headerName: getMessage('stuff.detail.planGridHeader.constructSpecificationMulti'),
|
||||
wrapText: true,
|
||||
autoHeight: true,
|
||||
cellStyle: { justifyContent: 'flex-start' /* 좌측정렬*/ },
|
||||
@ -265,11 +286,12 @@ export default function StuffDetail() {
|
||||
type="button"
|
||||
className="grid-btn"
|
||||
onClick={() => {
|
||||
console.log('엑셀버튼클릭')
|
||||
setFloorPlanObjectNo({ floorPlanObjectNo: params.data.objectNo })
|
||||
handleEstimatePopup(params.data.planNo)
|
||||
}}
|
||||
>
|
||||
<span className="excel"></span>
|
||||
{getMessage('stuff.detail.planGrid.btn2')}
|
||||
{getMessage('stuff.detail.planGrid.docDownload')}
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
@ -279,6 +301,12 @@ export default function StuffDetail() {
|
||||
],
|
||||
})
|
||||
|
||||
// 문서다운로드 팝업 오픈 셋팅
|
||||
const handleEstimatePopup = (planNo) => {
|
||||
setPopPlanNo(planNo)
|
||||
setEstimatePopupOpen(true)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (objectNo) {
|
||||
setEditMode('EDIT')
|
||||
@ -288,10 +316,12 @@ export default function StuffDetail() {
|
||||
}
|
||||
promiseGet({ url: `/api/object/${objectNo}/detail` }).then((res) => {
|
||||
if (res.status === 200) {
|
||||
if (res.data != null) {
|
||||
setDetailData(res.data)
|
||||
if (isObjectNotEmpty(res.data)) {
|
||||
setManagementState(res.data)
|
||||
} else {
|
||||
setDetailData({})
|
||||
setManagementState({})
|
||||
alert(getMessage('stuff.detail.header.notExistObjectNo'))
|
||||
router.push('/management/stuff')
|
||||
}
|
||||
if (isNotEmptyArray(res.data.planList)) {
|
||||
setPlanGridProps({ ...planGridProps, planGridData: res.data.planList })
|
||||
@ -299,8 +329,11 @@ export default function StuffDetail() {
|
||||
setPlanGridProps({ ...planGridProps, planGridData: [] })
|
||||
}
|
||||
} else {
|
||||
setDetailData({})
|
||||
setManagementState({})
|
||||
setPlanGridProps({ ...planGridProps, planGridData: [] })
|
||||
|
||||
alert(getMessage('stuff.detail.header.notExistObjectNo'))
|
||||
router.push('/management/stuff')
|
||||
}
|
||||
})
|
||||
} else {
|
||||
@ -396,6 +429,8 @@ export default function StuffDetail() {
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
setIsGlobalLoading(false)
|
||||
}
|
||||
}, [objectNo, session])
|
||||
|
||||
@ -415,158 +450,165 @@ export default function StuffDetail() {
|
||||
}, [commonCode])
|
||||
|
||||
useEffect(() => {
|
||||
if (isObjectNotEmpty(detailData)) {
|
||||
// 도도부현API
|
||||
get({ url: '/api/object/prefecture/list' }).then((res) => {
|
||||
if (!isEmptyArray(res)) {
|
||||
setPrefCodeList(res)
|
||||
}
|
||||
})
|
||||
if (objectNo) {
|
||||
if (isObjectNotEmpty(managementState)) {
|
||||
// 도도부현API
|
||||
get({ url: '/api/object/prefecture/list' }).then((res) => {
|
||||
if (!isEmptyArray(res)) {
|
||||
setPrefCodeList(res)
|
||||
}
|
||||
})
|
||||
|
||||
//1차점 : X167 T01
|
||||
//2차점 : 10X22, 201X112
|
||||
let url
|
||||
let firstList
|
||||
let otherList
|
||||
let favList
|
||||
//1차점 : X167 T01
|
||||
//2차점 : 10X22, 201X112
|
||||
let url
|
||||
let firstList
|
||||
let otherList
|
||||
let favList
|
||||
|
||||
if (session?.storeId === 'T01') {
|
||||
url = `/api/object/saleStore/${session?.storeId}/firstList?userId=${session?.userId}`
|
||||
} else {
|
||||
if (session.storeLvl === '1') {
|
||||
url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=1&userId=${session?.userId}`
|
||||
if (session?.storeId === 'T01') {
|
||||
url = `/api/object/saleStore/${session?.storeId}/firstList?userId=${session?.userId}`
|
||||
} else {
|
||||
url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=1&userId=${session?.userId}`
|
||||
}
|
||||
}
|
||||
get({ url: url }).then((res) => {
|
||||
if (!isEmptyArray(res)) {
|
||||
if (session?.storeId === 'T01') {
|
||||
firstList = res.filter((row) => row.saleStoreLevel === '1')
|
||||
firstList.sort((a, b) => (a.saleStoreId !== 'T01') - (b.saleStoreId !== 'T01') || a.saleStoreId - b.saleStoreId)
|
||||
favList = firstList.filter((row) => row.saleStoreId === 'T01' || row.priority !== 'B')
|
||||
setSaleStoreList(firstList)
|
||||
setFavoriteStoreList(favList)
|
||||
setShowSaleStoreList(favList)
|
||||
|
||||
if (detailData.firstAgentId != null) {
|
||||
form.setValue('saleStoreId', detailData.firstAgentId)
|
||||
setSelOptions(detailData.firstAgentId)
|
||||
} else {
|
||||
form.setValue('saleStoreId', detailData.saleStoreId)
|
||||
setSelOptions(detailData.saleStoreId)
|
||||
}
|
||||
|
||||
//상세데이터의 1차점 아이디로 2차점 목록 조회하기
|
||||
|
||||
let data = detailData?.firstAgentId ? detailData.firstAgentId : detailData.saleStoreId
|
||||
// url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=0&userId=${session?.userId}`
|
||||
url = `/api/object/saleStore/${data}/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
|
||||
setOriginOtherSaleStoreList(otherList)
|
||||
setOtherSaleStoreList(otherList)
|
||||
}
|
||||
})
|
||||
if (session.storeLvl === '1') {
|
||||
url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=1&userId=${session?.userId}`
|
||||
} else {
|
||||
//1차점 셀렉트박스
|
||||
if (session?.storeLvl === '1') {
|
||||
firstList = res
|
||||
favList = res.filter((row) => row.priority !== 'B')
|
||||
otherList = res.filter((row) => row.firstAgentYn === 'N')
|
||||
|
||||
setSaleStoreList(firstList)
|
||||
setFavoriteStoreList(firstList)
|
||||
setShowSaleStoreList(firstList)
|
||||
|
||||
setOtherSaleStoreList(otherList)
|
||||
} else {
|
||||
setSelOptions(res[0].saleStoreId)
|
||||
form.setValue('saleStoreId', res[0].saleStoreId)
|
||||
form.setValue('saleStoreLevel', res[0].storeLvl)
|
||||
setSaleStoreList(res)
|
||||
setFavoriteStoreList(res)
|
||||
setShowSaleStoreList(res)
|
||||
otherList = res.filter((row) => row.firstAgentYn === 'N')
|
||||
setOtherSaleStoreList(otherList)
|
||||
}
|
||||
url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=1&userId=${session?.userId}`
|
||||
}
|
||||
}
|
||||
get({ url: url }).then((res) => {
|
||||
if (!isEmptyArray(res)) {
|
||||
if (session?.storeId === 'T01') {
|
||||
firstList = res.filter((row) => row.saleStoreLevel === '1')
|
||||
firstList.sort((a, b) => (a.saleStoreId !== 'T01') - (b.saleStoreId !== 'T01') || a.saleStoreId - b.saleStoreId)
|
||||
favList = firstList.filter((row) => row.saleStoreId === 'T01' || row.priority !== 'B')
|
||||
setSaleStoreList(firstList)
|
||||
setFavoriteStoreList(favList)
|
||||
setShowSaleStoreList(favList)
|
||||
|
||||
//상세데이터가 1차점이면 1차점에 세팅
|
||||
//상세데이터가 2차점이면 2차점에 세팅하고 세션으로 1차점 세팅
|
||||
if (detailData.saleStoreLevel === '1') {
|
||||
setSelOptions(detailData.saleStoreId)
|
||||
form.setValue('saleStoreId', detailData.saleStoreId)
|
||||
form.setValue('saleStoreLevel', detailData.saleStoreLevel)
|
||||
} else {
|
||||
setOtherSelOptions(detailData.saleStoreId)
|
||||
form.setValue('otherSaleStoreId', detailData.saleStoreId)
|
||||
form.setValue('otherSaleStoreLevel', detailData.saleStoreLevel)
|
||||
}
|
||||
if (managementState.firstAgentId != null) {
|
||||
form.setValue('saleStoreId', managementState.firstAgentId)
|
||||
setSelOptions(managementState.firstAgentId)
|
||||
} else {
|
||||
form.setValue('saleStoreId', managementState.saleStoreId)
|
||||
setSelOptions(managementState.saleStoreId)
|
||||
}
|
||||
|
||||
//설계의뢰No.
|
||||
form.setValue('planReqNo', detailData.planReqNo)
|
||||
//담당자
|
||||
form.setValue('receiveUser', detailData.receiveUser)
|
||||
//상세데이터의 1차점 아이디로 2차점 목록 조회하기
|
||||
|
||||
//물건구분objectStatusId
|
||||
setSelectObjectStatusId(detailData.objectStatusId)
|
||||
form.setValue('objectStatusId', detailData.objectStatusId)
|
||||
let data = managementState?.firstAgentId ? managementState.firstAgentId : managementState.saleStoreId
|
||||
url = `/api/object/saleStore/${data}/list?firstFlg=0&userId=${session?.userId}`
|
||||
|
||||
//물건명
|
||||
form.setValue('objectName', detailData.objectName)
|
||||
get({ url: url }).then((res) => {
|
||||
if (!isEmptyArray(res)) {
|
||||
res.map((row) => {
|
||||
row.value = row.saleStoreId
|
||||
row.label = row.saleStoreName
|
||||
})
|
||||
|
||||
//경칭코드
|
||||
setSelHonorificCode(detailData.objectNameOmit)
|
||||
form.setValue('objectNameOmit', detailData.objectNameOmit)
|
||||
otherList = res
|
||||
setOriginOtherSaleStoreList(otherList)
|
||||
setOtherSaleStoreList(otherList)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
//1차점 셀렉트박스
|
||||
if (session?.storeLvl === '1') {
|
||||
firstList = res
|
||||
favList = res.filter((row) => row.priority !== 'B')
|
||||
otherList = res.filter((row) => row.firstAgentYn === 'N')
|
||||
|
||||
//물건명 후리가나
|
||||
form.setValue('objectNameKana', detailData.objectNameKana)
|
||||
setSaleStoreList(firstList)
|
||||
setFavoriteStoreList(firstList)
|
||||
setShowSaleStoreList(firstList)
|
||||
|
||||
//우편번호
|
||||
form.setValue('zipNo', detailData.zipNo)
|
||||
setOtherSaleStoreList(otherList)
|
||||
} else {
|
||||
setSelOptions(res[0].saleStoreId)
|
||||
form.setValue('saleStoreId', res[0].saleStoreId)
|
||||
form.setValue('saleStoreLevel', res[0].storeLvl)
|
||||
setSaleStoreList(res)
|
||||
setFavoriteStoreList(res)
|
||||
setShowSaleStoreList(res)
|
||||
otherList = res.filter((row) => row.firstAgentYn === 'N')
|
||||
setOtherSaleStoreList(otherList)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//도도부현 / 주소
|
||||
setPrefValue(detailData.prefId)
|
||||
form.setValue('prefId', detailData.prefId)
|
||||
form.setValue('address', detailData.address)
|
||||
//발전시뮬
|
||||
form.setValue('areaId', detailData.areaId)
|
||||
//상세데이터가 1차점이면 1차점에 세팅
|
||||
//상세데이터가 2차점이면 2차점에 세팅하고 세션으로 1차점 세팅
|
||||
if (managementState.saleStoreLevel === '1') {
|
||||
setSelOptions(managementState.saleStoreId)
|
||||
form.setValue('saleStoreId', managementState.saleStoreId)
|
||||
form.setValue('saleStoreLevel', managementState.saleStoreLevel)
|
||||
} else {
|
||||
setOtherSelOptions(managementState.saleStoreId)
|
||||
form.setValue('otherSaleStoreId', managementState.saleStoreId)
|
||||
form.setValue('otherSaleStoreLevel', managementState.saleStoreLevel)
|
||||
|
||||
//기준풍속
|
||||
form.setValue('standardWindSpeedId', detailData.standardWindSpeedId)
|
||||
//수직적설량
|
||||
form.setValue('verticalSnowCover', detailData.verticalSnowCover)
|
||||
//한랭지대책시행 coldRegionFlg 1이면 true
|
||||
form.setValue('coldRegionFlg', detailData.coldRegionFlg === '1' ? true : false)
|
||||
form.setValue('saleStoreLevel', '1')
|
||||
}
|
||||
|
||||
//면조도구분 surfaceType null로 내려오면 셋팅 안하고 저장할때 필수값 체크하도록
|
||||
// form.setValue('surfaceType', 'Ⅱ')
|
||||
// form.setValue('surfaceType', 'Ⅲ・Ⅳ')
|
||||
form.setValue('surfaceType', detailData.surfaceType)
|
||||
//염해지역용아이템사용 saltAreaFlg 1이면 true
|
||||
form.setValue('saltAreaFlg', detailData.saltAreaFlg === '1' ? true : false)
|
||||
//설치높이
|
||||
form.setValue('installHeight', detailData.installHeight)
|
||||
//계약조건 null로 내려오면 0으로 디폴트셋팅
|
||||
if (detailData.conType === null) {
|
||||
form.setValue('conType', '0')
|
||||
} else {
|
||||
form.setValue('conType', detailData.conType)
|
||||
}
|
||||
//메모
|
||||
form.setValue('remarks', detailData.remarks)
|
||||
})
|
||||
//설계의뢰No.
|
||||
form.setValue('planReqNo', managementState.planReqNo)
|
||||
//담당자
|
||||
form.setValue('receiveUser', managementState.receiveUser)
|
||||
|
||||
//물건구분objectStatusId
|
||||
setSelectObjectStatusId(managementState.objectStatusId)
|
||||
form.setValue('objectStatusId', managementState.objectStatusId)
|
||||
|
||||
//물건명
|
||||
form.setValue('objectName', managementState.objectName)
|
||||
|
||||
//경칭코드
|
||||
setSelHonorificCode(managementState.objectNameOmit)
|
||||
form.setValue('objectNameOmit', managementState.objectNameOmit)
|
||||
|
||||
//물건명 후리가나
|
||||
form.setValue('objectNameKana', managementState.objectNameKana)
|
||||
|
||||
//우편번호
|
||||
form.setValue('zipNo', managementState.zipNo)
|
||||
|
||||
//도도부현 / 주소
|
||||
setPrefValue(managementState.prefId)
|
||||
form.setValue('prefId', managementState.prefId)
|
||||
form.setValue('prefName', managementState.prefName)
|
||||
form.setValue('address', managementState.address)
|
||||
//발전시뮬
|
||||
form.setValue('areaId', managementState.areaId)
|
||||
|
||||
//기준풍속
|
||||
form.setValue('standardWindSpeedId', managementState.standardWindSpeedId)
|
||||
//수직적설량
|
||||
form.setValue('verticalSnowCover', managementState.verticalSnowCover)
|
||||
//한랭지대책시행 coldRegionFlg 1이면 true
|
||||
form.setValue('coldRegionFlg', managementState.coldRegionFlg === '1' ? true : false)
|
||||
|
||||
//면조도구분 surfaceType null로 내려오면 셋팅 안하고 저장할때 필수값 체크하도록
|
||||
// form.setValue('surfaceType', 'Ⅱ')
|
||||
// form.setValue('surfaceType', 'Ⅲ・Ⅳ')
|
||||
form.setValue('surfaceType', managementState.surfaceType)
|
||||
//염해지역용아이템사용 saltAreaFlg 1이면 true
|
||||
form.setValue('saltAreaFlg', managementState.saltAreaFlg === '1' ? true : false)
|
||||
//설치높이
|
||||
form.setValue('installHeight', managementState.installHeight)
|
||||
//계약조건 null로 내려오면 0으로 디폴트셋팅
|
||||
if (managementState.conType === null) {
|
||||
form.setValue('conType', '0')
|
||||
} else {
|
||||
form.setValue('conType', managementState.conType)
|
||||
}
|
||||
//메모
|
||||
form.setValue('remarks', managementState.remarks)
|
||||
})
|
||||
|
||||
//상세끝
|
||||
setIsGlobalLoading(false)
|
||||
}
|
||||
}
|
||||
}, [detailData, session])
|
||||
}, [managementState])
|
||||
|
||||
//경칭선택 변경 이벤트
|
||||
const onChangeHonorificCode = (key) => {
|
||||
@ -856,6 +898,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 +915,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 +1080,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 +1228,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), {})
|
||||
@ -1238,7 +1289,7 @@ export default function StuffDetail() {
|
||||
if (res.status === 201) {
|
||||
alert(getMessage('stuff.detail.save'))
|
||||
setFloorPlanObjectNo({ floorPlanObjectNo: objectNo })
|
||||
router.push(`/management/stuff/detail?objectNo=${res.data.objectNo.toString()}`)
|
||||
router.push(`/management/stuff/detail?objectNo=${res.data.objectNo.toString()}`, { scroll: false })
|
||||
}
|
||||
})
|
||||
} else {
|
||||
@ -1247,8 +1298,7 @@ export default function StuffDetail() {
|
||||
if (res.status === 201) {
|
||||
setFloorPlanObjectNo({ floorPlanObjectNo: res.data.objectNo })
|
||||
alert(getMessage('stuff.detail.save'))
|
||||
// router.refresh()
|
||||
router.push(`/management/stuff/detail?objectNo=${res.data.objectNo.toString()}`)
|
||||
router.push(`/management/stuff/detail?objectNo=${res.data.objectNo.toString()}`, { scroll: false })
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -1283,6 +1333,7 @@ export default function StuffDetail() {
|
||||
tempFlg: '1',
|
||||
workNo: null,
|
||||
workName: null,
|
||||
objectNo: objectNo ? objectNo : '',
|
||||
}
|
||||
|
||||
//1차점 or 2차점 안고르고 임시저장하면
|
||||
@ -1291,23 +1342,38 @@ export default function StuffDetail() {
|
||||
params.saleStoreLevel = session.storeLvl
|
||||
}
|
||||
|
||||
await promisePost({ url: '/api/object/save-object', data: params }).then((res) => {
|
||||
if (res.status === 201) {
|
||||
alert(getMessage('stuff.detail.tempSave.message1'))
|
||||
router.push(`${pathname}?objectNo=${res.data.objectNo.toString()}`)
|
||||
}
|
||||
})
|
||||
const apiUrl = '/api/object/save-object'
|
||||
if (objectNo) {
|
||||
await promisePut({ url: apiUrl, data: params }).then((res) => {
|
||||
if (res.status === 201) {
|
||||
alert(getMessage('stuff.detail.tempSave.message1'))
|
||||
router.push(`/management/stuff/tempdetail?objectNo=${res.data.objectNo.toString()}`, { scroll: false })
|
||||
}
|
||||
})
|
||||
} else {
|
||||
await promisePost({ url: apiUrl, data: params }).then((res) => {
|
||||
if (res.status === 201) {
|
||||
alert(getMessage('stuff.detail.tempSave.message1'))
|
||||
router.push(`/management/stuff/tempdetail?objectNo=${res.data.objectNo.toString()}`, { scroll: false })
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 물건삭제
|
||||
const onDelete = () => {
|
||||
const specificationConfirmDate = detailData.specificationConfirmDate
|
||||
const specificationConfirmDate = managementState.specificationConfirmDate
|
||||
if (specificationConfirmDate != null) {
|
||||
alert(getMessage('stuff.detail.delete.message1'))
|
||||
} else {
|
||||
if (confirm(getMessage('common.message.data.delete'))) {
|
||||
del({ url: `/api/object/${objectNo}` }).then(() => {
|
||||
setFloorPlanObjectNo({ floorPlanObjectNo: '' })
|
||||
if (session.storeId === 'T01') {
|
||||
stuffSearchParams.code = 'DELETE'
|
||||
} else {
|
||||
resetStuffRecoil()
|
||||
}
|
||||
router.push('/management/stuff')
|
||||
})
|
||||
}
|
||||
@ -1726,31 +1792,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 +1914,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 +1924,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 +2030,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 +2064,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 +2149,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 +2312,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 +2404,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 +2413,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>
|
||||
@ -2413,6 +2487,8 @@ export default function StuffDetail() {
|
||||
{showWindSpeedButtonValid && (
|
||||
<WindSelectPop setShowWindSpeedButtonValid={setShowWindSpeedButtonValid} prefName={form.watch('prefName')} windSpeedInfo={setWindSppedInfo} />
|
||||
)}
|
||||
|
||||
{estimatePopupOpen && <DocDownOptionPop planNo={popPlanNo} setEstimatePopupOpen={setEstimatePopupOpen} />}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -17,6 +17,8 @@ import { isObjectNotEmpty } from '@/util/common-utils'
|
||||
|
||||
import { SessionContext } from '@/app/SessionProvider'
|
||||
|
||||
import { QcastContext } from '@/app/QcastProvider'
|
||||
|
||||
export default function StuffSearchCondition() {
|
||||
const { session } = useContext(SessionContext)
|
||||
const setAppMessageState = useSetRecoilState(appMessageStore)
|
||||
@ -66,30 +68,167 @@ export default function StuffSearchCondition() {
|
||||
const [otherSaleStoreList, setOtherSaleStoreList] = useState([]) //1차점 이외 판매점목록
|
||||
const [otherSaleStoreId, setOtherSaleStoreId] = useState('')
|
||||
|
||||
const { setIsGlobalLoading } = useContext(QcastContext)
|
||||
|
||||
// 조회
|
||||
const onSubmit = () => {
|
||||
let diff = dayjs(endDate).diff(startDate, 'day')
|
||||
|
||||
if (diff > 366) {
|
||||
return alert(getMessage('stuff.message.periodError'))
|
||||
}
|
||||
|
||||
if (isNaN(diff)) {
|
||||
return alert(getMessage('stuff.message.periodError'))
|
||||
}
|
||||
|
||||
setIsGlobalLoading(true)
|
||||
|
||||
if (stuffSearch.code === 'S') {
|
||||
if (stuffSearch.pageNo !== 1) {
|
||||
setStuffSearch({
|
||||
schObjectNo: objectNo ? objectNo : stuffSearch.schObjectNo,
|
||||
schSaleStoreName: saleStoreName ? saleStoreName : '',
|
||||
schAddress: address ? address : '',
|
||||
schObjectName: objectName ? objectName : '',
|
||||
schDispCompanyName: dispCompanyName ? dispCompanyName : '',
|
||||
schSelSaleStoreId: stuffSearch?.schSelSaleStoreId ? stuffSearch.schSelSaleStoreId : '',
|
||||
schOtherSelSaleStoreId: stuffSearch?.schOtherSelSaleStoreId ? stuffSearch.schOtherSelSaleStoreId : '',
|
||||
schReceiveUser: receiveUser ? receiveUser : '',
|
||||
schDateType: dateType,
|
||||
schFromDt: startDate ? dayjs(startDate).format('YYYY-MM-DD') : '',
|
||||
schToDt: endDate ? dayjs(endDate).format('YYYY-MM-DD') : '',
|
||||
code: 'E',
|
||||
startRow: 1,
|
||||
endRow: 1 * stuffSearch?.pageSize,
|
||||
schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'R',
|
||||
pageNo: 1,
|
||||
pageSize: stuffSearch?.pageSize,
|
||||
})
|
||||
} else {
|
||||
setStuffSearch({
|
||||
schObjectNo: objectNo ? objectNo : stuffSearch.schObjectNo,
|
||||
schSaleStoreName: saleStoreName ? saleStoreName : '',
|
||||
schAddress: address ? address : '',
|
||||
schObjectName: objectName ? objectName : '',
|
||||
schDispCompanyName: dispCompanyName ? dispCompanyName : '',
|
||||
schSelSaleStoreId: stuffSearch?.schSelSaleStoreId ? stuffSearch.schSelSaleStoreId : '',
|
||||
schOtherSelSaleStoreId: stuffSearch?.schOtherSelSaleStoreId ? stuffSearch.schOtherSelSaleStoreId : '',
|
||||
schReceiveUser: receiveUser ? receiveUser : '',
|
||||
schDateType: dateType,
|
||||
schFromDt: startDate ? dayjs(startDate).format('YYYY-MM-DD') : '',
|
||||
schToDt: endDate ? dayjs(endDate).format('YYYY-MM-DD') : '',
|
||||
code: 'E',
|
||||
startRow: stuffSearch?.startRow ? stuffSearch.startRow : 1,
|
||||
endRow: stuffSearch?.endRow ? stuffSearch.endRow : 100,
|
||||
schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'R',
|
||||
pageNo: stuffSearch?.pageNo,
|
||||
pageSize: stuffSearch?.pageSize,
|
||||
})
|
||||
}
|
||||
} else if (stuffSearch.code === 'FINISH') {
|
||||
setStuffSearch({
|
||||
schObjectNo: objectNo ? objectNo : stuffSearch.schObjectNo,
|
||||
schSaleStoreName: saleStoreName ? saleStoreName : '',
|
||||
schAddress: address ? address : '',
|
||||
schObjectName: objectName ? objectName : '',
|
||||
schDispCompanyName: dispCompanyName ? dispCompanyName : '',
|
||||
schSelSaleStoreId: stuffSearch?.schOtherSelSaleStoreId ? stuffSearch.schOtherSelSaleStoreId : stuffSearch.schSelSaleStoreId,
|
||||
schReceiveUser: receiveUser ? receiveUser : '',
|
||||
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'),
|
||||
schFromDt: startDate ? dayjs(startDate).format('YYYY-MM-DD') : '',
|
||||
schToDt: endDate ? dayjs(endDate).format('YYYY-MM-DD') : '',
|
||||
code: 'E',
|
||||
startRow: stuffSearch?.startRow ? stuffSearch.startRow : 1,
|
||||
endRow: stuffSearch?.endRow ? stuffSearch.endRow : 100,
|
||||
startRow: 1,
|
||||
endRow: 100,
|
||||
schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'R',
|
||||
})
|
||||
} else if (stuffSearch.code === 'E') {
|
||||
if (session.storeId !== 'T01' && session.storeLvl === '1') {
|
||||
setStuffSearch({
|
||||
schObjectNo: stuffSearch?.schObjectNo ? stuffSearch.schObjectNo : objectNo,
|
||||
schSaleStoreName: stuffSearch?.schSaleStoreName ? stuffSearch.schSaleStoreName : saleStoreName,
|
||||
schAddress: stuffSearch?.schAddress ? stuffSearch.schAddress : address,
|
||||
schObjectName: stuffSearch?.schObjectName ? stuffSearch.schObjectName : objectName,
|
||||
schDispCompanyName: stuffSearch?.schDispCompanyName ? stuffSearch.schDispCompanyName : dispCompanyName,
|
||||
schSelSaleStoreId: otherSaleStoreId ? schSelSaleStoreId : '',
|
||||
schOtherSelSaleStoreId: otherSaleStoreId,
|
||||
schReceiveUser: stuffSearch?.schReceiveUser ? stuffSearch.schReceiveUser : receiveUser,
|
||||
schDateType: dateType,
|
||||
schFromDt: startDate ? dayjs(startDate).format('YYYY-MM-DD') : '',
|
||||
schToDt: endDate ? dayjs(endDate).format('YYYY-MM-DD') : '',
|
||||
code: 'E',
|
||||
startRow: stuffSearch?.startRow ? stuffSearch.startRow : 1,
|
||||
endRow: stuffSearch?.endRow ? stuffSearch.endRow : 100,
|
||||
schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'R',
|
||||
pageNo: stuffSearch?.pageNo,
|
||||
pageSize: stuffSearch?.pageSize,
|
||||
})
|
||||
} else if (session.storeId === 'T01') {
|
||||
if (stuffSearch.pageNo !== 1) {
|
||||
setStuffSearch({
|
||||
schObjectNo: objectNo ? objectNo : stuffSearch.schObjectNo,
|
||||
schSaleStoreName: saleStoreName ? saleStoreName : '',
|
||||
schAddress: address ? address : '',
|
||||
schObjectName: objectName ? objectName : '',
|
||||
schDispCompanyName: dispCompanyName ? dispCompanyName : '',
|
||||
schSelSaleStoreId: stuffSearch?.schSelSaleStoreId ? stuffSearch.schSelSaleStoreId : '',
|
||||
schOtherSelSaleStoreId: stuffSearch?.schOtherSelSaleStoreId ? stuffSearch.schOtherSelSaleStoreId : '',
|
||||
schReceiveUser: receiveUser ? receiveUser : '',
|
||||
schDateType: dateType,
|
||||
schFromDt: startDate ? dayjs(startDate).format('YYYY-MM-DD') : '',
|
||||
schToDt: endDate ? dayjs(endDate).format('YYYY-MM-DD') : '',
|
||||
code: 'E',
|
||||
startRow: 1,
|
||||
endRow: 1 * stuffSearch?.pageSize,
|
||||
// schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'R',
|
||||
schSortType: 'R',
|
||||
pageNo: 1,
|
||||
pageSize: stuffSearch?.pageSize,
|
||||
})
|
||||
} else {
|
||||
setStuffSearch({
|
||||
schObjectNo: stuffSearch?.schObjectNo ? stuffSearch.schObjectNo : objectNo,
|
||||
schSaleStoreName: stuffSearch?.schSaleStoreName ? stuffSearch.schSaleStoreName : saleStoreName,
|
||||
schAddress: stuffSearch?.schAddress ? stuffSearch.schAddress : address,
|
||||
schObjectName: stuffSearch?.schObjectName ? stuffSearch.schObjectName : objectName,
|
||||
schDispCompanyName: stuffSearch?.schDispCompanyName ? stuffSearch.schDispCompanyName : dispCompanyName,
|
||||
schSelSaleStoreId: schSelSaleStoreId,
|
||||
schOtherSelSaleStoreId: otherSaleStoreId,
|
||||
schReceiveUser: stuffSearch?.schReceiveUser ? stuffSearch.schReceiveUser : receiveUser,
|
||||
schDateType: dateType,
|
||||
schFromDt: startDate ? dayjs(startDate).format('YYYY-MM-DD') : '',
|
||||
schToDt: endDate ? dayjs(endDate).format('YYYY-MM-DD') : '',
|
||||
code: 'E',
|
||||
startRow: stuffSearch?.startRow ? stuffSearch.startRow : 1,
|
||||
endRow: stuffSearch?.endRow ? stuffSearch.endRow : 100,
|
||||
schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'R',
|
||||
pageNo: stuffSearch?.pageNo,
|
||||
pageSize: stuffSearch?.pageSize,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
setStuffSearch({
|
||||
schObjectNo: stuffSearch?.schObjectNo ? stuffSearch.schObjectNo : objectNo,
|
||||
schSaleStoreName: stuffSearch?.schSaleStoreName ? stuffSearch.schSaleStoreName : saleStoreName,
|
||||
schAddress: stuffSearch?.schAddress ? stuffSearch.schAddress : address,
|
||||
schObjectName: stuffSearch?.schObjectName ? stuffSearch.schObjectName : objectName,
|
||||
schDispCompanyName: stuffSearch?.schDispCompanyName ? stuffSearch.schDispCompanyName : dispCompanyName,
|
||||
schSelSaleStoreId: schSelSaleStoreId,
|
||||
schOtherSelSaleStoreId: otherSaleStoreId,
|
||||
schReceiveUser: stuffSearch?.schReceiveUser ? stuffSearch.schReceiveUser : receiveUser,
|
||||
schDateType: dateType,
|
||||
schFromDt: startDate ? dayjs(startDate).format('YYYY-MM-DD') : '',
|
||||
schToDt: endDate ? dayjs(endDate).format('YYYY-MM-DD') : '',
|
||||
code: 'E',
|
||||
startRow: stuffSearch?.startRow ? stuffSearch.startRow : 1,
|
||||
endRow: stuffSearch?.endRow ? stuffSearch.endRow : 100,
|
||||
schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'R',
|
||||
pageNo: stuffSearch?.pageNo,
|
||||
pageSize: stuffSearch?.pageSize,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
setStuffSearch({
|
||||
schObjectNo: objectNo,
|
||||
@ -97,15 +236,18 @@ export default function StuffSearchCondition() {
|
||||
schAddress: address,
|
||||
schObjectName: objectName,
|
||||
schDispCompanyName: dispCompanyName,
|
||||
schSelSaleStoreId: stuffSearch?.schOtherSelSaleStoreId ? stuffSearch.schOtherSelSaleStoreId : stuffSearch.schSelSaleStoreId,
|
||||
schSelSaleStoreId: schSelSaleStoreId,
|
||||
schOtherSelSaleStoreId: otherSaleStoreId,
|
||||
schReceiveUser: receiveUser,
|
||||
schDateType: dateType,
|
||||
schFromDt: dayjs(startDate).format('YYYY-MM-DD'),
|
||||
schToDt: dayjs(endDate).format('YYYY-MM-DD'),
|
||||
schFromDt: startDate ? dayjs(startDate).format('YYYY-MM-DD') : '',
|
||||
schToDt: endDate ? dayjs(endDate).format('YYYY-MM-DD') : '',
|
||||
code: 'E',
|
||||
startRow: 1,
|
||||
endRow: 100,
|
||||
startRow: stuffSearch?.startRow ? stuffSearch.startRow : 1,
|
||||
endRow: stuffSearch?.endRow ? stuffSearch.endRow : 100,
|
||||
schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'R',
|
||||
pageNo: stuffSearch?.pageNo,
|
||||
pageSize: stuffSearch?.pageSize,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -119,7 +261,7 @@ export default function StuffSearchCondition() {
|
||||
objectNameRef.current.value = ''
|
||||
dispCompanyNameRef.current.value = ''
|
||||
receiveUserRef.current.value = ''
|
||||
|
||||
stuffSearch.schDateType = 'U'
|
||||
setObjectNo('')
|
||||
setAddress('')
|
||||
setobjectName('')
|
||||
@ -131,12 +273,25 @@ export default function StuffSearchCondition() {
|
||||
setEndDate(dayjs(new Date()).format('YYYY-MM-DD'))
|
||||
if (session?.storeId === 'T01') {
|
||||
setSchSelSaleStoreId('')
|
||||
setOtherSaleStoreId('')
|
||||
handleClear1() //판매대리점선택 자동완성 클리어
|
||||
resetStuffRecoil()
|
||||
setStuffSearch({
|
||||
...stuffSearch,
|
||||
schObjectNo: '',
|
||||
schAddress: '',
|
||||
schObjectName: '',
|
||||
schSaleStoreName: '',
|
||||
schReceiveUser: '',
|
||||
schDispCompanyName: '',
|
||||
schSelSaleStoreId: '',
|
||||
schOtherSelSaleStoreId: '',
|
||||
schDateType: 'U',
|
||||
startRow: 1,
|
||||
endRow: 100,
|
||||
schSortType: 'R',
|
||||
pageNo: 1,
|
||||
pageSize: 100,
|
||||
code: 'S',
|
||||
})
|
||||
} else {
|
||||
if (otherSaleStoreList.length > 1) {
|
||||
@ -184,30 +339,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 +370,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 +388,8 @@ export default function StuffSearchCondition() {
|
||||
setStuffSearch({
|
||||
...stuffSearch,
|
||||
code: 'S',
|
||||
schSelSaleStoreId: otherList[0].saleStoreId,
|
||||
schSelSaleStoreId: res[0].saleStoreId,
|
||||
schOtherSelSaleStoreId: otherList[0].saleStoreId,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -267,7 +415,7 @@ export default function StuffSearchCondition() {
|
||||
const onInputChange = (key) => {
|
||||
if (key !== '') {
|
||||
setShowSaleStoreList(schSelSaleStoreList)
|
||||
setOtherSaleStoreList([])
|
||||
// setOtherSaleStoreList([])
|
||||
} else {
|
||||
setShowSaleStoreList(favoriteStoreList)
|
||||
}
|
||||
@ -298,8 +446,13 @@ export default function StuffSearchCondition() {
|
||||
})
|
||||
} else {
|
||||
//X누름
|
||||
//화면에선 지우는데 리코일은 조회누르지 않으면 보존
|
||||
setSchSelSaleStoreId('')
|
||||
stuffSearch.schSelSaleStoreId = ''
|
||||
setOtherSaleStoreId('')
|
||||
if (stuffSearch.code === 'S') {
|
||||
stuffSearch.schSelSaleStoreId = ''
|
||||
stuffSearch.schOtherSelSaleStoreId = ''
|
||||
}
|
||||
|
||||
//2차점 판매점목록비우기
|
||||
setOtherSaleStoreList([])
|
||||
@ -311,24 +464,79 @@ 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 === '') {
|
||||
// 화면에선 지우는데 조회누르기 전이면 리코일은 남김
|
||||
setSchSelSaleStoreId(session.storeId)
|
||||
} else {
|
||||
// 화면에선 지우는데 조회누르기 전이면 리코일은 남김
|
||||
setOtherSaleStoreId('')
|
||||
if (stuffSearch.code === 'S') {
|
||||
stuffSearch.schOtherSelSaleStoreId = ''
|
||||
}
|
||||
}
|
||||
} else {
|
||||
setOtherSaleStoreId('')
|
||||
setSchSelSaleStoreId(schSelSaleStoreId)
|
||||
stuffSearch.schOtherSelSaleStoreId = ''
|
||||
stuffSearch.schSelSaleStoreId = schSelSaleStoreId
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setStartDate(stuffSearch?.schFromDt ? stuffSearch.schFromDt : dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD'))
|
||||
setEndDate(stuffSearch?.schToDt ? stuffSearch.schToDt : dayjs(new Date()).format('YYYY-MM-DD'))
|
||||
setObjectNo(stuffSearch.schObjectNo ? stuffSearch.schObjectNo : objectNo)
|
||||
setSaleStoreName(stuffSearch.schSaleStoreName ? stuffSearch.schSaleStoreName : saleStoreName)
|
||||
setAddress(stuffSearch.schAddress ? stuffSearch.schAddress : address)
|
||||
setobjectName(stuffSearch.schObjectName ? stuffSearch.schObjectName : objectName)
|
||||
setDispCompanyName(stuffSearch.schDispCompanyName ? stuffSearch.schDispCompanyName : dispCompanyName)
|
||||
setReceiveUser(stuffSearch.schReceiveUser ? stuffSearch.schReceiveUser : receiveUser)
|
||||
//X42
|
||||
if (session?.storeId === 'T01') {
|
||||
if (stuffSearch.code === 'DELETE') {
|
||||
setObjectNo('')
|
||||
setSaleStoreName('')
|
||||
setAddress('')
|
||||
setobjectName('')
|
||||
setDispCompanyName('')
|
||||
setReceiveUser('')
|
||||
objectNoRef.current.value = ''
|
||||
saleStoreNameRef.current.value = ''
|
||||
addressRef.current.value = ''
|
||||
objectNameRef.current.value = ''
|
||||
dispCompanyNameRef.current.value = ''
|
||||
receiveUserRef.current.value = ''
|
||||
stuffSearch.schObjectNo = ''
|
||||
stuffSearch.schAddress = ''
|
||||
stuffSearch.schObjectName = ''
|
||||
stuffSearch.schSaleStoreName = ''
|
||||
stuffSearch.schReceiveUser = ''
|
||||
stuffSearch.schDispCompanyName = ''
|
||||
stuffSearch.schDateType = 'U'
|
||||
stuffSearch.schFromDt = dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD')
|
||||
stuffSearch.schToDt = dayjs(new Date()).format('YYYY-MM-DD')
|
||||
stuffSearch.startRow = 1
|
||||
stuffSearch.endRow = 100
|
||||
stuffSearch.schSelSaleStoreId = ''
|
||||
stuffSearch.schOtherSelSaleStoreId = ''
|
||||
stuffSearch.schSortType = 'R'
|
||||
stuffSearch.pageNo = 1
|
||||
stuffSearch.pageSize = 100
|
||||
|
||||
setSchSelSaleStoreId('')
|
||||
setOtherSaleStoreId('')
|
||||
}
|
||||
} else {
|
||||
setStartDate(stuffSearch?.schFromDt ? stuffSearch.schFromDt : dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD'))
|
||||
setEndDate(stuffSearch?.schToDt ? stuffSearch.schToDt : dayjs(new Date()).format('YYYY-MM-DD'))
|
||||
setObjectNo(stuffSearch.schObjectNo ? stuffSearch.schObjectNo : objectNo)
|
||||
setSaleStoreName(stuffSearch.schSaleStoreName ? stuffSearch.schSaleStoreName : saleStoreName)
|
||||
setAddress(stuffSearch.schAddress ? stuffSearch.schAddress : address)
|
||||
setobjectName(stuffSearch.schObjectName ? stuffSearch.schObjectName : objectName)
|
||||
setDispCompanyName(stuffSearch.schDispCompanyName ? stuffSearch.schDispCompanyName : dispCompanyName)
|
||||
setReceiveUser(stuffSearch.schReceiveUser ? stuffSearch.schReceiveUser : receiveUser)
|
||||
setDateType(stuffSearch.schDateType ? stuffSearch.schDateType : dateType)
|
||||
}
|
||||
}, [stuffSearch])
|
||||
|
||||
useEffect(() => {
|
||||
@ -358,14 +566,14 @@ export default function StuffSearchCondition() {
|
||||
<div className="left-unit-box">
|
||||
<Link href="/management/stuff/tempdetail" scroll={false}>
|
||||
<button type="button" className="btn-origin navy mr5">
|
||||
{getMessage('stuff.search.btn1')}
|
||||
{getMessage('stuff.search.btn.register')}
|
||||
</button>
|
||||
</Link>
|
||||
<button type="button" className="btn-origin navy mr5" onClick={onSubmit}>
|
||||
{getMessage('stuff.search.btn2')}
|
||||
{getMessage('stuff.search.btn.search')}
|
||||
</button>
|
||||
<button type="button" className="btn-origin grey" onClick={resetRecoil}>
|
||||
{getMessage('stuff.search.btn3')}
|
||||
{getMessage('stuff.search.btn.reset')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -392,6 +600,7 @@ export default function StuffSearchCondition() {
|
||||
className="input-light"
|
||||
defaultValue={stuffSearch?.schObjectNo ? stuffSearch.schObjectNo : objectNo}
|
||||
onChange={() => {
|
||||
stuffSearch.schObjectNo = objectNoRef.current.value
|
||||
setObjectNo(objectNoRef.current.value)
|
||||
}}
|
||||
onKeyUp={handleByOnKeyUp}
|
||||
@ -407,6 +616,7 @@ export default function StuffSearchCondition() {
|
||||
className="input-light"
|
||||
defaultValue={stuffSearch?.schSaleStoreName ? stuffSearch.schSaleStoreName : saleStoreName}
|
||||
onChange={() => {
|
||||
stuffSearch.schSaleStoreName = saleStoreNameRef.current.value
|
||||
setSaleStoreName(saleStoreNameRef.current.value)
|
||||
}}
|
||||
onKeyUp={handleByOnKeyUp}
|
||||
@ -422,6 +632,7 @@ export default function StuffSearchCondition() {
|
||||
className="input-light"
|
||||
defaultValue={stuffSearch?.schAddress ? stuffSearch.schAddress : address}
|
||||
onChange={() => {
|
||||
stuffSearch.schAddress = addressRef.current.value
|
||||
setAddress(addressRef.current.value)
|
||||
}}
|
||||
onKeyUp={handleByOnKeyUp}
|
||||
@ -437,6 +648,7 @@ export default function StuffSearchCondition() {
|
||||
className="input-light"
|
||||
defaultValue={stuffSearch?.schDispCompanyName ? stuffSearch.schDispCompanyName : dispCompanyName}
|
||||
onChange={() => {
|
||||
stuffSearch.schDispCompanyName = dispCompanyNameRef.current.value
|
||||
setDispCompanyName(dispCompanyNameRef.current.value)
|
||||
}}
|
||||
onKeyUp={handleByOnKeyUp}
|
||||
@ -454,6 +666,7 @@ export default function StuffSearchCondition() {
|
||||
className="input-light"
|
||||
defaultValue={stuffSearch?.schObjectName ? stuffSearch.schObjectName : objectName}
|
||||
onChange={() => {
|
||||
stuffSearch.schObjectName = objectNameRef.current.value
|
||||
setobjectName(objectNameRef.current.value)
|
||||
}}
|
||||
onKeyUp={handleByOnKeyUp}
|
||||
@ -469,6 +682,7 @@ export default function StuffSearchCondition() {
|
||||
ref={receiveUserRef}
|
||||
defaultValue={stuffSearch?.schReceiveUser ? stuffSearch.schReceiveUser : receiveUser}
|
||||
onChange={() => {
|
||||
stuffSearch.schReceiveUser = receiveUserRef.current.value
|
||||
setReceiveUser(receiveUserRef.current.value)
|
||||
}}
|
||||
onKeyUp={handleByOnKeyUp}
|
||||
@ -500,7 +714,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 +747,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 +824,11 @@ export default function StuffSearchCondition() {
|
||||
type="radio"
|
||||
name="radio_ptype"
|
||||
id="radio_u"
|
||||
checked={dateType === 'U' ? true : false}
|
||||
defaultChecked={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 +838,11 @@ export default function StuffSearchCondition() {
|
||||
type="radio"
|
||||
name="radio_ptype"
|
||||
id="radio_r"
|
||||
checked={dateType === 'R' ? true : false}
|
||||
defaultChecked={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>
|
||||
|
||||
@ -27,10 +27,10 @@ export default function StuffSubHeader({ type }) {
|
||||
|
||||
const param = {
|
||||
pid: '1',
|
||||
objectNo: objectNo,
|
||||
}
|
||||
//확인필요
|
||||
const url = `/floor-plan?${queryStringFormatter(param)}`
|
||||
console.log(url)
|
||||
|
||||
router.push(url)
|
||||
}
|
||||
|
||||
@ -107,7 +107,7 @@ export default function StuffSubHeader({ type }) {
|
||||
<span>{getMessage('header.menus.management')}</span>
|
||||
</li>
|
||||
<li className="location-item">
|
||||
<span>{getMessage('header.menus.management.newStuff')}</span>
|
||||
<span>{getMessage('header.menus.management.detail')}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -5,8 +5,9 @@ import { Bar } from 'react-chartjs-2'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
import { useEffect, useState, useRef } from 'react'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { useRecoilValue, useRecoilState } from 'recoil'
|
||||
import { floorPlanObjectState } from '@/store/floorPlanObjectAtom'
|
||||
import { pwrGnrSimTypeState } from '@/store/simulatorAtom'
|
||||
|
||||
import { useAxios } from '@/hooks/useAxios'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
@ -41,6 +42,7 @@ export default function Simulator() {
|
||||
|
||||
// 차트 관련
|
||||
const [chartData, setChartData] = useState([])
|
||||
|
||||
const data = {
|
||||
labels: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
|
||||
datasets: [
|
||||
@ -105,8 +107,10 @@ export default function Simulator() {
|
||||
useEffect(() => {
|
||||
if (objectNo) {
|
||||
fetchObjectDetail(objectNo)
|
||||
fetchSimulatorNotice()
|
||||
setPwrGnrSimType('D')
|
||||
setPwrRecoil({ ...pwrRecoil, type: 'D' })
|
||||
}
|
||||
fetchSimulatorNotice()
|
||||
}, [objectNo, plan])
|
||||
|
||||
// 물건 상세 정보 조회
|
||||
@ -118,13 +122,30 @@ export default function Simulator() {
|
||||
// 파워컨디셔너 조회
|
||||
const [pcsInfoList, setPcsInfoList] = useState([])
|
||||
|
||||
// 타입별 list 조회
|
||||
const [hatsudenryouAll, setHatsudenryouAll] = useState([])
|
||||
const [hatsudenryouAllSnow, setHatsudenryouAllSnow] = useState([])
|
||||
const [hatsudenryouPeakcutAll, setHatsudenryouPeakcutAll] = useState([])
|
||||
const [hatsudenryouPeakcutAllSnow, setHatsudenryouPeakcutAllSnow] = useState([])
|
||||
|
||||
const fetchObjectDetail = async (objectNo) => {
|
||||
const apiUrl = `/api/pwrGnrSimulation/calculations?objectNo=${objectNo}&planNo=${plan?.id}`
|
||||
|
||||
const resultData = await get({ url: apiUrl })
|
||||
if (resultData) {
|
||||
setObjectDetail(resultData)
|
||||
if (resultData.frcPwrGnrList) {
|
||||
setChartData(resultData.frcPwrGnrList)
|
||||
if (resultData.hatsudenryouAll) {
|
||||
setHatsudenryouAll(resultData.hatsudenryouAll)
|
||||
}
|
||||
if (resultData.hatsudenryouAllSnow) {
|
||||
setHatsudenryouAllSnow(resultData.hatsudenryouAllSnow)
|
||||
}
|
||||
if (resultData.hatsudenryouPeakcutAll) {
|
||||
setHatsudenryouPeakcutAll(resultData.hatsudenryouPeakcutAll)
|
||||
}
|
||||
if (resultData.hatsudenryouPeakcutAllSnow) {
|
||||
setHatsudenryouPeakcutAllSnow(resultData.hatsudenryouPeakcutAllSnow)
|
||||
setChartData(resultData.hatsudenryouPeakcutAllSnow)
|
||||
}
|
||||
if (resultData.pcsList) {
|
||||
setPcsInfoList(resultData.pcsList)
|
||||
@ -148,6 +169,28 @@ export default function Simulator() {
|
||||
})
|
||||
}
|
||||
|
||||
// 차트 데이터 변경 시, list type 셋팅
|
||||
const [pwrGnrSimType, setPwrGnrSimType] = useState('D')
|
||||
const [pwrRecoil, setPwrRecoil] = useRecoilState(pwrGnrSimTypeState)
|
||||
|
||||
const handleChartChangeData = (type) => {
|
||||
setPwrRecoil({ ...pwrRecoil, type: type })
|
||||
switch (type) {
|
||||
case 'A':
|
||||
setChartData(hatsudenryouAll)
|
||||
break
|
||||
case 'B':
|
||||
setChartData(hatsudenryouAllSnow)
|
||||
break
|
||||
case 'C':
|
||||
setChartData(hatsudenryouPeakcutAll)
|
||||
break
|
||||
case 'D':
|
||||
setChartData(hatsudenryouPeakcutAllSnow)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="sub-content estimate">
|
||||
<div className="sub-content-inner">
|
||||
@ -158,23 +201,25 @@ export default function Simulator() {
|
||||
<div className="estimate-box">
|
||||
<div className="estimate-tit">{getMessage('simulator.title.sub1')}</div>
|
||||
<div className="estimate-name">
|
||||
{objectDetail.objectNo} (Plan No: {objectDetail.planNo})
|
||||
{objectDetail.objectNo} (Plan No: {plan?.id})
|
||||
</div>
|
||||
</div>
|
||||
{/* 작성일 */}
|
||||
<div className="estimate-box">
|
||||
<div className="estimate-tit">{getMessage('simulator.title.sub2')}</div>
|
||||
<div className="estimate-name">{`${dayjs(objectDetail.drawingEstimateCreateDate).format('YYYY.MM.DD')}`}</div>
|
||||
<div className="estimate-name">
|
||||
{objectDetail.drawingEstimateCreateDate ? `${dayjs(objectDetail.drawingEstimateCreateDate).format('YYYY.MM.DD')}` : ''}
|
||||
</div>
|
||||
</div>
|
||||
{/* 시스템용량 */}
|
||||
<div className="estimate-box">
|
||||
<div className="estimate-tit">{getMessage('simulator.title.sub3')}</div>
|
||||
<div className="estimate-name">{convertNumberToPriceDecimal(objectDetail.capacity)}kW</div>
|
||||
<div className="estimate-name">{objectDetail.capacity ? `${convertNumberToPriceDecimal(objectDetail.capacity)}kW` : ''}</div>
|
||||
</div>
|
||||
{/* 연간예측발전량 */}
|
||||
<div className="estimate-box">
|
||||
<div className="estimate-tit">{getMessage('simulator.title.sub4')}</div>
|
||||
<div className="estimate-name">{convertNumberToPriceDecimal(objectDetail.anlFrcsGnrt)}</div>
|
||||
<div className="estimate-name">{chartData[chartData.length - 1]}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="estimate-list-wrap">
|
||||
@ -206,6 +251,24 @@ export default function Simulator() {
|
||||
<div className="chart-inner">
|
||||
<div className="sub-table-box">
|
||||
<div className="chart-box">
|
||||
{/* chart */}
|
||||
<div className="select-wrap">
|
||||
<select
|
||||
style={{ width: '30%' }}
|
||||
className="select-light"
|
||||
value={pwrGnrSimType}
|
||||
defaultValue={`D`}
|
||||
onChange={(e) => {
|
||||
handleChartChangeData(e.target.value)
|
||||
setPwrGnrSimType(e.target.value)
|
||||
}}
|
||||
>
|
||||
<option value={`A`}>積雪考慮なし(ピークカットなし発電量)</option>
|
||||
<option value={`B`}>積雪考慮なし(ピークカットあり発電量)</option>
|
||||
<option value={`C`}>積雪考慮あり(ピークカットなし発電量)</option>
|
||||
<option value={`D`}>積雪考慮あり(ピークカットあり発電量)</option>
|
||||
</select>
|
||||
</div>
|
||||
<Bar ref={chartRef} data={data} options={options} />
|
||||
</div>
|
||||
<div className="table-box-title-wrap">
|
||||
@ -237,7 +300,7 @@ export default function Simulator() {
|
||||
{chartData.length > 0 ? (
|
||||
<tr>
|
||||
{chartData.map((data) => (
|
||||
<td key={data}>{convertNumberToPriceDecimal(data)}</td>
|
||||
<td key={data}>{data}</td>
|
||||
))}
|
||||
</tr>
|
||||
) : (
|
||||
|
||||
@ -40,19 +40,19 @@ export function useCanvasConfigInitialize() {
|
||||
const flowTexts = canvas.getObjects().filter((obj) => obj.name === 'flowText')
|
||||
if (basicSetting.roofAngleSet === 'slope') {
|
||||
offsetTexts.forEach((obj) => {
|
||||
obj.set({ text: `${obj.originText}-∠${obj.pitch}${angleUnit}` })
|
||||
obj.set({ text: `${!obj.originText ? '' : obj.originText + '-'}∠${obj.pitch}${angleUnit}` })
|
||||
})
|
||||
flowTexts.forEach((obj) => {
|
||||
obj.set({ text: `${obj.originText}-∠${obj.pitch}${pitchText}` })
|
||||
obj.set({ text: `${!obj.originText ? '' : obj.originText + '-'}∠${obj.pitch}${pitchText}` })
|
||||
})
|
||||
}
|
||||
|
||||
if (basicSetting.roofAngleSet === 'flat') {
|
||||
offsetTexts.forEach((obj) => {
|
||||
obj.set({ text: `${obj.originText}-∠${getDegreeByChon(obj.pitch)}${angleUnit}` })
|
||||
obj.set({ text: `${!obj.originText ? '' : obj.originText + '-'}∠${getDegreeByChon(obj.pitch)}${angleUnit}` })
|
||||
})
|
||||
flowTexts.forEach((obj) => {
|
||||
obj.set({ text: `${obj.originText}-∠${getDegreeByChon(obj.pitch)}${pitchText}` })
|
||||
obj.set({ text: `${!obj.originText ? '' : obj.originText + '-'}∠${getDegreeByChon(obj.pitch)}${pitchText}` })
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -1,8 +1,30 @@
|
||||
import { menuNumberState } from '@/store/menuAtom'
|
||||
import { useRecoilState } from 'recoil'
|
||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||
import { useEffect } from 'react'
|
||||
import { canvasState } from '@/store/canvasAtom'
|
||||
import { usePolygon } from '@/hooks/usePolygon'
|
||||
import { POLYGON_TYPE } from '@/common/common'
|
||||
|
||||
export const useCanvasMenu = () => {
|
||||
const [menuNumber, setMenuNumber] = useRecoilState(menuNumberState)
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
const { drawDirectionArrow } = usePolygon()
|
||||
|
||||
useEffect(() => {
|
||||
/*
|
||||
* 모듈,회로 구성을 벗어나면 방향 표시 초기화 필요
|
||||
* */
|
||||
if (!canvas) return
|
||||
if (![4, 5].includes(menuNumber)) {
|
||||
canvas
|
||||
.getObjects()
|
||||
.filter((obj) => obj.name === POLYGON_TYPE.ROOF)
|
||||
.forEach((obj) => {
|
||||
obj.set('moduleCompass', null)
|
||||
drawDirectionArrow(obj)
|
||||
})
|
||||
}
|
||||
}, [menuNumber])
|
||||
|
||||
return {
|
||||
menuNumber,
|
||||
|
||||
@ -1,21 +1,20 @@
|
||||
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)
|
||||
const wordDisplay = useRecoilValue(wordDisplaySelector)
|
||||
const { addCanvasMouseEventListener, addDocumentEventListener, initEvent } = useEvent()
|
||||
// const { addCanvasMouseEventListener, addDocumentEventListener, initEvent } = useContext(EventContext)
|
||||
const dimensionSettings = useRecoilValue(dimensionLineSettingsState)
|
||||
const dimensionLineTextFont = useRecoilValue(fontSelector('dimensionLineText'))
|
||||
const commonTextFont = useRecoilValue(fontSelector('commonText'))
|
||||
@ -23,7 +22,7 @@ export function useCommonUtils() {
|
||||
const { addPopup } = usePopup()
|
||||
|
||||
useEffect(() => {
|
||||
initEvent()
|
||||
// initEvent()
|
||||
if (commonUtils.text) {
|
||||
commonTextMode()
|
||||
} else if (commonUtils.dimension) {
|
||||
@ -39,6 +38,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 +49,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 +263,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 +712,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,
|
||||
|
||||
@ -12,13 +12,13 @@ export function useFont() {
|
||||
const circuitNumberText = useRecoilValue(fontSelector('circuitNumberText'))
|
||||
|
||||
useEffect(() => {
|
||||
if (canvas) {
|
||||
if (canvas && commonText.fontWeight.value) {
|
||||
const textObjs = canvas?.getObjects().filter((obj) => obj.name === 'commonText')
|
||||
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,
|
||||
})
|
||||
@ -28,13 +28,13 @@ export function useFont() {
|
||||
}, [commonText])
|
||||
|
||||
useEffect(() => {
|
||||
if (canvas) {
|
||||
if (canvas && dimensionLineText.fontWeight.value) {
|
||||
const textObjs = canvas?.getObjects().filter((obj) => obj.name === 'dimensionLineText')
|
||||
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,
|
||||
})
|
||||
@ -44,13 +44,13 @@ export function useFont() {
|
||||
}, [dimensionLineText])
|
||||
|
||||
useEffect(() => {
|
||||
if (canvas) {
|
||||
const textObjs = canvas.getObjects().filter((obj) => obj.name === 'flowText')
|
||||
if (canvas && flowText.fontWeight.value) {
|
||||
const textObjs = canvas?.getObjects().filter((obj) => obj.name === 'flowText')
|
||||
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,
|
||||
})
|
||||
@ -60,8 +60,8 @@ export function useFont() {
|
||||
}, [flowText])
|
||||
|
||||
useEffect(() => {
|
||||
if (canvas) {
|
||||
const textObjs = canvas.getObjects().filter((obj) => obj.name === 'lengthText')
|
||||
if (canvas && lengthText.fontWeight.value) {
|
||||
const textObjs = canvas?.getObjects().filter((obj) => obj.name === 'lengthText')
|
||||
textObjs.forEach((obj) => {
|
||||
obj.set({
|
||||
fontFamily: lengthText.fontFamily.value,
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
import { useRef, useState } from 'react'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { useRecoilState } from 'recoil'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
import { useSwal } from '@/hooks/useSwal'
|
||||
import { convertDwgToPng } from '@/lib/cadAction'
|
||||
import { useAxios } from '../useAxios'
|
||||
import { currentCanvasPlanState } from '@/store/canvasAtom'
|
||||
import { convertDwgToPng, writeImageBuffer } from '@/lib/fileAction'
|
||||
|
||||
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')
|
||||
@ -16,7 +16,7 @@ export default function useRefFiles() {
|
||||
const queryRef = useRef(null)
|
||||
|
||||
const { swalFire } = useSwal()
|
||||
const { get, promisePut } = useAxios()
|
||||
const { get, promisePut, promisePost } = useAxios()
|
||||
// const { currentCanvasPlan, setCurrentCanvasPlan } = usePlan()
|
||||
|
||||
/**
|
||||
@ -25,7 +25,11 @@ export default function useRefFiles() {
|
||||
*/
|
||||
const handleRefFile = (file) => {
|
||||
setRefImage(file)
|
||||
file.name.split('.').pop() === 'dwg' ? handleUploadRefFile(file) : () => {}
|
||||
/**
|
||||
* 파일 확장자가 dwg일 경우 변환하여 이미지로 저장
|
||||
* 파일 확장자가 이미지일 경우 이미지 저장
|
||||
*/
|
||||
file.name.split('.').pop() === 'dwg' ? handleUploadConvertRefFile(file) : handleUploadImageRefFile(file)
|
||||
// handleUploadRefFile(file)
|
||||
}
|
||||
|
||||
@ -58,22 +62,29 @@ export default function useRefFiles() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 현재 플랜이 변경되면 플랜 상태 저장
|
||||
* 이미지 파일 업로드
|
||||
* @param {*} file
|
||||
*/
|
||||
// useEffect(() => {
|
||||
// const handleCurrentPlan = async () => {
|
||||
// await promisePut({ url: '/api/canvas-management/canvas-statuses', data: currentCanvasPlan }).then((res) => {
|
||||
// console.log('🚀 ~ awaitpromisePut ~ res:', res)
|
||||
// })
|
||||
// }
|
||||
// handleCurrentPlan()
|
||||
// }, [currentCanvasPlan])
|
||||
const handleUploadImageRefFile = async (file) => {
|
||||
console.log('🚀 ~ handleUploadImageRefFile ~ file:', file)
|
||||
const formData = new FormData()
|
||||
formData.append('file', file)
|
||||
|
||||
const response = await fetch('http://localhost:3000/api/image-upload', {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
})
|
||||
|
||||
const result = await response.json()
|
||||
console.log('🚀 ~ handleUploadImageRefFile ~ res:', result)
|
||||
// writeImageBuffer(file)
|
||||
}
|
||||
|
||||
/**
|
||||
* RefFile이 캐드 도면 파일일 경우 변환하여 이미지로 저장
|
||||
* @param {*} file
|
||||
*/
|
||||
const handleUploadRefFile = async (file) => {
|
||||
const handleUploadConvertRefFile = async (file) => {
|
||||
const formData = new FormData()
|
||||
formData.append('file', file)
|
||||
|
||||
@ -95,6 +106,19 @@ export default function useRefFiles() {
|
||||
setRefFileMethod(e.target.value)
|
||||
}
|
||||
|
||||
/**
|
||||
* 현재 플랜이 변경되면 플랜 상태 저장
|
||||
*/
|
||||
useEffect(() => {
|
||||
console.log('🚀 ~ useRefFiles ~ currentCanvasPlan:', currentCanvasPlan)
|
||||
// const handleCurrentPlan = async () => {
|
||||
// await promisePut({ url: '/api/canvas-management/canvas-statuses', data: currentCanvasPlan }).then((res) => {
|
||||
// console.log('🚀 ~ awaitpromisePut ~ res:', res)
|
||||
// })
|
||||
// }
|
||||
// handleCurrentPlan()
|
||||
}, [currentCanvasPlan])
|
||||
|
||||
return {
|
||||
refImage,
|
||||
queryRef,
|
||||
|
||||
30
src/hooks/contextpopup/useFlowDirectionSetting.js
Normal file
30
src/hooks/contextpopup/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 }
|
||||
}
|
||||
@ -6,34 +6,19 @@ import { estimateState, floorPlanObjectState } from '@/store/floorPlanObjectAtom
|
||||
import { isObjectNotEmpty } from '@/util/common-utils'
|
||||
import { SessionContext } from '@/app/SessionProvider'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
|
||||
const reducer = (prevState, nextState) => {
|
||||
return { ...prevState, ...nextState }
|
||||
}
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { FloorPlanContext } from '@/app/floor-plan/FloorPlanProvider'
|
||||
|
||||
// Constants
|
||||
const ESTIMATE_API_ENDPOINT = '/api/estimates' // API 엔드포인트 정의
|
||||
|
||||
const defaultEstimateData = {
|
||||
estimateDate: new Date(), //견적일
|
||||
charger: '', //담당자
|
||||
objectName: '', //안건명
|
||||
objectNameOmit: '', //경칭코드
|
||||
estimateType: 'YJOD', //주문분류
|
||||
remarks: '', //비고
|
||||
estimateOption: '', //견적특이사항
|
||||
itemList: [],
|
||||
fileList: [],
|
||||
fileFlg: '0', //후일 자료 제출 (체크 1 노체크 0)
|
||||
priceCd: '',
|
||||
}
|
||||
const ESTIMATE_API_ENDPOINT = '/api/estimate' // API 엔드포인트 정의
|
||||
|
||||
// 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) => {
|
||||
const router = useRouter()
|
||||
const { session } = useContext(SessionContext)
|
||||
const globalLocaleState = useRecoilValue(globalLocaleStore)
|
||||
const objectRecoil = useRecoilValue(floorPlanObjectState)
|
||||
@ -41,25 +26,32 @@ export const useEstimateController = (planNo) => {
|
||||
|
||||
const { getMessage } = useMessage()
|
||||
|
||||
const { get, post, promisePost } = useAxios(globalLocaleState)
|
||||
const { promiseGet, post, promisePost } = useAxios(globalLocaleState)
|
||||
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const [state, setState] = useReducer(reducer, defaultEstimateData)
|
||||
const { estimateContextState, setEstimateContextState } = useContext(FloorPlanContext)
|
||||
|
||||
useEffect(() => {
|
||||
if (!isLoading) {
|
||||
if (planNo && !isLoading) {
|
||||
if (objectRecoil.floorPlanObjectNo && planNo) {
|
||||
fetchSetting()
|
||||
fetchSetting(objectRecoil.floorPlanObjectNo, planNo)
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
// 상세 조회
|
||||
const fetchSetting = async () => {
|
||||
const fetchSetting = async (objectNo, planNo) => {
|
||||
try {
|
||||
await get({ url: `/api/estimate/${objectRecoil.floorPlanObjectNo}/${planNo}/detail` }).then((res) => {
|
||||
if (isObjectNotEmpty(res)) {
|
||||
setState(res)
|
||||
await promiseGet({ url: `/api/estimate/${objectNo}/${planNo}/detail` }).then((res) => {
|
||||
if (res.status === 200) {
|
||||
if (isObjectNotEmpty(res.data)) {
|
||||
if (res.data.itemList.length > 0) {
|
||||
res.data.itemList.map((item) => {
|
||||
item.delFlg = '0'
|
||||
})
|
||||
}
|
||||
setEstimateContextState(res.data)
|
||||
}
|
||||
}
|
||||
})
|
||||
setIsLoading(true)
|
||||
@ -69,95 +61,239 @@ export const useEstimateController = (planNo) => {
|
||||
}
|
||||
}
|
||||
|
||||
const updateItem = (itemId, updates) => {
|
||||
setState({
|
||||
itemList: updateItemInList(state.itemList, itemId, updates),
|
||||
const updateItem = (dispOrder, updates) => {
|
||||
setEstimateContextState({
|
||||
itemList: updateItemInList(estimateContextState.itemList, dispOrder, updates),
|
||||
})
|
||||
}
|
||||
|
||||
const addItem = () => {
|
||||
const newItemDispOrder = (Math.max(...state.itemList.map((item) => item.dispOrder)) + 1) * 100
|
||||
setState({
|
||||
let newItemDispOrder = Math.max(...estimateContextState.itemList.map((item) => item.dispOrder))
|
||||
newItemDispOrder = (Math.floor(newItemDispOrder / 100) + 1) * 100
|
||||
setEstimateContextState({
|
||||
itemList: [
|
||||
...state.itemList,
|
||||
...estimateContextState.itemList,
|
||||
{
|
||||
dispOrder: newItemDispOrder,
|
||||
objectNo: objectRecoil.floorPlanObjectNo,
|
||||
planNo: planNo,
|
||||
dispOrder: newItemDispOrder.toString(),
|
||||
itemId: '', //제품번호
|
||||
itemNo: '', //형명
|
||||
itemName: '',
|
||||
itemNo: '',
|
||||
itemName: '', //형명
|
||||
amount: '', //수량
|
||||
unitPrice: '0',
|
||||
unit: '', //단위
|
||||
salePrice: '0', //단가
|
||||
saleTotPrice: '0', //금액(부가세별도)
|
||||
salePrice: '', //단가
|
||||
saleTotPrice: '', //금액(부가세별도)
|
||||
itemChangeFlg: '1', //추가시 체인지플래그 1로
|
||||
partAdd: '1', //NEW 체인지 플래그
|
||||
delFlg: '0', //삭제 플래그 0 삭제하면 1
|
||||
addFlg: true,
|
||||
},
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setEstimateData({ ...state, userId: session.userId, sapSalesStoreCd: session.custCd })
|
||||
}, [state])
|
||||
setEstimateData({ ...estimateContextState, userId: session.userId, sapSalesStoreCd: session.custCd })
|
||||
}, [estimateContextState])
|
||||
|
||||
// 첨부파일 다운로드
|
||||
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)
|
||||
let fileFlg = true
|
||||
let itemFlg = true
|
||||
if (estimateData.charger.trim().length === 0) {
|
||||
flag = false
|
||||
return alert(getMessage('estimate.detail.save.requiredCharger'))
|
||||
}
|
||||
|
||||
if (estimateData.objectName.trim().length === 0) {
|
||||
flag = false
|
||||
return alert(getMessage('estimate.detail.save.requiredObjectName'))
|
||||
}
|
||||
|
||||
if (isNaN(Date.parse(estimateData.estimateDate))) {
|
||||
flag = false
|
||||
return alert(getMessage('estimate.detail.save.requiredEstimateDate'))
|
||||
}
|
||||
|
||||
//첨부파일을 첨부안했는데
|
||||
//아이템 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 (flag) {
|
||||
if (estimateData.fileList.length < 1) {
|
||||
if (estimateData.itemList.length > 1) {
|
||||
estimateData.itemList.map((row) => {
|
||||
if (row.delFlg === '0') {
|
||||
if (row.fileUploadFlg === '1') {
|
||||
if (fileFlg) {
|
||||
if (estimateData.fileFlg === '0') {
|
||||
fileFlg = false
|
||||
return alert(getMessage('estimate.detail.save.requiredFileUpload'))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fileFlg) {
|
||||
estimateData.itemList.map((item) => {
|
||||
if (item.delFlg === '0') {
|
||||
item.amount = item.amount?.replaceAll(',', '')
|
||||
item.salePrice = parseFloat(item.salePrice?.replaceAll(',', '')).toFixed(2)
|
||||
item.saleTotPrice = parseFloat(item.saleTotPrice?.replaceAll(',', '')).toFixed(2)
|
||||
|
||||
if (!item.paDispOrder) {
|
||||
if (itemFlg) {
|
||||
if (isNaN(item.amount)) {
|
||||
item.amount = '0'
|
||||
}
|
||||
|
||||
if (item.amount < 1) {
|
||||
itemFlg = false
|
||||
return alert(getMessage('estimate.detail.save.requiredAmount'))
|
||||
}
|
||||
|
||||
if (estimateData.estimateType !== 'YJSS') {
|
||||
if (isNaN(item.salePrice)) {
|
||||
item.salePrice = '0'
|
||||
}
|
||||
|
||||
if (item.salePrice < 1) {
|
||||
itemFlg = false
|
||||
return alert(getMessage('estimate.detail.save.requiredSalePrice'))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
if (flag) {
|
||||
//1. 첨부파일 저장
|
||||
|
||||
if (flag && fileFlg && itemFlg) {
|
||||
//1. 첨부파일 저장시작
|
||||
const formData = new FormData()
|
||||
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 post({ url: '/api/file/fileUpload', data: formData })
|
||||
//첨부파일저장끝
|
||||
|
||||
//2. 상세데이터 저장
|
||||
return
|
||||
await promisePost({ url: `${ESTIMATE_API_ENDPOINT}/save-estimate`, data: estimateData }).then((res) => {
|
||||
if (res) {
|
||||
alert(getMessage('estimate.detail.save.alertMsg'))
|
||||
//제품라인 추가했는데 아이템 안고르고 저장하면itemId=''은 날리고 나머지 저장하기
|
||||
estimateData.itemList = estimateData.itemList.filter((item) => item.itemId !== '')
|
||||
|
||||
let delCnt = 0
|
||||
estimateData.itemList.map((item) => {
|
||||
if (item.delFlg === '1') {
|
||||
delCnt++
|
||||
}
|
||||
})
|
||||
if (delCnt === estimateData.itemList.length) {
|
||||
return alert(getMessage('estimate.detail.save.requiredItem'))
|
||||
}
|
||||
|
||||
// console.log('최종 아이템 정보::;', estimateData.itemList)
|
||||
let option = []
|
||||
estimateData.itemList.forEach((item) => {
|
||||
if (item.specialNoteCd) {
|
||||
let split2 = item.specialNoteCd.split('、')
|
||||
option = option.concat(split2)
|
||||
}
|
||||
})
|
||||
|
||||
// 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
|
||||
// }
|
||||
// console.log('아이템리스트::', estimateData.itemList)
|
||||
// console.log('최종 정보::;', estimateData)
|
||||
//2. 상세데이터 저장
|
||||
// return
|
||||
try {
|
||||
await promisePost({ url: `${ESTIMATE_API_ENDPOINT}/save-estimate`, data: estimateData }).then((res) => {
|
||||
if (res.status === 201) {
|
||||
alert(getMessage('estimate.detail.save.alertMsg'))
|
||||
//어디로 보낼지
|
||||
fetchSetting(objectRecoil.floorPlanObjectNo, estimateData.planNo)
|
||||
}
|
||||
})
|
||||
} catch (e) {
|
||||
console.log('error::::::::::::', e.response.data.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 견적서 복사버튼
|
||||
* (견적서 번호(estimateData.docNo)가 생성된 이후 버튼 활성화 )
|
||||
* T01관리자 계정 및 1차판매점에게만 제공
|
||||
*/
|
||||
const handleEstimateCopy = async (sendPlanNo, copyReceiveUser, saleStoreId, otherSaleStoreId) => {
|
||||
if (saleStoreId === '') {
|
||||
return alert(getMessage('estimate.detail.productFeaturesPopup.requiredStoreId'))
|
||||
}
|
||||
|
||||
if (copyReceiveUser.trim().length === 0) {
|
||||
return alert(getMessage('estimate.detail.productFeaturesPopup.requiredReceiveUser'))
|
||||
}
|
||||
const params = {
|
||||
saleStoreId: session.storeId,
|
||||
sapSalesStoreCd: session.custCd,
|
||||
objectNo: objectRecoil.floorPlanObjectNo,
|
||||
planNo: sendPlanNo,
|
||||
copySaleStoreId: otherSaleStoreId ? otherSaleStoreId : saleStoreId,
|
||||
copyReceiveUser: copyReceiveUser,
|
||||
userId: session.userId,
|
||||
}
|
||||
|
||||
// return
|
||||
await promisePost({ url: '/api/estimate/save-estimate-copy', data: params }).then((res) => {
|
||||
if (res.status === 201) {
|
||||
if (isObjectNotEmpty(res.data)) {
|
||||
let newObjectNo = res.data.objectNo
|
||||
alert(getMessage('estimate.detail.estimateCopyPopup.copy.alertMessage'))
|
||||
router.push(`/management/stuff/detail?objectNo=${newObjectNo.toString()}`, { scroll: false })
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
state,
|
||||
setState,
|
||||
estimateContextState,
|
||||
setEstimateContextState,
|
||||
updateItem,
|
||||
addItem,
|
||||
handleEstimateSubmit,
|
||||
fetchSetting,
|
||||
handleEstimateFileDownload,
|
||||
handleEstimateCopy,
|
||||
}
|
||||
}
|
||||
|
||||
94
src/hooks/main/useMainContentsController.js
Normal file
94
src/hooks/main/useMainContentsController.js
Normal file
@ -0,0 +1,94 @@
|
||||
'use client'
|
||||
|
||||
import { useContext, useEffect } from 'react'
|
||||
import { useAxios } from '../useAxios'
|
||||
import { SessionContext } from '@/app/SessionProvider'
|
||||
import { QcastContext } from '@/app/QcastProvider'
|
||||
|
||||
export const useMainContentsController = () => {
|
||||
const { session } = useContext(SessionContext)
|
||||
const { promiseGet } = useAxios()
|
||||
const { setQcastState, setIsGlobalLoading } = useContext(QcastContext)
|
||||
|
||||
useEffect(() => {
|
||||
setIsGlobalLoading(true)
|
||||
}, [])
|
||||
|
||||
/**
|
||||
* main search area
|
||||
*/
|
||||
// const [saleStoreId, setSaleStoreId] = useState('')
|
||||
// const [saleStoreName, setSaleStoreName] = useState('')
|
||||
|
||||
/**
|
||||
* main contents area
|
||||
*/
|
||||
// const [objectList, setObjectList] = useState([])
|
||||
// const [businessCharger, setBusinessCharger] = useState(null)
|
||||
// const [businessChargerMail, setBusinessChargerMail] = useState(null)
|
||||
|
||||
/**
|
||||
* 최근 물건 목록 조회
|
||||
*/
|
||||
const fetchObjectList = async () => {
|
||||
try {
|
||||
const apiUrl = `/api/main-page/object/${session?.storeId}/list`
|
||||
await promiseGet({
|
||||
url: apiUrl,
|
||||
}).then((res) => {
|
||||
if (res.status === 200) {
|
||||
// setSaleStoreId(res.data.saleStoreId)
|
||||
// setSaleStoreName(res.data.saleStoreName)
|
||||
|
||||
// setObjectList(res.data.objectList)
|
||||
// setBusinessCharger(res.data.businessCharger)
|
||||
// setBusinessChargerMail(res.data.businessChargerMail)
|
||||
setQcastState({
|
||||
saleStoreId: res.data.saleStoreId,
|
||||
saleStoreName: res.data.saleStoreName,
|
||||
objectList: res.data.objectList,
|
||||
businessCharger: res.data.businessCharger,
|
||||
businessChargerMail: res.data.businessChargerMail,
|
||||
})
|
||||
setIsGlobalLoading(false)
|
||||
} else {
|
||||
// setSaleStoreId('')
|
||||
// setSaleStoreName('')
|
||||
|
||||
// setObjectList([])
|
||||
// setBusinessCharger(null)
|
||||
// setBusinessChargerMail(null)
|
||||
setQcastState({
|
||||
saleStoreId: '',
|
||||
saleStoreName: '',
|
||||
objectList: [],
|
||||
businessCharger: null,
|
||||
businessChargerMail: null,
|
||||
})
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('MAIN API fetching error:', error)
|
||||
}
|
||||
}
|
||||
|
||||
const initObjectList = () => {
|
||||
setQcastState({
|
||||
saleStoreId: '',
|
||||
saleStoreName: '',
|
||||
objectList: [],
|
||||
businessCharger: null,
|
||||
businessChargerMail: null,
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
// saleStoreId,
|
||||
// saleStoreName,
|
||||
// objectList,
|
||||
// businessCharger,
|
||||
// businessChargerMail,
|
||||
fetchObjectList,
|
||||
initObjectList,
|
||||
}
|
||||
}
|
||||
963
src/hooks/module/useModuleBasicSetting.js
Normal file
963
src/hooks/module/useModuleBasicSetting.js
Normal file
@ -0,0 +1,963 @@
|
||||
import { useContext, 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 { QLine } from '@/components/fabric/QLine'
|
||||
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'
|
||||
import { EventContext } from '@/app/floor-plan/EventProvider'
|
||||
|
||||
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()
|
||||
// const { addTargetMouseEventListener, addCanvasMouseEventListener, initEvent } = useContext(EventContext)
|
||||
const [flowModuleLine, setFlowModuleLine] = useState({})
|
||||
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
|
||||
//모듈설치영역?? 생성
|
||||
let 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,
|
||||
flipX: roof.flipX,
|
||||
flipY: roof.flipY,
|
||||
})
|
||||
|
||||
setupSurface.setViewLengthText(false)
|
||||
|
||||
canvas.add(setupSurface)
|
||||
|
||||
if (setupSurface.flowDirection === 'south' || setupSurface.flowDirection === 'north') {
|
||||
setFlowModuleLine(bottomTopFlowLine(setupSurface))
|
||||
} else {
|
||||
setFlowModuleLine(leftRightFlowLine(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 = (placementRef) => {
|
||||
const isChidori = placementRef.isChidori.current
|
||||
const setupLocation = placementRef.setupLocation.current
|
||||
const isMaxSetup = placementRef.isMaxSetup.current
|
||||
|
||||
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('기존 모듈은 제거됩니다.')
|
||||
moduleIsSetup.forEach((module) => {
|
||||
canvas?.remove(module)
|
||||
})
|
||||
}
|
||||
|
||||
notSelectedTrestlePolygons.forEach((obj) => {
|
||||
if (obj.modules) {
|
||||
obj.modules.forEach((module) => {
|
||||
canvas?.remove(module)
|
||||
})
|
||||
obj.modules = []
|
||||
}
|
||||
})
|
||||
|
||||
const moduleSetupArray = []
|
||||
moduleSetupSurfaces.forEach((moduleSetupSurface, index) => {
|
||||
moduleSetupSurface.fire('mousedown')
|
||||
|
||||
const surfaceMaxLines = findSetupSurfaceMaxLines(moduleSetupSurface)
|
||||
|
||||
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])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let width = maxLengthLine.flowDirection === 'east' || maxLengthLine.flowDirection === 'west' ? 172.2 : 113.4
|
||||
let height = maxLengthLine.flowDirection === 'east' || maxLengthLine.flowDirection === 'west' ? 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
|
||||
}
|
||||
|
||||
let square
|
||||
let startPoint, endPoint
|
||||
|
||||
if (setupLocation === 'eaves') {
|
||||
if (moduleSetupSurface.flowDirection === 'south') {
|
||||
startPoint = flowModuleLine.find((obj) => obj.target === 'bottom')
|
||||
endPoint = flowModuleLine.find((obj) => obj.target === 'top')
|
||||
const totalHeight = endPoint.y1 - startPoint.y1
|
||||
const diffHeight = Math.abs(totalHeight / height)
|
||||
let leftMargin = 0
|
||||
let bottomMargin = 0
|
||||
|
||||
for (let i = 0; i < diffHeight; i++) {
|
||||
leftMargin = i === 0 ? 1 : 0
|
||||
bottomMargin = i === 0 ? 0 : 1
|
||||
|
||||
square = [
|
||||
[startPoint.x1 + leftMargin, startPoint.y1 - height - bottomMargin],
|
||||
[startPoint.x1 + leftMargin, startPoint.y1 - bottomMargin],
|
||||
[startPoint.x1 + leftMargin + width, startPoint.y1 - bottomMargin],
|
||||
[startPoint.x1 + leftMargin + width, startPoint.y1 - height - bottomMargin],
|
||||
[startPoint.x1 + leftMargin, startPoint.y1 - height - bottomMargin],
|
||||
]
|
||||
|
||||
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) //도머가 여러개일수있으므로 겹치는게 있다면...
|
||||
})
|
||||
if (isDisjoint) {
|
||||
const tempModule = new QPolygon(points, {
|
||||
fill: '#BFFD9F',
|
||||
stroke: 'black',
|
||||
strokeWidth: 0.1,
|
||||
selectable: true, // 선택 가능하게 설정
|
||||
lockMovementX: false, // X 축 이동 잠금
|
||||
lockMovementY: false, // Y 축 이동 잠금
|
||||
lockRotation: false, // 회전 잠금
|
||||
lockScalingX: false, // X 축 크기 조정 잠금
|
||||
lockScalingY: false, // Y 축 크기 조정 잠금
|
||||
opacity: 0.8,
|
||||
parentId: moduleSetupSurface.parentId,
|
||||
name: 'module',
|
||||
})
|
||||
tempModule.setViewLengthText(false)
|
||||
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,
|
||||
name: 'module',
|
||||
})
|
||||
canvas?.add(tempModule)
|
||||
moduleSetupArray.push(tempModule)
|
||||
}
|
||||
startPoint = { x1: points[0].x, y1: points[0].y, x2: points[3].x, y2: points[3].y }
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (setupLocation === 'ridge') {
|
||||
} else {
|
||||
}
|
||||
|
||||
moduleSetupSurface.set({ modules: moduleSetupArray })
|
||||
})
|
||||
|
||||
setModuleIsSetup(moduleSetupArray)
|
||||
|
||||
console.log(calculateForApi(moduleSetupArray))
|
||||
}
|
||||
|
||||
const calculateForApi = (moduleSetupArray) => {
|
||||
const centerPoints = []
|
||||
moduleSetupArray.forEach((module, index) => {
|
||||
module.tempIndex = index
|
||||
const { x, y } = module.getCenterPoint()
|
||||
const { width, height } = module
|
||||
centerPoints.push({ x, y, width, height, index })
|
||||
const circle = new fabric.Circle({
|
||||
radius: 5,
|
||||
fill: 'red',
|
||||
name: 'redCircle',
|
||||
left: x - 5,
|
||||
top: y - 5,
|
||||
index: index,
|
||||
selectable: false,
|
||||
})
|
||||
canvas.add(circle)
|
||||
})
|
||||
|
||||
//완전 노출 하면
|
||||
let exposedBottom = 0
|
||||
// 반 노출 하면
|
||||
let exposedHalfBottom = 0
|
||||
// 완전 노출 상면
|
||||
let exposedTop = 0
|
||||
//반 노출 상면
|
||||
let exposedHalfTop = 0
|
||||
// 완전 접면
|
||||
let touchDimension = 0
|
||||
//반접면
|
||||
let halfTouchDimension = 0
|
||||
// 노출하면 체크
|
||||
centerPoints.forEach((centerPoint, index) => {
|
||||
const { x, y, width, height } = centerPoint
|
||||
// centerPoints중에 현재 centerPoint와 x값이 같고, y값이 y-height값과 같은 centerPoint가 있는지 확인
|
||||
const bottomCell = centerPoints.filter((centerPoint) => centerPoint.x === x && Math.abs(centerPoint.y - (y + height)) < 2)
|
||||
if (bottomCell.length === 1) {
|
||||
touchDimension++
|
||||
return
|
||||
}
|
||||
|
||||
const bottomLeftPoint = { x: x - width / 2, y: y + height }
|
||||
const bottomRightPoint = { x: x + width / 2, y: y + height }
|
||||
|
||||
// 바로 아래에 셀이 없는 경우 물떼세 배치가 왼쪽 되어있는 셀을 찾는다.
|
||||
const leftBottomCnt = centerPoints.filter(
|
||||
(centerPoint) => Math.abs(centerPoint.x - bottomLeftPoint.x) < 2 && Math.abs(centerPoint.y - bottomLeftPoint.y) < 2,
|
||||
).length
|
||||
const rightBottomCnt = centerPoints.filter(
|
||||
(centerPoint) => Math.abs(centerPoint.x - bottomRightPoint.x) < 2 && Math.abs(centerPoint.y - bottomRightPoint.y) < 2,
|
||||
).length
|
||||
if (leftBottomCnt + rightBottomCnt === 2) {
|
||||
touchDimension++
|
||||
return
|
||||
}
|
||||
if (leftBottomCnt + rightBottomCnt === 1) {
|
||||
halfTouchDimension++
|
||||
exposedHalfBottom++
|
||||
return
|
||||
}
|
||||
if (leftBottomCnt + rightBottomCnt === 0) {
|
||||
exposedBottom++
|
||||
return
|
||||
}
|
||||
})
|
||||
// 노출상면 체크
|
||||
|
||||
centerPoints.forEach((centerPoint, index) => {
|
||||
const { x, y, width, height } = centerPoint
|
||||
const topCell = centerPoints.filter((centerPoint) => centerPoint.x === x && Math.abs(centerPoint.y - (y - height)) < 2)
|
||||
if (topCell.length === 1) {
|
||||
return
|
||||
}
|
||||
|
||||
const topLeftPoint = { x: x - width / 2, y: y - height }
|
||||
const topRightPoint = { x: x + width / 2, y: y - height }
|
||||
|
||||
const leftTopCnt = centerPoints.filter(
|
||||
(centerPoint) => Math.abs(centerPoint.x - topLeftPoint.x) < 2 && Math.abs(centerPoint.y - topRightPoint.y) < 2,
|
||||
).length
|
||||
const rightTopCnt = centerPoints.filter(
|
||||
(centerPoint) => Math.abs(centerPoint.x - topRightPoint.x) < 2 && Math.abs(centerPoint.y - topRightPoint.y) < 2,
|
||||
).length
|
||||
|
||||
if (leftTopCnt + rightTopCnt === 1) {
|
||||
exposedHalfTop++
|
||||
return
|
||||
}
|
||||
if (leftTopCnt + rightTopCnt === 0) {
|
||||
exposedTop++
|
||||
return
|
||||
}
|
||||
})
|
||||
return {
|
||||
exposedBottom,
|
||||
exposedHalfBottom,
|
||||
exposedTop,
|
||||
exposedHalfTop,
|
||||
touchDimension,
|
||||
halfTouchDimension,
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
const 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
|
||||
}
|
||||
|
||||
const bottomTopFlowLine = (surface) => {
|
||||
const flowArray = []
|
||||
const bottomFlow = surface.lines.reduce(
|
||||
(acc, line, index) => {
|
||||
if (line.y1 > acc.y1 || (line.y1 === acc.y1 && line.x1 > acc.x1)) {
|
||||
return { x1: line.x1, y1: line.y1, index: index }
|
||||
}
|
||||
return acc
|
||||
},
|
||||
{ x1: 0, y1: 0, index: -1 }, // 초기값: 무한대와 유효하지 않은 인덱스
|
||||
)
|
||||
flowArray.push(bottomFlow)
|
||||
|
||||
const topFlow = surface.lines.reduce(
|
||||
(acc, line, index) => {
|
||||
if (line.y1 < acc.y1 || (line.y1 === acc.y1 && line.x1 < acc.x1)) {
|
||||
return { x1: line.x1, y1: line.y1, index: index }
|
||||
}
|
||||
return acc
|
||||
},
|
||||
{ x1: Infinity, y1: Infinity, index: -1 }, // 초기값: 무한대와 유효하지 않은 인덱스
|
||||
)
|
||||
flowArray.push(topFlow)
|
||||
|
||||
let idx = 0
|
||||
let rtnObjArray = []
|
||||
flowArray.forEach((center) => {
|
||||
const linesArray = new Array()
|
||||
|
||||
surface.lines.filter((line) => {
|
||||
if ((center.x1 === line.x1 && center.y1 === line.y1) || (center.x1 === line.x2 && center.y1 === line.y2)) {
|
||||
linesArray.push(line)
|
||||
}
|
||||
})
|
||||
|
||||
let coords = []
|
||||
if (center.index === 0) {
|
||||
coords = [
|
||||
{ x: linesArray[0].x2, y: linesArray[0].y2 },
|
||||
{ x: center.x1, y: center.y1 },
|
||||
{ x: linesArray[1].x1, y: linesArray[1].y1 },
|
||||
]
|
||||
} else {
|
||||
coords = [
|
||||
{ x: linesArray[0].x1, y: linesArray[0].y1 },
|
||||
{ x: center.x1, y: center.y1 },
|
||||
{ x: linesArray[1].x2, y: linesArray[1].y2 },
|
||||
]
|
||||
}
|
||||
|
||||
const adjust1 = coords[0].x - coords[1].x
|
||||
const height1 = coords[1].y - coords[0].y
|
||||
const angle1 = Math.abs(Math.round(Math.atan(height1 / adjust1) * (180 / Math.PI) * 1000) / 1000)
|
||||
|
||||
const adjust2 = coords[2].x - coords[1].x
|
||||
const height2 = coords[2].y - coords[1].y
|
||||
const angle2 = Math.abs(Math.round(Math.atan(height2 / adjust2) * (180 / Math.PI) * 1000) / 1000)
|
||||
const angle3 = 180 - (angle1 + angle2)
|
||||
|
||||
const charlie = 173.3 + 3 // 평행선길이 약간 여유를 줌
|
||||
const alpha = (charlie * Math.sin((angle1 * Math.PI) / 180)) / Math.sin((angle3 * Math.PI) / 180)
|
||||
const beta = Math.sqrt(alpha ** 2 + charlie ** 2 - 2 * alpha * charlie * Math.cos((angle2 * Math.PI) / 180))
|
||||
const h = beta * Math.sin((angle1 * Math.PI) / 180) // 높이
|
||||
const sign = Math.sign(coords[0].y - coords[1].y) // 진행방향
|
||||
const top = coords[1].y + sign * h // 변경되는 높이 좌표 값
|
||||
// const line3 = new QLine([coords[1].x, coords[1].y, coords[1].x, top], {
|
||||
// stroke: 'blue',
|
||||
// strokeWidth: 1,
|
||||
// selectable: true,
|
||||
// })
|
||||
// // canvas?.add(line3)
|
||||
|
||||
const pointX1 = coords[0].x + ((coords[0].y - top) / (coords[0].y - coords[1].y)) * (coords[1].x - coords[0].x)
|
||||
const pointY1 = top
|
||||
const pointX2 = coords[2].x + ((coords[2].y - top) / (coords[2].y - coords[1].y)) * (coords[1].x - coords[2].x)
|
||||
const pointY2 = top
|
||||
|
||||
const finalLine = new QLine([pointX1, pointY1, pointX2, pointY2], {
|
||||
stroke: 'red',
|
||||
strokeWidth: 1,
|
||||
selectable: true,
|
||||
})
|
||||
canvas?.add(finalLine)
|
||||
canvas?.renderAll()
|
||||
|
||||
const rtnObj = { target: idx === 0 ? 'bottom' : 'top', x1: pointX1, y1: pointY1, x2: pointX2, y2: pointY2 }
|
||||
rtnObjArray.push(rtnObj)
|
||||
++idx
|
||||
})
|
||||
|
||||
return rtnObjArray
|
||||
}
|
||||
|
||||
const leftRightFlowLine = (surface) => {
|
||||
const flowArray = []
|
||||
const leftFlow = surface.lines.reduce(
|
||||
(acc, line, index) => {
|
||||
if (line.x1 < acc.x1 || (line.x1 === acc.x1 && line.y1 < acc.y1)) {
|
||||
return { x1: line.x1, y1: line.y1, index: index }
|
||||
}
|
||||
return acc
|
||||
},
|
||||
{ x1: Infinity, y1: Infinity, index: -1 }, // 초기값: 무한대와 유효하지 않은 인덱스
|
||||
)
|
||||
flowArray.push(leftFlow)
|
||||
|
||||
const rightFlow = surface.lines.reduce(
|
||||
(acc, line, index) => {
|
||||
if (line.x1 > acc.x1 || (line.x1 === acc.x1 && line.y1 > acc.y1)) {
|
||||
return { x1: line.x1, y1: line.y1, index: index }
|
||||
}
|
||||
return acc
|
||||
},
|
||||
{ x1: 0, y1: 0, index: -1 }, // 초기값: 무한대와 유효하지 않은 인덱스
|
||||
)
|
||||
flowArray.push(rightFlow)
|
||||
|
||||
let idx = 0
|
||||
let rtnObjArray = []
|
||||
flowArray.forEach((center) => {
|
||||
const linesArray = surface.lines.filter((line) => {
|
||||
if ((center.x1 === line.x1 && center.y1 === line.y1) || (center.x1 === line.x2 && center.y1 === line.y2)) {
|
||||
return line
|
||||
}
|
||||
})
|
||||
|
||||
let coords = []
|
||||
if (center.index === 0) {
|
||||
coords = [
|
||||
{ x: linesArray[1].x1, y: linesArray[1].y1 },
|
||||
{ x: center.x1, y: center.y1 },
|
||||
{ x: linesArray[0].x2, y: linesArray[0].y2 },
|
||||
]
|
||||
} else {
|
||||
coords = [
|
||||
{ x: linesArray[0].x1, y: linesArray[0].y1 },
|
||||
{ x: center.x1, y: center.y1 },
|
||||
{ x: linesArray[1].x2, y: linesArray[1].y2 },
|
||||
]
|
||||
}
|
||||
|
||||
const adjust1 = coords[0].x - coords[1].x
|
||||
const height1 = coords[1].y - coords[0].y
|
||||
const angle1 = Math.abs(Math.round(Math.atan(adjust1 / height1) * (180 / Math.PI) * 1000) / 1000)
|
||||
|
||||
const adjust2 = coords[2].x - coords[1].x
|
||||
const height2 = coords[2].y - coords[1].y
|
||||
const angle2 = Math.abs(Math.round(Math.atan(adjust2 / height2) * (180 / Math.PI) * 1000) / 1000)
|
||||
const angle3 = 180 - (angle1 + angle2)
|
||||
|
||||
const charlie = 173.3 + 3 // 평행선길이 약간 여유를줌
|
||||
const alpha = (charlie * Math.sin((angle1 * Math.PI) / 180)) / Math.sin((angle3 * Math.PI) / 180)
|
||||
const beta = Math.sqrt(alpha ** 2 + charlie ** 2 - 2 * alpha * charlie * Math.cos((angle2 * Math.PI) / 180))
|
||||
|
||||
const h = beta * Math.sin((angle1 * Math.PI) / 180) // 높이
|
||||
const sign = Math.sign(coords[0].x - coords[1].x) // 진행방향
|
||||
const top = coords[1].x + sign * h // 변경되는 높이 좌표 값
|
||||
|
||||
// const line3 = new QLine([coords[1].x, coords[1].y, top, coords[1].y], {
|
||||
// stroke: 'blue',
|
||||
// strokeWidth: 1,
|
||||
// selectable: true,
|
||||
// })
|
||||
// canvas?.add(line3)
|
||||
|
||||
const pointX1 = top
|
||||
const pointY1 = coords[0].y + ((coords[0].x - top) / (coords[0].x - coords[1].x)) * (coords[1].y - coords[0].y)
|
||||
const pointX2 = top
|
||||
const pointY2 = coords[2].y + ((coords[2].x - top) / (coords[2].x - coords[1].x)) * (coords[1].y - coords[2].y)
|
||||
|
||||
const finalLine = new QLine([pointX1, pointY1, pointX2, pointY2], {
|
||||
stroke: 'red',
|
||||
strokeWidth: 1,
|
||||
selectable: true,
|
||||
})
|
||||
canvas?.add(finalLine)
|
||||
canvas?.renderAll()
|
||||
|
||||
const rtnObj = { target: idx === 0 ? 'left' : 'right', x1: pointX1, y1: pointY1, x2: pointX2, y2: pointY2 }
|
||||
rtnObjArray.push(rtnObj)
|
||||
++idx
|
||||
})
|
||||
}
|
||||
|
||||
const findSetupSurfaceMaxLines = (surface) => {
|
||||
const leftFlow = surface.lines.reduce(
|
||||
(acc, line, index) => {
|
||||
if (line.x1 < acc.x1 || (line.x1 === acc.x1 && line.y1 < acc.y1)) {
|
||||
return { x1: line.x1, y1: line.y1 }
|
||||
}
|
||||
return acc
|
||||
},
|
||||
{ x1: Infinity, y1: Infinity, index: -1 }, // 초기값: 무한대와 유효하지 않은 인덱스
|
||||
)
|
||||
|
||||
const rightFlow = surface.lines.reduce(
|
||||
(acc, line, index) => {
|
||||
if (line.x1 > acc.x1 || (line.x1 === acc.x1 && line.y1 > acc.y1)) {
|
||||
return { x1: line.x1, y1: line.y1 }
|
||||
}
|
||||
return acc
|
||||
},
|
||||
{ x1: 0, y1: 0, index: -1 }, // 초기값: 무한대와 유효하지 않은 인덱스
|
||||
)
|
||||
|
||||
const topFlow = surface.lines.reduce(
|
||||
(acc, line, index) => {
|
||||
if (line.y1 < acc.y1 || (line.y1 === acc.y1 && line.x1 < acc.x1)) {
|
||||
return { x1: line.x1, y1: line.y1 }
|
||||
}
|
||||
return acc
|
||||
},
|
||||
{ x1: Infinity, y1: Infinity, index: -1 }, // 초기값: 무한대와 유효하지 않은 인덱스
|
||||
)
|
||||
|
||||
const bottomFlow = surface.lines.reduce(
|
||||
(acc, line, index) => {
|
||||
if (line.y1 > acc.y1 || (line.y1 === acc.y1 && line.x1 > acc.x1)) {
|
||||
return { x1: line.x1, y1: line.y1 }
|
||||
}
|
||||
return acc
|
||||
},
|
||||
{ x1: 0, y1: 0, index: -1 }, // 초기값: 무한대와 유효하지 않은 인덱스
|
||||
)
|
||||
|
||||
const obj = {
|
||||
left: leftFlow,
|
||||
right: rightFlow,
|
||||
top: topFlow,
|
||||
bottom: bottomFlow,
|
||||
}
|
||||
|
||||
return obj
|
||||
}
|
||||
|
||||
return {
|
||||
makeModuleInstArea,
|
||||
manualModuleSetup,
|
||||
autoModuleSetup,
|
||||
}
|
||||
}
|
||||
36
src/hooks/module/useOrientation.js
Normal file
36
src/hooks/module/useOrientation.js
Normal file
@ -0,0 +1,36 @@
|
||||
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'
|
||||
import { useEffect } from 'react'
|
||||
|
||||
// 모듈,회로 구성 탭 기본설정 > 방위설정 탭
|
||||
export function useOrientation() {
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
const [compasDeg, setCompasDeg] = useRecoilState(compasDegAtom)
|
||||
|
||||
const { drawDirectionArrow } = usePolygon()
|
||||
|
||||
useEffect(() => {
|
||||
const roofs = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
|
||||
roofs.forEach((roof) => {
|
||||
roof.set({
|
||||
moduleCompass: null,
|
||||
})
|
||||
drawDirectionArrow(roof)
|
||||
})
|
||||
}, [])
|
||||
|
||||
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 }
|
||||
}
|
||||
@ -17,6 +17,7 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
|
||||
const { getMessage } = useMessage()
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
const { addCanvasMouseEventListener, initEvent, addDocumentEventListener } = useEvent()
|
||||
// const { addCanvasMouseEventListener, initEvent, addDocumentEventListener } = useContext(EventContext)
|
||||
const { swalFire } = useSwal()
|
||||
const { drawDirectionArrow } = usePolygon()
|
||||
const lengthTextFont = useRecoilValue(fontSelector('lengthText'))
|
||||
|
||||
@ -1,16 +1,22 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
|
||||
import { adsorptionPointModeState, adsorptionRangeState, canvasState } from '@/store/canvasAtom'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||
import { adsorptionPointModeState, adsorptionRangeState, canvasState, planSizeSettingState } from '@/store/canvasAtom'
|
||||
import { globalLocaleStore } from '@/store/localeAtom'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { useAxios } from '@/hooks/useAxios'
|
||||
import { useSwal } from '@/hooks/useSwal'
|
||||
import { corridorDimensionSelector, settingModalFirstOptionsState, settingModalSecondOptionsState } from '@/store/settingAtom'
|
||||
import { setSurfaceShapePattern } from '@/util/canvas-util'
|
||||
import { correntObjectNoState, corridorDimensionSelector, settingModalFirstOptionsState, settingModalSecondOptionsState } from '@/store/settingAtom'
|
||||
import { POLYGON_TYPE } from '@/common/common'
|
||||
import { globalFontAtom } from '@/store/fontAtom'
|
||||
import { dimensionLineSettingsState } from '@/store/commonUtilsAtom'
|
||||
|
||||
let objectNo
|
||||
|
||||
export function useCanvasSetting() {
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
// canvas가 null이 아닐 때에만 getObjects 호출
|
||||
const canvasObjects = canvas ? canvas.getObjects() : []
|
||||
const [correntObjectNo, setCorrentObjectNo] = useRecoilState(correntObjectNoState)
|
||||
|
||||
const [settingModalFirstOptions, setSettingModalFirstOptions] = useRecoilState(settingModalFirstOptionsState)
|
||||
const [settingModalSecondOptions, setSettingModalSecondOptions] = useRecoilState(settingModalSecondOptionsState)
|
||||
@ -25,9 +31,16 @@ export function useCanvasSetting() {
|
||||
const { swalFire } = useSwal()
|
||||
|
||||
const [adsorptionPointMode, setAdsorptionPointMode] = useRecoilState(adsorptionPointModeState)
|
||||
const setAdsorptionRange = useSetRecoilState(adsorptionRangeState)
|
||||
const [adsorptionRange, setAdsorptionRange] = useRecoilState(adsorptionRangeState)
|
||||
const [planSizeSettingMode, setPlanSizeSettingMode] = useRecoilState(planSizeSettingState)
|
||||
//const setAdsorptionRange = useSetRecoilState(adsorptionRangeState)
|
||||
|
||||
const [objectNo, setObjectNo] = useState('test123240912001') // 이후 삭제 필요
|
||||
const [selectedFont, setSelectedFont] = useState()
|
||||
const [selectedFontWeight, setSelectedFontWeight] = useState()
|
||||
const [selectedFontSize, setSelectedFontSize] = useState()
|
||||
const [selectedFontColor, setSelectedFontColor] = useState()
|
||||
const [globalFont, setGlobalFont] = useRecoilState(globalFontAtom)
|
||||
const [dimensionLineSettings, setDimensionLineSettings] = useRecoilState(dimensionLineSettingsState)
|
||||
|
||||
useEffect(() => {
|
||||
if (!canvas) {
|
||||
@ -56,199 +69,352 @@ export function useCanvasSetting() {
|
||||
})
|
||||
break
|
||||
}
|
||||
canvas.renderAll()
|
||||
canvas?.renderAll()
|
||||
}, [corridorDimension])
|
||||
|
||||
useEffect(() => {
|
||||
console.log('useCanvasSetting useEffect 실행1')
|
||||
fetchSettings()
|
||||
}, [objectNo])
|
||||
console.log('useCanvasSetting useEffect 실행1', correntObjectNo)
|
||||
}, [])
|
||||
|
||||
//흡착점 ON/OFF 변경 시
|
||||
useEffect(() => {
|
||||
console.log('useCanvasSetting useEffect 실행2')
|
||||
//fetchSettings()
|
||||
//onClickOption()
|
||||
//fetchSettings()
|
||||
console.log('useCanvasSetting useEffect 실행2', adsorptionPointMode.fontFlag, correntObjectNo)
|
||||
|
||||
if (adsorptionPointMode.fontFlag) {
|
||||
onClickOption2()
|
||||
frontSettings()
|
||||
fetchSettings()
|
||||
}
|
||||
}, [adsorptionPointMode])
|
||||
|
||||
// 1 과 2 변경 시
|
||||
useEffect(() => {
|
||||
console.log('useCanvasSetting useEffect 실행3')
|
||||
//fetchSettings()
|
||||
//onClickOption()
|
||||
//fetchSettings()
|
||||
console.log('useCanvasSetting useEffect 실행3', settingModalFirstOptions.fontFlag, settingModalSecondOptions.fontFlag, correntObjectNo)
|
||||
if (settingModalFirstOptions.fontFlag || settingModalSecondOptions.fontFlag) {
|
||||
onClickOption2()
|
||||
frontSettings()
|
||||
fetchSettings()
|
||||
}
|
||||
}, [settingModalFirstOptions, settingModalSecondOptions])
|
||||
|
||||
// 글꼴 변경 시
|
||||
useEffect(() => {
|
||||
console.log('useCanvasSetting useEffect 실행4', globalFont.fontFlag, correntObjectNo)
|
||||
if (globalFont.fontFlag) {
|
||||
onClickOption2()
|
||||
frontSettings()
|
||||
fetchSettings()
|
||||
}
|
||||
}, [globalFont])
|
||||
|
||||
// 도명크기 변경 시
|
||||
useEffect(() => {
|
||||
console.log('useCanvasSetting useEffect 실행5', planSizeSettingMode.flag, correntObjectNo)
|
||||
|
||||
if (planSizeSettingMode.flag) {
|
||||
onClickOption2()
|
||||
frontSettings()
|
||||
fetchSettings()
|
||||
}
|
||||
}, [planSizeSettingMode])
|
||||
|
||||
const getFonts = (itemValue) => {
|
||||
if (!itemValue) return { id: 1, name: 'MS PGothic', value: 'MS PGothic' }
|
||||
const data = [
|
||||
{ id: 1, name: 'MS PGothic', value: 'MS PGothic' },
|
||||
{ id: 2, name: '@Yu Gothic', value: '@Yu Gothic' },
|
||||
{ id: 3, name: 'Yu Gothic', value: 'Yu Gothic' },
|
||||
{ id: 4, name: '@Yu Gothic UI', value: '@Yu Gothic UI' },
|
||||
{ id: 5, name: 'Yu Gothic UI', value: 'Yu Gothic UI' },
|
||||
].filter((font) => font.value === itemValue)
|
||||
if (data.length !== 0) {
|
||||
return data[0]
|
||||
} else {
|
||||
return { id: 1, name: 'MS PGothic', value: 'MS PGothic' }
|
||||
}
|
||||
}
|
||||
|
||||
const getFontSizes = (itemValue) => {
|
||||
if (!itemValue) return { id: 16, name: 16, value: 16 }
|
||||
const data = [
|
||||
...Array.from({ length: 4 }).map((_, index) => {
|
||||
return { id: index + 8, name: index + 8, value: index + 8 }
|
||||
}),
|
||||
...Array.from({ length: 9 }).map((_, index) => {
|
||||
return { id: (index + 6) * 2, name: (index + 6) * 2, value: (index + 6) * 2 }
|
||||
}),
|
||||
{ id: 36, name: 36, value: 36 },
|
||||
{ id: 48, name: 48, value: 48 },
|
||||
{ id: 72, name: 72, value: 72 },
|
||||
].filter((fontSize) => fontSize.value === itemValue)
|
||||
if (data.length !== 0) {
|
||||
return data[0]
|
||||
} else {
|
||||
return { id: 16, name: 16, value: 16 }
|
||||
}
|
||||
}
|
||||
|
||||
const getFontStyles = (itemValue) => {
|
||||
if (!itemValue) return { id: 'normal', name: getMessage('font.style.normal'), value: 'normal' }
|
||||
const data = [
|
||||
{ id: 'normal', name: getMessage('font.style.normal'), value: 'normal' },
|
||||
{ id: 'italic', name: getMessage('font.style.italic'), value: 'italic' },
|
||||
{ id: 'bold', name: getMessage('font.style.bold'), value: 'bold' },
|
||||
{ id: 'boldAndItalic', name: getMessage('font.style.bold.italic'), value: 'boldAndItalic' },
|
||||
].filter((fontStyle) => fontStyle.value === itemValue)
|
||||
if (data.length !== 0) {
|
||||
return data[0]
|
||||
} else {
|
||||
return { id: 'normal', name: getMessage('font.style.normal'), value: 'normal' }
|
||||
}
|
||||
}
|
||||
|
||||
const getFontColors = (itemValue) => {
|
||||
if (!itemValue) return { id: 'black', name: getMessage('color.black'), value: 'black' }
|
||||
const data = [
|
||||
{ id: 'black', name: getMessage('color.black'), value: 'black' },
|
||||
{ id: 'red', name: getMessage('color.red'), value: 'red' },
|
||||
{ id: 'blue', name: getMessage('color.blue'), value: 'blue' },
|
||||
{ id: 'gray', name: getMessage('color.gray'), value: 'gray' },
|
||||
{ id: 'yellow', name: getMessage('color.yellow'), value: 'yellow' },
|
||||
{ id: 'green', name: getMessage('color.green'), value: 'green' },
|
||||
{ id: 'pink', name: getMessage('color.pink'), value: 'pink' },
|
||||
{ id: 'gold', name: getMessage('color.gold'), value: 'gold' },
|
||||
{ id: 'darkblue', name: getMessage('color.darkblue'), value: 'darkblue' },
|
||||
].filter((fontColor) => fontColor.value === itemValue)
|
||||
if (data.length !== 0) {
|
||||
return data[0]
|
||||
} else {
|
||||
return { id: 'black', name: getMessage('color.black'), value: 'black' }
|
||||
}
|
||||
}
|
||||
|
||||
const fetchSettings = async () => {
|
||||
try {
|
||||
const res = await get({ url: `/api/canvas-management/canvas-settings/by-object/${objectNo}` })
|
||||
const res = await get({ url: `/api/canvas-management/canvas-settings/by-object/${correntObjectNo}` })
|
||||
console.log('res', res)
|
||||
const optionData1 = settingModalFirstOptions.option1.map((item) => ({ ...item, selected: res[item.column] }))
|
||||
const optionData2 = settingModalFirstOptions.option2.map((item) => ({ ...item, selected: res[item.column] }))
|
||||
const optionData3 = settingModalSecondOptions.option3.map((item) => ({ ...item }))
|
||||
const optionData4 = settingModalSecondOptions.option4.map((item) => ({ ...item, selected: res[item.column] }))
|
||||
const optionData5 = settingModalFirstOptions.dimensionDisplay.map((item) => ({ ...item }))
|
||||
|
||||
const patternData = {
|
||||
adsorpPoint: res.adsorpPoint,
|
||||
if (res) {
|
||||
const optionData1 = settingModalFirstOptions.option1.map((item) => ({ ...item, selected: res[item.column] }))
|
||||
const optionData2 = settingModalFirstOptions.option2.map((item) => ({ ...item, selected: res[item.column] }))
|
||||
const optionData3 = settingModalSecondOptions.option3.map((item) => ({ ...item }))
|
||||
const optionData4 = settingModalSecondOptions.option4.map((item) => ({ ...item, selected: res[item.column] }))
|
||||
const optionData5 = settingModalFirstOptions.dimensionDisplay.map((item) => ({ ...item }))
|
||||
|
||||
//흡착점 ON/OFF
|
||||
setAdsorptionPointMode({ ...adsorptionPointMode, adsorptionPoint: res.adsorpPoint, fontFlag: false })
|
||||
|
||||
//치수선 설정
|
||||
setDimensionLineSettings({ ...dimensionLineSettings, pixel: res.originPixel, color: res.originColor })
|
||||
|
||||
//도면크기 설정
|
||||
setPlanSizeSettingMode({
|
||||
...planSizeSettingMode,
|
||||
originHorizon: res.originHorizon,
|
||||
originVertical: res.originVertical,
|
||||
flag: false,
|
||||
})
|
||||
|
||||
// 데이터 설정
|
||||
setSettingModalFirstOptions({
|
||||
...settingModalFirstOptions,
|
||||
option1: optionData1,
|
||||
option2: optionData2,
|
||||
dimensionDisplay: optionData5,
|
||||
fontFlag: false,
|
||||
})
|
||||
setSettingModalSecondOptions({
|
||||
...settingModalSecondOptions,
|
||||
option3: optionData3,
|
||||
option4: optionData4,
|
||||
fontFlag: false,
|
||||
})
|
||||
|
||||
const fontPatternData = {
|
||||
commonText: {
|
||||
//문자 글꼴 조회 데이터
|
||||
fontFamily: getFonts(res.wordFont),
|
||||
fontWeight: getFontStyles(res.wordFontStyle),
|
||||
fontSize: getFontSizes(res.wordFontSize),
|
||||
fontColor: getFontColors(res.wordFontColor),
|
||||
},
|
||||
flowText: {
|
||||
//흐름방향 글꼴 조회 데이터
|
||||
fontFamily: getFonts(res.flowFont),
|
||||
fontWeight: getFontStyles(res.flowFontStyle),
|
||||
fontSize: getFontSizes(res.flowFontSize),
|
||||
fontColor: getFontColors(res.flowFontColor),
|
||||
},
|
||||
dimensionLineText: {
|
||||
//치수 글꼴 조회 데이터
|
||||
fontFamily: getFonts(res.dimensioFont),
|
||||
fontWeight: getFontStyles(res.dimensioFontStyle),
|
||||
fontSize: getFontSizes(res.dimensioFontSize),
|
||||
fontColor: getFontColors(res.dimensioFontColor),
|
||||
},
|
||||
circuitNumberText: {
|
||||
//회로번호 글꼴 조회 데이터
|
||||
fontFamily: getFonts(res.circuitNumFont),
|
||||
fontWeight: getFontStyles(res.circuitNumFontStyle),
|
||||
fontSize: getFontSizes(res.circuitNumFontSize),
|
||||
fontColor: getFontColors(res.circuitNumFontColor),
|
||||
},
|
||||
lengthText: {
|
||||
//치수선 글꼴 조회 데이터
|
||||
fontFamily: getFonts(res.lengthFont),
|
||||
fontWeight: getFontStyles(res.lengthFontStyle),
|
||||
fontSize: getFontSizes(res.lengthFontSize),
|
||||
fontColor: getFontColors(res.lengthFontColor),
|
||||
},
|
||||
//글꼴 설정 Flag
|
||||
fontFlag: false,
|
||||
}
|
||||
console.log('fontPatternData', fontPatternData)
|
||||
|
||||
//조회된 글꼴 데이터 set
|
||||
setGlobalFont(fontPatternData)
|
||||
} else {
|
||||
//조회된 글꼴 데이터가 없는 경우
|
||||
|
||||
//흡착점 ON/OFF
|
||||
setAdsorptionPointMode({ ...adsorptionPointMode, adsorptionPoint: false, fontFlag: false })
|
||||
|
||||
//치수선 설정
|
||||
setDimensionLineSettings({ ...dimensionLineSettings })
|
||||
|
||||
//도면크기 설정
|
||||
setPlanSizeSettingMode({
|
||||
...planSizeSettingMode,
|
||||
flag: false,
|
||||
})
|
||||
|
||||
// 데이터 설정
|
||||
setSettingModalFirstOptions({
|
||||
...settingModalFirstOptions,
|
||||
fontFlag: false,
|
||||
})
|
||||
setSettingModalSecondOptions({
|
||||
...settingModalSecondOptions,
|
||||
fontFlag: false,
|
||||
})
|
||||
|
||||
setGlobalFont({ ...globalFont, fontFlag: false })
|
||||
}
|
||||
|
||||
// 데이터 설정
|
||||
setSettingModalFirstOptions({
|
||||
option1: optionData1,
|
||||
option2: optionData2,
|
||||
dimensionDisplay: optionData5,
|
||||
})
|
||||
setSettingModalSecondOptions({
|
||||
option3: optionData3,
|
||||
option4: optionData4,
|
||||
})
|
||||
|
||||
setAdsorptionPointMode(patternData.adsorpPoint)
|
||||
|
||||
console.log('adsorptionPointMode', adsorptionPointMode)
|
||||
frontSettings()
|
||||
} catch (error) {
|
||||
console.error('Data fetching error:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 옵션 클릭 후 저장
|
||||
const onClickOption = async (item) => {
|
||||
//치수 표시(단 건 선택)
|
||||
if (item.column === 'corridorDimension' || item.column === 'realDimension' || item.column === 'noneDimension') {
|
||||
console.log('치수 표시 ', item)
|
||||
const options = settingModalFirstOptions?.dimensionDisplay.map((option) => {
|
||||
option.selected = option.id === item.id
|
||||
return option
|
||||
})
|
||||
const onClickOption2 = useCallback(async () => {
|
||||
// 서버에 전송할 데이터
|
||||
const dataToSend = {
|
||||
firstOption1: option1.map((item) => ({
|
||||
column: item.column,
|
||||
selected: item.selected,
|
||||
})),
|
||||
firstOption2: option2.map((item) => ({
|
||||
column: item.column,
|
||||
selected: item.selected,
|
||||
})),
|
||||
firstOption3: dimensionDisplay.map((item) => ({
|
||||
column: item.column,
|
||||
selected: item.selected,
|
||||
})),
|
||||
secondOption2: option4.map((item) => ({
|
||||
column: item.column,
|
||||
selected: item.selected,
|
||||
})),
|
||||
}
|
||||
// console.log('globalFont', globalFont)
|
||||
const patternData = {
|
||||
//견적서 번호
|
||||
objectNo: correntObjectNo,
|
||||
//디스플레이 설정(다중)
|
||||
allocDisplay: dataToSend.firstOption1[0].selected,
|
||||
outlineDisplay: dataToSend.firstOption1[1].selected,
|
||||
gridDisplay: dataToSend.firstOption1[2].selected,
|
||||
lineDisplay: dataToSend.firstOption1[3].selected,
|
||||
wordDisplay: dataToSend.firstOption1[4].selected,
|
||||
circuitNumDisplay: dataToSend.firstOption1[5].selected,
|
||||
flowDisplay: dataToSend.firstOption1[6].selected,
|
||||
trestleDisplay: dataToSend.firstOption1[7].selected,
|
||||
imageDisplay: dataToSend.firstOption1[8].selected,
|
||||
totalDisplay: dataToSend.firstOption1[9].selected,
|
||||
//차수 표시(단 건)
|
||||
corridorDimension: dataToSend.firstOption3[0].selected,
|
||||
realDimension: dataToSend.firstOption3[1].selected,
|
||||
noneDimension: dataToSend.firstOption3[2].selected,
|
||||
//화면 표시(단 건)
|
||||
onlyBorder: dataToSend.firstOption2[0].selected,
|
||||
lineHatch: dataToSend.firstOption2[1].selected,
|
||||
allPainted: dataToSend.firstOption2[2].selected,
|
||||
//흡착범위 설정(단 건)
|
||||
adsorpRangeSmall: dataToSend.secondOption2[0].selected,
|
||||
adsorpRangeSmallSemi: dataToSend.secondOption2[1].selected,
|
||||
adsorpRangeMedium: dataToSend.secondOption2[2].selected,
|
||||
adsorpRangeLarge: dataToSend.secondOption2[3].selected,
|
||||
//흡착점 ON/OFF
|
||||
adsorpPoint: adsorptionPointMode.adsorptionPoint,
|
||||
//??: adsorptionRange, 사용여부 확인 필요
|
||||
|
||||
//화면 표시(단 건 선택)
|
||||
} else if (item.column === 'onlyBorder' || item.column === 'lineHatch' || item.column === 'allPainted') {
|
||||
console.log('화면 표시 ', item)
|
||||
const options2 = settingModalFirstOptions?.option2.map((option2) => {
|
||||
option2.selected = option2.id === item.id
|
||||
return option2
|
||||
})
|
||||
//글꼴 설정
|
||||
//문자 글꼴
|
||||
wordFont: globalFont.commonText.fontFamily?.value ?? 'MS PGothic',
|
||||
wordFontStyle: globalFont.commonText.fontWeight?.value ?? 'normal',
|
||||
wordFontSize: globalFont.commonText.fontSize?.value ?? 16,
|
||||
wordFontColor: globalFont.commonText.fontColor?.value ?? 'black',
|
||||
|
||||
const polygons = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
|
||||
//흐름방향 글꼴
|
||||
flowFont: globalFont.flowText.fontFamily?.value ?? 'MS PGothic',
|
||||
flowFontStyle: globalFont.flowText.fontWeight?.value ?? 'normal',
|
||||
flowFontSize: globalFont.flowText.fontSize?.value ?? 16,
|
||||
flowFontColor: globalFont.flowText.fontColor?.value ?? 'black',
|
||||
|
||||
polygons.forEach((polygon) => {
|
||||
setSurfaceShapePattern(polygon, item.column)
|
||||
})
|
||||
//치수 글꼴
|
||||
dimensioFont: globalFont.dimensionLineText.fontFamily?.value ?? 'MS PGothic',
|
||||
dimensioFontStyle: globalFont.dimensionLineText.fontWeight?.value ?? 'normal',
|
||||
dimensioFontSize: globalFont.dimensionLineText.fontSize?.value ?? 16,
|
||||
dimensioFontColor: globalFont.dimensionLineText.fontColor?.value ?? 'black',
|
||||
|
||||
//흡착범위 설정(단 건 선택)
|
||||
} else if (
|
||||
item.column === 'adsorpRangeSmall' ||
|
||||
item.column === 'adsorpRangeSmallSemi' ||
|
||||
item.column === 'adsorpRangeMedium' ||
|
||||
item.column === 'adsorpRangeLarge'
|
||||
) {
|
||||
console.log('화면 표시2 ', item, option4)
|
||||
// option4에서 한 개만 선택 가능하도록 처리
|
||||
const updatedOption4 = option4.map((option) =>
|
||||
option.id === item.id
|
||||
? { ...option, selected: true }
|
||||
: {
|
||||
...option,
|
||||
selected: false,
|
||||
},
|
||||
)
|
||||
//회로번호 글꼴
|
||||
circuitNumFont: globalFont.circuitNumberText.fontFamily?.value ?? 'MS PGothic',
|
||||
circuitNumFontStyle: globalFont.circuitNumberText.fontWeight?.value ?? 'normal',
|
||||
circuitNumFontSize: globalFont.circuitNumberText.fontSize?.value ?? 16,
|
||||
circuitNumFontColor: globalFont.circuitNumberText.fontColor?.value ?? 'black',
|
||||
|
||||
setSettingModalSecondOptions({ option3, option4: updatedOption4 })
|
||||
//치수선 글꼴
|
||||
lengthFont: globalFont.lengthText.fontFamily?.value ?? 'MS PGothic',
|
||||
lengthFontStyle: globalFont.lengthText.fontWeight?.value ?? 'normal',
|
||||
lengthFontSize: globalFont.lengthText.fontSize?.value ?? 16,
|
||||
lengthFontColor: globalFont.lengthText.fontColor?.value ?? 'black',
|
||||
|
||||
//흡착점 ON / OFF
|
||||
} else if (item === 'adsorpPoint') {
|
||||
console.log('흡착점 ON / OFF ', item)
|
||||
const options2 = settingModalFirstOptions?.option2.map((option2) => {
|
||||
option2.selected = option2.id === item.id
|
||||
return option2
|
||||
})
|
||||
//치수선 설정
|
||||
originPixel: dimensionLineSettings.pixel,
|
||||
originColor: dimensionLineSettings.color,
|
||||
|
||||
const polygons = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
|
||||
|
||||
polygons.forEach((polygon) => {
|
||||
setSurfaceShapePattern(polygon, item.column)
|
||||
})
|
||||
|
||||
//디스플레이 설정(다 건 선택)
|
||||
} else {
|
||||
//console.log('디스플레이 설정1 ', item.column)
|
||||
console.log('디스플레이 설정 ', item)
|
||||
item.selected = !item.selected
|
||||
//치수선 설정
|
||||
originHorizon: planSizeSettingMode.originHorizon,
|
||||
originVertical: planSizeSettingMode.originVertical,
|
||||
}
|
||||
|
||||
setSettingModalFirstOptions({ option1, option2, dimensionDisplay })
|
||||
console.log('patternData ', patternData)
|
||||
|
||||
try {
|
||||
// 서버에 전송할 데이터
|
||||
const dataToSend = {
|
||||
firstOption1: option1.map((item) => ({
|
||||
column: item.column,
|
||||
selected: item.selected,
|
||||
})),
|
||||
firstOption2: option2.map((item) => ({
|
||||
column: item.column,
|
||||
selected: item.selected,
|
||||
})),
|
||||
firstOption3: dimensionDisplay.map((item) => ({
|
||||
column: item.column,
|
||||
selected: item.selected,
|
||||
})),
|
||||
// secondOption1: secondOptions[0].option1.map((item) => ({
|
||||
// name: item.id,
|
||||
// name: item.name,
|
||||
// // 필요한 경우 데이터 항목 추가
|
||||
// })),
|
||||
secondOption2: option4.map((item) => ({
|
||||
column: item.column,
|
||||
selected: item.selected,
|
||||
})),
|
||||
}
|
||||
|
||||
const patternData = {
|
||||
objectNo,
|
||||
//디스플레이 설정(다중)
|
||||
allocDisplay: dataToSend.firstOption1[0].selected,
|
||||
outlineDisplay: dataToSend.firstOption1[1].selected,
|
||||
gridDisplay: dataToSend.firstOption1[2].selected,
|
||||
lineDisplay: dataToSend.firstOption1[3].selected,
|
||||
wordDisplay: dataToSend.firstOption1[4].selected,
|
||||
circuitNumDisplay: dataToSend.firstOption1[5].selected,
|
||||
flowDisplay: dataToSend.firstOption1[6].selected,
|
||||
trestleDisplay: dataToSend.firstOption1[7].selected,
|
||||
imageDisplay: dataToSend.firstOption1[8].selected,
|
||||
totalDisplay: dataToSend.firstOption1[9].selected,
|
||||
//차수 표시(단 건)
|
||||
corridorDimension: dataToSend.firstOption3[0].selected,
|
||||
realDimension: dataToSend.firstOption3[1].selected,
|
||||
noneDimension: dataToSend.firstOption3[2].selected,
|
||||
//화면 표시(단 건)
|
||||
onlyBorder: dataToSend.firstOption2[0].selected,
|
||||
lineHatch: dataToSend.firstOption2[1].selected,
|
||||
allPainted: dataToSend.firstOption2[2].selected,
|
||||
//흡착범위 설정(단 건)
|
||||
adsorpRangeSmall: dataToSend.secondOption2[0].selected,
|
||||
adsorpRangeSmallSemi: dataToSend.secondOption2[1].selected,
|
||||
adsorpRangeMedium: dataToSend.secondOption2[2].selected,
|
||||
adsorpRangeLarge: dataToSend.secondOption2[3].selected,
|
||||
//흡착점 ON/OFF
|
||||
adsorpPoint: adsorptionPointMode,
|
||||
}
|
||||
|
||||
console.log('patternData ', patternData)
|
||||
|
||||
// HTTP POST 요청 보내기
|
||||
await post({ url: `/api/canvas-management/canvas-settings`, data: patternData }).then((res) => {
|
||||
// HTTP POST 요청 보내기
|
||||
await post({ url: `/api/canvas-management/canvas-settings`, data: patternData })
|
||||
.then((res) => {
|
||||
swalFire({ text: getMessage(res.returnMessage) })
|
||||
|
||||
// Canvas 디스플레이 설정 시 해당 옵션 적용
|
||||
frontSettings()
|
||||
})
|
||||
} catch (error) {
|
||||
swalFire({ text: getMessage(res.returnMessage), icon: 'error' })
|
||||
}
|
||||
.catch((error) => {
|
||||
swalFire({ text: getMessage(res.returnMessage), icon: 'error' })
|
||||
})
|
||||
|
||||
setAdsorptionRange(item.range)
|
||||
}
|
||||
//setAdsorptionRange(item.range)
|
||||
}, [settingModalFirstOptions, settingModalSecondOptions, adsorptionPointMode, globalFont, planSizeSettingMode])
|
||||
|
||||
// Canvas 디스플레이 설정 시 해당 옵션 적용
|
||||
const frontSettings = async () => {
|
||||
@ -260,7 +426,7 @@ export function useCanvasSetting() {
|
||||
// 'lineDisplay' 지붕선 표시 'roof', POLYGON_TYPE.ROOF
|
||||
// 'wordDisplay' 문자 표시
|
||||
// 'circuitNumDisplay' 회로번호 표시
|
||||
// 'flowDisplay' 흐름방향 표시 'arrow'
|
||||
// 'flowDisplay' 흐름방향 표시 'arrow', 'flowText'
|
||||
// 'trestleDisplay' 가대 표시
|
||||
// 'imageDisplay' 이미지 표시
|
||||
// 'totalDisplay' 집계표 표시
|
||||
@ -277,13 +443,13 @@ export function useCanvasSetting() {
|
||||
optionName = ['outerLine', POLYGON_TYPE.WALL]
|
||||
break
|
||||
case 'gridDisplay': //그리드 표시
|
||||
optionName = ['lindGrid', 'dotGrid']
|
||||
optionName = ['lindGrid', 'dotGrid', 'tempGrid']
|
||||
break
|
||||
case 'lineDisplay': //지붕선 표시
|
||||
optionName = ['roof', POLYGON_TYPE.ROOF]
|
||||
break
|
||||
case 'wordDisplay': //문자 표시
|
||||
optionName = ['6']
|
||||
optionName = ['commonText']
|
||||
break
|
||||
case 'circuitNumDisplay': //회로번호 표시
|
||||
optionName = ['7']
|
||||
@ -304,8 +470,8 @@ export function useCanvasSetting() {
|
||||
// 표시 선택 상태(true/false)
|
||||
optionSelected = option1[i].selected
|
||||
|
||||
canvas
|
||||
.getObjects()
|
||||
//canvas.getObjects() >> canvasObjects
|
||||
canvasObjects
|
||||
.filter((obj) => optionName.includes(obj.name))
|
||||
//.filter((obj) => obj.name === optionName)
|
||||
.forEach((obj) => {
|
||||
@ -313,6 +479,8 @@ export function useCanvasSetting() {
|
||||
//obj.set({ visible: !obj.visible })
|
||||
})
|
||||
|
||||
canvas?.renderAll()
|
||||
|
||||
// console.log(
|
||||
// 'optionName',
|
||||
// optionName,
|
||||
@ -322,14 +490,31 @@ export function useCanvasSetting() {
|
||||
}
|
||||
|
||||
return {
|
||||
canvas,
|
||||
settingModalFirstOptions,
|
||||
setSettingModalFirstOptions,
|
||||
settingModalSecondOptions,
|
||||
setSettingModalSecondOptions,
|
||||
adsorptionPointMode,
|
||||
setAdsorptionPointMode,
|
||||
adsorptionRange,
|
||||
setAdsorptionRange,
|
||||
fetchSettings,
|
||||
onClickOption,
|
||||
//onClickOption,
|
||||
frontSettings,
|
||||
globalFont,
|
||||
setGlobalFont,
|
||||
selectedFont,
|
||||
setSelectedFont,
|
||||
selectedFontWeight,
|
||||
setSelectedFontWeight,
|
||||
selectedFontSize,
|
||||
setSelectedFontSize,
|
||||
selectedFontColor,
|
||||
setSelectedFontColor,
|
||||
dimensionLineSettings,
|
||||
setDimensionLineSettings,
|
||||
planSizeSettingMode,
|
||||
setPlanSizeSettingMode,
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,81 +0,0 @@
|
||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||
import { canvasState } from '@/store/canvasAtom'
|
||||
import { useEffect } from 'react'
|
||||
import { settingModalFirstOptionsState } from '@/store/settingAtom'
|
||||
import { POLYGON_TYPE } from '@/common/common'
|
||||
|
||||
export function useFirstOption() {
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
|
||||
const [settingModalFirstOptions, setSettingModalFirstOptions] = useRecoilState(settingModalFirstOptionsState)
|
||||
|
||||
useEffect(() => {
|
||||
const option1 = settingModalFirstOptions.option1
|
||||
|
||||
// 'allocDisplay' 할당 표시
|
||||
// 'outlineDisplay' 외벽선 표시 'outerLine', POLYGON_TYPE.WALL
|
||||
// 'gridDisplay' 그리드 표시 'lindGrid', 'dotGrid'
|
||||
// 'lineDisplay' 지붕선 표시 'roof', POLYGON_TYPE.ROOF
|
||||
// 'wordDisplay' 문자 표시
|
||||
// 'circuitNumDisplay' 회로번호 표시
|
||||
// 'flowDisplay' 흐름방향 표시 'arrow'
|
||||
// 'trestleDisplay' 가대 표시
|
||||
// 'totalDisplay' 집계표 표시
|
||||
|
||||
let optionName //옵션명
|
||||
let optionSelected //옵션상태
|
||||
|
||||
for (let i = 0; i < option1.length; i++) {
|
||||
switch (option1[i].column) {
|
||||
case 'allocDisplay': //할당 표시
|
||||
optionName = ['1']
|
||||
break
|
||||
case 'outlineDisplay': //외벽선 표시
|
||||
optionName = ['outerLine', POLYGON_TYPE.WALL]
|
||||
break
|
||||
case 'gridDisplay': //그리드 표시
|
||||
optionName = ['lineGrid', 'dotGrid', 'adsorptionPoint', 'tempGrid']
|
||||
break
|
||||
case 'lineDisplay': //지붕선 표시
|
||||
optionName = ['roof', POLYGON_TYPE.ROOF]
|
||||
break
|
||||
case 'wordDisplay': //문자 표시
|
||||
optionName = ['6']
|
||||
break
|
||||
case 'circuitNumDisplay': //회로번호 표시
|
||||
optionName = ['7']
|
||||
break
|
||||
case 'flowDisplay': //흐름방향 표시
|
||||
optionName = ['arrow', 'flowText']
|
||||
break
|
||||
case 'trestleDisplay': //가대 표시
|
||||
optionName = ['8']
|
||||
break
|
||||
case 'totalDisplay': //집계표 표시
|
||||
optionName = ['9']
|
||||
break
|
||||
}
|
||||
// 표시 선택 상태(true/false)
|
||||
optionSelected = option1[i].selected
|
||||
|
||||
canvas
|
||||
.getObjects()
|
||||
.filter((obj) => optionName.includes(obj.name))
|
||||
//.filter((obj) => obj.name === optionName)
|
||||
.forEach((obj) => {
|
||||
obj.set({ visible: optionSelected })
|
||||
//obj.set({ visible: !obj.visible })
|
||||
})
|
||||
|
||||
canvas.renderAll()
|
||||
|
||||
// console.log(
|
||||
// 'optionName',
|
||||
// optionName,
|
||||
// canvas.getObjects().filter((obj) => optionName.includes(obj.name)),
|
||||
// )
|
||||
}
|
||||
}, [settingModalFirstOptions])
|
||||
|
||||
return { settingModalFirstOptions, setSettingModalFirstOptions }
|
||||
}
|
||||
@ -15,20 +15,19 @@ import {
|
||||
outerLineLength2State,
|
||||
outerLineTypeState,
|
||||
} from '@/store/outerLineAtom'
|
||||
import { calculateIntersection, distanceBetweenPoints, findClosestPoint, isPointOnLine, polygonToTurfPolygon } from '@/util/canvas-util'
|
||||
import { calculateIntersection, distanceBetweenPoints, findClosestPoint, isPointOnLine } from '@/util/canvas-util'
|
||||
import { fabric } from 'fabric'
|
||||
import { useAdsorptionPoint } from '@/hooks/useAdsorptionPoint'
|
||||
import { useSwal } from '@/hooks/useSwal'
|
||||
import { booleanPointInPolygon } from '@turf/turf'
|
||||
import { usePopup } from '@/hooks/usePopup'
|
||||
import { calculateAngle, isSamePoint } from '@/util/qpolygon-utils'
|
||||
import { QPolygon } from '@/components/fabric/QPolygon'
|
||||
import { POLYGON_TYPE } from '@/common/common'
|
||||
|
||||
// 보조선 작성
|
||||
export function useAuxiliaryDrawing(id) {
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
const { addCanvasMouseEventListener, addDocumentEventListener, removeMouseLine, initEvent } = useEvent()
|
||||
// const { addCanvasMouseEventListener, addDocumentEventListener, removeMouseLine, initEvent } = useContext(EventContext)
|
||||
const { getIntersectMousePoint } = useMouse()
|
||||
const { addLine, removeLine } = useLine()
|
||||
const { tempGridMode } = useTempGrid()
|
||||
@ -94,7 +93,7 @@ export function useAuxiliaryDrawing(id) {
|
||||
|
||||
addCanvasMouseEventListener('mouse:move', mouseMove)
|
||||
addCanvasMouseEventListener('mouse:down', mouseDown)
|
||||
addDocumentEventListener('contextmenu', document, cutAuxiliary)
|
||||
// addDocumentEventListener('contextmenu', document, cutAuxiliary)
|
||||
addDocumentEventListener('keydown', document, keydown[type])
|
||||
|
||||
return () => {
|
||||
@ -122,6 +121,21 @@ export function useAuxiliaryDrawing(id) {
|
||||
setOuterLineDiagonalLength(0)
|
||||
}
|
||||
|
||||
const move = (object, x, y) => {
|
||||
const line = copy(object, x, y)
|
||||
canvas.remove(object)
|
||||
canvas.setActiveObject(line)
|
||||
}
|
||||
|
||||
const copy = (object, x, y) => {
|
||||
return addLine([object.x1 + x, object.y1 + y, object.x2 + x, object.y2 + y], {
|
||||
stroke: 'red',
|
||||
strokeWidth: 1,
|
||||
selectable: true,
|
||||
name: 'auxiliaryLine',
|
||||
})
|
||||
}
|
||||
|
||||
const keydown = {
|
||||
outerLine: (e) => {
|
||||
if (mousePointerArr.current.length === 0) {
|
||||
@ -130,7 +144,7 @@ export function useAuxiliaryDrawing(id) {
|
||||
// 포커스가 length1에 있지 않으면 length1에 포커스를 줌
|
||||
const activeElem = document.activeElement
|
||||
if (activeElem !== length1Ref.current) {
|
||||
length1Ref.current.focus()
|
||||
length1Ref?.current?.focus()
|
||||
}
|
||||
|
||||
const key = e.key
|
||||
@ -180,7 +194,7 @@ export function useAuxiliaryDrawing(id) {
|
||||
|
||||
const activeElem = document.activeElement
|
||||
if (activeElem !== length1Ref.current && activeElem !== length2Ref.current) {
|
||||
length1Ref.current.focus()
|
||||
length1Ref?.current?.focus()
|
||||
}
|
||||
|
||||
switch (key) {
|
||||
@ -455,9 +469,24 @@ export function useAuxiliaryDrawing(id) {
|
||||
name: 'auxiliaryLine',
|
||||
})
|
||||
|
||||
lineHistory.current.push(line)
|
||||
const historyLines = [...lineHistory.current]
|
||||
|
||||
const hasSameLine = historyLines.some((history) => {
|
||||
return (
|
||||
(isSamePoint(history.startPoint, line.startPoint) && isSamePoint(history.endPoint, line.endPoint)) ||
|
||||
(isSamePoint(history.startPoint, line.endPoint) && isSamePoint(history.endPoint, line.startPoint))
|
||||
)
|
||||
})
|
||||
|
||||
mousePointerArr.current = []
|
||||
clear()
|
||||
|
||||
if (hasSameLine) {
|
||||
canvas.remove(line)
|
||||
return
|
||||
}
|
||||
|
||||
lineHistory.current.push(line)
|
||||
}
|
||||
|
||||
const mouseDown = (e) => {
|
||||
@ -520,8 +549,24 @@ export function useAuxiliaryDrawing(id) {
|
||||
otherAdsorptionPoints.push(intersectionPoint)
|
||||
})
|
||||
})
|
||||
let innerLinePoints = []
|
||||
canvas
|
||||
.getObjects()
|
||||
.filter((obj) => obj.innerLines)
|
||||
.forEach((polygon) => {
|
||||
polygon.innerLines.forEach((line) => {
|
||||
innerLinePoints.push({ x: line.x1, y: line.y1 })
|
||||
innerLinePoints.push({ x: line.x2, y: line.y2 })
|
||||
})
|
||||
})
|
||||
|
||||
const adsorptionPoints = [...getAdsorptionPoints(), ...roofAdsorptionPoints.current, ...otherAdsorptionPoints, ...intersectionPoints.current]
|
||||
const adsorptionPoints = [
|
||||
...getAdsorptionPoints(),
|
||||
...roofAdsorptionPoints.current,
|
||||
...otherAdsorptionPoints,
|
||||
...intersectionPoints.current,
|
||||
...innerLinePoints,
|
||||
]
|
||||
|
||||
let arrivalPoint = { x: pointer.x, y: pointer.y }
|
||||
|
||||
@ -780,7 +825,6 @@ export function useAuxiliaryDrawing(id) {
|
||||
|
||||
//lineHistory.current에 있는 선들 중 startPoint와 endPoint가 겹치는 line은 제거
|
||||
// 겹치는 선 하나는 canvas에서 제거한다.
|
||||
|
||||
const tempLines = [...lineHistory.current]
|
||||
lineHistory.current = []
|
||||
tempLines.forEach((line) => {
|
||||
@ -804,27 +848,30 @@ export function useAuxiliaryDrawing(id) {
|
||||
const tempPolygonPoints = [...roofBase.points].map((obj) => {
|
||||
return { x: Math.round(obj.x), y: Math.round(obj.y) }
|
||||
})
|
||||
const roofInnerLines = innerLines.filter((line) => {
|
||||
const roofInnerLines = [...roofBase.innerLines, ...innerLines].filter((line) => {
|
||||
const inPolygon1 =
|
||||
tempPolygonPoints.some((point) => point.x === line.x1 && point.y === line.y1) ||
|
||||
roofBase.inPolygon({ x: line.x1, y: line.y1 }) ||
|
||||
roofBase.lines.some((line) => isPointOnLine(line, { x: line.x1, y: line.y1 }))
|
||||
tempPolygonPoints.some((point) => Math.round(point.x) === Math.round(line.x1) && Math.round(point.y) === Math.round(line.y1)) ||
|
||||
roofBase.inPolygon({ x: Math.round(line.x1), y: Math.round(line.y1) }) ||
|
||||
roofBase.lines.some((line) => isPointOnLine(line, { x: Math.round(line.x1), y: Math.round(line.y1) }))
|
||||
const inPolygon2 =
|
||||
tempPolygonPoints.some((point) => point.x === line.x2 && point.y === line.y2) ||
|
||||
roofBase.inPolygon({ x: line.x2, y: line.y2 }) ||
|
||||
roofBase.lines.some((line) => isPointOnLine(line, { x: line.x2, y: line.y2 }))
|
||||
tempPolygonPoints.some((point) => Math.round(point.x) === Math.round(line.x2) && Math.round(point.y) === Math.round(line.y2)) ||
|
||||
roofBase.inPolygon({ x: Math.round(line.x2), y: Math.round(line.y2) }) ||
|
||||
roofBase.lines.some((line) => isPointOnLine(line, { x: Math.round(line.x2), y: Math.round(line.y2) }))
|
||||
|
||||
if (inPolygon1 && inPolygon2) {
|
||||
line.attributes = { ...line.attributes, roofId: roofBase.id, actualSize: 0, planeSize: line.getLength() }
|
||||
line.attributes = {
|
||||
...line.attributes,
|
||||
roofId: roofBase.id,
|
||||
actualSize: line.attributes?.actualSize ?? 0,
|
||||
planeSize: line.getLength(),
|
||||
}
|
||||
return true
|
||||
}
|
||||
})
|
||||
|
||||
roofBase.innerLines = [...roofInnerLines]
|
||||
|
||||
roofBase.innerLines = lineHistory.current.length !== 0 ? [...roofInnerLines] : roofBase.innerLines
|
||||
canvas.renderAll()
|
||||
})
|
||||
|
||||
closePopup(id)
|
||||
}
|
||||
|
||||
@ -856,5 +903,8 @@ export function useAuxiliaryDrawing(id) {
|
||||
handleRollback,
|
||||
buttonAct,
|
||||
setButtonAct,
|
||||
move,
|
||||
copy,
|
||||
cutAuxiliary,
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,6 +16,7 @@ export function useEavesGableEdit(id) {
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
const { getMessage } = useMessage()
|
||||
const { addCanvasMouseEventListener, initEvent } = useEvent()
|
||||
// const { addCanvasMouseEventListener, initEvent } = useContext(EventContext)
|
||||
const { closePopup } = usePopup()
|
||||
const TYPES = {
|
||||
EAVES: 'eaves',
|
||||
|
||||
@ -6,7 +6,6 @@ import { useEffect, useRef, useState } from 'react'
|
||||
import { useEvent } from '@/hooks/useEvent'
|
||||
import { POLYGON_TYPE } from '@/common/common'
|
||||
import { OUTER_LINE_TYPE } from '@/store/outerLineAtom'
|
||||
import { QLine } from '@/components/fabric/QLine'
|
||||
|
||||
//동선이동 형 올림 내림
|
||||
export function useMovementSetting(id) {
|
||||
@ -16,6 +15,7 @@ export function useMovementSetting(id) {
|
||||
}
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
const { initEvent, addCanvasMouseEventListener } = useEvent()
|
||||
// const { initEvent, addCanvasMouseEventListener } = useContext(EventContext)
|
||||
const { closePopup } = usePopup()
|
||||
const { getMessage } = useMessage()
|
||||
const currentObject = useRecoilValue(currentObjectState)
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { useEffect, useRef } from 'react'
|
||||
import { useContext, 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,
|
||||
@ -31,6 +31,7 @@ import { fabric } from 'fabric'
|
||||
import { outlineDisplaySelector } from '@/store/settingAtom'
|
||||
import { usePopup } from '@/hooks/usePopup'
|
||||
import PropertiesSetting from '@/components/floor-plan/modal/outerlinesetting/PropertiesSetting'
|
||||
import { EventContext } from '@/app/floor-plan/EventProvider'
|
||||
|
||||
//외벽선 그리기
|
||||
export function useOuterLineWall(id, propertiesId) {
|
||||
@ -43,6 +44,14 @@ export function useOuterLineWall(id, propertiesId) {
|
||||
removeAllDocumentEventListeners,
|
||||
removeMouseEvent,
|
||||
} = useEvent()
|
||||
// const {
|
||||
// initEvent,
|
||||
// // addCanvasMouseEventListener,
|
||||
// // addDocumentEventListener,
|
||||
// removeAllMouseEventListeners,
|
||||
// removeAllDocumentEventListeners,
|
||||
// removeMouseEvent,
|
||||
// } = useContext(EventContext)
|
||||
const { getIntersectMousePoint } = useMouse()
|
||||
const { addLine, removeLine } = useLine()
|
||||
const { tempGridMode } = useTempGrid()
|
||||
@ -65,6 +74,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 +98,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])
|
||||
|
||||
|
||||
@ -125,6 +125,12 @@ export function usePropertiesSetting(id) {
|
||||
}
|
||||
const lines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
|
||||
|
||||
const notSetAttributes = lines.filter((line) => !line.attributes?.type)
|
||||
if (notSetAttributes.length > 0) {
|
||||
alert('설정되지 않은 외벽선이 있습니다.')
|
||||
return
|
||||
}
|
||||
|
||||
lines.forEach((line) => {
|
||||
line.set({
|
||||
attributes: line.attributes ? line.attributes : { offset: 0, type: LINE_TYPE.WALLLINE.WALL },
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { canvasState, currentObjectState } from '@/store/canvasAtom'
|
||||
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
|
||||
import { canvasState, currentMenuState, currentObjectState } from '@/store/canvasAtom'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { setSurfaceShapePattern } from '@/util/canvas-util'
|
||||
import { useSwal } from '@/hooks/useSwal'
|
||||
@ -10,17 +10,22 @@ import { POLYGON_TYPE } from '@/common/common'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import ActualSizeSetting from '@/components/floor-plan/modal/roofAllocation/ActualSizeSetting'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import useMenu from '@/hooks/common/useMenu'
|
||||
import { useCanvasMenu } from '@/hooks/common/useCanvasMenu'
|
||||
import { menuTypeState } from '@/store/menuAtom'
|
||||
|
||||
// 지붕면 할당
|
||||
export function useRoofAllocationSetting(id) {
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
const roofDisplay = useRecoilValue(roofDisplaySelector)
|
||||
const { drawDirectionArrow, addLengthText, splitPolygonWithLines } = usePolygon()
|
||||
const { drawDirectionArrow, addLengthText, splitPolygonWithLines, splitPolygonWithSeparate } = usePolygon()
|
||||
const [popupId, setPopupId] = useState(uuidv4())
|
||||
const { addPopup, closePopup, closeAll } = usePopup()
|
||||
const { getMessage } = useMessage()
|
||||
const currentObject = useRecoilValue(currentObjectState)
|
||||
const { swalFire } = useSwal()
|
||||
const { setMenuNumber } = useCanvasMenu()
|
||||
const setMenuType = useSetRecoilState(menuTypeState)
|
||||
const roofMaterials = [
|
||||
{
|
||||
id: 'A',
|
||||
@ -107,7 +112,7 @@ export function useRoofAllocationSetting(id) {
|
||||
}
|
||||
})
|
||||
})
|
||||
if (currentObject && currentObject.name && !currentObject.name.toLowerCase().includes('text')) {
|
||||
if (currentObject && currentObject.name && ['auxiliaryLine', 'ridge', 'hip'].includes(currentObject.name)) {
|
||||
currentObject.set({
|
||||
strokeWidth: 4,
|
||||
stroke: '#EA10AC',
|
||||
@ -132,6 +137,9 @@ export function useRoofAllocationSetting(id) {
|
||||
setValues(values.filter((value) => value.id !== id))
|
||||
}
|
||||
|
||||
const { handleMenu } = useMenu()
|
||||
const [currentMenu, setCurrentMenu] = useRecoilState(currentMenuState)
|
||||
|
||||
// 선택한 지붕재로 할당
|
||||
const handleSave = () => {
|
||||
// 모두 actualSize 있으면 바로 적용 없으면 actualSize 설정
|
||||
@ -176,20 +184,23 @@ 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 {
|
||||
splitPolygonWithLines(roofBase)
|
||||
if (roofBase.separatePolygon.length > 0) {
|
||||
splitPolygonWithSeparate(roofBase.separatePolygon)
|
||||
} else {
|
||||
splitPolygonWithLines(roofBase)
|
||||
}
|
||||
} catch (e) {
|
||||
return
|
||||
}
|
||||
|
||||
roofBase.innerLines.forEach((line) => {
|
||||
canvas.remove(line)
|
||||
})
|
||||
|
||||
// canvas.remove(roofBase)
|
||||
canvas.remove(roofBase)
|
||||
})
|
||||
|
||||
wallLines.forEach((wallLine) => {
|
||||
@ -199,6 +210,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)
|
||||
})
|
||||
@ -209,6 +224,8 @@ export function useRoofAllocationSetting(id) {
|
||||
})
|
||||
setEditingLines([])
|
||||
closeAll()
|
||||
setMenuNumber(3)
|
||||
setMenuType('surface')
|
||||
}
|
||||
|
||||
const setLineSize = (id, size) => {
|
||||
|
||||
@ -26,6 +26,7 @@ export function useRoofShapePassivitySetting(id) {
|
||||
const { showLine, hideLine, addPitchTextsByOuterLines } = useLine()
|
||||
const { swalFire } = useSwal()
|
||||
const { addCanvasMouseEventListener, initEvent } = useEvent()
|
||||
// const { addCanvasMouseEventListener, initEvent } = useContext(EventContext)
|
||||
const { drawRoofPolygon } = useMode()
|
||||
const { addPolygonByLines } = usePolygon()
|
||||
const currentObject = useRecoilValue(currentObjectState)
|
||||
@ -202,7 +203,11 @@ export function useRoofShapePassivitySetting(id) {
|
||||
wall = addPolygonByLines(lines, { name: POLYGON_TYPE.WALL, fill: 'transparent', stroke: 'black' })
|
||||
} else {
|
||||
// 그냥 닫을 경우 처리
|
||||
wall = addPolygonByLines([...initLines.current], { name: POLYGON_TYPE.WALL, fill: 'transparent', stroke: 'black' })
|
||||
wall = addPolygonByLines([...initLines.current], {
|
||||
name: POLYGON_TYPE.WALL,
|
||||
fill: 'transparent',
|
||||
stroke: 'black',
|
||||
})
|
||||
lines.forEach((line, idx) => {
|
||||
line.attributes = initLines.current[idx].attributes
|
||||
})
|
||||
|
||||
@ -377,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)
|
||||
.filter((obj) => obj.name === POLYGON_TYPE.ROOF && !obj.isFixed)
|
||||
.forEach((obj) => {
|
||||
canvas.remove(...obj.innerLines)
|
||||
canvas.remove(obj)
|
||||
})*/
|
||||
})
|
||||
|
||||
const polygon = addPolygonByLines(outerLines, { name: POLYGON_TYPE.WALL, direction })
|
||||
polygon.lines = [...outerLines]
|
||||
|
||||
@ -15,6 +15,7 @@ export function useWallLineOffsetSetting(id) {
|
||||
const { closePopup } = usePopup()
|
||||
const { swalFire } = useSwal()
|
||||
const { addCanvasMouseEventListener, initEvent } = useEvent()
|
||||
// const { addCanvasMouseEventListener, initEvent } = useContext(EventContext)
|
||||
const wallLineEditRef = useRef(null)
|
||||
const length1Ref = useRef(null)
|
||||
const length2Ref = useRef(null)
|
||||
|
||||
@ -33,12 +33,15 @@ import { POLYGON_TYPE } from '@/common/common'
|
||||
import { usePopup } from '@/hooks/usePopup'
|
||||
|
||||
import { roofDisplaySelector } from '@/store/settingAtom'
|
||||
|
||||
// 면형상 배치
|
||||
export function usePlacementShapeDrawing(id) {
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
const roofDisplay = useRecoilValue(roofDisplaySelector)
|
||||
const { addCanvasMouseEventListener, addDocumentEventListener, removeAllMouseEventListeners, removeAllDocumentEventListeners, removeMouseEvent } =
|
||||
useEvent()
|
||||
// const { addCanvasMouseEventListener, addDocumentEventListener, removeAllMouseEventListeners, removeAllDocumentEventListeners, removeMouseEvent } =
|
||||
// useContext(EventContext)
|
||||
const { getIntersectMousePoint } = useMouse()
|
||||
const { addLine, removeLine } = useLine()
|
||||
const { addPolygonByLines, drawDirectionArrow } = usePolygon()
|
||||
@ -429,56 +432,104 @@ export function usePlacementShapeDrawing(id) {
|
||||
if (prev.length === 0) {
|
||||
return []
|
||||
}
|
||||
return [...prev, { x: prev[prev.length - 1].x + length1Value / 10, y: prev[prev.length - 1].y + length2Value / 10 }]
|
||||
return [
|
||||
...prev,
|
||||
{
|
||||
x: prev[prev.length - 1].x + length1Value / 10,
|
||||
y: prev[prev.length - 1].y + length2Value / 10,
|
||||
},
|
||||
]
|
||||
})
|
||||
} else if (arrow1Value === '↓' && arrow2Value === '←') {
|
||||
setPoints((prev) => {
|
||||
if (prev.length === 0) {
|
||||
return []
|
||||
}
|
||||
return [...prev, { x: prev[prev.length - 1].x - length1Value / 10, y: prev[prev.length - 1].y + length2Value / 10 }]
|
||||
return [
|
||||
...prev,
|
||||
{
|
||||
x: prev[prev.length - 1].x - length1Value / 10,
|
||||
y: prev[prev.length - 1].y + length2Value / 10,
|
||||
},
|
||||
]
|
||||
})
|
||||
} else if (arrow1Value === '↑' && arrow2Value === '→') {
|
||||
setPoints((prev) => {
|
||||
if (prev.length === 0) {
|
||||
return []
|
||||
}
|
||||
return [...prev, { x: prev[prev.length - 1].x + length1Value / 10, y: prev[prev.length - 1].y - length2Value / 10 }]
|
||||
return [
|
||||
...prev,
|
||||
{
|
||||
x: prev[prev.length - 1].x + length1Value / 10,
|
||||
y: prev[prev.length - 1].y - length2Value / 10,
|
||||
},
|
||||
]
|
||||
})
|
||||
} else if (arrow1Value === '↑' && arrow2Value === '←') {
|
||||
setPoints((prev) => {
|
||||
if (prev.length === 0) {
|
||||
return []
|
||||
}
|
||||
return [...prev, { x: prev[prev.length - 1].x - length1Value / 10, y: prev[prev.length - 1].y - length2Value / 10 }]
|
||||
return [
|
||||
...prev,
|
||||
{
|
||||
x: prev[prev.length - 1].x - length1Value / 10,
|
||||
y: prev[prev.length - 1].y - length2Value / 10,
|
||||
},
|
||||
]
|
||||
})
|
||||
} else if (arrow1Value === '→' && arrow2Value === '↓') {
|
||||
setPoints((prev) => {
|
||||
if (prev.length === 0) {
|
||||
return []
|
||||
}
|
||||
return [...prev, { x: prev[prev.length - 1].x + length2Value / 10, y: prev[prev.length - 1].y + length1Value / 10 }]
|
||||
return [
|
||||
...prev,
|
||||
{
|
||||
x: prev[prev.length - 1].x + length2Value / 10,
|
||||
y: prev[prev.length - 1].y + length1Value / 10,
|
||||
},
|
||||
]
|
||||
})
|
||||
} else if (arrow1Value === '→' && arrow2Value === '↑') {
|
||||
setPoints((prev) => {
|
||||
if (prev.length === 0) {
|
||||
return []
|
||||
}
|
||||
return [...prev, { x: prev[prev.length - 1].x + length2Value / 10, y: prev[prev.length - 1].y - length1Value / 10 }]
|
||||
return [
|
||||
...prev,
|
||||
{
|
||||
x: prev[prev.length - 1].x + length2Value / 10,
|
||||
y: prev[prev.length - 1].y - length1Value / 10,
|
||||
},
|
||||
]
|
||||
})
|
||||
} else if (arrow1Value === '←' && arrow2Value === '↓') {
|
||||
setPoints((prev) => {
|
||||
if (prev.length === 0) {
|
||||
return []
|
||||
}
|
||||
return [...prev, { x: prev[prev.length - 1].x - length2Value / 10, y: prev[prev.length - 1].y + length1Value / 10 }]
|
||||
return [
|
||||
...prev,
|
||||
{
|
||||
x: prev[prev.length - 1].x - length2Value / 10,
|
||||
y: prev[prev.length - 1].y + length1Value / 10,
|
||||
},
|
||||
]
|
||||
})
|
||||
} else if (arrow1Value === '←' && arrow2Value === '↑') {
|
||||
setPoints((prev) => {
|
||||
if (prev.length === 0) {
|
||||
return []
|
||||
}
|
||||
return [...prev, { x: prev[prev.length - 1].x - length2Value / 10, y: prev[prev.length - 1].y - length1Value / 10 }]
|
||||
return [
|
||||
...prev,
|
||||
{
|
||||
x: prev[prev.length - 1].x - length2Value / 10,
|
||||
y: prev[prev.length - 1].y - length1Value / 10,
|
||||
},
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
@ -534,35 +585,65 @@ export function usePlacementShapeDrawing(id) {
|
||||
if (prev.length === 0) {
|
||||
return []
|
||||
}
|
||||
return [...prev, { x: prev[prev.length - 1].x + length2Value / 10, y: prev[prev.length - 1].y + length1Value / 10 }]
|
||||
return [
|
||||
...prev,
|
||||
{
|
||||
x: prev[prev.length - 1].x + length2Value / 10,
|
||||
y: prev[prev.length - 1].y + length1Value / 10,
|
||||
},
|
||||
]
|
||||
})
|
||||
} else if (arrow1Value === '↓' && arrow2Value === '←') {
|
||||
setPoints((prev) => {
|
||||
if (prev.length === 0) {
|
||||
return []
|
||||
}
|
||||
return [...prev, { x: prev[prev.length - 1].x - length2Value / 10, y: prev[prev.length - 1].y + length1Value / 10 }]
|
||||
return [
|
||||
...prev,
|
||||
{
|
||||
x: prev[prev.length - 1].x - length2Value / 10,
|
||||
y: prev[prev.length - 1].y + length1Value / 10,
|
||||
},
|
||||
]
|
||||
})
|
||||
} else if (arrow1Value === '↑' && arrow2Value === '→') {
|
||||
setPoints((prev) => {
|
||||
if (prev.length === 0) {
|
||||
return []
|
||||
}
|
||||
return [...prev, { x: prev[prev.length - 1].x + length2Value / 10, y: prev[prev.length - 1].y - length1Value / 10 }]
|
||||
return [
|
||||
...prev,
|
||||
{
|
||||
x: prev[prev.length - 1].x + length2Value / 10,
|
||||
y: prev[prev.length - 1].y - length1Value / 10,
|
||||
},
|
||||
]
|
||||
})
|
||||
} else if (arrow1Value === '↑' && arrow2Value === '←') {
|
||||
setPoints((prev) => {
|
||||
if (prev.length === 0) {
|
||||
return []
|
||||
}
|
||||
return [...prev, { x: prev[prev.length - 1].x - length2Value / 10, y: prev[prev.length - 1].y - length1Value / 10 }]
|
||||
return [
|
||||
...prev,
|
||||
{
|
||||
x: prev[prev.length - 1].x - length2Value / 10,
|
||||
y: prev[prev.length - 1].y - length1Value / 10,
|
||||
},
|
||||
]
|
||||
})
|
||||
} else if (arrow1Value === '→' && arrow2Value === '↓') {
|
||||
setPoints((prev) => {
|
||||
if (prev.length === 0) {
|
||||
return []
|
||||
}
|
||||
return [...prev, { x: prev[prev.length - 1].x + length1Value / 10, y: prev[prev.length - 1].y + length2Value / 10 }]
|
||||
return [
|
||||
...prev,
|
||||
{
|
||||
x: prev[prev.length - 1].x + length1Value / 10,
|
||||
y: prev[prev.length - 1].y + length2Value / 10,
|
||||
},
|
||||
]
|
||||
})
|
||||
} else if (arrow1Value === '→' && arrow2Value === '↑') {
|
||||
setPoints((prev) => {
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { canvasState, globalPitchState } from '@/store/canvasAtom'
|
||||
import { MENU, BATCH_TYPE, POLYGON_TYPE } from '@/common/common'
|
||||
import { MENU, POLYGON_TYPE } from '@/common/common'
|
||||
import { getIntersectionPoint, setSurfaceShapePattern } from '@/util/canvas-util'
|
||||
import { degreesToRadians } from '@turf/turf'
|
||||
import { QPolygon } from '@/components/fabric/QPolygon'
|
||||
@ -27,6 +27,7 @@ export function useSurfaceShapeBatch() {
|
||||
const slope = useRecoilValue(slopeSelector(globalPitch))
|
||||
const { swalFire } = useSwal()
|
||||
const { addCanvasMouseEventListener, initEvent } = useEvent()
|
||||
// const { addCanvasMouseEventListener, initEvent } = useContext(EventContext)
|
||||
const { closePopup } = usePopup()
|
||||
|
||||
const applySurfaceShape = (surfaceRefs, selectedType, id) => {
|
||||
@ -102,6 +103,15 @@ export function useSurfaceShapeBatch() {
|
||||
|
||||
canvas?.add(obj)
|
||||
|
||||
canvas?.renderAll()
|
||||
closePopup(id)
|
||||
})
|
||||
|
||||
addCanvasMouseEventListener('mouse:down', (e) => {
|
||||
isDrawing = false
|
||||
|
||||
canvas?.remove(obj)
|
||||
|
||||
//각도 추가
|
||||
let originAngle = 0 //기본 남쪽
|
||||
let direction = 'south'
|
||||
@ -119,21 +129,31 @@ export function useSurfaceShapeBatch() {
|
||||
direction = 'north'
|
||||
}
|
||||
|
||||
obj.set({ direction: direction })
|
||||
obj.set({ originAngle: originAngle })
|
||||
|
||||
canvas?.renderAll()
|
||||
//회전, flip등이 먹은 기준으로 새로생성
|
||||
const batchSurface = new QPolygon(obj.getCurrentPoints(), {
|
||||
fill: 'transparent',
|
||||
stroke: 'red',
|
||||
strokeWidth: 1,
|
||||
strokeDasharray: [10, 4],
|
||||
fontSize: 12,
|
||||
selectable: true,
|
||||
lockMovementX: true, // X 축 이동 잠금
|
||||
lockMovementY: true, // Y 축 이동 잠금
|
||||
lockRotation: true, // 회전 잠금
|
||||
lockScalingX: true, // X 축 크기 조정 잠금
|
||||
lockScalingY: true, // Y 축 크기 조정 잠금
|
||||
name: POLYGON_TYPE.ROOF,
|
||||
originX: 'center',
|
||||
originY: 'center',
|
||||
pitch: globalPitch,
|
||||
surfaceId: surfaceId,
|
||||
direction: direction,
|
||||
})
|
||||
canvas?.add(batchSurface)
|
||||
setSurfaceShapePattern(batchSurface, roofDisplay.column)
|
||||
drawDirectionArrow(batchSurface)
|
||||
closePopup(id)
|
||||
})
|
||||
|
||||
addCanvasMouseEventListener('mouse:down', (e) => {
|
||||
isDrawing = false
|
||||
obj.set('name', POLYGON_TYPE.ROOF)
|
||||
obj.set('surfaceId', surfaceId)
|
||||
initEvent()
|
||||
setSurfaceShapePattern(obj, roofDisplay.column)
|
||||
closePopup(id)
|
||||
drawDirectionArrow(obj)
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -307,8 +327,14 @@ export function useSurfaceShapeBatch() {
|
||||
const angleInRadians = Math.asin(length2 / length3)
|
||||
points = [
|
||||
{ x: pointer.x - length1 / 2, y: pointer.y + length2 / 2 },
|
||||
{ x: pointer.x - length1 / 2 + length3 * Math.cos(angleInRadians), y: pointer.y + length2 / 2 - length3 * Math.sin(angleInRadians) },
|
||||
{ x: pointer.x + length1 / 2 + length3 * Math.cos(angleInRadians), y: pointer.y + length2 / 2 - length3 * Math.sin(angleInRadians) },
|
||||
{
|
||||
x: pointer.x - length1 / 2 + length3 * Math.cos(angleInRadians),
|
||||
y: pointer.y + length2 / 2 - length3 * Math.sin(angleInRadians),
|
||||
},
|
||||
{
|
||||
x: pointer.x + length1 / 2 + length3 * Math.cos(angleInRadians),
|
||||
y: pointer.y + length2 / 2 - length3 * Math.sin(angleInRadians),
|
||||
},
|
||||
{ x: pointer.x + length1 / 2, y: pointer.y + length2 / 2 },
|
||||
]
|
||||
|
||||
@ -319,9 +345,18 @@ export function useSurfaceShapeBatch() {
|
||||
{ x: pointer.x - (length1 + length2 + length3) / 2, y: pointer.y - (length4 + length5) / 2 },
|
||||
{ x: pointer.x - (length1 + length2 + length3) / 2, y: pointer.y + (length4 + length5) / 2 },
|
||||
{ x: pointer.x - (length1 + length2 + length3) / 2 + length1, y: pointer.y + (length4 + length5) / 2 },
|
||||
{ x: pointer.x - (length1 + length2 + length3) / 2 + length1, y: pointer.y + (length4 + length5) / 2 - length5 },
|
||||
{ x: pointer.x - (length1 + length2 + length3) / 2 + length1 + length2, y: pointer.y + (length4 + length5) / 2 - length5 },
|
||||
{ x: pointer.x - (length1 + length2 + length3) / 2 + length1 + length2, y: pointer.y + (length4 + length5) / 2 - length5 + length5 },
|
||||
{
|
||||
x: pointer.x - (length1 + length2 + length3) / 2 + length1,
|
||||
y: pointer.y + (length4 + length5) / 2 - length5,
|
||||
},
|
||||
{
|
||||
x: pointer.x - (length1 + length2 + length3) / 2 + length1 + length2,
|
||||
y: pointer.y + (length4 + length5) / 2 - length5,
|
||||
},
|
||||
{
|
||||
x: pointer.x - (length1 + length2 + length3) / 2 + length1 + length2,
|
||||
y: pointer.y + (length4 + length5) / 2 - length5 + length5,
|
||||
},
|
||||
{
|
||||
x: pointer.x - (length1 + length2 + length3) / 2 + length1 + length2 + length3,
|
||||
y: pointer.y + (length4 + length5) / 2 - length5 + length5,
|
||||
@ -340,8 +375,14 @@ export function useSurfaceShapeBatch() {
|
||||
{ x: pointer.x - length1 / 2 + length1, y: pointer.y + length4 / 2 },
|
||||
{ x: pointer.x - length1 / 2 + length1, y: pointer.y + length4 / 2 - length3 },
|
||||
{ x: pointer.x - length1 / 2 + length1 - (length1 - length2), y: pointer.y + length4 / 2 - length3 },
|
||||
{ x: pointer.x - length1 / 2 + length1 - (length1 - length2), y: pointer.y + length4 / 2 - length3 + (length3 - length4) },
|
||||
{ x: pointer.x - length1 / 2 + length1 - (length1 - length2) - length2, y: pointer.y + length4 / 2 - length3 + (length3 - length4) },
|
||||
{
|
||||
x: pointer.x - length1 / 2 + length1 - (length1 - length2),
|
||||
y: pointer.y + length4 / 2 - length3 + (length3 - length4),
|
||||
},
|
||||
{
|
||||
x: pointer.x - length1 / 2 + length1 - (length1 - length2) - length2,
|
||||
y: pointer.y + length4 / 2 - length3 + (length3 - length4),
|
||||
},
|
||||
]
|
||||
|
||||
break
|
||||
@ -352,8 +393,14 @@ export function useSurfaceShapeBatch() {
|
||||
{ x: pointer.x - length1 / 2 + length1, y: pointer.y + length4 / 2 },
|
||||
{ x: pointer.x - length1 / 2 + length1, y: pointer.y + length4 / 2 - length4 },
|
||||
{ x: pointer.x - length1 / 2 + length1 - length3, y: pointer.y + length4 / 2 - length4 },
|
||||
{ x: pointer.x - length1 / 2 + length1 - length3, y: pointer.y + length4 / 2 - length4 + (length4 - length5) },
|
||||
{ x: pointer.x - length1 / 2 + length1 - length3 - length2, y: pointer.y + length4 / 2 - length4 + (length4 - length5) },
|
||||
{
|
||||
x: pointer.x - length1 / 2 + length1 - length3,
|
||||
y: pointer.y + length4 / 2 - length4 + (length4 - length5),
|
||||
},
|
||||
{
|
||||
x: pointer.x - length1 / 2 + length1 - length3 - length2,
|
||||
y: pointer.y + length4 / 2 - length4 + (length4 - length5),
|
||||
},
|
||||
]
|
||||
break
|
||||
}
|
||||
@ -363,8 +410,14 @@ export function useSurfaceShapeBatch() {
|
||||
{ x: pointer.x + length1 / 2 - length1, y: pointer.y + length4 / 2 },
|
||||
{ x: pointer.x + length1 / 2 - length1, y: pointer.y + length4 / 2 - length5 },
|
||||
{ x: pointer.x + length1 / 2 - length1 + length2, y: pointer.y + length4 / 2 - length5 },
|
||||
{ x: pointer.x + length1 / 2 - length1 + length2, y: pointer.y + length4 / 2 - length5 - (length4 - length5) },
|
||||
{ x: pointer.x + length1 / 2 - length1 + length2 + length3, y: pointer.y + length4 / 2 - length5 - (length4 - length5) },
|
||||
{
|
||||
x: pointer.x + length1 / 2 - length1 + length2,
|
||||
y: pointer.y + length4 / 2 - length5 - (length4 - length5),
|
||||
},
|
||||
{
|
||||
x: pointer.x + length1 / 2 - length1 + length2 + length3,
|
||||
y: pointer.y + length4 / 2 - length5 - (length4 - length5),
|
||||
},
|
||||
]
|
||||
break
|
||||
}
|
||||
@ -374,8 +427,14 @@ export function useSurfaceShapeBatch() {
|
||||
{ x: pointer.x - length1 / 2 + length1, y: pointer.y + length4 / 2 },
|
||||
{ x: pointer.x - length1 / 2 + length1, y: pointer.y + length4 / 2 - length5 },
|
||||
{ x: pointer.x - length1 / 2 + length1 - length2, y: pointer.y + length4 / 2 - length5 },
|
||||
{ x: pointer.x - length1 / 2 + length1 - length2, y: pointer.y + length4 / 2 - length5 - (length4 - length5) },
|
||||
{ x: pointer.x - length1 / 2 + length1 - length2 - length3, y: pointer.y + length4 / 2 - length5 - (length4 - length5) },
|
||||
{
|
||||
x: pointer.x - length1 / 2 + length1 - length2,
|
||||
y: pointer.y + length4 / 2 - length5 - (length4 - length5),
|
||||
},
|
||||
{
|
||||
x: pointer.x - length1 / 2 + length1 - length2 - length3,
|
||||
y: pointer.y + length4 / 2 - length5 - (length4 - length5),
|
||||
},
|
||||
]
|
||||
break
|
||||
}
|
||||
@ -386,7 +445,10 @@ export function useSurfaceShapeBatch() {
|
||||
const leftAngle = Math.acos((length1 - length2) / 2 / leftHypotenuse)
|
||||
|
||||
points = [
|
||||
{ x: pointer.x - length1 / 2 + leftHypotenuse * Math.cos(leftAngle), y: pointer.y + length3 / 2 - leftHypotenuse * Math.sin(leftAngle) },
|
||||
{
|
||||
x: pointer.x - length1 / 2 + leftHypotenuse * Math.cos(leftAngle),
|
||||
y: pointer.y + length3 / 2 - leftHypotenuse * Math.sin(leftAngle),
|
||||
},
|
||||
{ x: pointer.x - length1 / 2, y: pointer.y + length3 / 2 },
|
||||
{ x: pointer.x + length1 / 2, y: pointer.y + length3 / 2 },
|
||||
{
|
||||
@ -419,8 +481,8 @@ export function useSurfaceShapeBatch() {
|
||||
{
|
||||
fill: 'transparent',
|
||||
stroke: 'black', //black
|
||||
strokeWidth: 2,
|
||||
selectable: true,
|
||||
strokeWidth: 1,
|
||||
selectable: false,
|
||||
fontSize: 0,
|
||||
},
|
||||
)
|
||||
@ -429,6 +491,7 @@ export function useSurfaceShapeBatch() {
|
||||
const scale = (length1 - length2) / coord.x
|
||||
|
||||
tmpPolygon.set({ scaleX: scale })
|
||||
tmpPolygon.setViewLengthText(false)
|
||||
|
||||
pointsArray[0].x = 0
|
||||
pointsArray[0].y = length3 //바닥면부터 시작하게
|
||||
@ -474,7 +537,10 @@ export function useSurfaceShapeBatch() {
|
||||
{ x: pointer.x - length1 / 2 + length1, y: pointer.y + length4 / 2 - length4 },
|
||||
{ x: pointer.x - length1 / 2 + length1, y: pointer.y + length4 / 2 - length4 + length4 },
|
||||
{ x: pointer.x - length1 / 2 + length1 - length3, y: pointer.y + length4 / 2 - length4 + length4 },
|
||||
{ x: pointer.x - length1 / 2 + length2 + (length1 - length2 - length3) / 2, y: pointer.y + length4 / 2 - length4 + length5 },
|
||||
{
|
||||
x: pointer.x - length1 / 2 + length2 + (length1 - length2 - length3) / 2,
|
||||
y: pointer.y + length4 / 2 - length4 + length5,
|
||||
},
|
||||
]
|
||||
break
|
||||
}
|
||||
@ -584,18 +650,6 @@ 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.clear()
|
||||
swalFire({ text: '삭제 완료 되었습니다.' })
|
||||
},
|
||||
@ -661,34 +715,6 @@ export function useSurfaceShapeBatch() {
|
||||
return groupObjectsArray
|
||||
}
|
||||
|
||||
function getAllRelatedObjects(id) {
|
||||
const ult = []
|
||||
const map = new Map()
|
||||
|
||||
// Create a map of objects by their id
|
||||
canvas.getObjects().forEach((obj) => {
|
||||
map.set(obj.id, obj)
|
||||
})
|
||||
|
||||
// Helper function to recursively find all related objects
|
||||
function findRelatedObjects(id) {
|
||||
const obj = map.get(id)
|
||||
if (obj) {
|
||||
result.push(obj)
|
||||
canvas.getObjects().forEach((o) => {
|
||||
if (o.parentId === id) {
|
||||
findRelatedObjects(o.id)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Start the search with the given parentId
|
||||
findRelatedObjects(id)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
const moveSurfaceShapeBatch = () => {
|
||||
const roof = canvas.getActiveObject()
|
||||
|
||||
@ -828,136 +854,75 @@ export function useSurfaceShapeBatch() {
|
||||
canvas.renderAll()
|
||||
}
|
||||
|
||||
const surfaceShapeActualSize = () => {
|
||||
const roofs = canvas.getObjects().filter((obj) => obj.name === 'roof')
|
||||
let notParallelLines = []
|
||||
|
||||
const roofArray = []
|
||||
|
||||
roofs.forEach((obj) => {
|
||||
const roof = {
|
||||
id: obj.id,
|
||||
surfaceId: obj.surfaceId,
|
||||
targetLines: [],
|
||||
baseLines: [],
|
||||
direction: obj.direction,
|
||||
}
|
||||
obj.lines.forEach((line) => {
|
||||
if (obj.direction === 'north' || obj.direction === 'south') {
|
||||
if (line.y1 !== line.y2) {
|
||||
roof.targetLines.push(line)
|
||||
} else {
|
||||
roof.baseLines.push(line)
|
||||
}
|
||||
} else {
|
||||
if (line.x1 !== line.x2) {
|
||||
roof.targetLines.push(line)
|
||||
} else {
|
||||
roof.baseLines.push(line)
|
||||
}
|
||||
}
|
||||
})
|
||||
roofArray.push(roof)
|
||||
})
|
||||
|
||||
const slopeRadians = slope.angleValue * (Math.PI / 180)
|
||||
const tanSlope = Math.tan(slopeRadians)
|
||||
|
||||
roofArray.forEach((roof) => {
|
||||
roof.targetLines.forEach((line) => {
|
||||
let calcLength = line.length
|
||||
|
||||
if (roof.surfaceId === 1) {
|
||||
calcLength = roof.direction === 'north' || roof.direction === 'south' ? Math.abs(line.y1 - line.y2) : Math.abs(line.x1 - line.x2)
|
||||
}
|
||||
|
||||
const h = calcLength * tanSlope
|
||||
const resultLength = Math.sqrt(Math.pow(calcLength, 2) + Math.pow(h, 2))
|
||||
|
||||
line.set({
|
||||
attributes: {
|
||||
...line.attributes,
|
||||
actualSize: parseInt((Math.floor(resultLength) * 10).toFixed(0)),
|
||||
planeSize: parseInt(line.length * 10),
|
||||
},
|
||||
})
|
||||
})
|
||||
roof.baseLines.forEach((line) => {
|
||||
line.set({
|
||||
attributes: {
|
||||
...line.attributes,
|
||||
actualSize: parseInt((Math.floor(line.length) * 10).toFixed(0)),
|
||||
planeSize: parseInt(line.length * 10),
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const changeSurfaceLinePropertyEvent = (roof) => {
|
||||
const changeSurfaceLinePropertyEvent = () => {
|
||||
let tmpLines = []
|
||||
roof.set({
|
||||
selectable: false,
|
||||
})
|
||||
canvas.discardActiveObject()
|
||||
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',
|
||||
index: index,
|
||||
const roof = canvas.getActiveObject()
|
||||
|
||||
if (roof) {
|
||||
roof.set({
|
||||
selectable: false,
|
||||
})
|
||||
|
||||
tmpLines.push(tmpLine)
|
||||
canvas.add(tmpLine)
|
||||
})
|
||||
|
||||
addCanvasMouseEventListener('mouse:down', (e) => {
|
||||
const selectedLine = e.target
|
||||
if (selectedLine) {
|
||||
selectedLine.set({
|
||||
stroke: 'red',
|
||||
name: 'selectedLineProperty',
|
||||
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.forEach((line) => {
|
||||
if (line.index !== selectedLine.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',
|
||||
})
|
||||
}
|
||||
})
|
||||
} else {
|
||||
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.renderAll()
|
||||
}
|
||||
canvas.discardActiveObject()
|
||||
}
|
||||
|
||||
const changeSurfaceLineProperty = (property) => {
|
||||
console.log(property)
|
||||
const changeSurfaceLineProperty = (property, roof) => {
|
||||
if (!property) {
|
||||
swalFire({ text: getMessage('modal.line.property.change'), icon: 'error' })
|
||||
return
|
||||
}
|
||||
|
||||
const selectedLine = canvas.getActiveObjects()
|
||||
if (selectedLine && selectedLine[0].name === 'selectedLineProperty') {
|
||||
const selectedLine = canvas.getActiveObjects()[0] //배열로 떨어짐 한개만 선택가능
|
||||
if (selectedLine && selectedLine.name === 'selectedLineProperty') {
|
||||
swalFire({
|
||||
text: getMessage('modal.line.property.change.confirm'),
|
||||
type: 'confirm',
|
||||
confirmFn: () => {
|
||||
selectedLine.set({
|
||||
const lineIndex = selectedLine.lineIndex
|
||||
roof.lines[lineIndex].attributes = {
|
||||
...roof.lines[lineIndex].attributes,
|
||||
type: property.value,
|
||||
})
|
||||
}
|
||||
canvas.renderAll()
|
||||
},
|
||||
})
|
||||
} else {
|
||||
@ -965,22 +930,70 @@ export function useSurfaceShapeBatch() {
|
||||
}
|
||||
}
|
||||
|
||||
const changeSurfaceFlowDirection = (roof, direction, orientation) => {
|
||||
roof.set({
|
||||
direction: direction,
|
||||
const changeSurfaceLinePropertyReset = (roof) => {
|
||||
const lines = canvas.getObjects().filter((obj) => obj.name === 'lineProperty' || obj.name === 'selectedLineProperty')
|
||||
|
||||
lines.forEach((line) => {
|
||||
canvas.remove(line)
|
||||
})
|
||||
drawDirectionArrow(roof)
|
||||
|
||||
if (roof) {
|
||||
roof.set({
|
||||
selectable: true,
|
||||
})
|
||||
}
|
||||
canvas?.renderAll()
|
||||
}
|
||||
|
||||
const updateFlippedPoints = (polygon) => {
|
||||
if (!(polygon instanceof fabric.Polygon)) {
|
||||
console.error('The object is not a Polygon.')
|
||||
return
|
||||
}
|
||||
|
||||
const { flipX, flipY, width, height, points, left, top, scaleX, scaleY } = polygon
|
||||
|
||||
// 현재 points의 사본 가져오기
|
||||
const newPoints = points.map((point) => {
|
||||
let x = point.x
|
||||
let y = point.y
|
||||
|
||||
// flipX 적용
|
||||
if (flipX) {
|
||||
x = width - x
|
||||
}
|
||||
|
||||
// flipY 적용
|
||||
if (flipY) {
|
||||
y = height - y
|
||||
}
|
||||
|
||||
// 스케일 및 전역 좌표 고려
|
||||
x = (x - width / 2) * scaleX + width / 2
|
||||
y = (y - height / 2) * scaleY + height / 2
|
||||
|
||||
return { x, y }
|
||||
})
|
||||
|
||||
// flipX, flipY를 초기화
|
||||
polygon.flipX = false
|
||||
polygon.flipY = false
|
||||
|
||||
// points 업데이트
|
||||
polygon.set({ points: newPoints })
|
||||
polygon.setCoords()
|
||||
|
||||
return polygon
|
||||
}
|
||||
|
||||
return {
|
||||
applySurfaceShape,
|
||||
deleteAllSurfacesAndObjects,
|
||||
moveSurfaceShapeBatch,
|
||||
resizeSurfaceShapeBatch,
|
||||
surfaceShapeActualSize,
|
||||
changeSurfaceFlowDirection,
|
||||
|
||||
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' })
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
|
||||
import { currentMenuState, currentObjectState } from '@/store/canvasAtom'
|
||||
import { canvasState, currentMenuState, currentObjectState } from '@/store/canvasAtom'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { MENU } from '@/common/common'
|
||||
import AuxiliaryMove from '@/components/floor-plan/modal/auxiliary/AuxiliaryMove'
|
||||
import AuxiliarySize from '@/components/floor-plan/modal/auxiliary/AuxiliarySize'
|
||||
import { usePopup } from '@/hooks/usePopup'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
@ -11,7 +10,7 @@ import GridCopy from '@/components/floor-plan/modal/grid/GridCopy'
|
||||
import ColorPickerModal from '@/components/common/color-picker/ColorPickerModal'
|
||||
import { gridColorState } from '@/store/gridAtom'
|
||||
import { contextPopupPositionState, contextPopupState } from '@/store/popupAtom'
|
||||
import AuxiliaryCopy from '@/components/floor-plan/modal/auxiliary/AuxiliaryCopy'
|
||||
import AuxiliaryEdit from '@/components/floor-plan/modal/auxiliary/AuxiliaryEdit'
|
||||
import SizeSetting from '@/components/floor-plan/modal/object/SizeSetting'
|
||||
import RoofMaterialSetting from '@/components/floor-plan/modal/object/RoofMaterialSetting'
|
||||
import DormerOffset from '@/components/floor-plan/modal/object/DormerOffset'
|
||||
@ -24,7 +23,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,8 +32,12 @@ 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'
|
||||
import { useLine } from '@/hooks/useLine'
|
||||
import { useSwal } from '@/hooks/useSwal'
|
||||
|
||||
export function useContextMenu() {
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
const currentMenu = useRecoilValue(currentMenuState) // 현재 메뉴
|
||||
const setContextPopupPosition = useSetRecoilState(contextPopupPositionState) // 현재 메뉴
|
||||
const [contextMenu, setContextMenu] = useRecoilState(contextMenuListState) // 메뉴.object 별 context menu
|
||||
@ -51,7 +53,12 @@ export function useContextMenu() {
|
||||
const [column, setColumn] = useState(null)
|
||||
const { handleZoomClear } = useCanvasEvent()
|
||||
const { moveObjectBatch } = useObjectBatch({})
|
||||
const { moveSurfaceShapeBatch, surfaceShapeActualSize } = useSurfaceShapeBatch()
|
||||
const { moveSurfaceShapeBatch } = useSurfaceShapeBatch()
|
||||
const [globalFont, setGlobalFont] = useRecoilState(globalFontAtom)
|
||||
const { addLine, removeLine } = useLine()
|
||||
const commonTextFont = useRecoilValue(fontSelector('commonText'))
|
||||
const { swalFire } = useSwal()
|
||||
|
||||
const currentMenuSetting = () => {
|
||||
switch (currentMenu) {
|
||||
case MENU.PLAN_DRAWING:
|
||||
@ -95,11 +102,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 +123,6 @@ export function useContextMenu() {
|
||||
name: getMessage('contextmenu.wallline.remove'),
|
||||
fn: () => deleteOuterLineObject(),
|
||||
},
|
||||
{
|
||||
id: 'imageSizeEdit',
|
||||
name: getMessage('modal.image.size.setting'),
|
||||
component: <ImageSizeSetting id={popupId} />,
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
@ -137,30 +134,81 @@ export function useContextMenu() {
|
||||
id: 'auxiliaryMove',
|
||||
name: `${getMessage('contextmenu.auxiliary.move')}(M)`,
|
||||
shortcut: ['m', 'M'],
|
||||
component: <AuxiliaryMove id={popupId} />,
|
||||
component: <AuxiliaryEdit id={popupId} type={'move'} />,
|
||||
},
|
||||
{
|
||||
id: 'auxiliaryCopy',
|
||||
name: `${getMessage('contextmenu.auxiliary.copy')}(C)`,
|
||||
shortcut: ['c', 'C'],
|
||||
component: <AuxiliaryCopy id={popupId} />,
|
||||
component: <AuxiliaryEdit id={popupId} type={'copy'} />,
|
||||
},
|
||||
{
|
||||
id: 'auxiliaryRemove',
|
||||
shortcut: ['d', 'D'],
|
||||
name: `${getMessage('contextmenu.auxiliary.remove')}(D)`,
|
||||
fn: () => {
|
||||
const roof = canvas.getObjects().filter((obj) => obj.id === currentObject.attributes.roofId)[0]
|
||||
const innerLines = roof.innerLines?.filter((line) => currentObject.id !== line.id)
|
||||
roof.innerLines = [...innerLines]
|
||||
canvas.remove(currentObject)
|
||||
canvas.discardActiveObject()
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'auxiliaryVerticalBisector',
|
||||
name: getMessage('contextmenu.auxiliary.vertical.bisector'),
|
||||
},
|
||||
{
|
||||
id: 'auxiliaryCut',
|
||||
name: getMessage('contextmenu.auxiliary.cut'),
|
||||
fn: () => {
|
||||
const slope = (currentObject.y2 - currentObject.y1) / (currentObject.x2 - currentObject.x1)
|
||||
const length = currentObject.length
|
||||
|
||||
let startX, startY, endX, endY
|
||||
if (slope === 0) {
|
||||
startX = endX = (currentObject.x1 + currentObject.x2) / 2
|
||||
startY = currentObject.y2 - length / 2
|
||||
endY = currentObject.y2 + length / 2
|
||||
} else if (slope === Infinity) {
|
||||
startX = currentObject.x1 - length / 2
|
||||
startY = endY = (currentObject.y1 + currentObject.y2) / 2
|
||||
endX = currentObject.x1 + length / 2
|
||||
} else {
|
||||
const bisectorSlope = -1 / slope
|
||||
const dx = length / 2 / Math.sqrt(1 + bisectorSlope * bisectorSlope)
|
||||
const dy = bisectorSlope * dx
|
||||
|
||||
startX = (currentObject.x1 + currentObject.x2) / 2 + dx
|
||||
startY = (currentObject.y1 + currentObject.y2) / 2 + dy
|
||||
endX = (currentObject.x1 + currentObject.x2) / 2 - dx
|
||||
endY = (currentObject.y1 + currentObject.y2) / 2 - dy
|
||||
}
|
||||
|
||||
const line = addLine([startX, startY, endX, endY], {
|
||||
stroke: 'red',
|
||||
strokeWidth: 1,
|
||||
selectable: true,
|
||||
name: 'auxiliaryLine',
|
||||
attributes: { ...currentObject.attributes },
|
||||
})
|
||||
canvas
|
||||
.getObjects()
|
||||
.filter((obj) => obj.id === currentObject.attributes.roofId)[0]
|
||||
.innerLines.push(line)
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'auxiliaryRemoveAll',
|
||||
name: getMessage('contextmenu.auxiliary.remove.all'),
|
||||
fn: () => {
|
||||
if (!currentObject) {
|
||||
swalFire({ text: '지붕을 선택해주세요.' })
|
||||
return
|
||||
}
|
||||
const innerLines = canvas.getObjects().filter((obj) => obj.id === currentObject.attributes.roofId)[0].innerLines
|
||||
innerLines.forEach((line) => {
|
||||
canvas.remove(line)
|
||||
})
|
||||
innerLines.length = 0
|
||||
canvas.renderAll()
|
||||
},
|
||||
},
|
||||
],
|
||||
])
|
||||
@ -193,16 +241,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',
|
||||
@ -260,6 +304,7 @@ export function useContextMenu() {
|
||||
|
||||
useEffect(() => {
|
||||
if (currentObject?.name) {
|
||||
console.log('object', currentObject)
|
||||
switch (currentObject.name) {
|
||||
case 'triangleDormer':
|
||||
case 'pentagonDormer':
|
||||
@ -304,11 +349,6 @@ export function useContextMenu() {
|
||||
case 'roof':
|
||||
setContextMenu([
|
||||
[
|
||||
{
|
||||
id: 'surfaceShapeActualSize',
|
||||
name: '면형상 실측',
|
||||
fn: () => surfaceShapeActualSize(),
|
||||
},
|
||||
{
|
||||
id: 'sizeEdit',
|
||||
name: '사이즈 변경',
|
||||
@ -429,7 +469,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',
|
||||
@ -440,19 +499,24 @@ export function useContextMenu() {
|
||||
])
|
||||
break
|
||||
case 'lineGrid':
|
||||
case 'dotGrid':
|
||||
case 'tempGrid':
|
||||
setContextMenu([
|
||||
[
|
||||
{
|
||||
id: 'gridMove',
|
||||
name: getMessage('modal.grid.move'),
|
||||
component: <GridMove id={popupId} />,
|
||||
},
|
||||
{
|
||||
id: 'gridCopy',
|
||||
name: getMessage('modal.grid.copy'),
|
||||
component: <GridCopy id={popupId} />,
|
||||
},
|
||||
{
|
||||
id: 'gridColorEdit',
|
||||
name: getMessage('contextmenu.grid.color.edit'),
|
||||
component: <ColorPickerModal id={popupId} color={gridColor} setColor={setGridColor} />,
|
||||
},
|
||||
{
|
||||
id: 'remove',
|
||||
@ -461,6 +525,7 @@ export function useContextMenu() {
|
||||
{
|
||||
id: 'removeAll',
|
||||
name: getMessage('contextmenu.remove.all'),
|
||||
fn: () => {},
|
||||
},
|
||||
],
|
||||
])
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { useEffect, useRef } from 'react'
|
||||
import { useCallback, useEffect, useRef } from 'react'
|
||||
import { useRecoilValue, useSetRecoilState } from 'recoil'
|
||||
import { canvasState, canvasZoomState, currentMenuState, textModeState } from '@/store/canvasAtom'
|
||||
import { fabric } from 'fabric'
|
||||
@ -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,
|
||||
|
||||
@ -1265,7 +1265,6 @@ export function useMode() {
|
||||
|
||||
const wall = makePolygon(null, sort)
|
||||
wall.name = 'wall'
|
||||
// wall.set({ name: 'wall' })
|
||||
|
||||
return wall
|
||||
}
|
||||
@ -1363,7 +1362,8 @@ export function useMode() {
|
||||
const polygon = new QPolygon(
|
||||
points,
|
||||
{
|
||||
stroke: 'black',
|
||||
stroke: '#1083E3',
|
||||
strokeWidth: 2,
|
||||
fill: 'transparent',
|
||||
viewLengthText: true,
|
||||
fontSize: fontSize,
|
||||
@ -1510,53 +1510,29 @@ export function useMode() {
|
||||
polygon.lines.forEach((line, index) => {
|
||||
line.attributes = {
|
||||
type: LINE_TYPE.WALLLINE.EAVES,
|
||||
offset: 40,
|
||||
offset: 50,
|
||||
width: 50,
|
||||
pitch: 4,
|
||||
sleeve: true,
|
||||
}
|
||||
/*if (index === 1) {
|
||||
/*if (index % 2 !== 0) {
|
||||
line.attributes = {
|
||||
type: LINE_TYPE.WALLLINE.SHED,
|
||||
offset: 30, //출폭
|
||||
width: 30, //폭
|
||||
pitch: 4, //구배
|
||||
sleeve: true, //소매
|
||||
}
|
||||
} else if (index === 5 || index === 3) {
|
||||
line.attributes = {
|
||||
type: LINE_TYPE.WALLLINE.EAVES,
|
||||
offset: 50, //출폭
|
||||
width: 30, //폭
|
||||
pitch: 4, //구배
|
||||
sleeve: true, //소매
|
||||
type: LINE_TYPE.WALLLINE.GABLE,
|
||||
offset: 30,
|
||||
width: 50,
|
||||
pitch: 4,
|
||||
sleeve: true,
|
||||
}
|
||||
} else {
|
||||
line.attributes = {
|
||||
type: LINE_TYPE.WALLLINE.GABLE,
|
||||
offset: 20,
|
||||
type: LINE_TYPE.WALLLINE.EAVES,
|
||||
offset: 50,
|
||||
width: 50,
|
||||
pitch: 4,
|
||||
sleeve: true,
|
||||
}
|
||||
}*/
|
||||
/* if (index === 1) {
|
||||
line.attributes = {
|
||||
type: LINE_TYPE.WALLLINE.SHED,
|
||||
offset: 20, //출폭
|
||||
width: 30, //폭
|
||||
pitch: 4, //구배
|
||||
sleeve: true, //소매
|
||||
}
|
||||
} else if (index === 3) {
|
||||
line.attributes = {
|
||||
type: LINE_TYPE.WALLLINE.EAVES,
|
||||
offset: 50, //출폭
|
||||
width: 30, //폭
|
||||
pitch: 4, //구배
|
||||
sleeve: true, //소매
|
||||
}
|
||||
} else {
|
||||
/*if (index === polygon.lines.length - 1) {
|
||||
line.attributes = {
|
||||
type: LINE_TYPE.WALLLINE.GABLE,
|
||||
offset: 30,
|
||||
@ -1564,6 +1540,14 @@ export function useMode() {
|
||||
pitch: 4,
|
||||
sleeve: true,
|
||||
}
|
||||
} else {
|
||||
line.attributes = {
|
||||
type: LINE_TYPE.WALLLINE.EAVES,
|
||||
offset: 50,
|
||||
width: 50,
|
||||
pitch: 4,
|
||||
sleeve: true,
|
||||
}
|
||||
}*/
|
||||
})
|
||||
|
||||
@ -1781,6 +1765,10 @@ export function useMode() {
|
||||
roofId: roof.id,
|
||||
}
|
||||
|
||||
canvas
|
||||
.getObjects()
|
||||
.filter((line) => line.attributes?.wallId === wall.id)
|
||||
.forEach((line) => canvas.remove(line))
|
||||
wall.lines.forEach((line, index) => {
|
||||
const lineLength = Math.sqrt(
|
||||
Math.pow(Math.round(Math.abs(line.x1 - line.x2) * 10), 2) + Math.pow(Math.round(Math.abs(line.y1 - line.y2) * 10), 2),
|
||||
@ -1789,11 +1777,45 @@ export function useMode() {
|
||||
line.attributes.currentRoof = roof.lines[index].id
|
||||
line.attributes.planeSize = lineLength
|
||||
line.attributes.actualSize = lineLength
|
||||
|
||||
let wallStroke, wallStrokeWidth
|
||||
switch (line.attributes.type) {
|
||||
case LINE_TYPE.WALLLINE.EAVES:
|
||||
wallStroke = '#45CD7D'
|
||||
wallStrokeWidth = 4
|
||||
break
|
||||
case LINE_TYPE.WALLLINE.HIPANDGABLE:
|
||||
wallStroke = '#45CD7D'
|
||||
wallStrokeWidth = 4
|
||||
break
|
||||
case LINE_TYPE.WALLLINE.GABLE:
|
||||
wallStroke = '#3FBAE6'
|
||||
wallStrokeWidth = 4
|
||||
break
|
||||
case LINE_TYPE.WALLLINE.JERKINHEAD:
|
||||
wallStroke = '#3FBAE6'
|
||||
wallStrokeWidth = 4
|
||||
break
|
||||
case LINE_TYPE.WALLLINE.SHED:
|
||||
wallStroke = '#000000'
|
||||
wallStrokeWidth = 4
|
||||
break
|
||||
case LINE_TYPE.WALLLINE.WALL:
|
||||
wallStroke = '#000000'
|
||||
wallStrokeWidth = 4
|
||||
break
|
||||
}
|
||||
const wallLine = new fabric.Line([line.x1, line.y1, line.x2, line.y2], {
|
||||
attributes: { wallId: wall.id },
|
||||
stroke: wallStroke,
|
||||
strokeWidth: wallStrokeWidth,
|
||||
selectable: false,
|
||||
})
|
||||
canvas.add(wallLine)
|
||||
})
|
||||
|
||||
setRoof(roof)
|
||||
setWall(wall)
|
||||
|
||||
return roof
|
||||
}
|
||||
|
||||
|
||||
@ -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')
|
||||
@ -141,7 +142,9 @@ export function usePlan() {
|
||||
*/
|
||||
const checkUnsavedCanvasPlan = async (userId) => {
|
||||
swalFire({
|
||||
text: `저장 안된 ${currentCanvasPlan.name} PLAN을 저장하시겠습니까? `,
|
||||
text:
|
||||
(!initCanvasPlans.some((initCanvasPlans) => initCanvasPlans.id === plan.id) ? 'New ' : '') +
|
||||
`Plan ${plan.ordering}의 변경 사항을 저장하시겠습니까?`,
|
||||
type: 'confirm',
|
||||
confirmFn: async () => {
|
||||
initCanvasPlans.some((plan) => plan.id === currentCanvasPlan.id)
|
||||
@ -181,20 +184,20 @@ export function usePlan() {
|
||||
*/
|
||||
const getCanvasByObjectNo = async (userId, objectNo) => {
|
||||
return get({ url: `/api/canvas-management/canvas-statuses/by-object/${objectNo}/${userId}` }).then((res) =>
|
||||
res.map((item) => ({
|
||||
res.map((item, index) => ({
|
||||
id: item.id,
|
||||
name: item.objectNo + '-' + item.id, // tab button에 표출될 이름 (임시)
|
||||
userId: item.userId,
|
||||
canvasStatus: dbToCanvasFormat(item.canvasStatus),
|
||||
isCurrent: false,
|
||||
bgImageName: item.bgImageName,
|
||||
mapPositionAddress: item.mapPositionAddress,
|
||||
ordering: index + 1,
|
||||
})),
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* canvas 데이터를 추가
|
||||
* 신규 canvas 데이터를 저장
|
||||
*/
|
||||
const postCanvasStatus = async (userId, canvasStatus) => {
|
||||
const planData = {
|
||||
@ -212,7 +215,6 @@ export function usePlan() {
|
||||
? {
|
||||
...plan,
|
||||
id: res.data,
|
||||
name: currentCanvasPlan.objectNo + '-' + res.data,
|
||||
canvasStatus: canvasStatus,
|
||||
}
|
||||
: plan,
|
||||
@ -302,7 +304,10 @@ export function usePlan() {
|
||||
const handleAddPlan = (userId, objectNo) => {
|
||||
JSON.parse(currentCanvasData()).objects.length > 0
|
||||
? swalFire({
|
||||
text: `${currentCanvasPlan.name} ` + getMessage('plan.message.confirm.copy'),
|
||||
text:
|
||||
(!initCanvasPlans.some((initCanvasPlans) => initCanvasPlans.id === currentCanvasPlan.id) ? 'New ' : '') +
|
||||
`Plan ${currentCanvasPlan.ordering} ` +
|
||||
getMessage('plan.message.confirm.copy'),
|
||||
type: 'confirm',
|
||||
confirmFn: () => {
|
||||
addPlan(userId, objectNo, currentCanvasData())
|
||||
@ -317,10 +322,10 @@ export function usePlan() {
|
||||
const id = uuidv4()
|
||||
const newPlan = {
|
||||
id: id,
|
||||
name: `Plan ${planNum + 1}`,
|
||||
objectNo: objectNo,
|
||||
userId: userId,
|
||||
canvasStatus: canvasStatus,
|
||||
ordering: planNum + 1,
|
||||
}
|
||||
setPlans([...plans, newPlan])
|
||||
handleCurrentPlan(userId, id)
|
||||
@ -363,16 +368,16 @@ export function usePlan() {
|
||||
/**
|
||||
* plan 조회
|
||||
*/
|
||||
const loadCanvasPlanData = (userId, objectNo) => {
|
||||
const loadCanvasPlanData = (userId, objectNo, pid) => {
|
||||
getCanvasByObjectNo(userId, objectNo).then((res) => {
|
||||
// console.log('canvas 목록 ', res)
|
||||
if (res.length > 0) {
|
||||
setInitCanvasPlans(res)
|
||||
setPlans(res)
|
||||
updateCurrentPlan(res.at(-1).id) // last 데이터에 포커싱
|
||||
updateCurrentPlan(Number(pid))
|
||||
setPlanNum(res.length)
|
||||
} else {
|
||||
addPlan(userId, objectNo)
|
||||
addPlan(userId, objectNo, '')
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -380,6 +385,7 @@ export function usePlan() {
|
||||
return {
|
||||
canvas,
|
||||
plans,
|
||||
initCanvasPlans,
|
||||
selectedPlan,
|
||||
currentCanvasPlan,
|
||||
modifiedPlans,
|
||||
|
||||
@ -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
|
||||
@ -752,6 +1026,47 @@ export const usePolygon = () => {
|
||||
})
|
||||
}
|
||||
|
||||
const splitPolygonWithSeparate = (separates) => {
|
||||
separates.forEach((separate) => {
|
||||
const points = separate.lines.map((line) => {
|
||||
return { x: line.x1, y: line.y1 }
|
||||
})
|
||||
let defense = ''
|
||||
switch (separate.attributes.direction) {
|
||||
case 'top':
|
||||
defense = 'east'
|
||||
break
|
||||
case 'right':
|
||||
defense = 'south'
|
||||
break
|
||||
case 'bottom':
|
||||
defense = 'west'
|
||||
break
|
||||
case 'left':
|
||||
defense = 'north'
|
||||
break
|
||||
}
|
||||
|
||||
const roof = new QPolygon(points, {
|
||||
fontSize: separate.fontSize,
|
||||
stroke: 'black',
|
||||
fill: 'transparent',
|
||||
strokeWidth: 3,
|
||||
name: POLYGON_TYPE.ROOF,
|
||||
originX: 'center',
|
||||
originY: 'center',
|
||||
selectable: true,
|
||||
defense: defense,
|
||||
pitch: separate.attributes.pitch,
|
||||
direction: defense,
|
||||
})
|
||||
|
||||
canvas.add(roof)
|
||||
})
|
||||
|
||||
canvas.renderAll()
|
||||
}
|
||||
|
||||
return {
|
||||
addPolygon,
|
||||
addPolygonByLines,
|
||||
@ -759,5 +1074,6 @@ export const usePolygon = () => {
|
||||
drawDirectionArrow,
|
||||
addLengthText,
|
||||
splitPolygonWithLines,
|
||||
splitPolygonWithSeparate,
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,10 @@
|
||||
'use server'
|
||||
|
||||
/**
|
||||
* Deprecated
|
||||
* 개발후 삭제 예정
|
||||
*/
|
||||
|
||||
import fs from 'fs/promises'
|
||||
|
||||
const imageSavePath = 'public/cadImages'
|
||||
|
||||
@ -1,5 +1,10 @@
|
||||
'use server'
|
||||
|
||||
/**
|
||||
* Deprecated
|
||||
* 개발후 삭제 예정
|
||||
*/
|
||||
|
||||
// import { PrismaClient } from '@prisma/client'
|
||||
import fs from 'fs/promises'
|
||||
|
||||
|
||||
@ -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)
|
||||
}
|
||||
64
src/lib/fileAction.js
Normal file
64
src/lib/fileAction.js
Normal file
@ -0,0 +1,64 @@
|
||||
'use server'
|
||||
|
||||
import fs from 'fs/promises'
|
||||
|
||||
const CAD_FILE_PATH = 'public/cad-images'
|
||||
const IMAGE_FILE_PATH = 'public/plan-bg-images'
|
||||
|
||||
/**
|
||||
* 파일 변환 & 저장
|
||||
* @param {*} fileName
|
||||
* @param {*} data
|
||||
* @returns
|
||||
*/
|
||||
const convertDwgToPng = async (fileName, data) => {
|
||||
console.log('fileName', fileName)
|
||||
try {
|
||||
await fs.readdir(CAD_FILE_PATH)
|
||||
} catch {
|
||||
await fs.mkdir(CAD_FILE_PATH)
|
||||
}
|
||||
|
||||
return await fs.writeFile(`${CAD_FILE_PATH}/${fileName}`, data, 'base64')
|
||||
}
|
||||
|
||||
/**
|
||||
* 이미지 저장
|
||||
* base64 형식으로 저장
|
||||
* @param {*} title
|
||||
* @param {*} data
|
||||
* @returns
|
||||
*/
|
||||
const writeImageBase64 = async (title, data) => {
|
||||
// 해당 경로에 Directory 가 없다면 생성
|
||||
try {
|
||||
await fs.readdir(IMAGE_FILE_PATH)
|
||||
} catch {
|
||||
await fs.mkdir(IMAGE_FILE_PATH)
|
||||
}
|
||||
|
||||
return fs.writeFile(`${IMAGE_FILE_PATH}/${title}.png`, data, 'base64')
|
||||
}
|
||||
|
||||
/**
|
||||
* 이미지 저장
|
||||
* Buffer 형식으로 저장
|
||||
* @param {*} title
|
||||
* @param {*} data
|
||||
* @returns
|
||||
*/
|
||||
const writeImageBuffer = async (file) => {
|
||||
// 해당 경로에 Directory 가 없다면 생성
|
||||
try {
|
||||
await fs.readdir(IMAGE_FILE_PATH)
|
||||
} catch {
|
||||
await fs.mkdir(IMAGE_FILE_PATH)
|
||||
}
|
||||
|
||||
const arrayBuffer = await fileURLToPath.arrayBuffer()
|
||||
const buffer = new Uint8Array(arrayBuffer)
|
||||
|
||||
return fs.writeFile(`${IMAGE_FILE_PATH}/${file.fileName}`, buffer)
|
||||
}
|
||||
|
||||
export { convertDwgToPng, writeImageBase64, writeImageBuffer }
|
||||
@ -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,
|
||||
}
|
||||
}
|
||||
@ -4,6 +4,7 @@
|
||||
"header.menus.home": "ホームへv",
|
||||
"header.menus.management": "物品及び図面管理",
|
||||
"header.menus.management.newStuff": "新規 物件 登録",
|
||||
"header.menus.management.detail": "物件詳細",
|
||||
"header.menus.management.stuffList": "物件の状況",
|
||||
"header.menus.community": "コミュニティ",
|
||||
"header.menus.community.notice": "お知らせ",
|
||||
@ -85,12 +86,14 @@
|
||||
"modal.module.basic.setting.orientation.setting.angle.passivity": "角度を直接入力",
|
||||
"modal.module.basic.setting.module.roof.material": "屋根材",
|
||||
"modal.module.basic.setting.module.trestle.maker": "架台メーカー",
|
||||
"modal.module.basic.setting.module.rafter.margin": "マンドンピッチ",
|
||||
"modal.module.basic.setting.module.construction.method": "工法",
|
||||
"modal.module.basic.setting.module.under.roof": "屋根の下",
|
||||
"modal.module.basic.setting.module.setting": "モジュールの選択",
|
||||
"modal.module.basic.setting.module.setting.info1": "※勾配の 範囲には制限があります。屋根傾斜が2.5値未満、10値を超える場合には施工が可能か 施工マニュアルを確認してください。",
|
||||
"modal.module.basic.setting.module.setting.info2": "モジュール配置時は、施工マニュアルに記載されている<モジュール配置条件>を必ず確認してください",
|
||||
"modal.module.basic.setting.module.cotton.classification": "綿調道区分",
|
||||
"modal.module.basic.setting.module.stuff.info": "商品情報",
|
||||
"modal.module.basic.setting.module.surface.type": "면조도구분(JA)",
|
||||
"modal.module.basic.setting.module.fitting.height": "設置高さ",
|
||||
"modal.module.basic.setting.module.standard.wind.speed": "基準風速",
|
||||
"modal.module.basic.setting.module.standard.snowfall.amount": "基準積雪量",
|
||||
@ -160,7 +163,7 @@
|
||||
"plan.menu.estimate.docDown": "文書のダウンロード",
|
||||
"plan.menu.estimate.save": "保存",
|
||||
"plan.menu.estimate.reset": "初期化",
|
||||
"plan.menu.estimate.copy": "コピー",
|
||||
"plan.menu.estimate.copy": "見積書のコピー",
|
||||
"plan.menu.simulation": "発展シミュレーション",
|
||||
"plan.menu.simulation.excel": "Excel",
|
||||
"plan.menu.simulation.pdf": "PDF",
|
||||
@ -284,7 +287,6 @@
|
||||
"modal.panel.batch.statistic.total": "合計",
|
||||
"modal.flow.direction.setting": "流れ方向の設定",
|
||||
"modal.flow.direction.setting.info": "流れ方向を選択してください。",
|
||||
"modal.image.size.setting": "画像のサイズ変更",
|
||||
"modal.actual.size.setting": "実測値設定",
|
||||
"modal.actual.size.setting.info": "※隅棟・谷・棟の実際の寸法を入力してください。",
|
||||
"modal.actual.size.setting.not.exist.auxiliary.line": "실측치 입력할 보조선을 선택해 주세요(JA)",
|
||||
@ -616,9 +618,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": "更新日時",
|
||||
@ -667,6 +669,7 @@
|
||||
"stuff.detail.btn.save": "保存",
|
||||
"stuff.detail.btn.tempSave": "一時保存",
|
||||
"stuff.detail.save": "保存しました",
|
||||
"stuff.detail.tempSave": "一時保存されました",
|
||||
"stuff.detail.noChgData": "変更内容はありません",
|
||||
"stuff.detail.save.valierror1": "垂直説説は0より大きい値を入力してください",
|
||||
"stuff.detail.save.valierror2": "設置高さ0より大きい値を入力してください",
|
||||
@ -694,11 +697,12 @@
|
||||
"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": "照会",
|
||||
"stuff.search.btn3": "初期化",
|
||||
"stuff.search.btn.register": "新規 物件 登録",
|
||||
"stuff.search.btn.search": "照会",
|
||||
"stuff.search.btn.reset": "初期化",
|
||||
"stuff.search.schObjectNo": "品番",
|
||||
"stuff.search.schSaleStoreName": "販売代理店名",
|
||||
"stuff.search.schAddress": "商品アドレス",
|
||||
@ -727,12 +731,12 @@
|
||||
"stuff.detail.planGridHeader.moduleModel": "モジュール",
|
||||
"stuff.detail.planGridHeader.capacity": "システム容量",
|
||||
"stuff.detail.planGridHeader.roofMaterialIdMulti": "屋根材",
|
||||
"stuff.detail.planGridHeader.constructSpecification": "施工方法",
|
||||
"stuff.detail.planGridHeader.constructSpecificationMulti": "施工方法",
|
||||
"stuff.detail.planGridHeader.supportMethodIdMulti": "架台",
|
||||
"stuff.detail.planGridHeader.pcTypeNo": "パワーコンディショナー",
|
||||
"stuff.detail.planGridHeader.management": "管理",
|
||||
"stuff.detail.planGrid.btn1": "見積書の照会",
|
||||
"stuff.detail.planGrid.btn2": "Excel",
|
||||
"stuff.detail.planGrid.docDownload": "文書のダウンロード",
|
||||
"stuff.grid.noData": "照会されたデータがありません",
|
||||
"length": "長さ",
|
||||
"height": "高さ",
|
||||
@ -822,6 +826,8 @@
|
||||
"estimate.detail.objectName": "案件名",
|
||||
"estimate.detail.objectRemarks": "メモ",
|
||||
"estimate.detail.estimateType": "注文分類",
|
||||
"estimate.detail.estimateType.yjss": "住宅PKG",
|
||||
"estimate.detail.estimateType.yjod": "積上げ( YJOD )",
|
||||
"estimate.detail.roofCns": "屋根材・仕様施工",
|
||||
"estimate.detail.remarks": "備考",
|
||||
"estimate.detail.fileFlg": "後日資料提出",
|
||||
@ -830,19 +836,18 @@
|
||||
"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)",
|
||||
"estimate.detail.sepcialEstimateProductInfo.pkgPrice": "PKG金額",
|
||||
"estimate.detail.sepcialEstimateProductInfo.calcFormula1": "(モジュール容量 × 数量)÷1000",
|
||||
"estimate.detail.sepcialEstimateProductInfo.calcFormula2": "PKG単価 (W)×PKG容量(W)",
|
||||
"estimate.detail.header.showPrice": "価格表示",
|
||||
"estimate.detail.header.unitPrice": "定価",
|
||||
"estimate.detail.showPrice.pricingBtn": "Pricing",
|
||||
"estimate.detail.showPrice.pricingBtn.noItemId": "Pricingが欠落しているアイテムがあります。 Pricingを進めてください.",
|
||||
"estimate.detail.showPrice.description1": "製品価格 OPEN",
|
||||
"estimate.detail.showPrice.description2": "追加, 変更資材",
|
||||
"estimate.detail.showPrice.description3": "添付必須",
|
||||
@ -859,25 +864,41 @@
|
||||
"estimate.detail.docPopup.title": "ドキュメントダウンロードオプションの設定",
|
||||
"estimate.detail.docPopup.explane": "ダウンロードする文書のオプションを選択したら、 [文書のダウンロード]ボタンをクリックします.",
|
||||
"estimate.detail.docPopup.schUnitPriceFlg": "ダウンロードファイル",
|
||||
"estimate.detail.docPopup.schUnitPriceFlg.schUnitPriceFlg0": "見積もり Excel",
|
||||
"estimate.detail.docPopup.schUnitPriceFlg.schUnitPriceFlg1": "定価用 Excel",
|
||||
"estimate.detail.docPopup.schUnitPriceFlg.schUnitPriceFlg2": "見積もり PDF",
|
||||
"estimate.detail.docPopup.schUnitPriceFlg.schUnitPriceFlg3": "定価用 PDF",
|
||||
"estimate.detail.docPopup.schUnitPriceFlg.excelFlg0": "見積もり Excel",
|
||||
"estimate.detail.docPopup.schUnitPriceFlg.excelFlg1": "定価用 Excel",
|
||||
"estimate.detail.docPopup.schUnitPriceFlg.pdfFlg0": "見積もり PDF",
|
||||
"estimate.detail.docPopup.schUnitPriceFlg.pdfFlg1": "定価用 PDF",
|
||||
"estimate.detail.docPopup.schDisplayFlg": "見積提出先表示名",
|
||||
"estimate.detail.docPopup.schDisplayFlg.schDisplayFlg0": "販売店名",
|
||||
"estimate.detail.docPopup.schDisplayFlg.schDisplayFlg1": "案件名",
|
||||
"estimate.detail.docPopup.schWeightFlg": "架台重量表を含む",
|
||||
"estimate.detail.docPopup.schWeightFlg.schWeightFlg0": "含む",
|
||||
"estimate.detail.docPopup.schWeightFlg.schWeightFlg1": "含まない",
|
||||
"estimate.detail.docPopup.schWeightFlg.schWeightFlg1": "含む",
|
||||
"estimate.detail.docPopup.schWeightFlg.schWeightFlg0": "含まない",
|
||||
"estimate.detail.docPopup.schDrawingFlg": "図面/シミュレーションファイルを含む",
|
||||
"estimate.detail.docPopup.schDrawingFlg.schDrawingFlg0": "含む",
|
||||
"estimate.detail.docPopup.schDrawingFlg.schDrawingFlg1": "含まない",
|
||||
"estimate.detail.docPopup.schDrawingFlg.schDrawingFlg1": "含む",
|
||||
"estimate.detail.docPopup.schDrawingFlg.schDrawingFlg0": "含まない",
|
||||
"estimate.detail.docPopup.close": "閉じる",
|
||||
"estimate.detail.docPopup.docDownload": "文書のダウンロード",
|
||||
"estimate.detail.estimateCopyPopup.title": "見積もり",
|
||||
"estimate.detail.estimateCopyPopup.explane": "見積書をコピーする販売店を設定します。見積もりは定価にコピーされます.",
|
||||
"estimate.detail.estimateCopyPopup.label.saleStoreId": "一次販売店名 / ID",
|
||||
"estimate.detail.estimateCopyPopup.label.otherSaleStoreId": "二次販売店名 / ID",
|
||||
"estimate.detail.estimateCopyPopup.label.receiveUser": "担当者",
|
||||
"estimate.detail.estimateCopyPopup.close": "閉じる",
|
||||
"estimate.detail.estimateCopyPopup.copyBtn": "見積もり",
|
||||
"estimate.detail.estimateCopyPopup.copy.alertMessage": "見積書がコピーされました. コピーした商品情報に移動します.",
|
||||
"estimate.detail.productFeaturesPopup.title": "製品特異事項",
|
||||
"estimate.detail.productFeaturesPopup.close": "閉じる",
|
||||
"estimate.detail.productFeaturesPopup.requiredStoreId": "一次販売店は必須です.",
|
||||
"estimate.detail.productFeaturesPopup.requiredReceiveUser": "担当者は必須です.",
|
||||
"estimate.detail.save.alertMsg": "保存されている見積書で製品を変更した場合、図面や回路には反映されません.",
|
||||
"estimate.detail.save.requiredMsg": "ファイル添付が必須のアイテムがあります。ファイルを添付するか、後日添付をチェックしてください.",
|
||||
"estimate.detail.save.requiredFileUpload": "ファイル添付が必須のアイテムがあります。ファイルを添付するか、後日添付をチェックしてください.",
|
||||
"estimate.detail.save.requiredItem": "製品は1つ以上登録する必要があります.",
|
||||
"estimate.detail.save.requiredCharger": "担当者は必須です.",
|
||||
"estimate.detail.save.requiredObjectName": "案件名は必須です.",
|
||||
"estimate.detail.save.requiredEstimateDate": "見積日は必須です.",
|
||||
"estimate.detail.save.requiredAmount": "数量は0より大きい値を入力してください.",
|
||||
"estimate.detail.save.requiredSalePrice": "単価は0より大きい値を入力してください.",
|
||||
"estimate.detail.reset.confirmMsg": "保存した見積書情報が初期化され、図面情報が反映されます。本当に初期化しますか?",
|
||||
"simulator.title.sub1": "物件番号",
|
||||
"simulator.title.sub2": "作成日",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user