Merge branch 'dev'

This commit is contained in:
yoosangwook 2024-11-28 10:03:07 +09:00
commit 5b40f4ca44
118 changed files with 7599 additions and 2843 deletions

View File

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

View File

@ -32,6 +32,7 @@
"react-icons": "^5.3.0", "react-icons": "^5.3.0",
"react-loading-skeleton": "^3.5.0", "react-loading-skeleton": "^3.5.0",
"react-responsive-modal": "^6.4.2", "react-responsive-modal": "^6.4.2",
"react-spinners": "^0.14.1",
"recoil": "^0.7.7", "recoil": "^0.7.7",
"sweetalert2": "^11.14.1", "sweetalert2": "^11.14.1",
"sweetalert2-react-content": "^5.0.7", "sweetalert2-react-content": "^5.0.7",

View File

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

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -1,18 +1,35 @@
'use client' 'use client'
import { useEffect, useState } from 'react' import { createContext, useEffect, useState } from 'react'
import { ErrorBoundary } from 'next/dist/client/components/error-boundary' import { ErrorBoundary } from 'next/dist/client/components/error-boundary'
import { useCommonCode } from '@/hooks/common/useCommonCode' import { useCommonCode } from '@/hooks/common/useCommonCode'
import { usePlan } from '@/hooks/usePlan' import { usePlan } from '@/hooks/usePlan'
import ServerError from './error' import ServerError from './error'
import '@/styles/common.scss' import '@/styles/common.scss'
import GlobalSpinner from '@/components/common/spinner/GlobalSpinner'
export const QcastContext = createContext({
qcastState: {},
setQcastState: () => {},
isGlobalLoading: false,
setIsGlobalLoading: () => {},
})
export const QcastProvider = ({ children }) => { export const QcastProvider = ({ children }) => {
const [planSave, setPlanSave] = useState(false) const [planSave, setPlanSave] = useState(false)
const [isGlobalLoading, setIsGlobalLoading] = useState(false)
const { currentCanvasPlan, modifiedPlans, checkUnsavedCanvasPlan } = usePlan() const { currentCanvasPlan, modifiedPlans, checkUnsavedCanvasPlan } = usePlan()
const { commonCode, findCommonCode } = useCommonCode() const { commonCode, findCommonCode } = useCommonCode()
const [qcastState, setQcastState] = useState({
saleStoreId: '',
saleStoreName: '',
objectList: [],
businessCharger: null,
businessChargerMail: null,
})
useEffect(() => { useEffect(() => {
const targetElement = document.getElementById('canvas') const targetElement = document.getElementById('canvas')
if (!targetElement && currentCanvasPlan?.id && planSave) { if (!targetElement && currentCanvasPlan?.id && planSave) {
@ -30,7 +47,14 @@ export const QcastProvider = ({ children }) => {
return ( return (
<> <>
{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> <ErrorBoundary fallback={<ServerError />}>{children}</ErrorBoundary>
</QcastContext.Provider>
</> </>
) )
} }

View File

@ -5,7 +5,7 @@ import fs from 'fs/promises'
import { NextResponse } from 'next/server' import { NextResponse } from 'next/server'
export async function GET(req) { export async function GET(req) {
const path = 'public/mapImages' const path = 'public/plan-map-images'
const q = req.nextUrl.searchParams.get('q') const q = req.nextUrl.searchParams.get('q')
const fileNm = req.nextUrl.searchParams.get('fileNm') const fileNm = req.nextUrl.searchParams.get('fileNm')
const zoom = req.nextUrl.searchParams.get('zoom') const zoom = req.nextUrl.searchParams.get('zoom')

View 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}` })
}

View 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

View File

@ -1,9 +1,73 @@
'ues client' 'ues client'
import { ErrorBoundary } from 'next/dist/client/components/error-boundary' import { correntObjectNoState } from '@/store/settingAtom'
import ServerError from '../error' 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 }) => { const reducer = (prevState, nextState) => {
console.log('FloorPlanProvider') return { ...prevState, ...nextState }
return <ErrorBoundary fallback={<ServerError />}>{children}</ErrorBoundary>
} }
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

View File

@ -1,17 +1,22 @@
'use client' 'use client'
import FloorPlanProvider from './FloorPlanProvider'
import FloorPlan from '@/components/floor-plan/FloorPlan' import FloorPlan from '@/components/floor-plan/FloorPlan'
import { FloorPlanProvider } from './FloorPlanProvider'
import CanvasLayout from '@/components/floor-plan/CanvasLayout' import CanvasLayout from '@/components/floor-plan/CanvasLayout'
import EventProvider from './EventProvider'
export default function FloorPlanLayout({ children }) { export default function FloorPlanLayout({ children }) {
console.log('FloorPlanLayout') console.log('🚀 ~ FloorPlanLayout ~ FloorPlanLayout:')
return ( return (
<> <>
{/*<EventProvider>*/}
<FloorPlanProvider> <FloorPlanProvider>
<FloorPlan> <FloorPlan>
<CanvasLayout>{children}</CanvasLayout> <CanvasLayout>{children}</CanvasLayout>
</FloorPlan> </FloorPlan>
</FloorPlanProvider> </FloorPlanProvider>
{/*</EventProvider>*/}
</> </>
) )
} }

View File

@ -62,16 +62,16 @@ export default async function RootLayout({ children }) {
{headerPathname === '/login' || headerPathname === '/join' ? ( {headerPathname === '/login' || headerPathname === '/join' ? (
<QcastProvider>{children}</QcastProvider> <QcastProvider>{children}</QcastProvider>
) : ( ) : (
<QcastProvider>
<div className="wrap"> <div className="wrap">
<Header userSession={sessionProps} /> <Header userSession={sessionProps} />
<div className="content"> <div className="content">
<Dimmed /> <Dimmed />
<QcastProvider>
<SessionProvider useSession={sessionProps}>{children}</SessionProvider> <SessionProvider useSession={sessionProps}>{children}</SessionProvider>
</QcastProvider>
</div> </div>
<Footer /> <Footer />
</div> </div>
</QcastProvider>
)} )}
<QModal /> <QModal />
<PopupManager /> <PopupManager />

View File

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

View File

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

View File

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

View File

@ -1,9 +1,8 @@
'use client' 'use client'
import React, { useEffect, useState } from 'react' import { useEffect, useState, useContext } from 'react'
import { useRouter } from 'next/navigation' import { useRouter } from 'next/navigation'
import { useRecoilState, useRecoilValue } from 'recoil' import { useRecoilState, useRecoilValue } from 'recoil'
import { sessionStore } from '@/store/commonAtom'
import { useAxios } from '@/hooks/useAxios' import { useAxios } from '@/hooks/useAxios'
import { globalLocaleStore } from '@/store/localeAtom' import { globalLocaleStore } from '@/store/localeAtom'
import MainContents from './main/MainContents' import MainContents from './main/MainContents'
@ -12,8 +11,11 @@ import { stuffSearchState } from '@/store/stuffAtom'
import '@/styles/contents.scss' import '@/styles/contents.scss'
import ChangePasswordPop from './main/ChangePasswordPop' import ChangePasswordPop from './main/ChangePasswordPop'
import { searchState } from '@/store/boardAtom' import { searchState } from '@/store/boardAtom'
import { SessionContext } from '@/app/SessionProvider'
import { QcastContext } from '@/app/QcastProvider'
export default function MainPage() { export default function MainPage() {
const sessionState = useRecoilValue(sessionStore) const { session } = useContext(SessionContext)
const globalLocaleState = useRecoilValue(globalLocaleStore) const globalLocaleState = useRecoilValue(globalLocaleStore)
@ -25,22 +27,24 @@ export default function MainPage() {
const [searchRadioType, setSearchRadioType] = useState('object') const [searchRadioType, setSearchRadioType] = useState('object')
const [saleStoreId, setSaleStoreId] = useState('') // const [saleStoreId, setSaleStoreId] = useState('')
const [saleStoreName, setSaleStoreName] = useState('') // const [saleStoreName, setSaleStoreName] = useState('')
const [stuffSearch, setStuffSearch] = useRecoilState(stuffSearchState) const [stuffSearch, setStuffSearch] = useRecoilState(stuffSearchState)
const [searchForm, setSearchForm] = useRecoilState(searchState) const [searchForm, setSearchForm] = useRecoilState(searchState)
useEffect(() => { const { qcastState } = useContext(QcastContext)
if (sessionState.pwdInitYn === 'Y') {
fetchObjectList() // useEffect(() => {
} // if (session.pwdInitYn === 'Y') {
}, [sessionState]) // fetchObjectList()
// }
// }, [session])
const fetchObjectList = async () => { const fetchObjectList = async () => {
try { try {
const apiUrl = `/api/main-page/object/${sessionState?.storeId}/list` const apiUrl = `/api/main-page/object/${session?.storeId}/list`
await promiseGet({ await promiseGet({
url: apiUrl, url: apiUrl,
}).then((res) => { }).then((res) => {
@ -95,7 +99,7 @@ export default function MainPage() {
return ( return (
<> <>
{(sessionState?.pwdInitYn !== 'N' && ( {(session?.pwdInitYn !== 'N' && (
<> <>
<div className="background-bord"></div> <div className="background-bord"></div>
<div className="main-contents"> <div className="main-contents">
@ -107,7 +111,7 @@ export default function MainPage() {
</div> </div>
<span className="store-arr"></span> <span className="store-arr"></span>
<div className="store-id-name"> <div className="store-id-name">
{saleStoreId} / {saleStoreName} {qcastState?.saleStoreId} / {qcastState?.saleStoreName}
</div> </div>
</div> </div>
<div className="main-search-wrap"> <div className="main-search-wrap">

View File

@ -5,7 +5,7 @@ import { useEffect, useRef, useState } from 'react'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
import { useMode } from '@/hooks/useMode' import { useMode } from '@/hooks/useMode'
import { LINE_TYPE, Mode } from '@/common/common' 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 RangeSlider from './ui/RangeSlider'
import { useRecoilState, useRecoilValue } from 'recoil' import { useRecoilState, useRecoilValue } from 'recoil'
import { import {
@ -410,10 +410,10 @@ export default function Roof2(props) {
] ]
const rectangleType1 = [ const rectangleType1 = [
{ x: 100, y: 100 }, { x: 500, y: 100 },
{ x: 100, y: 600 }, { x: 500, y: 800 },
{ x: 300, y: 600 }, { x: 900, y: 800 },
{ x: 300, y: 100 }, { x: 900, y: 100 },
] ]
const rectangleType2 = [ const rectangleType2 = [
@ -445,11 +445,31 @@ export default function Roof2(props) {
{ x: 113, y: 371.9 }, { x: 113, y: 371.9 },
{ x: 762, y: 371.9 }, { x: 762, y: 371.9 },
{ x: 762, y: 818.7 }, { x: 762, y: 818.7 },
{ x: 1468.6, y: 818.7 }, { x: 1478.6, y: 818.7 },
{ x: 1468.6, y: 114.9 }, { 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', fill: 'transparent',
stroke: 'green', stroke: 'green',
strokeWidth: 1, strokeWidth: 1,
@ -1010,7 +1030,7 @@ export default function Roof2(props) {
</> </>
)} )}
<ThumbnailList {...thumbnailProps} /> <ThumbnailList {...thumbnailProps} />
<div className={'flex'}> {/* <div className={'flex'}>
<p className={'m-1 p-3'}>각도 입력(0~360) 방향설정 클릭</p> <p className={'m-1 p-3'}>각도 입력(0~360) 방향설정 클릭</p>
<Input <Input
className="m-1 p-3" className="m-1 p-3"
@ -1028,14 +1048,14 @@ export default function Roof2(props) {
<Button className="m-1 p-3" onClick={setDirectionStringToArrow}> <Button className="m-1 p-3" onClick={setDirectionStringToArrow}>
방향 설정 방향 설정
</Button> </Button>
</div> </div>*/}
<div className="relative w-48 h-48 flex justify-center items-center border-2 border-gray-300 rounded-full"> {/* <div className="relative w-48 h-48 flex justify-center items-center border-2 border-gray-300 rounded-full">
{/* Compass Circle */} Compass Circle
<div <div
className="absolute w-full h-full border-2 border-gray-300 rounded-full flex justify-center items-center" className="absolute w-full h-full border-2 border-gray-300 rounded-full flex justify-center items-center"
style={{ rotate: `${globalCampass}deg` }} 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 top-2 text-lg font-bold">N</div>
<div className="absolute bottom-2 text-lg font-bold">S</div> <div className="absolute bottom-2 text-lg font-bold">S</div>
<div className="absolute right-2 text-lg font-bold" style={{ rotate: '90deg' }}> <div className="absolute right-2 text-lg font-bold" style={{ rotate: '90deg' }}>
@ -1046,9 +1066,9 @@ export default function Roof2(props) {
</div> </div>
</div> </div>
{/* Compass Pointer */} Compass Pointer
<div className="relative w-10 h-10"> <div className="relative w-10 h-10">
{/* Red Upper Triangle */} Red Upper Triangle
<div <div
className="absolute top-0" className="absolute top-0"
style={{ style={{
@ -1061,7 +1081,7 @@ export default function Roof2(props) {
}} }}
/> />
</div> </div>
</div> </div>*/}
<div className="flex justify-start my-8 mx-2 w-full"> <div className="flex justify-start my-8 mx-2 w-full">
<canvas ref={canvasRef} id="canvas" style={{ border: '1px solid black' }} /> <canvas ref={canvasRef} id="canvas" style={{ border: '1px solid black' }} />
{!canvas ? null : mode === Mode.DRAW_LINE ? ( {!canvas ? null : mode === Mode.DRAW_LINE ? (

View File

@ -80,79 +80,32 @@ export default function Login() {
e.preventDefault() e.preventDefault()
const formData = new FormData(e.target) const formData = new FormData(e.target)
/////////////////////////////////////////////////////////// //
// const param = {
setSession({ loginId: formData.get('id'),
userId: 'NEW016610', pwd: formData.get('password'),
saleStoreId: null, }
name: null, await promisePost({ url: '/api/login/v1.0/login', data: param })
mail: null, .then((res) => {
tel: null, if (res) {
storeId: 'TEMP02', if (res.data.result.resultCode === 'S') {
userNm: 'ㅇㅇ6610', setSession(res.data.data)
userNmKana: '신규사용자 16610', setSessionState(res.data.data)
category: '인상6610', // ID SAVE ,
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) { if (chkLoginId) {
Cookies.set('chkLoginId', formData.get('id'), { expires: 7 }) Cookies.set('chkLoginId', formData.get('id'), { expires: 7 })
} else { } else {
Cookies.remove('chkLoginId') Cookies.remove('chkLoginId')
} }
router.push('/') router.push('/')
// } else {
/////////////////////////////////////////////////////////// alert(res.data.result.resultMsg)
}
// - ** ** }
// const param = { })
// loginId: formData.get('id'), .catch((error) => {
// pwd: formData.get('password'), 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)
// })
} }
// //

View File

@ -15,6 +15,7 @@ export default function QContextMenu(props) {
const { tempGridMode, setTempGridMode } = useTempGrid() const { tempGridMode, setTempGridMode } = useTempGrid()
const { handleKeyup } = useContextMenu() const { handleKeyup } = useContextMenu()
const { addDocumentEventListener, removeDocumentEvent } = useEvent() const { addDocumentEventListener, removeDocumentEvent } = useEvent()
// const { addDocumentEventListener, removeDocumentEvent } = useContext(EventContext)
let contextType = '' let contextType = ''

View File

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

View File

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

View File

@ -0,0 +1,7 @@
'use client'
import { HashLoader } from 'react-spinners'
export default function GlobalSpinner() {
return <HashLoader />
}

View File

@ -69,7 +69,12 @@ export default function BoardDetailModal({ noticeNo, setOpen }) {
</dl> </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> </div>
</div> </div>

File diff suppressed because it is too large Load Diff

View File

@ -6,36 +6,88 @@ import { useRecoilValue } from 'recoil'
import { floorPlanObjectState } from '@/store/floorPlanObjectAtom' import { floorPlanObjectState } from '@/store/floorPlanObjectAtom'
export default function DocDownOptionPop({ planNo, setEstimatePopupOpen }) { export default function DocDownOptionPop({ planNo, setEstimatePopupOpen }) {
// console.log('::::::::::::', planNo)
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { promiseGet } = useAxios() const { promisePost } = useAxios()
//EXCEL, PDF
const [schDownload, setSchDownload] = useState('EXCEL')
// EXCEL // EXCEL
const [schUnitPriceFlg, setSchUnitPriceFlg] = useState('0') const [schUnitPriceFlg, setSchUnitPriceFlg] = useState('0')
// //
const [schDisplayFlg, setSchSchDisplayFlg] = useState('0') const [schDisplayFlg, setSchSchDisplayFlg] = useState('0')
// // (:1 : 0)
const [schWeightFlg, setSchWeightFlg] = useState('0') const [schWeightFlg, setSchWeightFlg] = useState('1')
/// ///
const [schDrawingFlg, setSchDrawingFlg] = useState('0') const [schDrawingFlg, setSchDrawingFlg] = useState('1')
// recoil // recoil
const objectRecoil = useRecoilValue(floorPlanObjectState) const objectRecoil = useRecoilValue(floorPlanObjectState)
// //
const handleFileDown = async () => { 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 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' } 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 ( return (
@ -74,28 +126,30 @@ export default function DocDownOptionPop({ planNo, setEstimatePopupOpen }) {
<div className="d-check-radio light mr10"> <div className="d-check-radio light mr10">
<input <input
type="radio" type="radio"
id="schUnitPriceFlg0" id="schUnitPriceExcelFlg0"
name="schUnitPriceFlg" name="schUnitPriceFlg"
value={'0'} value={'0'}
checked={schUnitPriceFlg === '0'} checked={schUnitPriceFlg === '0'}
onChange={(e) => { onChange={(e) => {
setSchDownload('EXCEL')
setSchUnitPriceFlg(e.target.value) 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>
<div className="d-check-radio light mr10"> <div className="d-check-radio light mr10">
<input <input
type="radio" type="radio"
id="schUnitPriceFlg1" id="schUnitPriceExcelFlg1"
name="schUnitPriceFlg" name="schUnitPriceFlg"
value={'1'} value={'1'}
checked={schUnitPriceFlg === '1'} checked={schUnitPriceFlg === '1'}
onChange={(e) => { onChange={(e) => {
setSchDownload('EXCEL')
setSchUnitPriceFlg(e.target.value) 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>
<div className="d-check-radio light mr10"> <div className="d-check-radio light mr10">
<input <input
@ -105,10 +159,11 @@ export default function DocDownOptionPop({ planNo, setEstimatePopupOpen }) {
value={'2'} value={'2'}
checked={schUnitPriceFlg === '2'} checked={schUnitPriceFlg === '2'}
onChange={(e) => { onChange={(e) => {
setSchDownload('PDF')
setSchUnitPriceFlg(e.target.value) 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>
<div className="d-check-radio light "> <div className="d-check-radio light ">
<input <input
@ -118,10 +173,11 @@ export default function DocDownOptionPop({ planNo, setEstimatePopupOpen }) {
value={'3'} value={'3'}
checked={schUnitPriceFlg === '3'} checked={schUnitPriceFlg === '3'}
onChange={(e) => { onChange={(e) => {
setSchDownload('PDF')
setSchUnitPriceFlg(e.target.value) 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>
</div> </div>
</td> </td>
@ -168,19 +224,6 @@ export default function DocDownOptionPop({ planNo, setEstimatePopupOpen }) {
<td> <td>
<div className="form-flex-wrap"> <div className="form-flex-wrap">
<div className="d-check-radio light mr10"> <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 <input
type="radio" type="radio"
name="schWeightFlg" name="schWeightFlg"
@ -193,6 +236,19 @@ export default function DocDownOptionPop({ planNo, setEstimatePopupOpen }) {
/> />
<label htmlFor="schWeightFlg1">{getMessage('estimate.detail.docPopup.schWeightFlg.schWeightFlg1')}</label> <label htmlFor="schWeightFlg1">{getMessage('estimate.detail.docPopup.schWeightFlg.schWeightFlg1')}</label>
</div> </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> </div>
</td> </td>
</tr> </tr>
@ -201,6 +257,19 @@ export default function DocDownOptionPop({ planNo, setEstimatePopupOpen }) {
<td> <td>
<div className="form-flex-wrap"> <div className="form-flex-wrap">
<div className="d-check-radio light mr10"> <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 <input
type="radio" type="radio"
name="schDrawingFlg" name="schDrawingFlg"
@ -213,19 +282,6 @@ export default function DocDownOptionPop({ planNo, setEstimatePopupOpen }) {
/> />
<label htmlFor="schDrawingFlg0">{getMessage('estimate.detail.docPopup.schDrawingFlg.schDrawingFlg0')}</label> <label htmlFor="schDrawingFlg0">{getMessage('estimate.detail.docPopup.schDrawingFlg.schDrawingFlg0')}</label>
</div> </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> </div>
</td> </td>
</tr> </tr>

View 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>
)
}

View File

@ -1,13 +1,13 @@
'use client' 'use client'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
export default function ProductFeaturesPop({ specialNoteList, showProductFeatureData, setProductFeaturesPopupOpen }) { export default function ProductFeaturesPop({ popShowSpecialNoteList, showProductFeatureData, setProductFeaturesPopupOpen }) {
const [showSpecialNoteList, setShowSpecialNoteList] = useState([]) const [showSpecialNoteList, setShowSpecialNoteList] = useState([])
const { getMessage } = useMessage() const { getMessage } = useMessage()
useEffect(() => { useEffect(() => {
let pushData = [] let pushData = []
specialNoteList.map((row) => { popShowSpecialNoteList.map((row) => {
let option = showProductFeatureData.split('、') let option = showProductFeatureData.split('、')
option.map((row2) => { option.map((row2) => {
if (row.code === row2) { if (row.code === row2) {
@ -16,7 +16,7 @@ export default function ProductFeaturesPop({ specialNoteList, showProductFeature
}) })
}) })
setShowSpecialNoteList(pushData) setShowSpecialNoteList(pushData)
}, [specialNoteList]) }, [popShowSpecialNoteList])
return ( return (
<div className="modal-popup"> <div className="modal-popup">
@ -42,7 +42,8 @@ export default function ProductFeaturesPop({ specialNoteList, showProductFeature
return ( return (
<dl> <dl>
<dt>{row.codeNm}</dt> <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> </dl>
) )
})} })}

View File

@ -1,15 +1,8 @@
import { fabric } from 'fabric' import { fabric } from 'fabric'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
import { QLine } from '@/components/fabric/QLine' import { QLine } from '@/components/fabric/QLine'
import { import { distanceBetweenPoints, findTopTwoIndexesByDistance, getDirectionByPoint, sortedPointLessEightPoint, sortedPoints } from '@/util/canvas-util'
distanceBetweenPoints, import { calculateAngle, drawGabledRoof, drawRidgeRoof, drawShedRoof, inPolygon, toGeoJSON } from '@/util/qpolygon-utils'
findTopTwoIndexesByDistance,
getAllRelatedObjects,
getDirectionByPoint,
sortedPointLessEightPoint,
sortedPoints,
} from '@/util/canvas-util'
import { calculateAngle, drawRidgeRoof, drawShedRoof, inPolygon, toGeoJSON } from '@/util/qpolygon-utils'
import * as turf from '@turf/turf' import * as turf from '@turf/turf'
import { LINE_TYPE } from '@/common/common' import { LINE_TYPE } from '@/common/common'
@ -38,6 +31,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
this.cells = [] this.cells = []
this.innerLines = [] this.innerLines = []
this.children = [] this.children = []
this.separatePolygon = []
// 소수점 전부 제거 // 소수점 전부 제거
points.forEach((point) => { points.forEach((point) => {
@ -131,15 +125,14 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
this.on('selected', () => { this.on('selected', () => {
Object.keys(this.controls).forEach((controlKey) => { Object.keys(this.controls).forEach((controlKey) => {
if (controlKey !== 'ml' && controlKey !== 'mr') {
this.setControlVisible(controlKey, false) this.setControlVisible(controlKey, false)
}
}) })
this.set({ hasBorders: false })
}) })
this.on('removed', () => { this.on('removed', () => {
// const children = getAllRelatedObjects(this.id, this.canvas) // const children = getAllRelatedObjects(this.id, this.canvas)
const children = this.canvas.getObjects().filter((obj) => obj.parentId === this.id && obj.name === 'lengthText') const children = this.canvas.getObjects().filter((obj) => obj.parentId === this.id)
children.forEach((child) => { children.forEach((child) => {
this.canvas.remove(child) this.canvas.remove(child)
}) })
@ -167,6 +160,10 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
}, },
initLines() { initLines() {
// if (this.lines.length > 0) {
// return
// }
this.lines = [] this.lines = []
this.points.forEach((point, i) => { this.points.forEach((point, i) => {
@ -189,6 +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() { drawHelpLine() {
// drawHelpLineInHexagon(this, pitch) // 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))) || (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))) (gableEven.every((type) => type === LINE_TYPE.WALLLINE.EAVES) && gableOdd.every((type) => gableType.includes(type)))
) { ) {
console.log('박공 지붕') drawGabledRoof(this.id, this.canvas)
} else if (hasShed) { } else if (hasShed) {
const sheds = this.lines.filter((line) => line.attributes !== undefined && line.attributes.type === LINE_TYPE.WALLLINE.SHED) const sheds = this.lines.filter((line) => line.attributes !== undefined && line.attributes.type === LINE_TYPE.WALLLINE.SHED)
const areLinesParallel = function (line1, line2) { const areLinesParallel = function (line1, line2) {
@ -263,9 +266,11 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
this.texts = [] this.texts = []
points.forEach((start, i) => { points.forEach((start, i) => {
const end = points[(i + 1) % points.length] const end = points[(i + 1) % points.length]
// planeSize: Math.round(Math.sqrt(Math.pow(newLine.x1 - newLine.x2, 2) + Math.pow(newLine.y1 - newLine.y2, 2))) * 10,
// actualSize: Math.round(Math.sqrt(Math.pow(newLine.x1 - newLine.x2, 2) + Math.pow(newLine.y1 - newLine.y2, 2))) * 10,
const dx = end.x - start.x const dx = end.x - start.x
const dy = end.y - start.y const dy = end.y - start.y
const length = Number(Math.sqrt(dx * dx + dy * dy).toFixed(1)) * 10 const length = Math.round(Math.sqrt(Math.pow(end.x - start.x, 2) + Math.pow(end.y - start.y, 2))) * 10
let midPoint let midPoint

View File

@ -1,6 +1,6 @@
'use client' 'use client'
import { useEffect, useRef } from 'react' import { useContext, useEffect, useRef } from 'react'
import { useRecoilValue } from 'recoil' import { useRecoilValue } from 'recoil'
@ -13,6 +13,9 @@ import QContextMenu from '@/components/common/context-menu/QContextMenu'
import { useCanvasConfigInitialize } from '@/hooks/common/useCanvasConfigInitialize' import { useCanvasConfigInitialize } from '@/hooks/common/useCanvasConfigInitialize'
import { MENU } from '@/common/common' import { MENU } from '@/common/common'
import PanelBatchStatistics from '@/components/floor-plan/modal/panelBatch/PanelBatchStatistics' import PanelBatchStatistics from '@/components/floor-plan/modal/panelBatch/PanelBatchStatistics'
import { totalDisplaySelector } from '@/store/settingAtom'
import ImgLoad from '@/components/floor-plan/modal/ImgLoad'
import { EventContext } from '@/app/floor-plan/EventProvider'
export default function CanvasFrame() { export default function CanvasFrame() {
const canvasRef = useRef(null) const canvasRef = useRef(null)
@ -21,7 +24,10 @@ export default function CanvasFrame() {
const currentMenu = useRecoilValue(currentMenuState) const currentMenu = useRecoilValue(currentMenuState)
const { contextMenu, handleClick } = useContextMenu() const { contextMenu, handleClick } = useContextMenu()
const { selectedPlan, modifiedPlanFlag, checkCanvasObjectEvent, resetModifiedPlans, currentCanvasPlan } = usePlan() const { selectedPlan, modifiedPlanFlag, checkCanvasObjectEvent, resetModifiedPlans, currentCanvasPlan } = usePlan()
useEvent() const totalDisplay = useRecoilValue(totalDisplaySelector) //
// useEvent()
// const { initEvent } = useContext(EventContext)
// initEvent()
const loadCanvas = () => { const loadCanvas = () => {
if (canvas) { if (canvas) {
@ -72,7 +78,10 @@ export default function CanvasFrame() {
MENU.MODULE_CIRCUIT_SETTING.BASIC_SETTING, MENU.MODULE_CIRCUIT_SETTING.BASIC_SETTING,
MENU.MODULE_CIRCUIT_SETTING.CIRCUIT_TRESTLE_SETTING, MENU.MODULE_CIRCUIT_SETTING.CIRCUIT_TRESTLE_SETTING,
MENU.MODULE_CIRCUIT_SETTING.PLAN_ORIENTATION, MENU.MODULE_CIRCUIT_SETTING.PLAN_ORIENTATION,
].includes(currentMenu) && <PanelBatchStatistics />} ].includes(currentMenu) &&
totalDisplay && <PanelBatchStatistics />}
{/* 이미지 로드 팝업 */}
<ImgLoad />
</div> </div>
) )
} }

View File

@ -1,8 +1,8 @@
'use client' 'use client'
import { useContext, useEffect, useState } from 'react' import { useContext, useEffect } from 'react'
import { useRecoilValue } from 'recoil' import { useRecoilValue } from 'recoil'
import CanvasFrame from './CanvasFrame' import { FloorPlanContext } from '@/app/floor-plan/FloorPlanProvider'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { useSwal } from '@/hooks/useSwal' import { useSwal } from '@/hooks/useSwal'
import { usePlan } from '@/hooks/usePlan' import { usePlan } from '@/hooks/usePlan'
@ -14,15 +14,16 @@ export default function CanvasLayout({ children }) {
// const { menuNumber } = props // const { menuNumber } = props
const { menuNumber } = useCanvasMenu() const { menuNumber } = useCanvasMenu()
const { session } = useContext(SessionContext) const { session } = useContext(SessionContext)
const [objectNo, setObjectNo] = useState('test123240822001') // const { floorPlanState } = useContext(FloorPlanContext)
const { objectNo, pid } = floorPlanState
const globalLocaleState = useRecoilValue(globalLocaleStore) const globalLocaleState = useRecoilValue(globalLocaleStore)
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { swalFire } = useSwal() const { swalFire } = useSwal()
const { plans, modifiedPlans, loadCanvasPlanData, handleCurrentPlan, handleAddPlan, handleDeletePlan } = usePlan() const { plans, initCanvasPlans, modifiedPlans, loadCanvasPlanData, handleCurrentPlan, handleAddPlan, handleDeletePlan } = usePlan()
useEffect(() => { useEffect(() => {
loadCanvasPlanData(session.userId, objectNo) loadCanvasPlanData(session.userId, objectNo, pid)
}, []) }, [])
return ( return (
@ -36,14 +37,18 @@ export default function CanvasLayout({ children }) {
onClick={() => handleCurrentPlan(session.userId, plan.id)} onClick={() => handleCurrentPlan(session.userId, plan.id)}
> >
<span> <span>
{plan.name} {!initCanvasPlans.some((initCanvasPlans) => initCanvasPlans.id === plan.id) && 'New '}
{`Plan ${plan.ordering}`}
{modifiedPlans.some((modifiedPlan) => modifiedPlan === plan.id) && ' [ M ]'} {modifiedPlans.some((modifiedPlan) => modifiedPlan === plan.id) && ' [ M ]'}
</span> </span>
<i <i
className="close" className="close"
onClick={(e) => onClick={(e) =>
swalFire({ 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', type: 'confirm',
confirmFn: () => { confirmFn: () => {
handleDeletePlan(e, plan.id) handleDeletePlan(e, plan.id)

View File

@ -1,6 +1,6 @@
'use client' 'use client'
import { useEffect, useState } from 'react' import { useContext, useEffect, useState } from 'react'
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil' import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
@ -33,8 +33,12 @@ import useMenu from '@/hooks/common/useMenu'
import { MENU } from '@/common/common' import { MENU } from '@/common/common'
import { useEstimateController } from '@/hooks/floorPlan/estimate/useEstimateController' 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 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) { export default function CanvasMenu(props) {
const { menuNumber, setMenuNumber } = props const { menuNumber, setMenuNumber } = props
@ -59,14 +63,17 @@ export default function CanvasMenu(props) {
const { handleEstimateSubmit } = useEstimateController() const { handleEstimateSubmit } = useEstimateController()
const estimateRecoilState = useRecoilValue(estimateState) const estimateRecoilState = useRecoilValue(estimateState)
const [estimatePopupOpen, setEstimatePopupOpen] = useState(false) const [estimatePopupOpen, setEstimatePopupOpen] = useState(false)
const [estimateCopyPopupOpen, setEstimateCopyPopupOpen] = useState(false)
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { currentCanvasPlan, saveCanvas } = usePlan() const { currentCanvasPlan, saveCanvas } = usePlan()
const { swalFire } = useSwal() const { swalFire } = useSwal()
const { initEvent, addCanvasMouseEventListener, addDocumentEventListener } = useEvent() const { initEvent, addCanvasMouseEventListener, addDocumentEventListener } = useEvent()
// const { initEvent, addCanvasMouseEventListener, addDocumentEventListener } = useContext(EventContext)
const commonUtils = useRecoilValue(commonUtilsState) const commonUtils = useRecoilValue(commonUtilsState)
const { commonFunctions } = useCommonUtils() const { commonFunctions } = useCommonUtils()
const SelectOption = [{ name: '瓦53A' }, { name: '瓦53A' }] const SelectOption = [{ name: '瓦53A' }, { name: '瓦53A' }]
const { floorPlanState, setFloorPlanState } = useContext(FloorPlanContext)
const onClickNav = (menu) => { const onClickNav = (menu) => {
setMenuNumber(menu.index) 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(() => { useEffect(() => {
if (globalLocale === 'ko') { if (globalLocale === 'ko') {
setAppMessageState(KO) setAppMessageState(KO)
@ -184,7 +177,7 @@ export default function CanvasMenu(props) {
}, [type, globalLocale]) }, [type, globalLocale])
useEffect(() => { useEffect(() => {
if (['2', '3'].includes(canvasSetting?.roofSizeSet?.toString())) { if ([2, 3].some((num) => num === canvasSetting?.roofSizeSet)) {
setMenuNumber(3) setMenuNumber(3)
setType('surface') setType('surface')
setCurrentMenu(MENU.BATCH_CANVAS.BATCH_DRAWING) setCurrentMenu(MENU.BATCH_CANVAS.BATCH_DRAWING)
@ -195,8 +188,60 @@ export default function CanvasMenu(props) {
} }
}, [canvasSetting]) }, [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 ( 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"> <div className="canvas-menu-inner">
<ul className="canvas-menu-list"> <ul className="canvas-menu-list">
{canvasMenus.map((menu) => { {canvasMenus.map((menu) => {
@ -205,11 +250,12 @@ export default function CanvasMenu(props) {
key={`canvas-menu-${menu.index}`} key={`canvas-menu-${menu.index}`}
className={`canvas-menu-item ${menuNumber === menu.index ? 'active' : ''}`} className={`canvas-menu-item ${menuNumber === menu.index ? 'active' : ''}`}
onClick={() => { 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) 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> <span className={`menu-icon ${menu.icon}`}></span>
{getMessage(menu.name)} {getMessage(menu.name)}
</button> </button>
@ -218,7 +264,7 @@ export default function CanvasMenu(props) {
})} })}
</ul> </ul>
<div className="canvas-side-btn-wrap"> <div className="canvas-side-btn-wrap">
{menuNumber !== 6 && menuNumber !== 5 && ( {![5, 6].some((num) => num === menuNumber) && (
<> <>
{ {
<div className={`vertical-horizontal ${verticalHorizontalMode ? 'on' : ''}`}> <div className={`vertical-horizontal ${verticalHorizontalMode ? 'on' : ''}`}>
@ -235,6 +281,10 @@ export default function CanvasMenu(props) {
<QSelectBox title={'瓦53A'} option={SelectOption} /> <QSelectBox title={'瓦53A'} option={SelectOption} />
</div> </div>
<div className="btn-from"> <div className="btn-from">
<button
className={`btn10 ${floorPlanState.refFileModalOpen && 'active'}`}
onClick={() => setFloorPlanState({ ...floorPlanState, refFileModalOpen: true })}
></button>
{/*<button className="btn04" onClick={() => setShowCanvasSettingModal(true)}></button>*/} {/*<button className="btn04" onClick={() => setShowCanvasSettingModal(true)}></button>*/}
<button className="btn04" onClick={handlePopup}></button> <button className="btn04" onClick={handlePopup}></button>
<button className="btn05"></button> <button className="btn05"></button>
@ -247,7 +297,7 @@ export default function CanvasMenu(props) {
handleZoom(false) handleZoom(false)
}} }}
></button> ></button>
<span>{canvasZoom}%</span> <span onClick={handleZoomClear}>{canvasZoom}%</span>
<button <button
className="control-btn plus" className="control-btn plus"
onClick={() => { onClick={() => {
@ -273,7 +323,6 @@ export default function CanvasMenu(props) {
<span className="ico ico02"></span> <span className="ico ico02"></span>
<span>{getMessage('plan.menu.estimate.save')}</span> <span>{getMessage('plan.menu.estimate.save')}</span>
</button> </button>
{/* {estimateRecoilState?.docNo != null && ( */}
<button <button
className="btn-frame gray ico-flx" className="btn-frame gray ico-flx"
onClick={() => { onClick={() => {
@ -283,28 +332,29 @@ export default function CanvasMenu(props) {
<span className="ico ico03"></span> <span className="ico ico03"></span>
<span>{getMessage('plan.menu.estimate.reset')}</span> <span>{getMessage('plan.menu.estimate.reset')}</span>
</button> </button>
{/* )} */}
{estimateRecoilState?.docNo !== null && (sessionState.storeId === 'T01' || sessionState.storeLvl === '1') && (
<button <button
className="btn-frame gray ico-flx" className="btn-frame gray ico-flx"
onClick={() => { onClick={() => {
handleEstimateCopy() setEstimateCopyPopupOpen(true)
}} }}
> >
<span className="ico ico04"></span> <span className="ico ico04"></span>
<span>{getMessage('plan.menu.estimate.copy')}</span> <span>{getMessage('plan.menu.estimate.copy')}</span>
</button> </button>
)}
</div> </div>
</> </>
)} )}
{menuNumber === 6 && ( {menuNumber === 6 && (
<> <>
<div className="ico-btn-from"> <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 className="ico ico01"></span>
<span>{getMessage('plan.menu.simulation.excel')}</span> <span>{getMessage('plan.menu.simulation.excel')}</span>
</button> </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 className="ico ico01"></span>
<span>{getMessage('plan.menu.simulation.pdf')}</span> <span>{getMessage('plan.menu.simulation.pdf')}</span>
</button> </button>
@ -313,11 +363,13 @@ export default function CanvasMenu(props) {
)} )}
</div> </div>
</div> </div>
<div className={`canvas-depth2-wrap ${menuNumber === 2 || menuNumber === 3 || menuNumber === 4 ? 'active' : ''}`}> <div className={`canvas-depth2-wrap ${[2, 3, 4].some((num) => num === menuNumber) ? 'active' : ''}`}>
{(menuNumber === 2 || menuNumber === 3 || menuNumber === 4) && <MenuDepth01 />} {[2, 3, 4].some((num) => num === menuNumber) && <MenuDepth01 />}
</div> </div>
{/* 견적서(menuNumber=== 5) 상세화면인경우 문서다운로드 팝업 */} {/* 견적서(menuNumber=== 5) 상세화면인경우 문서다운로드 팝업 */}
{estimatePopupOpen && <DocDownOptionPop planNo={estimateRecoilState?.planNo} setEstimatePopupOpen={setEstimatePopupOpen} />} {estimatePopupOpen && <DocDownOptionPop planNo={estimateRecoilState?.planNo} setEstimatePopupOpen={setEstimatePopupOpen} />}
{/* 견적서(menuNumber ===5)복사 팝업 */}
{estimateCopyPopupOpen && <EstimateCopyPop planNo={estimateRecoilState?.planNo} setEstimateCopyPopupOpen={setEstimateCopyPopupOpen} />}
</div> </div>
) )
} }

View File

@ -1,20 +1,20 @@
'use client' 'use client'
import { useEffect, useState } from 'react' import { useContext, useEffect } from 'react'
import { useRecoilState, useRecoilValue } from 'recoil' //import { useRecoilState } from 'recoil'
import { settingModalFirstOptionsState, settingModalSecondOptionsState } from '@/store/settingAtom'
import CanvasMenu from '@/components/floor-plan/CanvasMenu' import CanvasMenu from '@/components/floor-plan/CanvasMenu'
import { useCanvasMenu } from '@/hooks/common/useCanvasMenu' import { useCanvasMenu } from '@/hooks/common/useCanvasMenu'
import { useCanvasSetting } from '@/hooks/option/useCanvasSetting' 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' import '@/styles/contents.scss'
export default function FloorPlan({ children }) { export default function FloorPlan({ children }) {
const [settingModalFirstOptions, setSettingModalFirstOptions] = useRecoilState(settingModalFirstOptionsState) //const { floorPlanState, setFloorPlanState } = useContext(FloorPlanContext)
const [settingModalSecondOptions, setSettingModalSecondOptions] = useRecoilState(settingModalSecondOptionsState) //const [correntObjectNo, setCorrentObjectNo] = useRecoilState(correntObjectNoState)
const [objectNo, setObjectNo] = useState('test123240912001') // const { closeAll } = usePopup()
const { menuNumber, setMenuNumber } = useCanvasMenu() const { menuNumber, setMenuNumber } = useCanvasMenu()
const { fetchSettings } = useCanvasSetting() const { fetchSettings } = useCanvasSetting()
const modalProps = { const modalProps = {
@ -23,8 +23,13 @@ export default function FloorPlan({ children }) {
} }
useEffect(() => { useEffect(() => {
///setCorrentObjectNo(floorPlanState.objectNo)
//console.log('FloorPlan objectNo ', floorPlanState.objectNo, correntObjectNo)
fetchSettings() fetchSettings()
}, [objectNo]) return () => {
closeAll()
}
}, [])
return ( return (
<> <>

View File

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

View File

@ -0,0 +1,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>
)
}

View File

@ -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>
)
}

View File

@ -49,6 +49,7 @@ export default function AuxiliaryDrawing({ id, pos = { x: 50, y: 230 } }) {
handleFix, handleFix,
buttonAct, buttonAct,
setButtonAct, setButtonAct,
cutAuxiliary,
} = useAuxiliaryDrawing(id) } = useAuxiliaryDrawing(id)
const outerLineProps = { const outerLineProps = {
@ -151,6 +152,9 @@ export default function AuxiliaryDrawing({ id, pos = { x: 50, y: 230 } }) {
<button className="btn-frame modal mr5" onClick={handleRollback}> <button className="btn-frame modal mr5" onClick={handleRollback}>
{getMessage('modal.cover.outline.rollback')} {getMessage('modal.cover.outline.rollback')}
</button> </button>
<button className="btn-frame modal mr5" onClick={cutAuxiliary}>
{getMessage('contextmenu.auxiliary.cut')}
</button>
<button className="btn-frame modal act" onClick={() => handleFix(id)}> <button className="btn-frame modal act" onClick={() => handleFix(id)}>
{getMessage('apply')} {getMessage('apply')}
</button> </button>

View File

@ -1,37 +1,66 @@
'use client'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import WithDraggable from '@/components/common/draggable/WithDraggable' import WithDraggable from '@/components/common/draggable/WithDraggable'
import { usePopup } from '@/hooks/usePopup'
import { useRecoilValue } from 'recoil' import { useRecoilValue } from 'recoil'
import { contextPopupPositionState } from '@/store/popupAtom' import { contextPopupPositionState } from '@/store/popupAtom'
import { usePopup } from '@/hooks/usePopup'
import { useState } from 'react' 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 contextPopupPosition = useRecoilValue(contextPopupPositionState)
const { id, pos = contextPopupPosition } = props const { id, pos = contextPopupPosition, type } = props
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { closePopup } = usePopup() const { closePopup } = usePopup()
const { move, copy } = useAuxiliaryDrawing()
const [verticalSize, setVerticalSize] = useState('0')
const [horizonSize, setHorizonSize] = useState('0')
const [arrow1, setArrow1] = useState(null) const [arrow1, setArrow1] = useState(null)
const [arrow2, setArrow2] = 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 ( return (
<WithDraggable isShow={true} pos={pos}> <WithDraggable isShow={true} pos={pos}>
<div className={`modal-pop-wrap xm mount`}> <div className={`modal-pop-wrap xm mount`}>
<div className="modal-head"> <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 className="modal-close" onClick={() => closePopup(id)}>
닫기 닫기
</button> </button>
</div> </div>
<div className="modal-body"> <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-wrap">
<div className="grid-option-box"> <div className="grid-option-box">
<div className="move-form"> <div className="move-form">
<p className="mb5">{getMessage('length')}</p> <p className="mb5">{getMessage('length')}</p>
<div className="input-move-wrap mb5"> <div className="input-move-wrap mb5">
<div className="input-move"> <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> </div>
<span>mm</span> <span>mm</span>
<div className="direction-move-wrap"> <div className="direction-move-wrap">
@ -53,7 +82,7 @@ export default function AuxiliaryMove(props) {
</div> </div>
<div className="input-move-wrap"> <div className="input-move-wrap">
<div className="input-move"> <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> </div>
<span>mm</span> <span>mm</span>
<div className="direction-move-wrap"> <div className="direction-move-wrap">
@ -77,7 +106,9 @@ export default function AuxiliaryMove(props) {
</div> </div>
</div> </div>
<div className="grid-btn-wrap"> <div className="grid-btn-wrap">
<button className="btn-frame modal act">{getMessage('modal.common.save')}</button> <button className="btn-frame modal act" onClick={handleSave}>
{getMessage('modal.common.save')}
</button>
</div> </div>
</div> </div>
</div> </div>

View File

@ -3,12 +3,14 @@ import WithDraggable from '@/components/common/draggable/WithDraggable'
import { usePopup } from '@/hooks/usePopup' import { usePopup } from '@/hooks/usePopup'
import { useRecoilValue } from 'recoil' import { useRecoilValue } from 'recoil'
import { contextPopupPositionState } from '@/store/popupAtom' import { contextPopupPositionState } from '@/store/popupAtom'
import { useCanvas } from '@/hooks/useCanvas'
export default function AuxiliarySize(props) { export default function AuxiliarySize(props) {
const contextPopupPosition = useRecoilValue(contextPopupPositionState) const contextPopupPosition = useRecoilValue(contextPopupPositionState)
const { id, pos = contextPopupPosition } = props const { id, pos = contextPopupPosition } = props
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { closePopup } = usePopup() const { closePopup } = usePopup()
const { currentObject } = useCanvas()
return ( return (
<WithDraggable isShow={true} pos={pos}> <WithDraggable isShow={true} pos={pos}>
<div className={`modal-pop-wrap xm mount`}> <div className={`modal-pop-wrap xm mount`}>
@ -26,7 +28,7 @@ export default function AuxiliarySize(props) {
</div> </div>
<div className="outline-form mb15"> <div className="outline-form mb15">
<div className="input-grid mr5" style={{ flex: '1 1 auto' }}> <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> </div>
<span className="thin">mm</span> <span className="thin">mm</span>
</div> </div>
@ -45,14 +47,14 @@ export default function AuxiliarySize(props) {
</div> </div>
<div className="outline-form mb15"> <div className="outline-form mb15">
<div className="input-grid mr5" style={{ flex: '1 1 auto' }}> <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> </div>
<span className="thin">mm</span> <span className="thin">mm</span>
</div> </div>
<div className="outline-form"> <div className="outline-form">
<span style={{ width: 'auto' }}>{getMessage('length')}</span> <span style={{ width: 'auto' }}>{getMessage('length')}</span>
<div className="input-grid mr5"> <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> </div>
<span className="thin">mm</span> <span className="thin">mm</span>
</div> </div>

View File

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

View File

@ -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 ( return (
<> <>
<div className="module-table-flex-wrap mb10"> <div className="module-table-flex-wrap mb10">
@ -47,11 +57,13 @@ export default function Module({}) {
<table> <table>
<thead> <thead>
<tr> <tr>
{moduleData.header.map((data) => ( {moduleData.header.map((header) => {
<th key={data.prop} style={{ width: data.width ? data.width : '' }}> return (
{data.name} <th key={header.prop} style={{ width: header.width ? header.width + 'px' : '' }}>
{header.name}
</th> </th>
))} )
})}
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -88,16 +100,85 @@ export default function Module({}) {
</div> </div>
</div> </div>
</div> </div>
<div className="module-table-box"> <div className="module-table-box none-flex">
<div className="module-table-inner">
<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.surface.type')}</div>
<div className="eaves-keraba-td">
<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.fitting.height')}</div>
<div className="eaves-keraba-td">
<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.standard.wind.speed')}</div>
<div className="eaves-keraba-td">
<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>
</div>
</div>
</div>
</div>
<div className="module-table-box mb10">
<div className="module-box-tab"> <div className="module-box-tab">
<button className="module-btn act">スレート</button> <button className="module-btn act">屋根材1</button>
<button className="module-btn">平板瓦</button> <button className="module-btn">屋根材2</button>
<button className="module-btn">53A</button> <button className="module-btn">屋根材3</button>
<button className="module-btn">53A</button> <button className="module-btn">屋根材4</button>
</div> </div>
<div className="module-table-inner"> <div className="module-table-inner">
<div className="module-table-tit">{getMessage('modal.module.basic.setting.module.roof.material')}: スレーツ4)</div> <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-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-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.trestle.maker')}</div>
<div className="eaves-keraba-td"> <div className="eaves-keraba-td">
@ -118,62 +199,21 @@ export default function Module({}) {
<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.under.roof')}</div>
<div className="eaves-keraba-td"> <div className="eaves-keraba-td">
<div className="grid-select"> <div className="grid-select">
<QSelectBox title={'構造溶合板 12mm以上'} option={SelectOption01} /> <QSelectBox title={'構造用合板12mm以上 又はOSB12mm以上'} option={SelectOption01} />
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<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> <div className="grid-check-form">
<div className="module-table-box mb10">
<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>
</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>
</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>
</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>
</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"> <div className="d-check-box pop">
<input type="checkbox" id="ch01" /> <input type="checkbox" id="ch01" />
<label htmlFor="ch01">{getMessage('modal.module.basic.setting.module.eaves.bar.fitting')}</label> <label htmlFor="ch01">{getMessage('modal.module.basic.setting.module.eaves.bar.fitting')}</label>
@ -183,6 +223,23 @@ export default function Module({}) {
<label htmlFor="ch02">{getMessage('modal.module.basic.setting.module.blind.metal.fitting')}</label> <label htmlFor="ch02">{getMessage('modal.module.basic.setting.module.blind.metal.fitting')}</label>
</div> </div>
</div> </div>
</div>
</div>
</div>
</div>
<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 className="reset-word"> 施工方法が選択できません 基準風速または基準積雪量を確認してください</div>
</div>
</> </>
) )
} }

View File

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

View File

@ -1,7 +1,12 @@
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { forwardRef, useState } from 'react'
export default function Placement() { const Placement = forwardRef((props, refs) => {
const { getMessage } = useMessage() const { getMessage } = useMessage()
const [isChidori, setIsChidori] = useState('false')
const [setupLocation, setSetupLocation] = useState('center')
const [isMaxSetup, setIsMaxSetup] = useState('false')
const moduleData = { const moduleData = {
header: [ header: [
{ type: 'check', name: '', prop: 'check', width: 70 }, { 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 ( return (
<> <>
<div className="module-table-flex-wrap mb10"> <div className="module-table-flex-wrap mb10">
@ -96,12 +124,20 @@ export default function Placement() {
<div className="self-item-td"> <div className="self-item-td">
<div className="pop-form-radio"> <div className="pop-form-radio">
<div className="d-check-radio pop"> <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> <label htmlFor="ra01">{getMessage('modal.module.basic.setting.module.placement.do')}</label>
</div> </div>
<div className="d-check-radio pop"> <div className="d-check-radio pop">
<input type="radio" name="radio01" id="ra02" /> <input
<label htmlFor="ra01">{getMessage('modal.module.basic.setting.module.placement.do.not')}</label> 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> </div>
</div> </div>
@ -111,15 +147,36 @@ export default function Placement() {
<div className="self-item-td"> <div className="self-item-td">
<div className="pop-form-radio"> <div className="pop-form-radio">
<div className="d-check-radio pop"> <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> <label htmlFor="ra03">{getMessage('modal.module.basic.setting.module.placement.arrangement.standard.center')}</label>
</div> </div>
<div className="d-check-radio pop"> <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> <label htmlFor="ra04">{getMessage('modal.module.basic.setting.module.placement.arrangement.standard.eaves')}</label>
</div> </div>
<div className="d-check-radio pop"> <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> <label htmlFor="ra05">{getMessage('modal.module.basic.setting.module.placement.arrangement.standard.ridge')}</label>
</div> </div>
</div> </div>
@ -128,7 +185,7 @@ export default function Placement() {
</div> </div>
<div className="self-table-flx"> <div className="self-table-flx">
<div className="d-check-box pop"> <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> <label htmlFor="ch04">{getMessage('modal.module.basic.setting.module.placement.maximum')}</label>
</div> </div>
</div> </div>
@ -137,4 +194,6 @@ export default function Placement() {
</div> </div>
</> </>
) )
} })
export default Placement

View File

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

View File

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

View File

@ -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>
)
}

View File

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

View File

@ -5,11 +5,12 @@ import { useMessage } from '@/hooks/useMessage'
import WithDraggable from '@/components/common/draggable/WithDraggable' import WithDraggable from '@/components/common/draggable/WithDraggable'
import { usePopup } from '@/hooks/usePopup' import { usePopup } from '@/hooks/usePopup'
import { contextPopupPositionState } from '@/store/popupAtom' 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 { useObjectBatch } from '@/hooks/object/useObjectBatch'
import { useEvent } from '@/hooks/useEvent' import { useEvent } from '@/hooks/useEvent'
import { BATCH_TYPE, POLYGON_TYPE } from '@/common/common' import { BATCH_TYPE, POLYGON_TYPE } from '@/common/common'
import { useSurfaceShapeBatch } from '@/hooks/surface/useSurfaceShapeBatch' import { useSurfaceShapeBatch } from '@/hooks/surface/useSurfaceShapeBatch'
import { EventContext } from '@/app/floor-plan/EventProvider'
export default function SizeSetting(props) { export default function SizeSetting(props) {
const contextPopupPosition = useRecoilValue(contextPopupPositionState) const contextPopupPosition = useRecoilValue(contextPopupPositionState)
@ -22,11 +23,12 @@ export default function SizeSetting(props) {
const widthRef = useRef(null) const widthRef = useRef(null)
const heightRef = useRef(null) const heightRef = useRef(null)
const { initEvent } = useEvent() // const { initEvent } = useEvent()
// const { initEvent } = useContext(EventContext)
useEffect(() => { // useEffect(() => {
initEvent() // initEvent()
}, []) // }, [])
const handleReSizeObject = () => { const handleReSizeObject = () => {
const width = widthRef.current.value const width = widthRef.current.value

View File

@ -82,7 +82,7 @@ const PentagonDormer = forwardRef((props, refs) => {
</div> </div>
</div> </div>
<div className="discrimination-box"> <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="object-direction-wrap">
<div className="plane-direction"> <div className="plane-direction">
<span className="top">{getMessage('commons.north')}</span> <span className="top">{getMessage('commons.north')}</span>

View File

@ -60,7 +60,7 @@ const TriangleDormer = forwardRef((props, refs) => {
</div> </div>
</div> </div>
<div className="discrimination-box"> <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="object-direction-wrap">
<div className="plane-direction"> <div className="plane-direction">
<span className="top">{getMessage('commons.north')}</span> <span className="top">{getMessage('commons.north')}</span>

View File

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

View File

@ -1,19 +1,46 @@
import React, { useEffect, useState } from 'react' import React, { useEffect, useState } from 'react'
import { useCanvasSetting } from '@/hooks/option/useCanvasSetting' import { useCanvasSetting } from '@/hooks/option/useCanvasSetting'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { POLYGON_TYPE } from '@/common/common'
import { setSurfaceShapePattern } from '@/util/canvas-util'
export default function FirstOption() { export default function FirstOption() {
const [objectNo, setObjectNo] = useState('test123240912001') //
const { settingModalFirstOptions, setSettingModalFirstOptions } = useCanvasSetting()
const { settingModalSecondOptions, setSettingModalSecondOptions } = useCanvasSetting()
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { canvas, settingModalFirstOptions, setSettingModalFirstOptions } = useCanvasSetting()
const { fetchSettings, frontSettings, onClickOption } = useCanvasSetting() const { option1, option2, dimensionDisplay } = settingModalFirstOptions
// //
useEffect(() => { useEffect(() => {
console.log('FirstOption 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 ( return (
<> <>

View File

@ -1,4 +1,3 @@
import { useRecoilValue } from 'recoil'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import React, { useEffect, useState } from 'react' import React, { useEffect, useState } from 'react'
import DimensionLineSetting from '@/components/floor-plan/modal/setting01/dimensionLine/DimensionLineSetting' import DimensionLineSetting from '@/components/floor-plan/modal/setting01/dimensionLine/DimensionLineSetting'
@ -6,80 +5,44 @@ import { usePopup } from '@/hooks/usePopup'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
import FontSetting from '@/components/common/font/FontSetting' import FontSetting from '@/components/common/font/FontSetting'
import PlanSizeSetting from '@/components/floor-plan/modal/setting01/planSize/PlanSizeSetting' import PlanSizeSetting from '@/components/floor-plan/modal/setting01/planSize/PlanSizeSetting'
import { dimensionLineSettingsState } from '@/store/commonUtilsAtom'
import { useCanvasSetting } from '@/hooks/option/useCanvasSetting' import { useCanvasSetting } from '@/hooks/option/useCanvasSetting'
import { useRecoilState, useRecoilValue } from 'recoil'
import { fontSelector, globalFontAtom } from '@/store/fontAtom'
export default function SecondOption() { export default function SecondOption() {
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { addPopup, closePopup, closePopups } = usePopup() const { addPopup, closePopup } = usePopup()
const [showFontSettingModal, setShowFontSettingModal] = useState(false) const [showFontSettingModal, setShowFontSettingModal] = useState(false)
const [showDimensionLineSettingModal, setShowDimensionLineSettingModal] = useState(false) const [showDimensionLineSettingModal, setShowDimensionLineSettingModal] = useState(false)
const [showPlanSizeSettingModal, setShowPlanSizeSettingModal] = useState(false) const [showPlanSizeSettingModal, setShowPlanSizeSettingModal] = useState(false)
const dimensionSettings = useRecoilValue(dimensionLineSettingsState) const [globalFont, setGlobalFont] = useRecoilState(globalFontAtom)
const 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 {
const { settingModalFirstOptions, setSettingModalFirstOptions } = useCanvasSetting() fetchSettings,
const { settingModalSecondOptions, setSettingModalSecondOptions } = useCanvasSetting() planSizeSettingMode,
const { adsorptionPointMode, setAdsorptionPointMode } = useCanvasSetting() setPlanSizeSettingMode,
const { fetchSettings, frontSettings, onClickOption } = useCanvasSetting() settingModalSecondOptions,
setSettingModalSecondOptions,
adsorptionPointMode,
setAdsorptionPointMode,
setAdsorptionRange,
} = useCanvasSetting()
const { option3, option4 } = settingModalSecondOptions
// //
useEffect(() => { useEffect(() => {
console.log('SecondOption 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) => { const handlePopup = (type) => {
setShowDimensionLineSettingModal(false)
setShowPlanSizeSettingModal(false) setShowPlanSizeSettingModal(false)
setShowFontSettingModal(false) setShowFontSettingModal(false)
@ -89,6 +52,7 @@ export default function SecondOption() {
setShowFontSettingModal(true) setShowFontSettingModal(true)
setShowDimensionLineSettingModal(false) setShowDimensionLineSettingModal(false)
fontProps.type = 'commonText' fontProps.type = 'commonText'
fontProps.font = commonFont
fontProps.id = fontId + 1 fontProps.id = fontId + 1
addPopup(fontId + 1, 2, <FontSetting {...fontProps} />, true) addPopup(fontId + 1, 2, <FontSetting {...fontProps} />, true)
break break
@ -99,6 +63,7 @@ export default function SecondOption() {
setShowFontSettingModal(true) setShowFontSettingModal(true)
setShowDimensionLineSettingModal(false) setShowDimensionLineSettingModal(false)
fontProps.type = 'flowText' fontProps.type = 'flowText'
fontProps.font = flowFont
fontProps.id = fontId + 2 fontProps.id = fontId + 2
addPopup(fontId + 2, 2, <FontSetting {...fontProps} />, true) addPopup(fontId + 2, 2, <FontSetting {...fontProps} />, true)
break break
@ -107,9 +72,9 @@ export default function SecondOption() {
case 'font3': { case 'font3': {
// //
setShowFontSettingModal(true) setShowFontSettingModal(true)
setShowDimensionLineSettingModal(false) setShowDimensionLineSettingModal(false)
fontProps.type = 'lengthText' fontProps.type = 'lengthText'
fontProps.font = lengthFont
fontProps.id = fontId + 3 fontProps.id = fontId + 3
addPopup(fontId + 3, 2, <FontSetting {...fontProps} />, true) addPopup(fontId + 3, 2, <FontSetting {...fontProps} />, true)
break break
@ -120,6 +85,7 @@ export default function SecondOption() {
setShowFontSettingModal(true) setShowFontSettingModal(true)
setShowDimensionLineSettingModal(false) setShowDimensionLineSettingModal(false)
fontProps.type = 'circuitNumberText' fontProps.type = 'circuitNumberText'
fontProps.font = circuitNumberTextFont
fontProps.id = fontId fontProps.id = fontId
addPopup(fontId, 2, <FontSetting {...fontProps} />, true) addPopup(fontId, 2, <FontSetting {...fontProps} />, true)
break break
@ -129,6 +95,12 @@ export default function SecondOption() {
// //
if (!showDimensionLineSettingModal) { if (!showDimensionLineSettingModal) {
setShowDimensionLineSettingModal(true) setShowDimensionLineSettingModal(true)
fontProps.font = {
fontFamily: '',
fontWeight: '',
fontSize: '',
fontColor: '',
}
addPopup(dimensionId, 2, <DimensionLineSetting {...dimensionProps} />, true) addPopup(dimensionId, 2, <DimensionLineSetting {...dimensionProps} />, true)
} else { } else {
setShowDimensionLineSettingModal(false) setShowDimensionLineSettingModal(false)
@ -139,14 +111,84 @@ export default function SecondOption() {
case 'planSize': { case 'planSize': {
// //
if (!showPlanSizeSettingModal) {
fetchSettings() //
setShowPlanSizeSettingModal(true) setShowPlanSizeSettingModal(true)
setShowDimensionLineSettingModal(false)
addPopup(planSizeId, 2, <PlanSizeSetting {...planSizeProps} />, true) addPopup(planSizeId, 2, <PlanSizeSetting {...planSizeProps} />, true)
} else {
setShowPlanSizeSettingModal(false)
closePopup(planSizeId, true)
}
break 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 ( return (
<> <>
<div className="modal-check-btn-wrap"> <div className="modal-check-btn-wrap">
@ -181,12 +223,12 @@ export default function SecondOption() {
</button> </button>
<button <button
className="adsorption-point act" className="adsorption-point act"
onClick={(e) => { onClick={() => {
setAdsorptionPointMode(!adsorptionPointMode) onClickOption('adsorpPoint')
}} }}
> >
<span>{getMessage('modal.canvas.setting.font.plan.absorption.point')}</span> <span>{getMessage('modal.canvas.setting.font.plan.absorption.point')}</span>
<i>{adsorptionPointMode ? 'ON' : 'OFF'}</i> <i>{adsorptionPointMode.adsorptionPoint ? 'ON' : 'OFF'}</i>
</button> </button>
</div> </div>
</div> </div>

View File

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

View File

@ -1,17 +1,54 @@
import { useState } from 'react' import { useEffect, useState } from 'react'
import { useRecoilValue } from 'recoil' import { useRecoilValue } from 'recoil'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { usePopup } from '@/hooks/usePopup' import { usePopup } from '@/hooks/usePopup'
import WithDraggable from '@/components/common/draggable/WithDraggable' import WithDraggable from '@/components/common/draggable/WithDraggable'
import { canvasState } from '@/store/canvasAtom' import { canvasState } from '@/store/canvasAtom'
import { useCanvasSetting } from '@/hooks/option/useCanvasSetting'
import { onlyNumberInputChange } from '@/util/input-utils'
export default function PlanSizeSetting(props) { 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 { closePopup } = usePopup()
const { getMessage } = useMessage() const { getMessage } = useMessage()
const [originHorizon, setOriginHorizon] = useState(horizon)
const [originVertical, setOriginVertical] = useState(vertical)
const canvas = useRecoilValue(canvasState) 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 ( return (
<WithDraggable isShow={true} pos={pos}> <WithDraggable isShow={true} pos={pos}>
@ -33,7 +70,15 @@ export default function PlanSizeSetting(props) {
<div className="outline-form mb10"> <div className="outline-form mb10">
<span style={{ width: 'auto' }}>{getMessage('common.horizon')}</span> <span style={{ width: 'auto' }}>{getMessage('common.horizon')}</span>
<div className="input-grid mr5" style={{ width: '90px' }}> <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> </div>
<span className="thin">mm</span> <span className="thin">mm</span>
</div> </div>
@ -43,26 +88,18 @@ export default function PlanSizeSetting(props) {
<input <input
type="text" type="text"
className="input-origin block" className="input-origin block"
value={originVertical} name={`originVertical`}
onChange={(e) => setOriginVertical(Number(e.target.value))} 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> </div>
<span className="thin">mm</span> <span className="thin">mm</span>
</div> </div>
</div> </div>
<div className="grid-btn-wrap"> <div className="grid-btn-wrap">
<button <button className="btn-frame modal act" onClick={onSave}>
className="btn-frame modal act"
onClick={() => {
setHorizon(originHorizon)
setVertical(originVertical)
setIsShow(false)
closePopup(id)
canvas.setWidth(originHorizon)
canvas.setHeight(originVertical)
canvas.renderAll()
}}
>
{getMessage('modal.common.save')} {getMessage('modal.common.save')}
</button> </button>
</div> </div>

View File

@ -5,6 +5,7 @@ import { useEvent } from '@/hooks/useEvent'
export default function Offset({ length1Ref, arrow1Ref, currentWallLineRef }) { export default function Offset({ length1Ref, arrow1Ref, currentWallLineRef }) {
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { addDocumentEventListener, initEvent } = useEvent() const { addDocumentEventListener, initEvent } = useEvent()
// const { addDocumentEventListener, initEvent } = useContext(EventContext)
useEffect(() => { useEffect(() => {
addDocumentEventListener('keydown', document, keyDown) addDocumentEventListener('keydown', document, keyDown)

View File

@ -5,6 +5,7 @@ import { useEvent } from '@/hooks/useEvent'
export default forwardRef(function WallLine({ length1Ref, length2Ref, arrow1Ref, arrow2Ref, radioTypeRef, currentWallLineRef }, ref) { export default forwardRef(function WallLine({ length1Ref, length2Ref, arrow1Ref, arrow2Ref, radioTypeRef, currentWallLineRef }, ref) {
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { addDocumentEventListener, initEvent } = useEvent() const { addDocumentEventListener, initEvent } = useEvent()
// const { addDocumentEventListener, initEvent } = useContext(EventContext)
const [type, setType] = useState(1) const [type, setType] = useState(1)
const [arrow1, setArrow1] = useState('up') const [arrow1, setArrow1] = useState('up')
const [arrow2, setArrow2] = useState('up') const [arrow2, setArrow2] = useState('up')

View File

@ -1,10 +1,10 @@
'use client' 'use client'
import { Fragment, useCallback, useEffect, useState } from 'react' import { Fragment, useCallback, useContext, useEffect, useState } from 'react'
import Link from 'next/link' import Link from 'next/link'
import { usePathname } from 'next/navigation' import { usePathname } from 'next/navigation'
import { useRecoilState, useRecoilValue } from 'recoil' import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil'
import { dimmedStore, sessionStore } from '@/store/commonAtom' import { dimmedStore, sessionStore } from '@/store/commonAtom'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
@ -16,6 +16,9 @@ import UserInfoModal from '@/components/myInfo/UserInfoModal'
import { useAxios } from '@/hooks/useAxios' import { useAxios } from '@/hooks/useAxios'
import { globalLocaleStore } from '@/store/localeAtom' import { globalLocaleStore } from '@/store/localeAtom'
import { stuffSearchState } from '@/store/stuffAtom'
import { QcastContext } from '@/app/QcastProvider'
export const ToggleonMouse = (e, act, target) => { export const ToggleonMouse = (e, act, target) => {
const listWrap = e.target.closest(target) const listWrap = e.target.closest(target)
const ListItem = Array.from(listWrap.childNodes) const ListItem = Array.from(listWrap.childNodes)
@ -34,6 +37,8 @@ export const ToggleonMouse = (e, act, target) => {
export default function Header(props) { export default function Header(props) {
const [userInfoModal, setUserInfoModal] = useState(false) const [userInfoModal, setUserInfoModal] = useState(false)
const [stuffSearch, setStuffSearch] = useRecoilState(stuffSearchState)
const { userSession } = props const { userSession } = props
const [sessionState, setSessionState] = useRecoilState(sessionStore) const [sessionState, setSessionState] = useRecoilState(sessionStore)
const { getMessage } = useMessage() const { getMessage } = useMessage()
@ -43,6 +48,8 @@ export default function Header(props) {
// } // }
const [selected, setSelected] = useState('') const [selected, setSelected] = useState('')
const { isGlobalLoading } = useContext(QcastContext)
const dimmedState = useRecoilValue(dimmedStore) const dimmedState = useRecoilValue(dimmedStore)
const isDimmed = dimmedState ? 'opacity-50 bg-black' : '' const isDimmed = dimmedState ? 'opacity-50 bg-black' : ''
@ -160,7 +167,8 @@ export default function Header(props) {
} }
return ( return (
!(pathName.includes('login') || pathName.includes('join') || sessionState.pwdInitYn === 'N') && ( <>
{!isGlobalLoading && !(pathName.includes('login') || pathName.includes('join') || sessionState.pwdInitYn === 'N') && (
<header className={isDimmed}> <header className={isDimmed}>
<div className="header-inner"> <div className="header-inner">
<div className="header-right"> <div className="header-right">
@ -184,7 +192,16 @@ export default function Header(props) {
{userInfoModal && <UserInfoModal userId={sessionState.userId} userInfoModal={userInfoModal} setUserInfoModal={setUserInfoModal} />} {userInfoModal && <UserInfoModal userId={sessionState.userId} userInfoModal={userInfoModal} setUserInfoModal={setUserInfoModal} />}
</div> </div>
<div className="sign-out-box"> <div className="sign-out-box">
<button className="sign-out" onClick={() => logout()}> <button
className="sign-out"
onClick={() => {
setStuffSearch({
...stuffSearch,
code: 'DELETE',
})
logout()
}}
>
{getMessage('header.logout')} {getMessage('header.logout')}
</button> </button>
</div> </div>
@ -199,6 +216,7 @@ export default function Header(props) {
</div> </div>
</div> </div>
</header> </header>
) )}
</>
) )
} }

View File

@ -1,4 +1,6 @@
import React, { useEffect, useState } from 'react' 'use client'
import { useEffect, useState, useContext } from 'react'
import ProductItem from './ProductItem' import ProductItem from './ProductItem'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import Image from 'next/image' import Image from 'next/image'
@ -8,19 +10,20 @@ import { useRecoilValue } from 'recoil'
import { useRouter } from 'next/navigation' import { useRouter } from 'next/navigation'
import { globalLocaleStore } from '@/store/localeAtom' import { globalLocaleStore } from '@/store/localeAtom'
import { queryStringFormatter } from '@/util/common-utils' import { queryStringFormatter } from '@/util/common-utils'
import { sessionStore } from '@/store/commonAtom'
import MainSkeleton from '../ui/MainSkeleton' import MainSkeleton from '../ui/MainSkeleton'
import { SessionContext } from '@/app/SessionProvider'
import { useMainContentsController } from '@/hooks/main/useMainContentsController'
import { QcastContext } from '@/app/QcastProvider'
export default function MainContents() { export default function MainContents() {
const { session } = useContext(SessionContext)
const { getMessage } = useMessage() const { getMessage } = useMessage()
const router = useRouter() const router = useRouter()
const globalLocaleState = useRecoilValue(globalLocaleStore) const globalLocaleState = useRecoilValue(globalLocaleStore)
const { promiseGet } = useAxios(globalLocaleState) const { promiseGet } = useAxios(globalLocaleState)
const sessionState = useRecoilValue(sessionStore)
// //
const [objectList, setObjectList] = useState([]) // const [objectList, setObjectList] = useState([])
// //
const [recentNoticeList, setRecentNoticeList] = useState([]) const [recentNoticeList, setRecentNoticeList] = useState([])
@ -29,36 +32,42 @@ export default function MainContents() {
const [recentFaqList, setRecentFaqList] = useState([]) const [recentFaqList, setRecentFaqList] = useState([])
//Sales Contact info //Sales Contact info
const [businessCharger, setBusinessCharger] = useState(null) // const [businessCharger, setBusinessCharger] = useState(null)
const [businessChargerMail, setBusinessChargerMail] = useState(null) // const [businessChargerMail, setBusinessChargerMail] = useState(null)
const { qcastState } = useContext(QcastContext)
const { fetchObjectList, initObjectList } = useMainContentsController()
useEffect(() => { useEffect(() => {
fetchObjectList() fetchObjectList()
fetchNoticeList() fetchNoticeList()
fetchFaqList() fetchFaqList()
}, [sessionState]) return () => {
initObjectList()
}
}, [])
// / Sales Contact info // / Sales Contact info
const fetchObjectList = async () => { // const fetchObjectList = async () => {
try { // try {
const apiUrl = `/api/main-page/object/${sessionState?.storeId}/list` // const apiUrl = `/api/main-page/object/${session?.storeId}/list`
await promiseGet({ // await promiseGet({
url: apiUrl, // url: apiUrl,
}).then((res) => { // }).then((res) => {
if (res.status === 200) { // if (res.status === 200) {
setObjectList(res.data.objectList) // setObjectList(res.data.objectList)
setBusinessCharger(res.data.businessCharger) // setBusinessCharger(res.data.businessCharger)
setBusinessChargerMail(res.data.businessChargerMail) // setBusinessChargerMail(res.data.businessChargerMail)
} else { // } else {
setObjectList([]) // setObjectList([])
setBusinessCharger(null) // setBusinessCharger(null)
setBusinessChargerMail(null) // setBusinessChargerMail(null)
} // }
}) // })
} catch (error) { // } catch (error) {
console.error('MAIN API fetching error:', error) // console.error('MAIN API fetching error:', error)
} // }
} // }
// //
const fetchNoticeList = async () => { const fetchNoticeList = async () => {
@ -110,9 +119,9 @@ export default function MainContents() {
<div className="main-product-list-wrap"> <div className="main-product-list-wrap">
<div className="main-product-list"> <div className="main-product-list">
<ProductItem num={1} name={getMessage('main.content.objectList')}> <ProductItem num={1} name={getMessage('main.content.objectList')}>
{objectList.length > 0 ? ( {qcastState?.objectList.length > 0 ? (
<ul className="recently-list"> <ul className="recently-list">
{objectList.map((row) => { {qcastState?.objectList.map((row) => {
return ( return (
<li <li
key={row.objectNo} key={row.objectNo}
@ -191,7 +200,7 @@ export default function MainContents() {
<div className="icon-box"> <div className="icon-box">
<Image src="/static/images/main/user.svg" alt="react" width={20} height={20} /> <Image src="/static/images/main/user.svg" alt="react" width={20} height={20} />
</div> </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> <div className="infor-data pre">{getMessage('main.content.noBusiness')}</div>
)} )}
</li> </li>
@ -199,7 +208,7 @@ export default function MainContents() {
<div className="icon-box"> <div className="icon-box">
<Image src="/static/images/main/mail.svg" alt="react" width={20} height={20} /> <Image src="/static/images/main/mail.svg" alt="react" width={20} height={20} />
</div> </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> <div className="infor-data pre">{getMessage('main.content.noBusiness')}</div>
)} )}
</li> </li>

View File

@ -16,7 +16,11 @@ import JA from '@/locales/ja.json'
import QPagination from '../common/pagination/QPagination' import QPagination from '../common/pagination/QPagination'
import { SessionContext } from '@/app/SessionProvider' import { SessionContext } from '@/app/SessionProvider'
import { QcastContext } from '@/app/QcastProvider'
export default function Stuff() { export default function Stuff() {
const { setIsGlobalLoading } = useContext(QcastContext)
const resetStuffRecoil = useResetRecoilState(stuffSearchState) const resetStuffRecoil = useResetRecoilState(stuffSearchState)
const { session } = useContext(SessionContext) const { session } = useContext(SessionContext)
const setAppMessageState = useSetRecoilState(appMessageStore) const setAppMessageState = useSetRecoilState(appMessageStore)
@ -39,14 +43,15 @@ export default function Stuff() {
const copyNo = async (value) => { const copyNo = async (value) => {
try { try {
await navigator.clipboard.writeText(value) await navigator.clipboard.writeText(value)
alert(getMessage('stuff.detail.header.message2')) alert(getMessage('stuff.detail.header.successCopy'))
} catch (error) { } catch (error) {
alert(getMessage('stuff.detail.header.message3')) alert(getMessage('stuff.detail.header.failCopy'))
} }
} }
// //
const onDoubleClick = (data) => { const onDoubleClick = (data) => {
setIsGlobalLoading(true)
if (data.tempFlg === '0') { if (data.tempFlg === '0') {
router.push(`${pathname}/detail?objectNo=${data.objectNo.toString()}`, { scroll: false }) router.push(`${pathname}/detail?objectNo=${data.objectNo.toString()}`, { scroll: false })
} else { } else {
@ -149,6 +154,7 @@ export default function Stuff() {
} else { } else {
//T R //T R
if (event.data.objectNo) { if (event.data.objectNo) {
setIsGlobalLoading(true)
if (event.data.tempFlg === '0') { if (event.data.tempFlg === '0') {
router.push(`${pathname}/detail?objectNo=${event.data.objectNo.toString()}`, { scroll: false }) router.push(`${pathname}/detail?objectNo=${event.data.objectNo.toString()}`, { scroll: false })
} else { } else {
@ -172,12 +178,14 @@ export default function Stuff() {
schDateType: stuffSearchParams.schDateType, schDateType: stuffSearchParams.schDateType,
schFromDt: dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD'), schFromDt: dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD'),
schToDt: dayjs(new Date()).format('YYYY-MM-DD'), schToDt: dayjs(new Date()).format('YYYY-MM-DD'),
startRow: (pageNo - 1) * pageSize + 1, startRow: (stuffSearch.pageNo - 1) * stuffSearchParams.pageSize + 1,
endRow: pageNo * pageSize, endRow: stuffSearchParams?.endRow,
schSelSaleStoreId: stuffSearchParams?.schOtherSelSaleStoreId ? stuffSearchParams.schOtherSelSaleStoreId : stuffSearchParams.schSelSaleStoreId, schSelSaleStoreId: stuffSearchParams?.schSelSaleStoreId ? stuffSearchParams.schSelSaleStoreId : '',
schOtherSelSaleStoreId: stuffSearchParams?.schOtherSelSaleStoreId ? stuffSearchParams.schOtherSelSaleStoreId : '',
schSortType: stuffSearchParams.schSortType, schSortType: stuffSearchParams.schSortType,
pageNo: stuffSearchParams?.pageNo ? stuffSearchParams.pageNo : 1,
pageSize: stuffSearchParams?.pageSize ? stuffSearchParams.pageSize : 100,
} }
async function fetchData() { async function fetchData() {
const apiUrl = `/api/object/list?${queryStringFormatter(params)}` const apiUrl = `/api/object/list?${queryStringFormatter(params)}`
await get({ await get({
@ -186,15 +194,21 @@ export default function Stuff() {
if (!isEmptyArray(res)) { if (!isEmptyArray(res)) {
setGridProps({ ...gridProps, gridData: res, count: res[0].totCnt }) setGridProps({ ...gridProps, gridData: res, count: res[0].totCnt })
setTotalCount(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
} }
//if (session.storeId === 'T01') { setIsGlobalLoading(false)
})
}
fetchData() fetchData()
//} else if (stuffSearch.schSelSaleStoreId !== '') {
//fetchData()
//}
} else if (stuffSearchParams?.code === 'M') { } else if (stuffSearchParams?.code === 'M') {
const params = { const params = {
saleStoreId: session?.storeId, saleStoreId: session?.storeId,
@ -211,19 +225,47 @@ export default function Stuff() {
endRow: pageNo * pageSize, endRow: pageNo * pageSize,
schSelSaleStoreId: stuffSearchParams?.schOtherSelSaleStoreId ? stuffSearchParams.schOtherSelSaleStoreId : stuffSearchParams.schSelSaleStoreId, schSelSaleStoreId: stuffSearchParams?.schOtherSelSaleStoreId ? stuffSearchParams.schOtherSelSaleStoreId : stuffSearchParams.schSelSaleStoreId,
schSortType: 'R', schSortType: 'R',
code: 'S',
pageNo: 1,
pageSize: 100,
} }
setStuffSearch({ setStuffSearch({
...params, ...params,
}) })
} else if (stuffSearchParams?.code === 'E') { } 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.startRow = 1
stuffSearchParams.endRow = 1 * pageSize stuffSearchParams.endRow = 1 * pageSize
stuffSearchParams.schSortType = defaultSortType stuffSearchParams.schSortType = defaultSortType
setPageNo(1) setPageNo(1)
setStuffSearch({
...stuffSearch,
code: 'FINISH',
})
async function fetchData() { async function fetchData() {
const apiUrl = `/api/object/list?saleStoreId=${session?.storeId}&${queryStringFormatter(stuffSearchParams)}` const apiUrl = `/api/object/list?saleStoreId=${session?.storeId}&${queryStringFormatter(stuffSearchParams)}`
@ -237,10 +279,32 @@ export default function Stuff() {
} }
}) })
} }
fetchData() fetchData()
} else if (stuffSearchParams?.code === 'C') { } else if (stuffSearchParams?.code === 'DELETE') {
resetStuffRecoil() 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]) }, [stuffSearchParams])
@ -249,59 +313,46 @@ export default function Stuff() {
let startRow = (1 - 1) * e.target.value + 1 let startRow = (1 - 1) * e.target.value + 1
stuffSearchParams.startRow = startRow stuffSearchParams.startRow = startRow
stuffSearchParams.endRow = 1 * e.target.value stuffSearchParams.endRow = 1 * e.target.value
stuffSearchParams.schSelSaleStoreId = stuffSearchParams?.schOtherSelSaleStoreId stuffSearchParams.schSelSaleStoreId = stuffSearchParams.schSelSaleStoreId
? stuffSearchParams.schOtherSelSaleStoreId stuffSearchParams.schOtherSelSaleStoreId = stuffSearchParams.schOtherSelSaleStoreId
: stuffSearchParams.schSelSaleStoreId stuffSearchParams.pageNo = startRow
stuffSearchParams.pageSize = 1 * e.target.value
setPageSize(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({ setStuffSearch({
...stuffSearch, ...stuffSearch,
code: 'S', code: 'S',
startRow: startRow, 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, schSortType: e.target.value,
}) })
setPageNo(1) 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(() => { useEffect(() => {
@ -315,19 +366,24 @@ export default function Stuff() {
// //
const handleChangePage = (page) => { const handleChangePage = (page) => {
stuffSearchParams.code = 'S' stuffSearchParams.code = 'S'
stuffSearchParams.schSelSaleStoreId = stuffSearchParams?.schOtherSelSaleStoreId stuffSearchParams.schSelSaleStoreId = stuffSearchParams.schSelSaleStoreId
? stuffSearchParams.schOtherSelSaleStoreId stuffSearchParams.schOtherSelSaleStoreId = stuffSearchParams.schOtherSelSaleStoreId
: stuffSearchParams.schSelSaleStoreId
setStuffSearch({ setStuffSearch({
...stuffSearch, ...stuffSearch,
code: 'S', code: 'S',
startRow: (page - 1) * pageSize + 1, startRow: (page - 1) * pageSize + 1,
endRow: page * pageSize, endRow: page * stuffSearchParams?.pageSize,
pageNo: page,
}) })
setPageNo(page) setPageNo(page)
} }
useEffect(() => {
setIsGlobalLoading(true)
}, [])
return ( return (
<> <>
{/* 퍼블시작 */} {/* 퍼블시작 */}
@ -345,13 +401,13 @@ export default function Stuff() {
</div> </div>
<div className="left-unit-box"> <div className="left-unit-box">
<div className="select-box mr5" style={{ width: '110px' }}> <div className="select-box mr5" style={{ width: '110px' }}>
<select className="select-light black" name="" id="" onChange={onChangeSortType}> <select className="select-light black" onChange={onChangeSortType} value={stuffSearch.schSortType}>
<option value="R">{getMessage('stuff.search.grid.schSortTypeR')}</option> <option value="R">{getMessage('stuff.search.grid.schSortTypeR')}</option>
<option value="U">{getMessage('stuff.search.grid.schSortTypeU')}</option> <option value="U">{getMessage('stuff.search.grid.schSortTypeU')}</option>
</select> </select>
</div> </div>
<div className="select-box" style={{ width: '80px' }}> <div className="select-box" style={{ width: '80px' }}>
<select className="select-light black" name="" id="" onChange={onChangePerPage}> <select className="select-light black" onChange={onChangePerPage} value={stuffSearch.pageSize}>
<option value="100">100</option> <option value="100">100</option>
<option value="200">200</option> <option value="200">200</option>
<option value="300">300</option> <option value="300">300</option>
@ -363,7 +419,13 @@ export default function Stuff() {
<div className="q-grid"> <div className="q-grid">
<StuffQGrid {...gridProps} getCellDoubleClicked={getCellDoubleClicked} gridRef={gridRef} /> <StuffQGrid {...gridProps} getCellDoubleClicked={getCellDoubleClicked} gridRef={gridRef} />
<div className="pagination-wrap"> <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> </div>
</div> </div>

View File

@ -10,7 +10,7 @@ import { globalLocaleStore } from '@/store/localeAtom'
import { isEmptyArray, isNotEmptyArray, isObjectNotEmpty } from '@/util/common-utils' import { isEmptyArray, isNotEmptyArray, isObjectNotEmpty } from '@/util/common-utils'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { useForm } from 'react-hook-form' import { useForm } from 'react-hook-form'
import { useRecoilValue, useSetRecoilState } from 'recoil' import { useRecoilValue, useSetRecoilState, useResetRecoilState, useRecoilState } from 'recoil'
import { SessionContext } from '@/app/SessionProvider' import { SessionContext } from '@/app/SessionProvider'
import FindAddressPop from './popup/FindAddressPop' import FindAddressPop from './popup/FindAddressPop'
import PlanRequestPop from './popup/PlanRequestPop' import PlanRequestPop from './popup/PlanRequestPop'
@ -18,10 +18,21 @@ import WindSelectPop from './popup/WindSelectPop'
import { useCommonCode } from '@/hooks/common/useCommonCode' import { useCommonCode } from '@/hooks/common/useCommonCode'
import StuffPlanQGrid from './StuffPlanQGrid' import StuffPlanQGrid from './StuffPlanQGrid'
import { floorPlanObjectState } from '@/store/floorPlanObjectAtom' import { floorPlanObjectState } from '@/store/floorPlanObjectAtom'
import { ManagementContext } from '@/app/management/ManagementProvider'
import DocDownOptionPop from '../estimate/popup/DocDownOptionPop'
import { stuffSearchState } from '@/store/stuffAtom'
import { QcastContext } from '@/app/QcastProvider'
export default function StuffDetail() { export default function StuffDetail() {
const { setIsGlobalLoading } = useContext(QcastContext)
const resetStuffRecoil = useResetRecoilState(stuffSearchState)
const stuffSearchParams = useRecoilValue(stuffSearchState)
const setFloorPlanObjectNo = useSetRecoilState(floorPlanObjectState) // const setFloorPlanObjectNo = useSetRecoilState(floorPlanObjectState) //
const [estimatePopupOpen, setEstimatePopupOpen] = useState(false)
const [popPlanNo, setPopPlanNo] = useState('1') //default 1
// //
const { commonCode, findCommonCode } = useCommonCode() const { commonCode, findCommonCode } = useCommonCode()
const [selOptions, setSelOptions] = useState('') // 1 const [selOptions, setSelOptions] = useState('') // 1
@ -96,7 +107,7 @@ export default function StuffDetail() {
const objectNo = searchParams.get('objectNo') //url set const objectNo = searchParams.get('objectNo') //url set
const [editMode, setEditMode] = useState('NEW') const [editMode, setEditMode] = useState('NEW')
const [detailData, setDetailData] = useState({}) const { managementState, setManagementState } = useContext(ManagementContext)
const [planGridProps, setPlanGridProps] = useState({ const [planGridProps, setPlanGridProps] = useState({
planGridData: [], planGridData: [],
@ -131,6 +142,16 @@ export default function StuffDetail() {
headerName: getMessage('stuff.detail.planGridHeader.capacity'), headerName: getMessage('stuff.detail.planGridHeader.capacity'),
width: 120, width: 120,
cellStyle: { justifyContent: 'flex-end' /* 우측정렬*/ }, 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', field: 'roofMaterialIdMulti',
@ -159,8 +180,8 @@ export default function StuffDetail() {
}, },
}, },
{ {
field: 'constructSpecification', field: 'constructSpecificationMulti',
headerName: getMessage('stuff.detail.planGridHeader.constructSpecification'), headerName: getMessage('stuff.detail.planGridHeader.constructSpecificationMulti'),
wrapText: true, wrapText: true,
autoHeight: true, autoHeight: true,
cellStyle: { justifyContent: 'flex-start' /* 좌측정렬*/ }, cellStyle: { justifyContent: 'flex-start' /* 좌측정렬*/ },
@ -265,11 +286,12 @@ export default function StuffDetail() {
type="button" type="button"
className="grid-btn" className="grid-btn"
onClick={() => { onClick={() => {
console.log('엑셀버튼클릭') setFloorPlanObjectNo({ floorPlanObjectNo: params.data.objectNo })
handleEstimatePopup(params.data.planNo)
}} }}
> >
<span className="excel"></span> <span className="excel"></span>
{getMessage('stuff.detail.planGrid.btn2')} {getMessage('stuff.detail.planGrid.docDownload')}
</button> </button>
</div> </div>
</> </>
@ -279,6 +301,12 @@ export default function StuffDetail() {
], ],
}) })
//
const handleEstimatePopup = (planNo) => {
setPopPlanNo(planNo)
setEstimatePopupOpen(true)
}
useEffect(() => { useEffect(() => {
if (objectNo) { if (objectNo) {
setEditMode('EDIT') setEditMode('EDIT')
@ -288,10 +316,12 @@ export default function StuffDetail() {
} }
promiseGet({ url: `/api/object/${objectNo}/detail` }).then((res) => { promiseGet({ url: `/api/object/${objectNo}/detail` }).then((res) => {
if (res.status === 200) { if (res.status === 200) {
if (res.data != null) { if (isObjectNotEmpty(res.data)) {
setDetailData(res.data) setManagementState(res.data)
} else { } else {
setDetailData({}) setManagementState({})
alert(getMessage('stuff.detail.header.notExistObjectNo'))
router.push('/management/stuff')
} }
if (isNotEmptyArray(res.data.planList)) { if (isNotEmptyArray(res.data.planList)) {
setPlanGridProps({ ...planGridProps, planGridData: res.data.planList }) setPlanGridProps({ ...planGridProps, planGridData: res.data.planList })
@ -299,8 +329,11 @@ export default function StuffDetail() {
setPlanGridProps({ ...planGridProps, planGridData: [] }) setPlanGridProps({ ...planGridProps, planGridData: [] })
} }
} else { } else {
setDetailData({}) setManagementState({})
setPlanGridProps({ ...planGridProps, planGridData: [] }) setPlanGridProps({ ...planGridProps, planGridData: [] })
alert(getMessage('stuff.detail.header.notExistObjectNo'))
router.push('/management/stuff')
} }
}) })
} else { } else {
@ -396,6 +429,8 @@ export default function StuffDetail() {
} }
} }
}) })
setIsGlobalLoading(false)
} }
}, [objectNo, session]) }, [objectNo, session])
@ -415,7 +450,8 @@ export default function StuffDetail() {
}, [commonCode]) }, [commonCode])
useEffect(() => { useEffect(() => {
if (isObjectNotEmpty(detailData)) { if (objectNo) {
if (isObjectNotEmpty(managementState)) {
// API // API
get({ url: '/api/object/prefecture/list' }).then((res) => { get({ url: '/api/object/prefecture/list' }).then((res) => {
if (!isEmptyArray(res)) { if (!isEmptyArray(res)) {
@ -449,18 +485,17 @@ export default function StuffDetail() {
setFavoriteStoreList(favList) setFavoriteStoreList(favList)
setShowSaleStoreList(favList) setShowSaleStoreList(favList)
if (detailData.firstAgentId != null) { if (managementState.firstAgentId != null) {
form.setValue('saleStoreId', detailData.firstAgentId) form.setValue('saleStoreId', managementState.firstAgentId)
setSelOptions(detailData.firstAgentId) setSelOptions(managementState.firstAgentId)
} else { } else {
form.setValue('saleStoreId', detailData.saleStoreId) form.setValue('saleStoreId', managementState.saleStoreId)
setSelOptions(detailData.saleStoreId) setSelOptions(managementState.saleStoreId)
} }
// 1 2 // 1 2
let data = detailData?.firstAgentId ? detailData.firstAgentId : detailData.saleStoreId let data = managementState?.firstAgentId ? managementState.firstAgentId : managementState.saleStoreId
// url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=0&userId=${session?.userId}`
url = `/api/object/saleStore/${data}/list?firstFlg=0&userId=${session?.userId}` url = `/api/object/saleStore/${data}/list?firstFlg=0&userId=${session?.userId}`
get({ url: url }).then((res) => { get({ url: url }).then((res) => {
@ -502,71 +537,78 @@ export default function StuffDetail() {
// 1 1 // 1 1
// 2 2 1 // 2 2 1
if (detailData.saleStoreLevel === '1') { if (managementState.saleStoreLevel === '1') {
setSelOptions(detailData.saleStoreId) setSelOptions(managementState.saleStoreId)
form.setValue('saleStoreId', detailData.saleStoreId) form.setValue('saleStoreId', managementState.saleStoreId)
form.setValue('saleStoreLevel', detailData.saleStoreLevel) form.setValue('saleStoreLevel', managementState.saleStoreLevel)
} else { } else {
setOtherSelOptions(detailData.saleStoreId) setOtherSelOptions(managementState.saleStoreId)
form.setValue('otherSaleStoreId', detailData.saleStoreId) form.setValue('otherSaleStoreId', managementState.saleStoreId)
form.setValue('otherSaleStoreLevel', detailData.saleStoreLevel) form.setValue('otherSaleStoreLevel', managementState.saleStoreLevel)
form.setValue('saleStoreLevel', '1')
} }
//No. //No.
form.setValue('planReqNo', detailData.planReqNo) form.setValue('planReqNo', managementState.planReqNo)
// //
form.setValue('receiveUser', detailData.receiveUser) form.setValue('receiveUser', managementState.receiveUser)
//objectStatusId //objectStatusId
setSelectObjectStatusId(detailData.objectStatusId) setSelectObjectStatusId(managementState.objectStatusId)
form.setValue('objectStatusId', detailData.objectStatusId) form.setValue('objectStatusId', managementState.objectStatusId)
// //
form.setValue('objectName', detailData.objectName) form.setValue('objectName', managementState.objectName)
// //
setSelHonorificCode(detailData.objectNameOmit) setSelHonorificCode(managementState.objectNameOmit)
form.setValue('objectNameOmit', detailData.objectNameOmit) form.setValue('objectNameOmit', managementState.objectNameOmit)
// //
form.setValue('objectNameKana', detailData.objectNameKana) form.setValue('objectNameKana', managementState.objectNameKana)
// //
form.setValue('zipNo', detailData.zipNo) form.setValue('zipNo', managementState.zipNo)
// / // /
setPrefValue(detailData.prefId) setPrefValue(managementState.prefId)
form.setValue('prefId', detailData.prefId) form.setValue('prefId', managementState.prefId)
form.setValue('address', detailData.address) form.setValue('prefName', managementState.prefName)
form.setValue('address', managementState.address)
// //
form.setValue('areaId', detailData.areaId) form.setValue('areaId', managementState.areaId)
// //
form.setValue('standardWindSpeedId', detailData.standardWindSpeedId) form.setValue('standardWindSpeedId', managementState.standardWindSpeedId)
// //
form.setValue('verticalSnowCover', detailData.verticalSnowCover) form.setValue('verticalSnowCover', managementState.verticalSnowCover)
// coldRegionFlg 1 true // coldRegionFlg 1 true
form.setValue('coldRegionFlg', detailData.coldRegionFlg === '1' ? true : false) form.setValue('coldRegionFlg', managementState.coldRegionFlg === '1' ? true : false)
// surfaceType null // surfaceType null
// form.setValue('surfaceType', '') // form.setValue('surfaceType', '')
// form.setValue('surfaceType', '') // form.setValue('surfaceType', '')
form.setValue('surfaceType', detailData.surfaceType) form.setValue('surfaceType', managementState.surfaceType)
// saltAreaFlg 1 true // saltAreaFlg 1 true
form.setValue('saltAreaFlg', detailData.saltAreaFlg === '1' ? true : false) form.setValue('saltAreaFlg', managementState.saltAreaFlg === '1' ? true : false)
// //
form.setValue('installHeight', detailData.installHeight) form.setValue('installHeight', managementState.installHeight)
// null 0 // null 0
if (detailData.conType === null) { if (managementState.conType === null) {
form.setValue('conType', '0') form.setValue('conType', '0')
} else { } else {
form.setValue('conType', detailData.conType) form.setValue('conType', managementState.conType)
} }
// //
form.setValue('remarks', detailData.remarks) form.setValue('remarks', managementState.remarks)
}) })
//
setIsGlobalLoading(false)
} }
}, [detailData, session]) }
}, [managementState])
// //
const onChangeHonorificCode = (key) => { const onChangeHonorificCode = (key) => {
@ -856,6 +898,7 @@ export default function StuffDetail() {
const setPlanReqInfo = (info) => { const setPlanReqInfo = (info) => {
form.setValue('planReqNo', info.planReqNo) form.setValue('planReqNo', info.planReqNo)
form.setValue('objectStatusId', info.building) form.setValue('objectStatusId', info.building)
setSelectObjectStatusId(info.building)
form.setValue('objectName', info.planReqName) form.setValue('objectName', info.planReqName)
form.setValue('zipNo', info.zipNo) form.setValue('zipNo', info.zipNo)
form.setValue('address', info.address2) form.setValue('address', info.address2)
@ -872,8 +915,11 @@ export default function StuffDetail() {
form.setValue('standardWindSpeedId', `WL_${info.windSpeed}`) form.setValue('standardWindSpeedId', `WL_${info.windSpeed}`)
form.setValue('verticalSnowCover', info.verticalSnowCover) form.setValue('verticalSnowCover', info.verticalSnowCover)
form.setValue('surfaceType', info.surfaceType) form.setValue('surfaceType', info.surfaceType)
if (info.surfaceType === 'Ⅱ') { if (info.surfaceType === 'Ⅱ') {
form.setValue('saltAreaFlg', true) form.setValue('saltAreaFlg', true)
} else {
form.setValue('saltAreaFlg', false)
} }
form.setValue('installHeight', info.installHeight) form.setValue('installHeight', info.installHeight)
form.setValue('remarks', info.remarks) form.setValue('remarks', info.remarks)
@ -1034,8 +1080,13 @@ export default function StuffDetail() {
// //
const onSearchDesignRequestPopOpen = () => { const onSearchDesignRequestPopOpen = () => {
const saleStoreId = form.watch('saleStoreId')
if (saleStoreId === '') {
alert(getMessage('stuff.planReqPopup.error.message2'))
} else {
setShowDesignRequestButtonValid(true) setShowDesignRequestButtonValid(true)
} }
}
// //
const onSearchWindSpeedPopOpen = () => { const onSearchWindSpeedPopOpen = () => {
@ -1177,9 +1228,9 @@ export default function StuffDetail() {
return alert(getMessage('stuff.detail.save.valierror2')) return alert(getMessage('stuff.detail.save.valierror2'))
} }
let detail_sort = Object.keys(detailData) let detail_sort = Object.keys(managementState)
.sort() .sort()
.reduce((obj, key) => ((obj[key] = detailData[key]), obj), {}) .reduce((obj, key) => ((obj[key] = managementState[key]), obj), {})
let params_sort = Object.keys(params) let params_sort = Object.keys(params)
.sort() .sort()
.reduce((obj, key) => ((obj[key] = params[key]), obj), {}) .reduce((obj, key) => ((obj[key] = params[key]), obj), {})
@ -1238,7 +1289,7 @@ export default function StuffDetail() {
if (res.status === 201) { if (res.status === 201) {
alert(getMessage('stuff.detail.save')) alert(getMessage('stuff.detail.save'))
setFloorPlanObjectNo({ floorPlanObjectNo: objectNo }) 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 { } else {
@ -1247,8 +1298,7 @@ export default function StuffDetail() {
if (res.status === 201) { if (res.status === 201) {
setFloorPlanObjectNo({ floorPlanObjectNo: res.data.objectNo }) setFloorPlanObjectNo({ floorPlanObjectNo: res.data.objectNo })
alert(getMessage('stuff.detail.save')) alert(getMessage('stuff.detail.save'))
// router.refresh() router.push(`/management/stuff/detail?objectNo=${res.data.objectNo.toString()}`, { scroll: false })
router.push(`/management/stuff/detail?objectNo=${res.data.objectNo.toString()}`)
} }
}) })
} }
@ -1283,6 +1333,7 @@ export default function StuffDetail() {
tempFlg: '1', tempFlg: '1',
workNo: null, workNo: null,
workName: null, workName: null,
objectNo: objectNo ? objectNo : '',
} }
//1 or 2 //1 or 2
@ -1291,23 +1342,38 @@ export default function StuffDetail() {
params.saleStoreLevel = session.storeLvl params.saleStoreLevel = session.storeLvl
} }
await promisePost({ url: '/api/object/save-object', data: params }).then((res) => { const apiUrl = '/api/object/save-object'
if (objectNo) {
await promisePut({ url: apiUrl, data: params }).then((res) => {
if (res.status === 201) { if (res.status === 201) {
alert(getMessage('stuff.detail.tempSave.message1')) alert(getMessage('stuff.detail.tempSave.message1'))
router.push(`${pathname}?objectNo=${res.data.objectNo.toString()}`) 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 onDelete = () => {
const specificationConfirmDate = detailData.specificationConfirmDate const specificationConfirmDate = managementState.specificationConfirmDate
if (specificationConfirmDate != null) { if (specificationConfirmDate != null) {
alert(getMessage('stuff.detail.delete.message1')) alert(getMessage('stuff.detail.delete.message1'))
} else { } else {
if (confirm(getMessage('common.message.data.delete'))) { if (confirm(getMessage('common.message.data.delete'))) {
del({ url: `/api/object/${objectNo}` }).then(() => { del({ url: `/api/object/${objectNo}` }).then(() => {
setFloorPlanObjectNo({ floorPlanObjectNo: '' }) setFloorPlanObjectNo({ floorPlanObjectNo: '' })
if (session.storeId === 'T01') {
stuffSearchParams.code = 'DELETE'
} else {
resetStuffRecoil()
}
router.push('/management/stuff') router.push('/management/stuff')
}) })
} }
@ -1726,6 +1792,7 @@ export default function StuffDetail() {
</th> </th>
<td> <td>
<div className="flx-box"> <div className="flx-box">
<div className="radio-wrap flx-box" style={{ width: '239px' }}>
<div className="d-check-radio light mr10"> <div className="d-check-radio light mr10">
<input <input
type="radio" type="radio"
@ -1739,7 +1806,7 @@ export default function StuffDetail() {
/> />
<label htmlFor="surfaceType0"></label> <label htmlFor="surfaceType0"></label>
</div> </div>
<div className="d-check-radio light mr10"> <div className="d-check-radio light">
<input <input
type="radio" type="radio"
name="surfaceType" name="surfaceType"
@ -1752,6 +1819,7 @@ export default function StuffDetail() {
/> />
<label htmlFor="surfaceType1"></label> <label htmlFor="surfaceType1"></label>
</div> </div>
</div>
<div className="d-check-box light mr5"> <div className="d-check-box light mr5">
<input type="checkbox" id="saltAreaFlg" {...form.register('saltAreaFlg')} /> <input type="checkbox" id="saltAreaFlg" {...form.register('saltAreaFlg')} />
<label htmlFor="saltAreaFlg">{getMessage('stuff.detail.saltAreaFlg')}</label> <label htmlFor="saltAreaFlg">{getMessage('stuff.detail.saltAreaFlg')}</label>
@ -1846,8 +1914,7 @@ export default function StuffDetail() {
<div className="flx-box"> <div className="flx-box">
<div className="product-input-wrap mr5"> <div className="product-input-wrap mr5">
<input type="text" className="product-input" readOnly value={form.watch('planReqNo') || ''} /> <input type="text" className="product-input" readOnly value={form.watch('planReqNo') || ''} />
{/* {detailData?.tempFlg === '1' && form.watch('planReqNo') ? ( */} {managementState?.tempFlg === '1' && form.watch('planReqNo') ? (
{detailData?.tempFlg === '1' && form.watch('planReqNo') ? (
<button <button
type="button" type="button"
className="product-delete" className="product-delete"
@ -1857,8 +1924,7 @@ export default function StuffDetail() {
></button> ></button>
) : null} ) : null}
</div> </div>
{/* {detailData?.tempFlg === '1' ? ( */} {managementState?.tempFlg === '1' ? (
{detailData?.tempFlg === '1' ? (
<> <>
<Button className="btn-origin grey" onPress={onSearchDesignRequestPopOpen}> <Button className="btn-origin grey" onPress={onSearchDesignRequestPopOpen}>
{getMessage('stuff.planReqPopup.title')} {getMessage('stuff.planReqPopup.title')}
@ -1964,8 +2030,8 @@ export default function StuffDetail() {
onChange={onSelectionChange} onChange={onSelectionChange}
getOptionLabel={(x) => x.saleStoreName} getOptionLabel={(x) => x.saleStoreName}
getOptionValue={(x) => x.saleStoreId} getOptionValue={(x) => x.saleStoreId}
isClearable={detailData.tempFlg === '0' ? false : session?.storeLvl === '1' ? true : false} isClearable={managementState.tempFlg === '0' ? false : session?.storeLvl === '1' ? true : false}
isDisabled={detailData.tempFlg === '0' ? true : session?.storeLvl !== '1' ? true : false} isDisabled={managementState.tempFlg === '0' ? true : session?.storeLvl !== '1' ? true : false}
value={saleStoreList.filter(function (option) { value={saleStoreList.filter(function (option) {
return option.saleStoreId === selOptions return option.saleStoreId === selOptions
})} })}
@ -1998,7 +2064,13 @@ export default function StuffDetail() {
getOptionValue={(x) => x.saleStoreId} getOptionValue={(x) => x.saleStoreId}
isClearable={false} isClearable={false}
isDisabled={ isDisabled={
detailData.tempFlg === '0' ? true : session?.storeLvl !== '1' ? true : session?.storeId !== 'T01' ? true : false managementState.tempFlg === '0'
? true
: session?.storeLvl !== '1'
? true
: session?.storeId !== 'T01'
? true
: false
} }
value={showSaleStoreList.filter(function (option) { value={showSaleStoreList.filter(function (option) {
return option.saleStoreId === selOptions return option.saleStoreId === selOptions
@ -2077,9 +2149,9 @@ export default function StuffDetail() {
getOptionLabel={(x) => x.saleStoreName} getOptionLabel={(x) => x.saleStoreName}
getOptionValue={(x) => x.saleStoreId} getOptionValue={(x) => x.saleStoreId}
isDisabled={ isDisabled={
detailData.tempFlg === '0' ? true : session?.storeLvl === '1' && form.watch('saleStoreId') != '' ? false : true managementState.tempFlg === '0' ? true : session?.storeLvl === '1' && form.watch('saleStoreId') != '' ? false : true
} }
isClearable={detailData.tempFlg === '0' ? false : session?.storeLvl === '1' ? true : false} isClearable={managementState.tempFlg === '0' ? false : session?.storeLvl === '1' ? true : false}
value={otherSaleStoreList.filter(function (option) { value={otherSaleStoreList.filter(function (option) {
return option.saleStoreId === otherSelOptions return option.saleStoreId === otherSelOptions
})} })}
@ -2240,6 +2312,7 @@ export default function StuffDetail() {
</th> </th>
<td> <td>
<div className="flx-box"> <div className="flx-box">
<div className="radio-wrap flx-box" style={{ width: '239px' }}>
<div className="d-check-radio light mr10"> <div className="d-check-radio light mr10">
<input <input
type="radio" type="radio"
@ -2253,7 +2326,7 @@ export default function StuffDetail() {
/> />
<label htmlFor="surfaceType0"></label> <label htmlFor="surfaceType0"></label>
</div> </div>
<div className="d-check-radio light mr10"> <div className="d-check-radio light">
<input <input
type="radio" type="radio"
name="surfaceType" name="surfaceType"
@ -2266,6 +2339,8 @@ export default function StuffDetail() {
/> />
<label htmlFor="surfaceType1"></label> <label htmlFor="surfaceType1"></label>
</div> </div>
</div>
<div className="d-check-box light mr5"> <div className="d-check-box light mr5">
<input type="checkbox" id="saltAreaFlg" {...form.register('saltAreaFlg')} /> <input type="checkbox" id="saltAreaFlg" {...form.register('saltAreaFlg')} />
<label htmlFor="saltAreaFlg">{getMessage('stuff.detail.saltAreaFlg')}</label> <label htmlFor="saltAreaFlg">{getMessage('stuff.detail.saltAreaFlg')}</label>
@ -2329,8 +2404,7 @@ export default function StuffDetail() {
</table> </table>
</div> </div>
</div> </div>
{/* {detailData?.tempFlg === '0' ? ( */} {managementState?.tempFlg === '0' ? (
{detailData?.tempFlg === '0' ? (
<> <>
{/* 진짜R 플랜시작 */} {/* 진짜R 플랜시작 */}
<div className="table-box-title-wrap"> <div className="table-box-title-wrap">
@ -2339,7 +2413,7 @@ export default function StuffDetail() {
<ul className="info-wrap"> <ul className="info-wrap">
<li> <li>
{getMessage('stuff.detail.planList.cnt')} {getMessage('stuff.detail.planList.cnt')}
<span className="red"> {detailData.planList?.length}</span> <span className="red"> {managementState.planList?.length}</span>
</li> </li>
</ul> </ul>
</div> </div>
@ -2413,6 +2487,8 @@ export default function StuffDetail() {
{showWindSpeedButtonValid && ( {showWindSpeedButtonValid && (
<WindSelectPop setShowWindSpeedButtonValid={setShowWindSpeedButtonValid} prefName={form.watch('prefName')} windSpeedInfo={setWindSppedInfo} /> <WindSelectPop setShowWindSpeedButtonValid={setShowWindSpeedButtonValid} prefName={form.watch('prefName')} windSpeedInfo={setWindSppedInfo} />
)} )}
{estimatePopupOpen && <DocDownOptionPop planNo={popPlanNo} setEstimatePopupOpen={setEstimatePopupOpen} />}
</> </>
) )
} }

View File

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

View File

@ -17,6 +17,8 @@ import { isObjectNotEmpty } from '@/util/common-utils'
import { SessionContext } from '@/app/SessionProvider' import { SessionContext } from '@/app/SessionProvider'
import { QcastContext } from '@/app/QcastProvider'
export default function StuffSearchCondition() { export default function StuffSearchCondition() {
const { session } = useContext(SessionContext) const { session } = useContext(SessionContext)
const setAppMessageState = useSetRecoilState(appMessageStore) const setAppMessageState = useSetRecoilState(appMessageStore)
@ -66,30 +68,167 @@ export default function StuffSearchCondition() {
const [otherSaleStoreList, setOtherSaleStoreList] = useState([]) //1 const [otherSaleStoreList, setOtherSaleStoreList] = useState([]) //1
const [otherSaleStoreId, setOtherSaleStoreId] = useState('') const [otherSaleStoreId, setOtherSaleStoreId] = useState('')
const { setIsGlobalLoading } = useContext(QcastContext)
// //
const onSubmit = () => { const onSubmit = () => {
let diff = dayjs(endDate).diff(startDate, 'day') let diff = dayjs(endDate).diff(startDate, 'day')
if (diff > 366) { if (diff > 366) {
return alert(getMessage('stuff.message.periodError')) return alert(getMessage('stuff.message.periodError'))
} }
if (isNaN(diff)) {
return alert(getMessage('stuff.message.periodError'))
}
setIsGlobalLoading(true)
if (stuffSearch.code === 'S') { if (stuffSearch.code === 'S') {
if (stuffSearch.pageNo !== 1) {
setStuffSearch({ setStuffSearch({
schObjectNo: objectNo ? objectNo : stuffSearch.schObjectNo, schObjectNo: objectNo ? objectNo : stuffSearch.schObjectNo,
schSaleStoreName: saleStoreName ? saleStoreName : '', schSaleStoreName: saleStoreName ? saleStoreName : '',
schAddress: address ? address : '', schAddress: address ? address : '',
schObjectName: objectName ? objectName : '', schObjectName: objectName ? objectName : '',
schDispCompanyName: dispCompanyName ? dispCompanyName : '', schDispCompanyName: dispCompanyName ? dispCompanyName : '',
schSelSaleStoreId: stuffSearch?.schOtherSelSaleStoreId ? stuffSearch.schOtherSelSaleStoreId : stuffSearch.schSelSaleStoreId, schSelSaleStoreId: stuffSearch?.schSelSaleStoreId ? stuffSearch.schSelSaleStoreId : '',
schOtherSelSaleStoreId: stuffSearch?.schOtherSelSaleStoreId ? stuffSearch.schOtherSelSaleStoreId : '',
schReceiveUser: receiveUser ? receiveUser : '', schReceiveUser: receiveUser ? receiveUser : '',
schDateType: dateType, schDateType: dateType,
schFromDt: dayjs(startDate).format('YYYY-MM-DD'), schFromDt: startDate ? dayjs(startDate).format('YYYY-MM-DD') : '',
schToDt: dayjs(endDate).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', code: 'E',
startRow: stuffSearch?.startRow ? stuffSearch.startRow : 1, startRow: stuffSearch?.startRow ? stuffSearch.startRow : 1,
endRow: stuffSearch?.endRow ? stuffSearch.endRow : 100, endRow: stuffSearch?.endRow ? stuffSearch.endRow : 100,
schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'R', schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'R',
pageNo: stuffSearch?.pageNo,
pageSize: stuffSearch?.pageSize,
}) })
}
} else if (stuffSearch.code === 'FINISH') {
setStuffSearch({
schObjectNo: objectNo,
schSaleStoreName: saleStoreName,
schAddress: address,
schObjectName: objectName,
schDispCompanyName: dispCompanyName,
schSelSaleStoreId: schSelSaleStoreId,
schOtherSelSaleStoreId: otherSaleStoreId,
schReceiveUser: receiveUser,
schDateType: dateType,
schFromDt: startDate ? dayjs(startDate).format('YYYY-MM-DD') : '',
schToDt: endDate ? dayjs(endDate).format('YYYY-MM-DD') : '',
code: 'E',
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 { } else {
setStuffSearch({ setStuffSearch({
schObjectNo: objectNo, schObjectNo: objectNo,
@ -97,15 +236,18 @@ export default function StuffSearchCondition() {
schAddress: address, schAddress: address,
schObjectName: objectName, schObjectName: objectName,
schDispCompanyName: dispCompanyName, schDispCompanyName: dispCompanyName,
schSelSaleStoreId: stuffSearch?.schOtherSelSaleStoreId ? stuffSearch.schOtherSelSaleStoreId : stuffSearch.schSelSaleStoreId, schSelSaleStoreId: schSelSaleStoreId,
schOtherSelSaleStoreId: otherSaleStoreId,
schReceiveUser: receiveUser, schReceiveUser: receiveUser,
schDateType: dateType, schDateType: dateType,
schFromDt: dayjs(startDate).format('YYYY-MM-DD'), schFromDt: startDate ? dayjs(startDate).format('YYYY-MM-DD') : '',
schToDt: dayjs(endDate).format('YYYY-MM-DD'), schToDt: endDate ? dayjs(endDate).format('YYYY-MM-DD') : '',
code: 'E', code: 'E',
startRow: 1, startRow: stuffSearch?.startRow ? stuffSearch.startRow : 1,
endRow: 100, endRow: stuffSearch?.endRow ? stuffSearch.endRow : 100,
schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'R', schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'R',
pageNo: stuffSearch?.pageNo,
pageSize: stuffSearch?.pageSize,
}) })
} }
} }
@ -119,7 +261,7 @@ export default function StuffSearchCondition() {
objectNameRef.current.value = '' objectNameRef.current.value = ''
dispCompanyNameRef.current.value = '' dispCompanyNameRef.current.value = ''
receiveUserRef.current.value = '' receiveUserRef.current.value = ''
stuffSearch.schDateType = 'U'
setObjectNo('') setObjectNo('')
setAddress('') setAddress('')
setobjectName('') setobjectName('')
@ -131,12 +273,25 @@ export default function StuffSearchCondition() {
setEndDate(dayjs(new Date()).format('YYYY-MM-DD')) setEndDate(dayjs(new Date()).format('YYYY-MM-DD'))
if (session?.storeId === 'T01') { if (session?.storeId === 'T01') {
setSchSelSaleStoreId('') setSchSelSaleStoreId('')
setOtherSaleStoreId('')
handleClear1() // handleClear1() //
resetStuffRecoil() resetStuffRecoil()
setStuffSearch({ setStuffSearch({
...stuffSearch, schObjectNo: '',
schAddress: '',
schObjectName: '',
schSaleStoreName: '',
schReceiveUser: '',
schDispCompanyName: '',
schSelSaleStoreId: '', schSelSaleStoreId: '',
schOtherSelSaleStoreId: '', schOtherSelSaleStoreId: '',
schDateType: 'U',
startRow: 1,
endRow: 100,
schSortType: 'R',
pageNo: 1,
pageSize: 100,
code: 'S',
}) })
} else { } else {
if (otherSaleStoreList.length > 1) { if (otherSaleStoreList.length > 1) {
@ -184,30 +339,24 @@ export default function StuffSearchCondition() {
setSchSelSaleStoreList(allList) setSchSelSaleStoreList(allList)
setFavoriteStoreList(favList) setFavoriteStoreList(favList)
setShowSaleStoreList(favList) setShowSaleStoreList(favList)
// setSchSelSaleStoreId(session?.storeId) if (stuffSearch.schSelSaleStoreId != '') {
setStuffSearch({ setSchSelSaleStoreId(stuffSearch.schSelSaleStoreId)
...stuffSearch, url = `/api/object/saleStore/${stuffSearch.schSelSaleStoreId}/list?firstFlg=1&userId=${session?.userId}`
code: 'S', get({ url: url }).then((res) => {
// schSelSaleStoreId: session?.storeId, if (!isEmptyArray(res)) {
res.map((row) => {
row.value = row.saleStoreId
row.label = row.saleStoreName
}) })
//T01 2 1 storeId otherList = res.filter((row) => row.saleStoreLevel !== '1')
// setOtherSaleStoreList(otherList)
// url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=0&userId=${session?.userId}` setOtherSaleStoreId(stuffSearch.schOtherSelSaleStoreId)
} else {
// get({ url: url }).then((res) => { setOtherSaleStoreList([])
// if (!isEmptyArray(res)) { }
// res.map((row) => { })
// row.value = row.saleStoreId }
// row.label = row.saleStoreName
// })
// otherList = res
// setOtherSaleStoreList(otherList)
// } else {
// setOtherSaleStoreList([])
// }
// })
} else { } else {
if (session?.storeLvl === '1') { if (session?.storeLvl === '1') {
allList = res allList = res
@ -221,11 +370,9 @@ export default function StuffSearchCondition() {
setOtherSaleStoreList(otherList) setOtherSaleStoreList(otherList)
setStuffSearch({ if (stuffSearch.schOtherSelSaleStoreId != '') {
...stuffSearch, setOtherSaleStoreId(stuffSearch.schOtherSelSaleStoreId)
code: 'S', }
schSelSaleStoreId: allList[0].saleStoreId,
})
} else { } else {
//10X22, 201X112 2 //10X22, 201X112 2
//2 34 202X217 //2 34 202X217
@ -241,7 +388,8 @@ export default function StuffSearchCondition() {
setStuffSearch({ setStuffSearch({
...stuffSearch, ...stuffSearch,
code: 'S', code: 'S',
schSelSaleStoreId: otherList[0].saleStoreId, schSelSaleStoreId: res[0].saleStoreId,
schOtherSelSaleStoreId: otherList[0].saleStoreId,
}) })
} }
} }
@ -267,7 +415,7 @@ export default function StuffSearchCondition() {
const onInputChange = (key) => { const onInputChange = (key) => {
if (key !== '') { if (key !== '') {
setShowSaleStoreList(schSelSaleStoreList) setShowSaleStoreList(schSelSaleStoreList)
setOtherSaleStoreList([]) // setOtherSaleStoreList([])
} else { } else {
setShowSaleStoreList(favoriteStoreList) setShowSaleStoreList(favoriteStoreList)
} }
@ -298,8 +446,13 @@ export default function StuffSearchCondition() {
}) })
} else { } else {
//X //X
//
setSchSelSaleStoreId('') setSchSelSaleStoreId('')
setOtherSaleStoreId('')
if (stuffSearch.code === 'S') {
stuffSearch.schSelSaleStoreId = '' stuffSearch.schSelSaleStoreId = ''
stuffSearch.schOtherSelSaleStoreId = ''
}
//2 //2
setOtherSaleStoreList([]) setOtherSaleStoreList([])
@ -311,16 +464,69 @@ export default function StuffSearchCondition() {
if (isObjectNotEmpty(key)) { if (isObjectNotEmpty(key)) {
setOtherSaleStoreId(key.saleStoreId) setOtherSaleStoreId(key.saleStoreId)
stuffSearch.schOtherSelSaleStoreId = key.saleStoreId stuffSearch.schOtherSelSaleStoreId = key.saleStoreId
//2 1
stuffSearch.schSelSaleStoreId = schSelSaleStoreId
} else { } else {
//X 1 //X 1
if (session.storeLvl === '1') {
if (stuffSearch.schOtherSelSaleStoreId === '') {
//
setSchSelSaleStoreId(session.storeId)
} else {
//
setOtherSaleStoreId('')
if (stuffSearch.code === 'S') {
stuffSearch.schOtherSelSaleStoreId = ''
}
}
} else {
setOtherSaleStoreId('') setOtherSaleStoreId('')
setSchSelSaleStoreId(schSelSaleStoreId) setSchSelSaleStoreId(schSelSaleStoreId)
stuffSearch.schOtherSelSaleStoreId = '' stuffSearch.schOtherSelSaleStoreId = ''
stuffSearch.schSelSaleStoreId = schSelSaleStoreId stuffSearch.schSelSaleStoreId = schSelSaleStoreId
} }
} }
}
useEffect(() => { useEffect(() => {
//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')) 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')) setEndDate(stuffSearch?.schToDt ? stuffSearch.schToDt : dayjs(new Date()).format('YYYY-MM-DD'))
setObjectNo(stuffSearch.schObjectNo ? stuffSearch.schObjectNo : objectNo) setObjectNo(stuffSearch.schObjectNo ? stuffSearch.schObjectNo : objectNo)
@ -329,6 +535,8 @@ export default function StuffSearchCondition() {
setobjectName(stuffSearch.schObjectName ? stuffSearch.schObjectName : objectName) setobjectName(stuffSearch.schObjectName ? stuffSearch.schObjectName : objectName)
setDispCompanyName(stuffSearch.schDispCompanyName ? stuffSearch.schDispCompanyName : dispCompanyName) setDispCompanyName(stuffSearch.schDispCompanyName ? stuffSearch.schDispCompanyName : dispCompanyName)
setReceiveUser(stuffSearch.schReceiveUser ? stuffSearch.schReceiveUser : receiveUser) setReceiveUser(stuffSearch.schReceiveUser ? stuffSearch.schReceiveUser : receiveUser)
setDateType(stuffSearch.schDateType ? stuffSearch.schDateType : dateType)
}
}, [stuffSearch]) }, [stuffSearch])
useEffect(() => { useEffect(() => {
@ -358,14 +566,14 @@ export default function StuffSearchCondition() {
<div className="left-unit-box"> <div className="left-unit-box">
<Link href="/management/stuff/tempdetail" scroll={false}> <Link href="/management/stuff/tempdetail" scroll={false}>
<button type="button" className="btn-origin navy mr5"> <button type="button" className="btn-origin navy mr5">
{getMessage('stuff.search.btn1')} {getMessage('stuff.search.btn.register')}
</button> </button>
</Link> </Link>
<button type="button" className="btn-origin navy mr5" onClick={onSubmit}> <button type="button" className="btn-origin navy mr5" onClick={onSubmit}>
{getMessage('stuff.search.btn2')} {getMessage('stuff.search.btn.search')}
</button> </button>
<button type="button" className="btn-origin grey" onClick={resetRecoil}> <button type="button" className="btn-origin grey" onClick={resetRecoil}>
{getMessage('stuff.search.btn3')} {getMessage('stuff.search.btn.reset')}
</button> </button>
</div> </div>
</div> </div>
@ -392,6 +600,7 @@ export default function StuffSearchCondition() {
className="input-light" className="input-light"
defaultValue={stuffSearch?.schObjectNo ? stuffSearch.schObjectNo : objectNo} defaultValue={stuffSearch?.schObjectNo ? stuffSearch.schObjectNo : objectNo}
onChange={() => { onChange={() => {
stuffSearch.schObjectNo = objectNoRef.current.value
setObjectNo(objectNoRef.current.value) setObjectNo(objectNoRef.current.value)
}} }}
onKeyUp={handleByOnKeyUp} onKeyUp={handleByOnKeyUp}
@ -407,6 +616,7 @@ export default function StuffSearchCondition() {
className="input-light" className="input-light"
defaultValue={stuffSearch?.schSaleStoreName ? stuffSearch.schSaleStoreName : saleStoreName} defaultValue={stuffSearch?.schSaleStoreName ? stuffSearch.schSaleStoreName : saleStoreName}
onChange={() => { onChange={() => {
stuffSearch.schSaleStoreName = saleStoreNameRef.current.value
setSaleStoreName(saleStoreNameRef.current.value) setSaleStoreName(saleStoreNameRef.current.value)
}} }}
onKeyUp={handleByOnKeyUp} onKeyUp={handleByOnKeyUp}
@ -422,6 +632,7 @@ export default function StuffSearchCondition() {
className="input-light" className="input-light"
defaultValue={stuffSearch?.schAddress ? stuffSearch.schAddress : address} defaultValue={stuffSearch?.schAddress ? stuffSearch.schAddress : address}
onChange={() => { onChange={() => {
stuffSearch.schAddress = addressRef.current.value
setAddress(addressRef.current.value) setAddress(addressRef.current.value)
}} }}
onKeyUp={handleByOnKeyUp} onKeyUp={handleByOnKeyUp}
@ -437,6 +648,7 @@ export default function StuffSearchCondition() {
className="input-light" className="input-light"
defaultValue={stuffSearch?.schDispCompanyName ? stuffSearch.schDispCompanyName : dispCompanyName} defaultValue={stuffSearch?.schDispCompanyName ? stuffSearch.schDispCompanyName : dispCompanyName}
onChange={() => { onChange={() => {
stuffSearch.schDispCompanyName = dispCompanyNameRef.current.value
setDispCompanyName(dispCompanyNameRef.current.value) setDispCompanyName(dispCompanyNameRef.current.value)
}} }}
onKeyUp={handleByOnKeyUp} onKeyUp={handleByOnKeyUp}
@ -454,6 +666,7 @@ export default function StuffSearchCondition() {
className="input-light" className="input-light"
defaultValue={stuffSearch?.schObjectName ? stuffSearch.schObjectName : objectName} defaultValue={stuffSearch?.schObjectName ? stuffSearch.schObjectName : objectName}
onChange={() => { onChange={() => {
stuffSearch.schObjectName = objectNameRef.current.value
setobjectName(objectNameRef.current.value) setobjectName(objectNameRef.current.value)
}} }}
onKeyUp={handleByOnKeyUp} onKeyUp={handleByOnKeyUp}
@ -469,6 +682,7 @@ export default function StuffSearchCondition() {
ref={receiveUserRef} ref={receiveUserRef}
defaultValue={stuffSearch?.schReceiveUser ? stuffSearch.schReceiveUser : receiveUser} defaultValue={stuffSearch?.schReceiveUser ? stuffSearch.schReceiveUser : receiveUser}
onChange={() => { onChange={() => {
stuffSearch.schReceiveUser = receiveUserRef.current.value
setReceiveUser(receiveUserRef.current.value) setReceiveUser(receiveUserRef.current.value)
}} }}
onKeyUp={handleByOnKeyUp} onKeyUp={handleByOnKeyUp}
@ -500,7 +714,9 @@ export default function StuffSearchCondition() {
} else if (stuffSearch?.code === 'E' && schSelSaleStoreId !== '') { } else if (stuffSearch?.code === 'E' && schSelSaleStoreId !== '') {
return option.saleStoreId === schSelSaleStoreId return option.saleStoreId === schSelSaleStoreId
} else { } else {
if (stuffSearch?.schSelSaleStoreId !== '') { if (stuffSearch?.code === 'FINISH') {
return option.saleStoreId === schSelSaleStoreId
} else if (stuffSearch?.schSelSaleStoreId !== '') {
return option.saleStoreId === stuffSearch.schSelSaleStoreId return option.saleStoreId === stuffSearch.schSelSaleStoreId
} else { } else {
return false return false
@ -531,7 +747,9 @@ export default function StuffSearchCondition() {
} else if (stuffSearch?.code === 'E' && schSelSaleStoreId !== '') { } else if (stuffSearch?.code === 'E' && schSelSaleStoreId !== '') {
return option.saleStoreId === schSelSaleStoreId return option.saleStoreId === schSelSaleStoreId
} else { } else {
if (stuffSearch?.schSelSaleStoreId !== '') { if (stuffSearch?.code === 'FINISH') {
return option.saleStoreId === schSelSaleStoreId
} else if (stuffSearch?.schSelSaleStoreId !== '') {
return option.saleStoreId === stuffSearch.schSelSaleStoreId return option.saleStoreId === stuffSearch.schSelSaleStoreId
} else { } else {
return false return false
@ -606,10 +824,11 @@ export default function StuffSearchCondition() {
type="radio" type="radio"
name="radio_ptype" name="radio_ptype"
id="radio_u" id="radio_u"
checked={dateType === 'U' ? true : false} defaultChecked={stuffSearch.schDateType === 'U' ? true : false}
value={'U'} value={'U'}
onChange={(e) => { onChange={(e) => {
setDateType(e.target.value) setDateType(e.target.value)
stuffSearch.schDateType = e.target.value
}} }}
/> />
<label htmlFor="radio_u">{getMessage('stuff.search.schDateTypeU')}</label> <label htmlFor="radio_u">{getMessage('stuff.search.schDateTypeU')}</label>
@ -619,10 +838,11 @@ export default function StuffSearchCondition() {
type="radio" type="radio"
name="radio_ptype" name="radio_ptype"
id="radio_r" id="radio_r"
checked={dateType === 'R' ? true : false} defaultChecked={stuffSearch.schDateType === 'R' ? true : false}
value={'R'} value={'R'}
onChange={(e) => { onChange={(e) => {
setDateType(e.target.value) setDateType(e.target.value)
stuffSearch.schDateType = e.target.value
}} }}
/> />
<label htmlFor="radio_r">{getMessage('stuff.search.schDateTypeR')}</label> <label htmlFor="radio_r">{getMessage('stuff.search.schDateTypeR')}</label>

View File

@ -27,10 +27,10 @@ export default function StuffSubHeader({ type }) {
const param = { const param = {
pid: '1', pid: '1',
objectNo: objectNo,
} }
//
const url = `/floor-plan?${queryStringFormatter(param)}` const url = `/floor-plan?${queryStringFormatter(param)}`
console.log(url)
router.push(url) router.push(url)
} }
@ -107,7 +107,7 @@ export default function StuffSubHeader({ type }) {
<span>{getMessage('header.menus.management')}</span> <span>{getMessage('header.menus.management')}</span>
</li> </li>
<li className="location-item"> <li className="location-item">
<span>{getMessage('header.menus.management.newStuff')}</span> <span>{getMessage('header.menus.management.detail')}</span>
</li> </li>
</ul> </ul>
</> </>

View File

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

View File

@ -5,8 +5,9 @@ import { Bar } from 'react-chartjs-2'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { useEffect, useState, useRef } from 'react' import { useEffect, useState, useRef } from 'react'
import { useRecoilValue } from 'recoil' import { useRecoilValue, useRecoilState } from 'recoil'
import { floorPlanObjectState } from '@/store/floorPlanObjectAtom' import { floorPlanObjectState } from '@/store/floorPlanObjectAtom'
import { pwrGnrSimTypeState } from '@/store/simulatorAtom'
import { useAxios } from '@/hooks/useAxios' import { useAxios } from '@/hooks/useAxios'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
@ -41,6 +42,7 @@ export default function Simulator() {
// //
const [chartData, setChartData] = useState([]) const [chartData, setChartData] = useState([])
const data = { const data = {
labels: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'], labels: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
datasets: [ datasets: [
@ -105,8 +107,10 @@ export default function Simulator() {
useEffect(() => { useEffect(() => {
if (objectNo) { if (objectNo) {
fetchObjectDetail(objectNo) fetchObjectDetail(objectNo)
}
fetchSimulatorNotice() fetchSimulatorNotice()
setPwrGnrSimType('D')
setPwrRecoil({ ...pwrRecoil, type: 'D' })
}
}, [objectNo, plan]) }, [objectNo, plan])
// //
@ -118,13 +122,30 @@ export default function Simulator() {
// //
const [pcsInfoList, setPcsInfoList] = useState([]) 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 fetchObjectDetail = async (objectNo) => {
const apiUrl = `/api/pwrGnrSimulation/calculations?objectNo=${objectNo}&planNo=${plan?.id}` const apiUrl = `/api/pwrGnrSimulation/calculations?objectNo=${objectNo}&planNo=${plan?.id}`
const resultData = await get({ url: apiUrl }) const resultData = await get({ url: apiUrl })
if (resultData) { if (resultData) {
setObjectDetail(resultData) setObjectDetail(resultData)
if (resultData.frcPwrGnrList) { if (resultData.hatsudenryouAll) {
setChartData(resultData.frcPwrGnrList) 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) { if (resultData.pcsList) {
setPcsInfoList(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 ( return (
<div className="sub-content estimate"> <div className="sub-content estimate">
<div className="sub-content-inner"> <div className="sub-content-inner">
@ -158,23 +201,25 @@ export default function Simulator() {
<div className="estimate-box"> <div className="estimate-box">
<div className="estimate-tit">{getMessage('simulator.title.sub1')}</div> <div className="estimate-tit">{getMessage('simulator.title.sub1')}</div>
<div className="estimate-name"> <div className="estimate-name">
{objectDetail.objectNo} (Plan No: {objectDetail.planNo}) {objectDetail.objectNo} (Plan No: {plan?.id})
</div> </div>
</div> </div>
{/* 작성일 */} {/* 작성일 */}
<div className="estimate-box"> <div className="estimate-box">
<div className="estimate-tit">{getMessage('simulator.title.sub2')}</div> <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>
{/* 시스템용량 */} {/* 시스템용량 */}
<div className="estimate-box"> <div className="estimate-box">
<div className="estimate-tit">{getMessage('simulator.title.sub3')}</div> <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>
{/* 연간예측발전량 */} {/* 연간예측발전량 */}
<div className="estimate-box"> <div className="estimate-box">
<div className="estimate-tit">{getMessage('simulator.title.sub4')}</div> <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> </div>
<div className="estimate-list-wrap"> <div className="estimate-list-wrap">
@ -206,6 +251,24 @@ export default function Simulator() {
<div className="chart-inner"> <div className="chart-inner">
<div className="sub-table-box"> <div className="sub-table-box">
<div className="chart-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} /> <Bar ref={chartRef} data={data} options={options} />
</div> </div>
<div className="table-box-title-wrap"> <div className="table-box-title-wrap">
@ -237,7 +300,7 @@ export default function Simulator() {
{chartData.length > 0 ? ( {chartData.length > 0 ? (
<tr> <tr>
{chartData.map((data) => ( {chartData.map((data) => (
<td key={data}>{convertNumberToPriceDecimal(data)}</td> <td key={data}>{data}</td>
))} ))}
</tr> </tr>
) : ( ) : (

View File

@ -40,19 +40,19 @@ export function useCanvasConfigInitialize() {
const flowTexts = canvas.getObjects().filter((obj) => obj.name === 'flowText') const flowTexts = canvas.getObjects().filter((obj) => obj.name === 'flowText')
if (basicSetting.roofAngleSet === 'slope') { if (basicSetting.roofAngleSet === 'slope') {
offsetTexts.forEach((obj) => { offsetTexts.forEach((obj) => {
obj.set({ text: `${obj.originText}-${obj.pitch}${angleUnit}` }) obj.set({ text: `${!obj.originText ? '' : obj.originText + '-'}${obj.pitch}${angleUnit}` })
}) })
flowTexts.forEach((obj) => { 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') { if (basicSetting.roofAngleSet === 'flat') {
offsetTexts.forEach((obj) => { 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) => { flowTexts.forEach((obj) => {
obj.set({ text: `${obj.originText}-${getDegreeByChon(obj.pitch)}${pitchText}` }) obj.set({ text: `${!obj.originText ? '' : obj.originText + '-'}${getDegreeByChon(obj.pitch)}${pitchText}` })
}) })
} }

View File

@ -1,8 +1,30 @@
import { menuNumberState } from '@/store/menuAtom' 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 = () => { export const useCanvasMenu = () => {
const [menuNumber, setMenuNumber] = useRecoilState(menuNumberState) 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 { return {
menuNumber, menuNumber,

View File

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

View File

@ -12,13 +12,13 @@ export function useFont() {
const circuitNumberText = useRecoilValue(fontSelector('circuitNumberText')) const circuitNumberText = useRecoilValue(fontSelector('circuitNumberText'))
useEffect(() => { useEffect(() => {
if (canvas) { if (canvas && commonText.fontWeight.value) {
const textObjs = canvas?.getObjects().filter((obj) => obj.name === 'commonText') const textObjs = canvas?.getObjects().filter((obj) => obj.name === 'commonText')
textObjs.forEach((obj) => { textObjs.forEach((obj) => {
obj.set({ obj.set({
fontFamily: commonText.fontFamily.value, fontFamily: commonText.fontFamily.value,
fontWeight: lengthText.fontWeight.value.toLowerCase().includes('bold') ? 'bold' : 'normal', fontWeight: commonText.fontWeight.value.toLowerCase().includes('bold') ? 'bold' : 'normal',
fontStyle: lengthText.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal', fontStyle: commonText.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal',
fontSize: commonText.fontSize.value, fontSize: commonText.fontSize.value,
fill: commonText.fontColor.value, fill: commonText.fontColor.value,
}) })
@ -28,13 +28,13 @@ export function useFont() {
}, [commonText]) }, [commonText])
useEffect(() => { useEffect(() => {
if (canvas) { if (canvas && dimensionLineText.fontWeight.value) {
const textObjs = canvas?.getObjects().filter((obj) => obj.name === 'dimensionLineText') const textObjs = canvas?.getObjects().filter((obj) => obj.name === 'dimensionLineText')
textObjs.forEach((obj) => { textObjs.forEach((obj) => {
obj.set({ obj.set({
fontFamily: dimensionLineText.fontFamily.value, fontFamily: dimensionLineText.fontFamily.value,
fontWeight: lengthText.fontWeight.value.toLowerCase().includes('bold') ? 'bold' : 'normal', fontWeight: dimensionLineText.fontWeight.value.toLowerCase().includes('bold') ? 'bold' : 'normal',
fontStyle: lengthText.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal', fontStyle: dimensionLineText.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal',
fontSize: dimensionLineText.fontSize.value, fontSize: dimensionLineText.fontSize.value,
fill: dimensionLineText.fontColor.value, fill: dimensionLineText.fontColor.value,
}) })
@ -44,13 +44,13 @@ export function useFont() {
}, [dimensionLineText]) }, [dimensionLineText])
useEffect(() => { useEffect(() => {
if (canvas) { if (canvas && flowText.fontWeight.value) {
const textObjs = canvas.getObjects().filter((obj) => obj.name === 'flowText') const textObjs = canvas?.getObjects().filter((obj) => obj.name === 'flowText')
textObjs.forEach((obj) => { textObjs.forEach((obj) => {
obj.set({ obj.set({
fontFamily: flowText.fontFamily.value, fontFamily: flowText.fontFamily.value,
fontWeight: lengthText.fontWeight.value.toLowerCase().includes('bold') ? 'bold' : 'normal', fontWeight: flowText.fontWeight.value.toLowerCase().includes('bold') ? 'bold' : 'normal',
fontStyle: lengthText.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal', fontStyle: flowText.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal',
fontSize: flowText.fontSize.value, fontSize: flowText.fontSize.value,
fill: flowText.fontColor.value, fill: flowText.fontColor.value,
}) })
@ -60,8 +60,8 @@ export function useFont() {
}, [flowText]) }, [flowText])
useEffect(() => { useEffect(() => {
if (canvas) { if (canvas && lengthText.fontWeight.value) {
const textObjs = canvas.getObjects().filter((obj) => obj.name === 'lengthText') const textObjs = canvas?.getObjects().filter((obj) => obj.name === 'lengthText')
textObjs.forEach((obj) => { textObjs.forEach((obj) => {
obj.set({ obj.set({
fontFamily: lengthText.fontFamily.value, fontFamily: lengthText.fontFamily.value,

View File

@ -1,13 +1,13 @@
import { useRef, useState } from 'react' import { useEffect, useRef, useState } from 'react'
import { useRecoilState } from 'recoil' import { useRecoilState } from 'recoil'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
import { useSwal } from '@/hooks/useSwal' import { useSwal } from '@/hooks/useSwal'
import { convertDwgToPng } from '@/lib/cadAction'
import { useAxios } from '../useAxios' import { useAxios } from '../useAxios'
import { currentCanvasPlanState } from '@/store/canvasAtom' 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 converterUrl = process.env.NEXT_PUBLIC_CONVERTER_API_URL
const [refImage, setRefImage] = useState(null) const [refImage, setRefImage] = useState(null)
const [refFileMethod, setRefFileMethod] = useState('1') const [refFileMethod, setRefFileMethod] = useState('1')
@ -16,7 +16,7 @@ export default function useRefFiles() {
const queryRef = useRef(null) const queryRef = useRef(null)
const { swalFire } = useSwal() const { swalFire } = useSwal()
const { get, promisePut } = useAxios() const { get, promisePut, promisePost } = useAxios()
// const { currentCanvasPlan, setCurrentCanvasPlan } = usePlan() // const { currentCanvasPlan, setCurrentCanvasPlan } = usePlan()
/** /**
@ -25,7 +25,11 @@ export default function useRefFiles() {
*/ */
const handleRefFile = (file) => { const handleRefFile = (file) => {
setRefImage(file) setRefImage(file)
file.name.split('.').pop() === 'dwg' ? handleUploadRefFile(file) : () => {} /**
* 파일 확장자가 dwg일 경우 변환하여 이미지로 저장
* 파일 확장자가 이미지일 경우 이미지 저장
*/
file.name.split('.').pop() === 'dwg' ? handleUploadConvertRefFile(file) : handleUploadImageRefFile(file)
// handleUploadRefFile(file) // handleUploadRefFile(file)
} }
@ -58,22 +62,29 @@ export default function useRefFiles() {
} }
/** /**
* 현재 플랜이 변경되면 플랜 상태 저장 * 이미지 파일 업로드
* @param {*} file
*/ */
// useEffect(() => { const handleUploadImageRefFile = async (file) => {
// const handleCurrentPlan = async () => { console.log('🚀 ~ handleUploadImageRefFile ~ file:', file)
// await promisePut({ url: '/api/canvas-management/canvas-statuses', data: currentCanvasPlan }).then((res) => { const formData = new FormData()
// console.log('🚀 ~ awaitpromisePut ~ res:', res) formData.append('file', file)
// })
// } const response = await fetch('http://localhost:3000/api/image-upload', {
// handleCurrentPlan() method: 'POST',
// }, [currentCanvasPlan]) body: formData,
})
const result = await response.json()
console.log('🚀 ~ handleUploadImageRefFile ~ res:', result)
// writeImageBuffer(file)
}
/** /**
* RefFile이 캐드 도면 파일일 경우 변환하여 이미지로 저장 * RefFile이 캐드 도면 파일일 경우 변환하여 이미지로 저장
* @param {*} file * @param {*} file
*/ */
const handleUploadRefFile = async (file) => { const handleUploadConvertRefFile = async (file) => {
const formData = new FormData() const formData = new FormData()
formData.append('file', file) formData.append('file', file)
@ -95,6 +106,19 @@ export default function useRefFiles() {
setRefFileMethod(e.target.value) 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 { return {
refImage, refImage,
queryRef, queryRef,

View File

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

View File

@ -6,34 +6,19 @@ import { estimateState, floorPlanObjectState } from '@/store/floorPlanObjectAtom
import { isObjectNotEmpty } from '@/util/common-utils' import { isObjectNotEmpty } from '@/util/common-utils'
import { SessionContext } from '@/app/SessionProvider' import { SessionContext } from '@/app/SessionProvider'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { useRouter } from 'next/navigation'
const reducer = (prevState, nextState) => { import { FloorPlanContext } from '@/app/floor-plan/FloorPlanProvider'
return { ...prevState, ...nextState }
}
// Constants // Constants
const ESTIMATE_API_ENDPOINT = '/api/estimates' // API 엔드포인트 정의 const ESTIMATE_API_ENDPOINT = '/api/estimate' // API 엔드포인트 정의
const defaultEstimateData = {
estimateDate: new Date(), //견적일
charger: '', //담당자
objectName: '', //안건명
objectNameOmit: '', //경칭코드
estimateType: 'YJOD', //주문분류
remarks: '', //비고
estimateOption: '', //견적특이사항
itemList: [],
fileList: [],
fileFlg: '0', //후일 자료 제출 (체크 1 노체크 0)
priceCd: '',
}
// Helper functions // Helper functions
const updateItemInList = (itemList, itemId, updates) => { const updateItemInList = (itemList, dispOrder, updates) => {
return itemList.map((item) => (item.itemId === itemId ? { ...item, ...updates } : item)) return itemList.map((item) => (item.dispOrder === dispOrder ? { ...item, ...updates } : item))
} }
export const useEstimateController = (planNo) => { export const useEstimateController = (planNo) => {
const router = useRouter()
const { session } = useContext(SessionContext) const { session } = useContext(SessionContext)
const globalLocaleState = useRecoilValue(globalLocaleStore) const globalLocaleState = useRecoilValue(globalLocaleStore)
const objectRecoil = useRecoilValue(floorPlanObjectState) const objectRecoil = useRecoilValue(floorPlanObjectState)
@ -41,25 +26,32 @@ export const useEstimateController = (planNo) => {
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { get, post, promisePost } = useAxios(globalLocaleState) const { promiseGet, post, promisePost } = useAxios(globalLocaleState)
const [isLoading, setIsLoading] = useState(false) const [isLoading, setIsLoading] = useState(false)
const [state, setState] = useReducer(reducer, defaultEstimateData) const { estimateContextState, setEstimateContextState } = useContext(FloorPlanContext)
useEffect(() => { useEffect(() => {
if (!isLoading) { if (planNo && !isLoading) {
if (objectRecoil.floorPlanObjectNo && planNo) { if (objectRecoil.floorPlanObjectNo && planNo) {
fetchSetting() fetchSetting(objectRecoil.floorPlanObjectNo, planNo)
} }
} }
}, []) }, [])
// 상세 조회 // 상세 조회
const fetchSetting = async () => { const fetchSetting = async (objectNo, planNo) => {
try { try {
await get({ url: `/api/estimate/${objectRecoil.floorPlanObjectNo}/${planNo}/detail` }).then((res) => { await promiseGet({ url: `/api/estimate/${objectNo}/${planNo}/detail` }).then((res) => {
if (isObjectNotEmpty(res)) { if (res.status === 200) {
setState(res) if (isObjectNotEmpty(res.data)) {
if (res.data.itemList.length > 0) {
res.data.itemList.map((item) => {
item.delFlg = '0'
})
}
setEstimateContextState(res.data)
}
} }
}) })
setIsLoading(true) setIsLoading(true)
@ -69,95 +61,239 @@ export const useEstimateController = (planNo) => {
} }
} }
const updateItem = (itemId, updates) => { const updateItem = (dispOrder, updates) => {
setState({ setEstimateContextState({
itemList: updateItemInList(state.itemList, itemId, updates), itemList: updateItemInList(estimateContextState.itemList, dispOrder, updates),
}) })
} }
const addItem = () => { const addItem = () => {
const newItemDispOrder = (Math.max(...state.itemList.map((item) => item.dispOrder)) + 1) * 100 let newItemDispOrder = Math.max(...estimateContextState.itemList.map((item) => item.dispOrder))
setState({ newItemDispOrder = (Math.floor(newItemDispOrder / 100) + 1) * 100
setEstimateContextState({
itemList: [ itemList: [
...state.itemList, ...estimateContextState.itemList,
{ {
dispOrder: newItemDispOrder, objectNo: objectRecoil.floorPlanObjectNo,
planNo: planNo,
dispOrder: newItemDispOrder.toString(),
itemId: '', //제품번호 itemId: '', //제품번호
itemNo: '', //형명 itemNo: '',
itemName: '', itemName: '', //형명
amount: '', //수량 amount: '', //수량
unitPrice: '0', unitPrice: '0',
unit: '', //단위 unit: '', //단위
salePrice: '0', //단가 salePrice: '', //단가
saleTotPrice: '0', //금액(부가세별도) saleTotPrice: '', //금액(부가세별도)
itemChangeFlg: '1', //추가시 체인지플래그 1로
partAdd: '1', //NEW 체인지 플래그
delFlg: '0', //삭제 플래그 0 삭제하면 1
addFlg: true,
}, },
], ],
}) })
} }
useEffect(() => { useEffect(() => {
setEstimateData({ ...state, userId: session.userId, sapSalesStoreCd: session.custCd }) setEstimateData({ ...estimateContextState, userId: session.userId, sapSalesStoreCd: session.custCd })
}, [state]) }, [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 () => { const handleEstimateSubmit = async () => {
//0. 필수체크 //0. 필수체크
let flag = true 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 저장안돼 //아이템 fileUploadFlg가1(첨부파일 필수)이 1개라도 있는데 후일 자료 제출(fileFlg) 체크안했으면(0) alert 저장안돼
if (flag) {
if (estimateData.fileList.length < 1) {
if (estimateData.itemList.length > 1) { if (estimateData.itemList.length > 1) {
estimateData.itemList.map((row) => { estimateData.itemList.map((row) => {
if (row.delFlg === '0') {
if (row.fileUploadFlg === '1') { if (row.fileUploadFlg === '1') {
if (fileFlg) {
if (estimateData.fileFlg === '0') { if (estimateData.fileFlg === '0') {
alert(getMessage('estimate.detail.save.requiredMsg')) fileFlg = false
flag = false return alert(getMessage('estimate.detail.save.requiredFileUpload'))
}
}
} }
} }
}) })
} }
if (flag) { }
//1. 첨부파일 저장 }
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 && fileFlg && itemFlg) {
//1. 첨부파일 저장시작
const formData = new FormData() const formData = new FormData()
formData.append('file', estimateData.fileList) formData.append('file', estimateData.fileList)
formData.append('objectNo', estimateData.objectNo) formData.append('objectNo', estimateData.objectNo)
formData.append('planNo', estimateData.planNo) formData.append('planNo', estimateData.planNo)
formData.append('category', '10') formData.append('category', '10')
formData.append('userId', estimateData.userId) formData.append('userId', estimateData.userId)
// for (const value of formData.values()) {
// console.log('formData::', value)
// }
await post({ url: '/api/file/fileUpload', data: formData }) await post({ url: '/api/file/fileUpload', data: formData })
//첨부파일저장끝
//2. 상세데이터 저장 //제품라인 추가했는데 아이템 안고르고 저장하면itemId=''은 날리고 나머지 저장하기
return estimateData.itemList = estimateData.itemList.filter((item) => item.itemId !== '')
await promisePost({ url: `${ESTIMATE_API_ENDPOINT}/save-estimate`, data: estimateData }).then((res) => {
if (res) { let delCnt = 0
alert(getMessage('estimate.detail.save.alertMsg')) 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 { // console.log('아이템리스트::', estimateData.itemList)
// const result = await promisePost({ // console.log('최종 정보::;', estimateData)
// url: ESTIMATE_API_ENDPOINT, //2. 상세데이터 저장
// data: estimateData, // return
// }) try {
// alert(getMessage('estimate.detail.save.alertMsg')) await promisePost({ url: `${ESTIMATE_API_ENDPOINT}/save-estimate`, data: estimateData }).then((res) => {
// return result if (res.status === 201) {
// } catch (error) { alert(getMessage('estimate.detail.save.alertMsg'))
// console.error('Failed to submit estimate:', error) //어디로 보낼지
// throw error 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 { return {
state, estimateContextState,
setState, setEstimateContextState,
updateItem, updateItem,
addItem, addItem,
handleEstimateSubmit, handleEstimateSubmit,
fetchSetting, fetchSetting,
handleEstimateFileDownload,
handleEstimateCopy,
} }
} }

View 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,
}
}

View 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,
}
}

View 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 }
}

View File

@ -17,6 +17,7 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
const { getMessage } = useMessage() const { getMessage } = useMessage()
const canvas = useRecoilValue(canvasState) const canvas = useRecoilValue(canvasState)
const { addCanvasMouseEventListener, initEvent, addDocumentEventListener } = useEvent() const { addCanvasMouseEventListener, initEvent, addDocumentEventListener } = useEvent()
// const { addCanvasMouseEventListener, initEvent, addDocumentEventListener } = useContext(EventContext)
const { swalFire } = useSwal() const { swalFire } = useSwal()
const { drawDirectionArrow } = usePolygon() const { drawDirectionArrow } = usePolygon()
const lengthTextFont = useRecoilValue(fontSelector('lengthText')) const lengthTextFont = useRecoilValue(fontSelector('lengthText'))

View File

@ -1,16 +1,22 @@
import { useEffect, useState } from 'react' import { useCallback, useEffect, useState } from 'react'
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil' import { useRecoilState, useRecoilValue } from 'recoil'
import { adsorptionPointModeState, adsorptionRangeState, canvasState } from '@/store/canvasAtom' import { adsorptionPointModeState, adsorptionRangeState, canvasState, planSizeSettingState } from '@/store/canvasAtom'
import { globalLocaleStore } from '@/store/localeAtom' import { globalLocaleStore } from '@/store/localeAtom'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { useAxios } from '@/hooks/useAxios' import { useAxios } from '@/hooks/useAxios'
import { useSwal } from '@/hooks/useSwal' import { useSwal } from '@/hooks/useSwal'
import { corridorDimensionSelector, settingModalFirstOptionsState, settingModalSecondOptionsState } from '@/store/settingAtom' import { correntObjectNoState, corridorDimensionSelector, settingModalFirstOptionsState, settingModalSecondOptionsState } from '@/store/settingAtom'
import { setSurfaceShapePattern } from '@/util/canvas-util'
import { POLYGON_TYPE } from '@/common/common' import { POLYGON_TYPE } from '@/common/common'
import { globalFontAtom } from '@/store/fontAtom'
import { dimensionLineSettingsState } from '@/store/commonUtilsAtom'
let objectNo
export function useCanvasSetting() { export function useCanvasSetting() {
const canvas = useRecoilValue(canvasState) const canvas = useRecoilValue(canvasState)
// canvas가 null이 아닐 때에만 getObjects 호출
const canvasObjects = canvas ? canvas.getObjects() : []
const [correntObjectNo, setCorrentObjectNo] = useRecoilState(correntObjectNoState)
const [settingModalFirstOptions, setSettingModalFirstOptions] = useRecoilState(settingModalFirstOptionsState) const [settingModalFirstOptions, setSettingModalFirstOptions] = useRecoilState(settingModalFirstOptionsState)
const [settingModalSecondOptions, setSettingModalSecondOptions] = useRecoilState(settingModalSecondOptionsState) const [settingModalSecondOptions, setSettingModalSecondOptions] = useRecoilState(settingModalSecondOptionsState)
@ -25,9 +31,16 @@ export function useCanvasSetting() {
const { swalFire } = useSwal() const { swalFire } = useSwal()
const [adsorptionPointMode, setAdsorptionPointMode] = useRecoilState(adsorptionPointModeState) 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(() => { useEffect(() => {
if (!canvas) { if (!canvas) {
@ -56,129 +69,245 @@ export function useCanvasSetting() {
}) })
break break
} }
canvas.renderAll() canvas?.renderAll()
}, [corridorDimension]) }, [corridorDimension])
useEffect(() => { useEffect(() => {
console.log('useCanvasSetting useEffect 실행1') console.log('useCanvasSetting useEffect 실행1', correntObjectNo)
fetchSettings() }, [])
}, [objectNo])
//흡착점 ON/OFF 변경 시
useEffect(() => { useEffect(() => {
console.log('useCanvasSetting useEffect 실행2') console.log('useCanvasSetting useEffect 실행2', adsorptionPointMode.fontFlag, correntObjectNo)
//fetchSettings()
//onClickOption() if (adsorptionPointMode.fontFlag) {
//fetchSettings() onClickOption2()
frontSettings()
fetchSettings()
}
}, [adsorptionPointMode]) }, [adsorptionPointMode])
// 1 과 2 변경 시
useEffect(() => { useEffect(() => {
console.log('useCanvasSetting useEffect 실행3') console.log('useCanvasSetting useEffect 실행3', settingModalFirstOptions.fontFlag, settingModalSecondOptions.fontFlag, correntObjectNo)
//fetchSettings() if (settingModalFirstOptions.fontFlag || settingModalSecondOptions.fontFlag) {
//onClickOption() onClickOption2()
//fetchSettings() frontSettings()
fetchSettings()
}
}, [settingModalFirstOptions, settingModalSecondOptions]) }, [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 () => { const fetchSettings = async () => {
try { 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) console.log('res', res)
if (res) {
const optionData1 = settingModalFirstOptions.option1.map((item) => ({ ...item, selected: res[item.column] })) const optionData1 = settingModalFirstOptions.option1.map((item) => ({ ...item, selected: res[item.column] }))
const optionData2 = settingModalFirstOptions.option2.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 optionData3 = settingModalSecondOptions.option3.map((item) => ({ ...item }))
const optionData4 = settingModalSecondOptions.option4.map((item) => ({ ...item, selected: res[item.column] })) const optionData4 = settingModalSecondOptions.option4.map((item) => ({ ...item, selected: res[item.column] }))
const optionData5 = settingModalFirstOptions.dimensionDisplay.map((item) => ({ ...item })) const optionData5 = settingModalFirstOptions.dimensionDisplay.map((item) => ({ ...item }))
const patternData = { //흡착점 ON/OFF
adsorpPoint: res.adsorpPoint, 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({ setSettingModalFirstOptions({
...settingModalFirstOptions,
option1: optionData1, option1: optionData1,
option2: optionData2, option2: optionData2,
dimensionDisplay: optionData5, dimensionDisplay: optionData5,
fontFlag: false,
}) })
setSettingModalSecondOptions({ setSettingModalSecondOptions({
...settingModalSecondOptions,
option3: optionData3, option3: optionData3,
option4: optionData4, option4: optionData4,
fontFlag: false,
}) })
setAdsorptionPointMode(patternData.adsorpPoint) 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)
console.log('adsorptionPointMode', adsorptionPointMode) //조회된 글꼴 데이터 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 })
}
frontSettings()
} catch (error) { } catch (error) {
console.error('Data fetching error:', error) console.error('Data fetching error:', error)
} }
} }
// 옵션 클릭 후 저장 // 옵션 클릭 후 저장
const onClickOption = async (item) => { const onClickOption2 = useCallback(async () => {
//치수 표시(단 건 선택)
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
})
//화면 표시(단 건 선택)
} 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
})
const polygons = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
polygons.forEach((polygon) => {
setSurfaceShapePattern(polygon, item.column)
})
//흡착범위 설정(단 건 선택)
} 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,
},
)
setSettingModalSecondOptions({ option3, option4: updatedOption4 })
//흡착점 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
})
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
}
setSettingModalFirstOptions({ option1, option2, dimensionDisplay })
try {
// 서버에 전송할 데이터 // 서버에 전송할 데이터
const dataToSend = { const dataToSend = {
firstOption1: option1.map((item) => ({ firstOption1: option1.map((item) => ({
@ -193,19 +322,15 @@ export function useCanvasSetting() {
column: item.column, column: item.column,
selected: item.selected, selected: item.selected,
})), })),
// secondOption1: secondOptions[0].option1.map((item) => ({
// name: item.id,
// name: item.name,
// // 필요한 경우 데이터 항목 추가
// })),
secondOption2: option4.map((item) => ({ secondOption2: option4.map((item) => ({
column: item.column, column: item.column,
selected: item.selected, selected: item.selected,
})), })),
} }
// console.log('globalFont', globalFont)
const patternData = { const patternData = {
objectNo, //견적서 번호
objectNo: correntObjectNo,
//디스플레이 설정(다중) //디스플레이 설정(다중)
allocDisplay: dataToSend.firstOption1[0].selected, allocDisplay: dataToSend.firstOption1[0].selected,
outlineDisplay: dataToSend.firstOption1[1].selected, outlineDisplay: dataToSend.firstOption1[1].selected,
@ -231,24 +356,65 @@ export function useCanvasSetting() {
adsorpRangeMedium: dataToSend.secondOption2[2].selected, adsorpRangeMedium: dataToSend.secondOption2[2].selected,
adsorpRangeLarge: dataToSend.secondOption2[3].selected, adsorpRangeLarge: dataToSend.secondOption2[3].selected,
//흡착점 ON/OFF //흡착점 ON/OFF
adsorpPoint: adsorptionPointMode, adsorpPoint: adsorptionPointMode.adsorptionPoint,
//??: adsorptionRange, 사용여부 확인 필요
//글꼴 설정
//문자 글꼴
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',
//흐름방향 글꼴
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',
//치수 글꼴
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',
//회로번호 글꼴
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',
//치수선 글꼴
lengthFont: globalFont.lengthText.fontFamily?.value ?? 'MS PGothic',
lengthFontStyle: globalFont.lengthText.fontWeight?.value ?? 'normal',
lengthFontSize: globalFont.lengthText.fontSize?.value ?? 16,
lengthFontColor: globalFont.lengthText.fontColor?.value ?? 'black',
//치수선 설정
originPixel: dimensionLineSettings.pixel,
originColor: dimensionLineSettings.color,
//치수선 설정
originHorizon: planSizeSettingMode.originHorizon,
originVertical: planSizeSettingMode.originVertical,
} }
console.log('patternData ', patternData) console.log('patternData ', patternData)
// HTTP POST 요청 보내기 // HTTP POST 요청 보내기
await post({ url: `/api/canvas-management/canvas-settings`, data: patternData }).then((res) => { await post({ url: `/api/canvas-management/canvas-settings`, data: patternData })
.then((res) => {
swalFire({ text: getMessage(res.returnMessage) }) swalFire({ text: getMessage(res.returnMessage) })
// Canvas 디스플레이 설정 시 해당 옵션 적용 // Canvas 디스플레이 설정 시 해당 옵션 적용
frontSettings() frontSettings()
}) })
} catch (error) { .catch((error) => {
swalFire({ text: getMessage(res.returnMessage), icon: 'error' }) swalFire({ text: getMessage(res.returnMessage), icon: 'error' })
} })
setAdsorptionRange(item.range) //setAdsorptionRange(item.range)
} }, [settingModalFirstOptions, settingModalSecondOptions, adsorptionPointMode, globalFont, planSizeSettingMode])
// Canvas 디스플레이 설정 시 해당 옵션 적용 // Canvas 디스플레이 설정 시 해당 옵션 적용
const frontSettings = async () => { const frontSettings = async () => {
@ -260,7 +426,7 @@ export function useCanvasSetting() {
// 'lineDisplay' 지붕선 표시 'roof', POLYGON_TYPE.ROOF // 'lineDisplay' 지붕선 표시 'roof', POLYGON_TYPE.ROOF
// 'wordDisplay' 문자 표시 // 'wordDisplay' 문자 표시
// 'circuitNumDisplay' 회로번호 표시 // 'circuitNumDisplay' 회로번호 표시
// 'flowDisplay' 흐름방향 표시 'arrow' // 'flowDisplay' 흐름방향 표시 'arrow', 'flowText'
// 'trestleDisplay' 가대 표시 // 'trestleDisplay' 가대 표시
// 'imageDisplay' 이미지 표시 // 'imageDisplay' 이미지 표시
// 'totalDisplay' 집계표 표시 // 'totalDisplay' 집계표 표시
@ -277,13 +443,13 @@ export function useCanvasSetting() {
optionName = ['outerLine', POLYGON_TYPE.WALL] optionName = ['outerLine', POLYGON_TYPE.WALL]
break break
case 'gridDisplay': //그리드 표시 case 'gridDisplay': //그리드 표시
optionName = ['lindGrid', 'dotGrid'] optionName = ['lindGrid', 'dotGrid', 'tempGrid']
break break
case 'lineDisplay': //지붕선 표시 case 'lineDisplay': //지붕선 표시
optionName = ['roof', POLYGON_TYPE.ROOF] optionName = ['roof', POLYGON_TYPE.ROOF]
break break
case 'wordDisplay': //문자 표시 case 'wordDisplay': //문자 표시
optionName = ['6'] optionName = ['commonText']
break break
case 'circuitNumDisplay': //회로번호 표시 case 'circuitNumDisplay': //회로번호 표시
optionName = ['7'] optionName = ['7']
@ -304,8 +470,8 @@ export function useCanvasSetting() {
// 표시 선택 상태(true/false) // 표시 선택 상태(true/false)
optionSelected = option1[i].selected optionSelected = option1[i].selected
canvas //canvas.getObjects() >> canvasObjects
.getObjects() canvasObjects
.filter((obj) => optionName.includes(obj.name)) .filter((obj) => optionName.includes(obj.name))
//.filter((obj) => obj.name === optionName) //.filter((obj) => obj.name === optionName)
.forEach((obj) => { .forEach((obj) => {
@ -313,6 +479,8 @@ export function useCanvasSetting() {
//obj.set({ visible: !obj.visible }) //obj.set({ visible: !obj.visible })
}) })
canvas?.renderAll()
// console.log( // console.log(
// 'optionName', // 'optionName',
// optionName, // optionName,
@ -322,14 +490,31 @@ export function useCanvasSetting() {
} }
return { return {
canvas,
settingModalFirstOptions, settingModalFirstOptions,
setSettingModalFirstOptions, setSettingModalFirstOptions,
settingModalSecondOptions, settingModalSecondOptions,
setSettingModalSecondOptions, setSettingModalSecondOptions,
adsorptionPointMode, adsorptionPointMode,
setAdsorptionPointMode, setAdsorptionPointMode,
adsorptionRange,
setAdsorptionRange,
fetchSettings, fetchSettings,
onClickOption, //onClickOption,
frontSettings, frontSettings,
globalFont,
setGlobalFont,
selectedFont,
setSelectedFont,
selectedFontWeight,
setSelectedFontWeight,
selectedFontSize,
setSelectedFontSize,
selectedFontColor,
setSelectedFontColor,
dimensionLineSettings,
setDimensionLineSettings,
planSizeSettingMode,
setPlanSizeSettingMode,
} }
} }

View File

@ -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 }
}

View File

@ -15,20 +15,19 @@ import {
outerLineLength2State, outerLineLength2State,
outerLineTypeState, outerLineTypeState,
} from '@/store/outerLineAtom' } 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 { fabric } from 'fabric'
import { useAdsorptionPoint } from '@/hooks/useAdsorptionPoint' import { useAdsorptionPoint } from '@/hooks/useAdsorptionPoint'
import { useSwal } from '@/hooks/useSwal' import { useSwal } from '@/hooks/useSwal'
import { booleanPointInPolygon } from '@turf/turf'
import { usePopup } from '@/hooks/usePopup' import { usePopup } from '@/hooks/usePopup'
import { calculateAngle, isSamePoint } from '@/util/qpolygon-utils' import { calculateAngle, isSamePoint } from '@/util/qpolygon-utils'
import { QPolygon } from '@/components/fabric/QPolygon'
import { POLYGON_TYPE } from '@/common/common' import { POLYGON_TYPE } from '@/common/common'
// 보조선 작성 // 보조선 작성
export function useAuxiliaryDrawing(id) { export function useAuxiliaryDrawing(id) {
const canvas = useRecoilValue(canvasState) const canvas = useRecoilValue(canvasState)
const { addCanvasMouseEventListener, addDocumentEventListener, removeMouseLine, initEvent } = useEvent() const { addCanvasMouseEventListener, addDocumentEventListener, removeMouseLine, initEvent } = useEvent()
// const { addCanvasMouseEventListener, addDocumentEventListener, removeMouseLine, initEvent } = useContext(EventContext)
const { getIntersectMousePoint } = useMouse() const { getIntersectMousePoint } = useMouse()
const { addLine, removeLine } = useLine() const { addLine, removeLine } = useLine()
const { tempGridMode } = useTempGrid() const { tempGridMode } = useTempGrid()
@ -94,7 +93,7 @@ export function useAuxiliaryDrawing(id) {
addCanvasMouseEventListener('mouse:move', mouseMove) addCanvasMouseEventListener('mouse:move', mouseMove)
addCanvasMouseEventListener('mouse:down', mouseDown) addCanvasMouseEventListener('mouse:down', mouseDown)
addDocumentEventListener('contextmenu', document, cutAuxiliary) // addDocumentEventListener('contextmenu', document, cutAuxiliary)
addDocumentEventListener('keydown', document, keydown[type]) addDocumentEventListener('keydown', document, keydown[type])
return () => { return () => {
@ -122,6 +121,21 @@ export function useAuxiliaryDrawing(id) {
setOuterLineDiagonalLength(0) 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 = { const keydown = {
outerLine: (e) => { outerLine: (e) => {
if (mousePointerArr.current.length === 0) { if (mousePointerArr.current.length === 0) {
@ -130,7 +144,7 @@ export function useAuxiliaryDrawing(id) {
// 포커스가 length1에 있지 않으면 length1에 포커스를 줌 // 포커스가 length1에 있지 않으면 length1에 포커스를 줌
const activeElem = document.activeElement const activeElem = document.activeElement
if (activeElem !== length1Ref.current) { if (activeElem !== length1Ref.current) {
length1Ref.current.focus() length1Ref?.current?.focus()
} }
const key = e.key const key = e.key
@ -180,7 +194,7 @@ export function useAuxiliaryDrawing(id) {
const activeElem = document.activeElement const activeElem = document.activeElement
if (activeElem !== length1Ref.current && activeElem !== length2Ref.current) { if (activeElem !== length1Ref.current && activeElem !== length2Ref.current) {
length1Ref.current.focus() length1Ref?.current?.focus()
} }
switch (key) { switch (key) {
@ -455,9 +469,24 @@ export function useAuxiliaryDrawing(id) {
name: 'auxiliaryLine', 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 = [] mousePointerArr.current = []
clear() clear()
if (hasSameLine) {
canvas.remove(line)
return
}
lineHistory.current.push(line)
} }
const mouseDown = (e) => { const mouseDown = (e) => {
@ -520,8 +549,24 @@ export function useAuxiliaryDrawing(id) {
otherAdsorptionPoints.push(intersectionPoint) 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 } let arrivalPoint = { x: pointer.x, y: pointer.y }
@ -780,7 +825,6 @@ export function useAuxiliaryDrawing(id) {
//lineHistory.current에 있는 선들 중 startPoint와 endPoint가 겹치는 line은 제거 //lineHistory.current에 있는 선들 중 startPoint와 endPoint가 겹치는 line은 제거
// 겹치는 선 하나는 canvas에서 제거한다. // 겹치는 선 하나는 canvas에서 제거한다.
const tempLines = [...lineHistory.current] const tempLines = [...lineHistory.current]
lineHistory.current = [] lineHistory.current = []
tempLines.forEach((line) => { tempLines.forEach((line) => {
@ -804,27 +848,30 @@ export function useAuxiliaryDrawing(id) {
const tempPolygonPoints = [...roofBase.points].map((obj) => { const tempPolygonPoints = [...roofBase.points].map((obj) => {
return { x: Math.round(obj.x), y: Math.round(obj.y) } 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 = const inPolygon1 =
tempPolygonPoints.some((point) => point.x === line.x1 && point.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: line.x1, y: line.y1 }) || roofBase.inPolygon({ x: Math.round(line.x1), y: Math.round(line.y1) }) ||
roofBase.lines.some((line) => isPointOnLine(line, { x: line.x1, y: line.y1 })) roofBase.lines.some((line) => isPointOnLine(line, { x: Math.round(line.x1), y: Math.round(line.y1) }))
const inPolygon2 = const inPolygon2 =
tempPolygonPoints.some((point) => point.x === line.x2 && point.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: line.x2, y: line.y2 }) || roofBase.inPolygon({ x: Math.round(line.x2), y: Math.round(line.y2) }) ||
roofBase.lines.some((line) => isPointOnLine(line, { x: line.x2, y: line.y2 })) roofBase.lines.some((line) => isPointOnLine(line, { x: Math.round(line.x2), y: Math.round(line.y2) }))
if (inPolygon1 && inPolygon2) { 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 return true
} }
}) })
roofBase.innerLines = [...roofInnerLines] roofBase.innerLines = lineHistory.current.length !== 0 ? [...roofInnerLines] : roofBase.innerLines
canvas.renderAll() canvas.renderAll()
}) })
closePopup(id) closePopup(id)
} }
@ -856,5 +903,8 @@ export function useAuxiliaryDrawing(id) {
handleRollback, handleRollback,
buttonAct, buttonAct,
setButtonAct, setButtonAct,
move,
copy,
cutAuxiliary,
} }
} }

View File

@ -16,6 +16,7 @@ export function useEavesGableEdit(id) {
const canvas = useRecoilValue(canvasState) const canvas = useRecoilValue(canvasState)
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { addCanvasMouseEventListener, initEvent } = useEvent() const { addCanvasMouseEventListener, initEvent } = useEvent()
// const { addCanvasMouseEventListener, initEvent } = useContext(EventContext)
const { closePopup } = usePopup() const { closePopup } = usePopup()
const TYPES = { const TYPES = {
EAVES: 'eaves', EAVES: 'eaves',

View File

@ -6,7 +6,6 @@ import { useEffect, useRef, useState } from 'react'
import { useEvent } from '@/hooks/useEvent' import { useEvent } from '@/hooks/useEvent'
import { POLYGON_TYPE } from '@/common/common' import { POLYGON_TYPE } from '@/common/common'
import { OUTER_LINE_TYPE } from '@/store/outerLineAtom' import { OUTER_LINE_TYPE } from '@/store/outerLineAtom'
import { QLine } from '@/components/fabric/QLine'
//동선이동 형 올림 내림 //동선이동 형 올림 내림
export function useMovementSetting(id) { export function useMovementSetting(id) {
@ -16,6 +15,7 @@ export function useMovementSetting(id) {
} }
const canvas = useRecoilValue(canvasState) const canvas = useRecoilValue(canvasState)
const { initEvent, addCanvasMouseEventListener } = useEvent() const { initEvent, addCanvasMouseEventListener } = useEvent()
// const { initEvent, addCanvasMouseEventListener } = useContext(EventContext)
const { closePopup } = usePopup() const { closePopup } = usePopup()
const { getMessage } = useMessage() const { getMessage } = useMessage()
const currentObject = useRecoilValue(currentObjectState) const currentObject = useRecoilValue(currentObjectState)

View File

@ -1,6 +1,6 @@
import { useEffect, useRef } from 'react' import { useContext, useEffect, useRef } from 'react'
import { distanceBetweenPoints } from '@/util/canvas-util' import { distanceBetweenPoints } from '@/util/canvas-util'
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil' import { useRecoilState, useRecoilValue, useResetRecoilState, useSetRecoilState } from 'recoil'
import { import {
adsorptionPointAddModeState, adsorptionPointAddModeState,
adsorptionPointModeState, adsorptionPointModeState,
@ -31,6 +31,7 @@ import { fabric } from 'fabric'
import { outlineDisplaySelector } from '@/store/settingAtom' import { outlineDisplaySelector } from '@/store/settingAtom'
import { usePopup } from '@/hooks/usePopup' import { usePopup } from '@/hooks/usePopup'
import PropertiesSetting from '@/components/floor-plan/modal/outerlinesetting/PropertiesSetting' import PropertiesSetting from '@/components/floor-plan/modal/outerlinesetting/PropertiesSetting'
import { EventContext } from '@/app/floor-plan/EventProvider'
//외벽선 그리기 //외벽선 그리기
export function useOuterLineWall(id, propertiesId) { export function useOuterLineWall(id, propertiesId) {
@ -43,6 +44,14 @@ export function useOuterLineWall(id, propertiesId) {
removeAllDocumentEventListeners, removeAllDocumentEventListeners,
removeMouseEvent, removeMouseEvent,
} = useEvent() } = useEvent()
// const {
// initEvent,
// // addCanvasMouseEventListener,
// // addDocumentEventListener,
// removeAllMouseEventListeners,
// removeAllDocumentEventListeners,
// removeMouseEvent,
// } = useContext(EventContext)
const { getIntersectMousePoint } = useMouse() const { getIntersectMousePoint } = useMouse()
const { addLine, removeLine } = useLine() const { addLine, removeLine } = useLine()
const { tempGridMode } = useTempGrid() const { tempGridMode } = useTempGrid()
@ -65,6 +74,7 @@ export function useOuterLineWall(id, propertiesId) {
const [arrow1, setArrow1] = useRecoilState(outerLineArrow1State) const [arrow1, setArrow1] = useRecoilState(outerLineArrow1State)
const [arrow2, setArrow2] = useRecoilState(outerLineArrow2State) const [arrow2, setArrow2] = useRecoilState(outerLineArrow2State)
const [points, setPoints] = useRecoilState(outerLinePointsState) const [points, setPoints] = useRecoilState(outerLinePointsState)
const resetPoints = useResetRecoilState(outerLinePointsState)
const [type, setType] = useRecoilState(outerLineTypeState) const [type, setType] = useRecoilState(outerLineTypeState)
const [angle1, setAngle1] = useRecoilState(outerLineAngle1State) const [angle1, setAngle1] = useRecoilState(outerLineAngle1State)
const [angle2, setAngle2] = useRecoilState(outerLineAngle2State) const [angle2, setAngle2] = useRecoilState(outerLineAngle2State)
@ -88,6 +98,13 @@ export function useOuterLineWall(id, propertiesId) {
clear() clear()
return () => { return () => {
initEvent() initEvent()
canvas
.getObjects()
.filter((obj) => obj.name === 'startPoint')
.forEach((obj) => {
canvas.remove(obj)
})
} }
}, [verticalHorizontalMode, points, adsorptionPointAddMode, adsorptionPointMode, adsorptionRange, interval, tempGridMode]) }, [verticalHorizontalMode, points, adsorptionPointAddMode, adsorptionPointMode, adsorptionRange, interval, tempGridMode])

View File

@ -125,6 +125,12 @@ export function usePropertiesSetting(id) {
} }
const lines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') 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) => { lines.forEach((line) => {
line.set({ line.set({
attributes: line.attributes ? line.attributes : { offset: 0, type: LINE_TYPE.WALLLINE.WALL }, attributes: line.attributes ? line.attributes : { offset: 0, type: LINE_TYPE.WALLLINE.WALL },

View File

@ -1,5 +1,5 @@
import { useRecoilValue } from 'recoil' import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
import { canvasState, currentObjectState } from '@/store/canvasAtom' import { canvasState, currentMenuState, currentObjectState } from '@/store/canvasAtom'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { setSurfaceShapePattern } from '@/util/canvas-util' import { setSurfaceShapePattern } from '@/util/canvas-util'
import { useSwal } from '@/hooks/useSwal' import { useSwal } from '@/hooks/useSwal'
@ -10,17 +10,22 @@ import { POLYGON_TYPE } from '@/common/common'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
import ActualSizeSetting from '@/components/floor-plan/modal/roofAllocation/ActualSizeSetting' import ActualSizeSetting from '@/components/floor-plan/modal/roofAllocation/ActualSizeSetting'
import { useMessage } from '@/hooks/useMessage' 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) { export function useRoofAllocationSetting(id) {
const canvas = useRecoilValue(canvasState) const canvas = useRecoilValue(canvasState)
const roofDisplay = useRecoilValue(roofDisplaySelector) const roofDisplay = useRecoilValue(roofDisplaySelector)
const { drawDirectionArrow, addLengthText, splitPolygonWithLines } = usePolygon() const { drawDirectionArrow, addLengthText, splitPolygonWithLines, splitPolygonWithSeparate } = usePolygon()
const [popupId, setPopupId] = useState(uuidv4()) const [popupId, setPopupId] = useState(uuidv4())
const { addPopup, closePopup, closeAll } = usePopup() const { addPopup, closePopup, closeAll } = usePopup()
const { getMessage } = useMessage() const { getMessage } = useMessage()
const currentObject = useRecoilValue(currentObjectState) const currentObject = useRecoilValue(currentObjectState)
const { swalFire } = useSwal() const { swalFire } = useSwal()
const { setMenuNumber } = useCanvasMenu()
const setMenuType = useSetRecoilState(menuTypeState)
const roofMaterials = [ const roofMaterials = [
{ {
id: 'A', 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({ currentObject.set({
strokeWidth: 4, strokeWidth: 4,
stroke: '#EA10AC', stroke: '#EA10AC',
@ -132,6 +137,9 @@ export function useRoofAllocationSetting(id) {
setValues(values.filter((value) => value.id !== id)) setValues(values.filter((value) => value.id !== id))
} }
const { handleMenu } = useMenu()
const [currentMenu, setCurrentMenu] = useRecoilState(currentMenuState)
// 선택한 지붕재로 할당 // 선택한 지붕재로 할당
const handleSave = () => { const handleSave = () => {
// 모두 actualSize 있으면 바로 적용 없으면 actualSize 설정 // 모두 actualSize 있으면 바로 적용 없으면 actualSize 설정
@ -176,20 +184,23 @@ export function useRoofAllocationSetting(id) {
} }
const apply = () => { const apply = () => {
const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF) const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF && !obj.isFixed)
const wallLines = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.WALL) const wallLines = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.WALL)
roofBases.forEach((roofBase) => { roofBases.forEach((roofBase) => {
try { try {
if (roofBase.separatePolygon.length > 0) {
splitPolygonWithSeparate(roofBase.separatePolygon)
} else {
splitPolygonWithLines(roofBase) splitPolygonWithLines(roofBase)
}
} catch (e) { } catch (e) {
return return
} }
roofBase.innerLines.forEach((line) => { roofBase.innerLines.forEach((line) => {
canvas.remove(line) canvas.remove(line)
}) })
// canvas.remove(roofBase) canvas.remove(roofBase)
}) })
wallLines.forEach((wallLine) => { wallLines.forEach((wallLine) => {
@ -199,6 +210,10 @@ export function useRoofAllocationSetting(id) {
const roofs = canvas.getObjects().filter((obj) => obj.name === 'roof') const roofs = canvas.getObjects().filter((obj) => obj.name === 'roof')
roofs.forEach((roof) => { roofs.forEach((roof) => {
if (roof.isFixed) return
roof.set({
isFixed: true,
})
setSurfaceShapePattern(roof, roofDisplay.column) setSurfaceShapePattern(roof, roofDisplay.column)
drawDirectionArrow(roof) drawDirectionArrow(roof)
}) })
@ -209,6 +224,8 @@ export function useRoofAllocationSetting(id) {
}) })
setEditingLines([]) setEditingLines([])
closeAll() closeAll()
setMenuNumber(3)
setMenuType('surface')
} }
const setLineSize = (id, size) => { const setLineSize = (id, size) => {

View File

@ -26,6 +26,7 @@ export function useRoofShapePassivitySetting(id) {
const { showLine, hideLine, addPitchTextsByOuterLines } = useLine() const { showLine, hideLine, addPitchTextsByOuterLines } = useLine()
const { swalFire } = useSwal() const { swalFire } = useSwal()
const { addCanvasMouseEventListener, initEvent } = useEvent() const { addCanvasMouseEventListener, initEvent } = useEvent()
// const { addCanvasMouseEventListener, initEvent } = useContext(EventContext)
const { drawRoofPolygon } = useMode() const { drawRoofPolygon } = useMode()
const { addPolygonByLines } = usePolygon() const { addPolygonByLines } = usePolygon()
const currentObject = useRecoilValue(currentObjectState) const currentObject = useRecoilValue(currentObjectState)
@ -202,7 +203,11 @@ export function useRoofShapePassivitySetting(id) {
wall = addPolygonByLines(lines, { name: POLYGON_TYPE.WALL, fill: 'transparent', stroke: 'black' }) wall = addPolygonByLines(lines, { name: POLYGON_TYPE.WALL, fill: 'transparent', stroke: 'black' })
} else { } 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) => { lines.forEach((line, idx) => {
line.attributes = initLines.current[idx].attributes line.attributes = initLines.current[idx].attributes
}) })

View File

@ -377,20 +377,20 @@ export function useRoofShapeSetting(id) {
} }
// 기존 wallLine, roofBase 제거 // 기존 wallLine, roofBase 제거
/*canvas canvas
.getObjects() .getObjects()
.filter((obj) => obj.name === POLYGON_TYPE.WALL) .filter((obj) => obj.name === POLYGON_TYPE.WALL)
.forEach((line) => { .forEach((line) => {
canvas.remove(line) canvas.remove(line)
})*/ })
/*canvas canvas
.getObjects() .getObjects()
.filter((obj) => obj.name === POLYGON_TYPE.ROOF) .filter((obj) => obj.name === POLYGON_TYPE.ROOF && !obj.isFixed)
.forEach((obj) => { .forEach((obj) => {
canvas.remove(...obj.innerLines) canvas.remove(...obj.innerLines)
canvas.remove(obj) canvas.remove(obj)
})*/ })
const polygon = addPolygonByLines(outerLines, { name: POLYGON_TYPE.WALL, direction }) const polygon = addPolygonByLines(outerLines, { name: POLYGON_TYPE.WALL, direction })
polygon.lines = [...outerLines] polygon.lines = [...outerLines]

View File

@ -15,6 +15,7 @@ export function useWallLineOffsetSetting(id) {
const { closePopup } = usePopup() const { closePopup } = usePopup()
const { swalFire } = useSwal() const { swalFire } = useSwal()
const { addCanvasMouseEventListener, initEvent } = useEvent() const { addCanvasMouseEventListener, initEvent } = useEvent()
// const { addCanvasMouseEventListener, initEvent } = useContext(EventContext)
const wallLineEditRef = useRef(null) const wallLineEditRef = useRef(null)
const length1Ref = useRef(null) const length1Ref = useRef(null)
const length2Ref = useRef(null) const length2Ref = useRef(null)

View File

@ -33,12 +33,15 @@ import { POLYGON_TYPE } from '@/common/common'
import { usePopup } from '@/hooks/usePopup' import { usePopup } from '@/hooks/usePopup'
import { roofDisplaySelector } from '@/store/settingAtom' import { roofDisplaySelector } from '@/store/settingAtom'
// 면형상 배치 // 면형상 배치
export function usePlacementShapeDrawing(id) { export function usePlacementShapeDrawing(id) {
const canvas = useRecoilValue(canvasState) const canvas = useRecoilValue(canvasState)
const roofDisplay = useRecoilValue(roofDisplaySelector) const roofDisplay = useRecoilValue(roofDisplaySelector)
const { addCanvasMouseEventListener, addDocumentEventListener, removeAllMouseEventListeners, removeAllDocumentEventListeners, removeMouseEvent } = const { addCanvasMouseEventListener, addDocumentEventListener, removeAllMouseEventListeners, removeAllDocumentEventListeners, removeMouseEvent } =
useEvent() useEvent()
// const { addCanvasMouseEventListener, addDocumentEventListener, removeAllMouseEventListeners, removeAllDocumentEventListeners, removeMouseEvent } =
// useContext(EventContext)
const { getIntersectMousePoint } = useMouse() const { getIntersectMousePoint } = useMouse()
const { addLine, removeLine } = useLine() const { addLine, removeLine } = useLine()
const { addPolygonByLines, drawDirectionArrow } = usePolygon() const { addPolygonByLines, drawDirectionArrow } = usePolygon()
@ -429,56 +432,104 @@ export function usePlacementShapeDrawing(id) {
if (prev.length === 0) { if (prev.length === 0) {
return [] 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 === '←') { } else if (arrow1Value === '↓' && arrow2Value === '←') {
setPoints((prev) => { setPoints((prev) => {
if (prev.length === 0) { if (prev.length === 0) {
return [] 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 === '→') { } else if (arrow1Value === '↑' && arrow2Value === '→') {
setPoints((prev) => { setPoints((prev) => {
if (prev.length === 0) { if (prev.length === 0) {
return [] 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 === '←') { } else if (arrow1Value === '↑' && arrow2Value === '←') {
setPoints((prev) => { setPoints((prev) => {
if (prev.length === 0) { if (prev.length === 0) {
return [] 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 === '↓') { } else if (arrow1Value === '→' && arrow2Value === '↓') {
setPoints((prev) => { setPoints((prev) => {
if (prev.length === 0) { if (prev.length === 0) {
return [] 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 === '↑') { } else if (arrow1Value === '→' && arrow2Value === '↑') {
setPoints((prev) => { setPoints((prev) => {
if (prev.length === 0) { if (prev.length === 0) {
return [] 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 === '↓') { } else if (arrow1Value === '←' && arrow2Value === '↓') {
setPoints((prev) => { setPoints((prev) => {
if (prev.length === 0) { if (prev.length === 0) {
return [] 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 === '↑') { } else if (arrow1Value === '←' && arrow2Value === '↑') {
setPoints((prev) => { setPoints((prev) => {
if (prev.length === 0) { if (prev.length === 0) {
return [] 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) { if (prev.length === 0) {
return [] 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 === '←') { } else if (arrow1Value === '↓' && arrow2Value === '←') {
setPoints((prev) => { setPoints((prev) => {
if (prev.length === 0) { if (prev.length === 0) {
return [] 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 === '→') { } else if (arrow1Value === '↑' && arrow2Value === '→') {
setPoints((prev) => { setPoints((prev) => {
if (prev.length === 0) { if (prev.length === 0) {
return [] 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 === '←') { } else if (arrow1Value === '↑' && arrow2Value === '←') {
setPoints((prev) => { setPoints((prev) => {
if (prev.length === 0) { if (prev.length === 0) {
return [] 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 === '↓') { } else if (arrow1Value === '→' && arrow2Value === '↓') {
setPoints((prev) => { setPoints((prev) => {
if (prev.length === 0) { if (prev.length === 0) {
return [] 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 === '↑') { } else if (arrow1Value === '→' && arrow2Value === '↑') {
setPoints((prev) => { setPoints((prev) => {

View File

@ -2,7 +2,7 @@
import { useRecoilValue } from 'recoil' import { useRecoilValue } from 'recoil'
import { canvasState, globalPitchState } from '@/store/canvasAtom' 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 { getIntersectionPoint, setSurfaceShapePattern } from '@/util/canvas-util'
import { degreesToRadians } from '@turf/turf' import { degreesToRadians } from '@turf/turf'
import { QPolygon } from '@/components/fabric/QPolygon' import { QPolygon } from '@/components/fabric/QPolygon'
@ -27,6 +27,7 @@ export function useSurfaceShapeBatch() {
const slope = useRecoilValue(slopeSelector(globalPitch)) const slope = useRecoilValue(slopeSelector(globalPitch))
const { swalFire } = useSwal() const { swalFire } = useSwal()
const { addCanvasMouseEventListener, initEvent } = useEvent() const { addCanvasMouseEventListener, initEvent } = useEvent()
// const { addCanvasMouseEventListener, initEvent } = useContext(EventContext)
const { closePopup } = usePopup() const { closePopup } = usePopup()
const applySurfaceShape = (surfaceRefs, selectedType, id) => { const applySurfaceShape = (surfaceRefs, selectedType, id) => {
@ -102,6 +103,15 @@ export function useSurfaceShapeBatch() {
canvas?.add(obj) canvas?.add(obj)
canvas?.renderAll()
closePopup(id)
})
addCanvasMouseEventListener('mouse:down', (e) => {
isDrawing = false
canvas?.remove(obj)
//각도 추가 //각도 추가
let originAngle = 0 //기본 남쪽 let originAngle = 0 //기본 남쪽
let direction = 'south' let direction = 'south'
@ -119,21 +129,31 @@ export function useSurfaceShapeBatch() {
direction = 'north' direction = 'north'
} }
obj.set({ direction: direction }) //회전, flip등이 먹은 기준으로 새로생성
obj.set({ originAngle: originAngle }) const batchSurface = new QPolygon(obj.getCurrentPoints(), {
fill: 'transparent',
canvas?.renderAll() stroke: 'red',
closePopup(id) 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)
addCanvasMouseEventListener('mouse:down', (e) => { setSurfaceShapePattern(batchSurface, roofDisplay.column)
isDrawing = false drawDirectionArrow(batchSurface)
obj.set('name', POLYGON_TYPE.ROOF)
obj.set('surfaceId', surfaceId)
initEvent()
setSurfaceShapePattern(obj, roofDisplay.column)
closePopup(id) closePopup(id)
drawDirectionArrow(obj) initEvent()
}) })
} }
} }
@ -307,8 +327,14 @@ export function useSurfaceShapeBatch() {
const angleInRadians = Math.asin(length2 / length3) const angleInRadians = Math.asin(length2 / length3)
points = [ points = [
{ x: pointer.x - length1 / 2, y: pointer.y + length2 / 2 }, { 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 }, { 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, 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 },
{ 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,
{ x: pointer.x - (length1 + length2 + length3) / 2 + length1 + length2, y: pointer.y + (length4 + length5) / 2 - length5 + length5 }, 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, x: pointer.x - (length1 + length2 + length3) / 2 + length1 + length2 + length3,
y: pointer.y + (length4 + length5) / 2 - length5 + length5, 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 },
{ x: pointer.x - length1 / 2 + length1, y: pointer.y + length4 / 2 - length3 }, { 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 },
{ 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 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 },
{ x: pointer.x - length1 / 2 + length1, y: pointer.y + length4 / 2 - length4 }, { 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 },
{ 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 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 },
{ x: pointer.x + length1 / 2 - length1, y: pointer.y + length4 / 2 - length5 }, { 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 },
{ 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 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 },
{ x: pointer.x - length1 / 2 + length1, y: pointer.y + length4 / 2 - length5 }, { 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 },
{ 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 break
} }
@ -386,7 +445,10 @@ export function useSurfaceShapeBatch() {
const leftAngle = Math.acos((length1 - length2) / 2 / leftHypotenuse) const leftAngle = Math.acos((length1 - length2) / 2 / leftHypotenuse)
points = [ 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 },
{ 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', fill: 'transparent',
stroke: 'black', //black stroke: 'black', //black
strokeWidth: 2, strokeWidth: 1,
selectable: true, selectable: false,
fontSize: 0, fontSize: 0,
}, },
) )
@ -429,6 +491,7 @@ export function useSurfaceShapeBatch() {
const scale = (length1 - length2) / coord.x const scale = (length1 - length2) / coord.x
tmpPolygon.set({ scaleX: scale }) tmpPolygon.set({ scaleX: scale })
tmpPolygon.setViewLengthText(false)
pointsArray[0].x = 0 pointsArray[0].x = 0
pointsArray[0].y = length3 //바닥면부터 시작하게 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 },
{ x: pointer.x - length1 / 2 + length1, y: pointer.y + length4 / 2 - length4 + 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 + 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 break
} }
@ -584,18 +650,6 @@ export function useSurfaceShapeBatch() {
text: '배치면 내용을 전부 삭제하시겠습니까?', text: '배치면 내용을 전부 삭제하시겠습니까?',
type: 'confirm', type: 'confirm',
confirmFn: () => { 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() canvas.clear()
swalFire({ text: '삭제 완료 되었습니다.' }) swalFire({ text: '삭제 완료 되었습니다.' })
}, },
@ -661,34 +715,6 @@ export function useSurfaceShapeBatch() {
return groupObjectsArray 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 moveSurfaceShapeBatch = () => {
const roof = canvas.getActiveObject() const roof = canvas.getActiveObject()
@ -828,78 +854,15 @@ export function useSurfaceShapeBatch() {
canvas.renderAll() canvas.renderAll()
} }
const surfaceShapeActualSize = () => { const changeSurfaceLinePropertyEvent = () => {
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) => {
let tmpLines = [] let tmpLines = []
const roof = canvas.getActiveObject()
if (roof) {
roof.set({ roof.set({
selectable: false, selectable: false,
}) })
canvas.discardActiveObject()
roof.lines.forEach((obj, index) => { roof.lines.forEach((obj, index) => {
const tmpLine = new QLine([obj.x1, obj.y1, obj.x2, obj.y2], { const tmpLine = new QLine([obj.x1, obj.y1, obj.x2, obj.y2], {
...obj, ...obj,
@ -907,7 +870,7 @@ export function useSurfaceShapeBatch() {
strokeWidth: 8, strokeWidth: 8,
selectable: true, selectable: true,
name: 'lineProperty', name: 'lineProperty',
index: index, lineIndex: index,
}) })
tmpLines.push(tmpLine) tmpLines.push(tmpLine)
@ -916,18 +879,16 @@ export function useSurfaceShapeBatch() {
addCanvasMouseEventListener('mouse:down', (e) => { addCanvasMouseEventListener('mouse:down', (e) => {
const selectedLine = e.target const selectedLine = e.target
if (selectedLine) { if (selectedLine && selectedLine.name !== 'roof') {
selectedLine.set({
stroke: 'red',
name: 'selectedLineProperty',
})
tmpLines.forEach((line) => { tmpLines.forEach((line) => {
if (line.index !== selectedLine.index) {
line.set({ line.set({
stroke: 'rgb(3, 255, 0)', stroke: 'rgb(3, 255, 0)',
name: 'lineProperty', name: 'lineProperty',
}) })
} })
selectedLine.set({
stroke: 'red',
name: 'selectedLineProperty',
}) })
} else { } else {
tmpLines.forEach((line) => { tmpLines.forEach((line) => {
@ -941,23 +902,27 @@ export function useSurfaceShapeBatch() {
canvas.renderAll() canvas.renderAll()
} }
canvas.discardActiveObject()
}
const changeSurfaceLineProperty = (property) => { const changeSurfaceLineProperty = (property, roof) => {
console.log(property)
if (!property) { if (!property) {
swalFire({ text: getMessage('modal.line.property.change'), icon: 'error' }) swalFire({ text: getMessage('modal.line.property.change'), icon: 'error' })
return return
} }
const selectedLine = canvas.getActiveObjects() const selectedLine = canvas.getActiveObjects()[0] //배열로 떨어짐 한개만 선택가능
if (selectedLine && selectedLine[0].name === 'selectedLineProperty') { if (selectedLine && selectedLine.name === 'selectedLineProperty') {
swalFire({ swalFire({
text: getMessage('modal.line.property.change.confirm'), text: getMessage('modal.line.property.change.confirm'),
type: 'confirm', type: 'confirm',
confirmFn: () => { confirmFn: () => {
selectedLine.set({ const lineIndex = selectedLine.lineIndex
roof.lines[lineIndex].attributes = {
...roof.lines[lineIndex].attributes,
type: property.value, type: property.value,
}) }
canvas.renderAll()
}, },
}) })
} else { } else {
@ -965,22 +930,70 @@ export function useSurfaceShapeBatch() {
} }
} }
const changeSurfaceFlowDirection = (roof, direction, orientation) => { const changeSurfaceLinePropertyReset = (roof) => {
roof.set({ const lines = canvas.getObjects().filter((obj) => obj.name === 'lineProperty' || obj.name === 'selectedLineProperty')
direction: direction,
lines.forEach((line) => {
canvas.remove(line)
}) })
drawDirectionArrow(roof)
if (roof) {
roof.set({
selectable: true,
})
}
canvas?.renderAll() 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 { return {
applySurfaceShape, applySurfaceShape,
deleteAllSurfacesAndObjects, deleteAllSurfacesAndObjects,
moveSurfaceShapeBatch, moveSurfaceShapeBatch,
resizeSurfaceShapeBatch, resizeSurfaceShapeBatch,
surfaceShapeActualSize,
changeSurfaceFlowDirection,
changeSurfaceLinePropertyEvent, changeSurfaceLinePropertyEvent,
changeSurfaceLineProperty, changeSurfaceLineProperty,
changeSurfaceLinePropertyReset,
} }
} }

View File

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

View File

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

View File

@ -1,8 +1,7 @@
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil' 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 { useEffect, useState } from 'react'
import { MENU } from '@/common/common' import { MENU } from '@/common/common'
import AuxiliaryMove from '@/components/floor-plan/modal/auxiliary/AuxiliaryMove'
import AuxiliarySize from '@/components/floor-plan/modal/auxiliary/AuxiliarySize' import AuxiliarySize from '@/components/floor-plan/modal/auxiliary/AuxiliarySize'
import { usePopup } from '@/hooks/usePopup' import { usePopup } from '@/hooks/usePopup'
import { v4 as uuidv4 } from 'uuid' 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 ColorPickerModal from '@/components/common/color-picker/ColorPickerModal'
import { gridColorState } from '@/store/gridAtom' import { gridColorState } from '@/store/gridAtom'
import { contextPopupPositionState, contextPopupState } from '@/store/popupAtom' 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 SizeSetting from '@/components/floor-plan/modal/object/SizeSetting'
import RoofMaterialSetting from '@/components/floor-plan/modal/object/RoofMaterialSetting' import RoofMaterialSetting from '@/components/floor-plan/modal/object/RoofMaterialSetting'
import DormerOffset from '@/components/floor-plan/modal/object/DormerOffset' import DormerOffset from '@/components/floor-plan/modal/object/DormerOffset'
@ -24,7 +23,6 @@ import { useCommonUtils } from './common/useCommonUtils'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { useCanvasEvent } from '@/hooks/useCanvasEvent' import { useCanvasEvent } from '@/hooks/useCanvasEvent'
import { contextMenuListState, contextMenuState } from '@/store/contextMenu' import { contextMenuListState, contextMenuState } from '@/store/contextMenu'
import ImageSizeSetting from '@/components/floor-plan/modal/image/ImageSizeSetting'
import PanelEdit from '@/components/floor-plan/modal/module/PanelEdit' import PanelEdit from '@/components/floor-plan/modal/module/PanelEdit'
import DimensionLineSetting from '@/components/floor-plan/modal/dimensionLine/DimensionLineSetting' import DimensionLineSetting from '@/components/floor-plan/modal/dimensionLine/DimensionLineSetting'
import ColumnRemove from '@/components/floor-plan/modal/module/column/ColumnRemove' import ColumnRemove from '@/components/floor-plan/modal/module/column/ColumnRemove'
@ -34,8 +32,12 @@ import RowInsert from '@/components/floor-plan/modal/module/row/RowInsert'
import CircuitNumberEdit from '@/components/floor-plan/modal/module/CircuitNumberEdit' import CircuitNumberEdit from '@/components/floor-plan/modal/module/CircuitNumberEdit'
import { useObjectBatch } from '@/hooks/object/useObjectBatch' import { useObjectBatch } from '@/hooks/object/useObjectBatch'
import { useSurfaceShapeBatch } from '@/hooks/surface/useSurfaceShapeBatch' import { useSurfaceShapeBatch } from '@/hooks/surface/useSurfaceShapeBatch'
import { fontSelector, globalFontAtom } from '@/store/fontAtom'
import { useLine } from '@/hooks/useLine'
import { useSwal } from '@/hooks/useSwal'
export function useContextMenu() { export function useContextMenu() {
const canvas = useRecoilValue(canvasState)
const currentMenu = useRecoilValue(currentMenuState) // 현재 메뉴 const currentMenu = useRecoilValue(currentMenuState) // 현재 메뉴
const setContextPopupPosition = useSetRecoilState(contextPopupPositionState) // 현재 메뉴 const setContextPopupPosition = useSetRecoilState(contextPopupPositionState) // 현재 메뉴
const [contextMenu, setContextMenu] = useRecoilState(contextMenuListState) // 메뉴.object 별 context menu const [contextMenu, setContextMenu] = useRecoilState(contextMenuListState) // 메뉴.object 별 context menu
@ -51,7 +53,12 @@ export function useContextMenu() {
const [column, setColumn] = useState(null) const [column, setColumn] = useState(null)
const { handleZoomClear } = useCanvasEvent() const { handleZoomClear } = useCanvasEvent()
const { moveObjectBatch } = useObjectBatch({}) 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 = () => { const currentMenuSetting = () => {
switch (currentMenu) { switch (currentMenu) {
case MENU.PLAN_DRAWING: case MENU.PLAN_DRAWING:
@ -95,11 +102,6 @@ export function useContextMenu() {
case MENU.ROOF_COVERING.DEFAULT: case MENU.ROOF_COVERING.DEFAULT:
setContextMenu([ setContextMenu([
[ [
{
id: 'refresh',
name: getMessage('refresh'),
fn: () => handleZoomClear(),
},
{ {
id: 'roofMaterialPlacement', id: 'roofMaterialPlacement',
name: getMessage('contextmenu.roof.material.placement'), name: getMessage('contextmenu.roof.material.placement'),
@ -121,11 +123,6 @@ export function useContextMenu() {
name: getMessage('contextmenu.wallline.remove'), name: getMessage('contextmenu.wallline.remove'),
fn: () => deleteOuterLineObject(), fn: () => deleteOuterLineObject(),
}, },
{
id: 'imageSizeEdit',
name: getMessage('modal.image.size.setting'),
component: <ImageSizeSetting id={popupId} />,
},
], ],
[ [
{ {
@ -137,30 +134,81 @@ export function useContextMenu() {
id: 'auxiliaryMove', id: 'auxiliaryMove',
name: `${getMessage('contextmenu.auxiliary.move')}(M)`, name: `${getMessage('contextmenu.auxiliary.move')}(M)`,
shortcut: ['m', 'M'], shortcut: ['m', 'M'],
component: <AuxiliaryMove id={popupId} />, component: <AuxiliaryEdit id={popupId} type={'move'} />,
}, },
{ {
id: 'auxiliaryCopy', id: 'auxiliaryCopy',
name: `${getMessage('contextmenu.auxiliary.copy')}(C)`, name: `${getMessage('contextmenu.auxiliary.copy')}(C)`,
shortcut: ['c', 'C'], shortcut: ['c', 'C'],
component: <AuxiliaryCopy id={popupId} />, component: <AuxiliaryEdit id={popupId} type={'copy'} />,
}, },
{ {
id: 'auxiliaryRemove', id: 'auxiliaryRemove',
shortcut: ['d', 'D'], shortcut: ['d', 'D'],
name: `${getMessage('contextmenu.auxiliary.remove')}(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', id: 'auxiliaryVerticalBisector',
name: getMessage('contextmenu.auxiliary.vertical.bisector'), name: getMessage('contextmenu.auxiliary.vertical.bisector'),
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: 'auxiliaryCut',
name: getMessage('contextmenu.auxiliary.cut'),
}, },
{ {
id: 'auxiliaryRemoveAll', id: 'auxiliaryRemoveAll',
name: getMessage('contextmenu.auxiliary.remove.all'), 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'], shortcut: ['c', 'C'],
name: `${getMessage('contextmenu.copy')}(C)`, name: `${getMessage('contextmenu.copy')}(C)`,
}, },
{
id: 'imageSizeEdit',
name: getMessage('modal.image.size.setting'),
component: <ImageSizeSetting id={popupId} />,
},
], ],
[ [
{ {
id: 'roofMaterialEdit', id: 'roofMaterialEdit',
name: getMessage('contextmenu.roof.material.edit'), name: getMessage('contextmenu.roof.material.edit'),
component: <RoofAllocationSetting id={popupId} />,
}, },
{ {
id: 'linePropertyEdit', id: 'linePropertyEdit',
@ -260,6 +304,7 @@ export function useContextMenu() {
useEffect(() => { useEffect(() => {
if (currentObject?.name) { if (currentObject?.name) {
console.log('object', currentObject)
switch (currentObject.name) { switch (currentObject.name) {
case 'triangleDormer': case 'triangleDormer':
case 'pentagonDormer': case 'pentagonDormer':
@ -304,11 +349,6 @@ export function useContextMenu() {
case 'roof': case 'roof':
setContextMenu([ setContextMenu([
[ [
{
id: 'surfaceShapeActualSize',
name: '면형상 실측',
fn: () => surfaceShapeActualSize(),
},
{ {
id: 'sizeEdit', id: 'sizeEdit',
name: '사이즈 변경', name: '사이즈 변경',
@ -429,7 +469,26 @@ export function useContextMenu() {
{ {
id: 'commonTextFontSetting', id: 'commonTextFontSetting',
name: getMessage('contextmenu.font.setting'), name: getMessage('contextmenu.font.setting'),
component: <FontSetting id={popupId} type={'commonText'} />, component: (
<FontSetting
id={popupId}
type={'commonText'}
font={commonTextFont}
onSave={(font) => {
setGlobalFont((prev) => {
return {
...prev,
commonText: {
fontFamily: font.fontFamily,
fontWeight: font.fontWeight,
fontSize: font.fontSize,
fontColor: font.fontColor,
},
}
})
}}
/>
),
}, },
{ {
id: 'commonTextEdit', id: 'commonTextEdit',
@ -440,19 +499,24 @@ export function useContextMenu() {
]) ])
break break
case 'lineGrid': case 'lineGrid':
case 'dotGrid':
case 'tempGrid':
setContextMenu([ setContextMenu([
[ [
{ {
id: 'gridMove', id: 'gridMove',
name: getMessage('modal.grid.move'), name: getMessage('modal.grid.move'),
component: <GridMove id={popupId} />,
}, },
{ {
id: 'gridCopy', id: 'gridCopy',
name: getMessage('modal.grid.copy'), name: getMessage('modal.grid.copy'),
component: <GridCopy id={popupId} />,
}, },
{ {
id: 'gridColorEdit', id: 'gridColorEdit',
name: getMessage('contextmenu.grid.color.edit'), name: getMessage('contextmenu.grid.color.edit'),
component: <ColorPickerModal id={popupId} color={gridColor} setColor={setGridColor} />,
}, },
{ {
id: 'remove', id: 'remove',
@ -461,6 +525,7 @@ export function useContextMenu() {
{ {
id: 'removeAll', id: 'removeAll',
name: getMessage('contextmenu.remove.all'), name: getMessage('contextmenu.remove.all'),
fn: () => {},
}, },
], ],
]) ])

View File

@ -1,4 +1,4 @@
import { useEffect, useRef } from 'react' import { useCallback, useEffect, useRef } from 'react'
import { useRecoilValue, useSetRecoilState } from 'recoil' import { useRecoilValue, useSetRecoilState } from 'recoil'
import { canvasState, canvasZoomState, currentMenuState, textModeState } from '@/store/canvasAtom' import { canvasState, canvasZoomState, currentMenuState, textModeState } from '@/store/canvasAtom'
import { fabric } from 'fabric' import { fabric } from 'fabric'
@ -219,6 +219,12 @@ export function useEvent() {
mouseEventListeners.current.length = 0 // 배열 초기화 mouseEventListeners.current.length = 0 // 배열 초기화
} }
const addTargetMouseEventListener = (eventType, target, handler) => {
target.off(eventType)
target.on(eventType, handler)
mouseEventListeners.current.push({ eventType, handler })
}
/** /**
* document 이벤트의 경우 함수를 통해서만 등록 * document 이벤트의 경우 함수를 통해서만 등록
* @param eventType * @param eventType
@ -264,6 +270,7 @@ export function useEvent() {
return { return {
addDocumentEventListener, addDocumentEventListener,
addCanvasMouseEventListener, addCanvasMouseEventListener,
addTargetMouseEventListener,
removeAllMouseEventListeners, removeAllMouseEventListeners,
removeAllDocumentEventListeners, removeAllDocumentEventListeners,
removeDocumentEvent, removeDocumentEvent,

View File

@ -1265,7 +1265,6 @@ export function useMode() {
const wall = makePolygon(null, sort) const wall = makePolygon(null, sort)
wall.name = 'wall' wall.name = 'wall'
// wall.set({ name: 'wall' })
return wall return wall
} }
@ -1363,7 +1362,8 @@ export function useMode() {
const polygon = new QPolygon( const polygon = new QPolygon(
points, points,
{ {
stroke: 'black', stroke: '#1083E3',
strokeWidth: 2,
fill: 'transparent', fill: 'transparent',
viewLengthText: true, viewLengthText: true,
fontSize: fontSize, fontSize: fontSize,
@ -1510,53 +1510,29 @@ export function useMode() {
polygon.lines.forEach((line, index) => { polygon.lines.forEach((line, index) => {
line.attributes = { line.attributes = {
type: LINE_TYPE.WALLLINE.EAVES, type: LINE_TYPE.WALLLINE.EAVES,
offset: 40, offset: 50,
width: 50, width: 50,
pitch: 4, pitch: 4,
sleeve: true, sleeve: true,
} }
/*if (index === 1) { /*if (index % 2 !== 0) {
line.attributes = { line.attributes = {
type: LINE_TYPE.WALLLINE.SHED, type: LINE_TYPE.WALLLINE.GABLE,
offset: 30, //출폭 offset: 30,
width: 30, //폭 width: 50,
pitch: 4, //구배 pitch: 4,
sleeve: true, //소매 sleeve: true,
}
} else if (index === 5 || index === 3) {
line.attributes = {
type: LINE_TYPE.WALLLINE.EAVES,
offset: 50, //출폭
width: 30, //폭
pitch: 4, //구배
sleeve: true, //소매
} }
} else { } else {
line.attributes = { line.attributes = {
type: LINE_TYPE.WALLLINE.GABLE, type: LINE_TYPE.WALLLINE.EAVES,
offset: 20, offset: 50,
width: 50, width: 50,
pitch: 4, pitch: 4,
sleeve: true, sleeve: true,
} }
}*/ }*/
/* if (index === 1) { /*if (index === polygon.lines.length - 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 {
line.attributes = { line.attributes = {
type: LINE_TYPE.WALLLINE.GABLE, type: LINE_TYPE.WALLLINE.GABLE,
offset: 30, offset: 30,
@ -1564,6 +1540,14 @@ export function useMode() {
pitch: 4, pitch: 4,
sleeve: true, 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, roofId: roof.id,
} }
canvas
.getObjects()
.filter((line) => line.attributes?.wallId === wall.id)
.forEach((line) => canvas.remove(line))
wall.lines.forEach((line, index) => { wall.lines.forEach((line, index) => {
const lineLength = Math.sqrt( 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), 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.currentRoof = roof.lines[index].id
line.attributes.planeSize = lineLength line.attributes.planeSize = lineLength
line.attributes.actualSize = 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) setRoof(roof)
setWall(wall) setWall(wall)
return roof return roof
} }

View File

@ -57,6 +57,7 @@ export function usePlan() {
*/ */
const currentCanvasData = (mode = '') => { const currentCanvasData = (mode = '') => {
removeMouseLines() removeMouseLines()
canvas.discardActiveObject()
if (mode === 'save') { if (mode === 'save') {
const groups = canvas.getObjects().filter((obj) => obj.type === 'group') const groups = canvas.getObjects().filter((obj) => obj.type === 'group')
@ -141,7 +142,9 @@ export function usePlan() {
*/ */
const checkUnsavedCanvasPlan = async (userId) => { const checkUnsavedCanvasPlan = async (userId) => {
swalFire({ swalFire({
text: `저장 안된 ${currentCanvasPlan.name} PLAN을 저장하시겠습니까? `, text:
(!initCanvasPlans.some((initCanvasPlans) => initCanvasPlans.id === plan.id) ? 'New ' : '') +
`Plan ${plan.ordering}의 변경 사항을 저장하시겠습니까?`,
type: 'confirm', type: 'confirm',
confirmFn: async () => { confirmFn: async () => {
initCanvasPlans.some((plan) => plan.id === currentCanvasPlan.id) initCanvasPlans.some((plan) => plan.id === currentCanvasPlan.id)
@ -181,20 +184,20 @@ export function usePlan() {
*/ */
const getCanvasByObjectNo = async (userId, objectNo) => { const getCanvasByObjectNo = async (userId, objectNo) => {
return get({ url: `/api/canvas-management/canvas-statuses/by-object/${objectNo}/${userId}` }).then((res) => return get({ url: `/api/canvas-management/canvas-statuses/by-object/${objectNo}/${userId}` }).then((res) =>
res.map((item) => ({ res.map((item, index) => ({
id: item.id, id: item.id,
name: item.objectNo + '-' + item.id, // tab button에 표출될 이름 (임시)
userId: item.userId, userId: item.userId,
canvasStatus: dbToCanvasFormat(item.canvasStatus), canvasStatus: dbToCanvasFormat(item.canvasStatus),
isCurrent: false, isCurrent: false,
bgImageName: item.bgImageName, bgImageName: item.bgImageName,
mapPositionAddress: item.mapPositionAddress, mapPositionAddress: item.mapPositionAddress,
ordering: index + 1,
})), })),
) )
} }
/** /**
* canvas 데이터를 추가 * 신규 canvas 데이터를 저장
*/ */
const postCanvasStatus = async (userId, canvasStatus) => { const postCanvasStatus = async (userId, canvasStatus) => {
const planData = { const planData = {
@ -212,7 +215,6 @@ export function usePlan() {
? { ? {
...plan, ...plan,
id: res.data, id: res.data,
name: currentCanvasPlan.objectNo + '-' + res.data,
canvasStatus: canvasStatus, canvasStatus: canvasStatus,
} }
: plan, : plan,
@ -302,7 +304,10 @@ export function usePlan() {
const handleAddPlan = (userId, objectNo) => { const handleAddPlan = (userId, objectNo) => {
JSON.parse(currentCanvasData()).objects.length > 0 JSON.parse(currentCanvasData()).objects.length > 0
? swalFire({ ? 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', type: 'confirm',
confirmFn: () => { confirmFn: () => {
addPlan(userId, objectNo, currentCanvasData()) addPlan(userId, objectNo, currentCanvasData())
@ -317,10 +322,10 @@ export function usePlan() {
const id = uuidv4() const id = uuidv4()
const newPlan = { const newPlan = {
id: id, id: id,
name: `Plan ${planNum + 1}`,
objectNo: objectNo, objectNo: objectNo,
userId: userId, userId: userId,
canvasStatus: canvasStatus, canvasStatus: canvasStatus,
ordering: planNum + 1,
} }
setPlans([...plans, newPlan]) setPlans([...plans, newPlan])
handleCurrentPlan(userId, id) handleCurrentPlan(userId, id)
@ -363,16 +368,16 @@ export function usePlan() {
/** /**
* plan 조회 * plan 조회
*/ */
const loadCanvasPlanData = (userId, objectNo) => { const loadCanvasPlanData = (userId, objectNo, pid) => {
getCanvasByObjectNo(userId, objectNo).then((res) => { getCanvasByObjectNo(userId, objectNo).then((res) => {
// console.log('canvas 목록 ', res) // console.log('canvas 목록 ', res)
if (res.length > 0) { if (res.length > 0) {
setInitCanvasPlans(res) setInitCanvasPlans(res)
setPlans(res) setPlans(res)
updateCurrentPlan(res.at(-1).id) // last 데이터에 포커싱 updateCurrentPlan(Number(pid))
setPlanNum(res.length) setPlanNum(res.length)
} else { } else {
addPlan(userId, objectNo) addPlan(userId, objectNo, '')
} }
}) })
} }
@ -380,6 +385,7 @@ export function usePlan() {
return { return {
canvas, canvas,
plans, plans,
initCanvasPlans,
selectedPlan, selectedPlan,
currentCanvasPlan, currentCanvasPlan,
modifiedPlans, modifiedPlans,

View File

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

View File

@ -1,5 +1,10 @@
'use server' 'use server'
/**
* Deprecated
* 개발후 삭제 예정
*/
import fs from 'fs/promises' import fs from 'fs/promises'
const imageSavePath = 'public/cadImages' const imageSavePath = 'public/cadImages'

View File

@ -1,5 +1,10 @@
'use server' 'use server'
/**
* Deprecated
* 개발후 삭제 예정
*/
// import { PrismaClient } from '@prisma/client' // import { PrismaClient } from '@prisma/client'
import fs from 'fs/promises' import fs from 'fs/promises'

View File

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

64
src/lib/fileAction.js Normal file
View 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 }

View File

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

View File

@ -4,6 +4,7 @@
"header.menus.home": "ホームへv", "header.menus.home": "ホームへv",
"header.menus.management": "物品及び図面管理", "header.menus.management": "物品及び図面管理",
"header.menus.management.newStuff": "新規 物件 登録", "header.menus.management.newStuff": "新規 物件 登録",
"header.menus.management.detail": "物件詳細",
"header.menus.management.stuffList": "物件の状況", "header.menus.management.stuffList": "物件の状況",
"header.menus.community": "コミュニティ", "header.menus.community": "コミュニティ",
"header.menus.community.notice": "お知らせ", "header.menus.community.notice": "お知らせ",
@ -85,12 +86,14 @@
"modal.module.basic.setting.orientation.setting.angle.passivity": "角度を直接入力", "modal.module.basic.setting.orientation.setting.angle.passivity": "角度を直接入力",
"modal.module.basic.setting.module.roof.material": "屋根材", "modal.module.basic.setting.module.roof.material": "屋根材",
"modal.module.basic.setting.module.trestle.maker": "架台メーカー", "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.construction.method": "工法",
"modal.module.basic.setting.module.under.roof": "屋根の下", "modal.module.basic.setting.module.under.roof": "屋根の下",
"modal.module.basic.setting.module.setting": "モジュールの選択", "modal.module.basic.setting.module.setting": "モジュールの選択",
"modal.module.basic.setting.module.setting.info1": "※勾配の 範囲には制限があります。屋根傾斜が2.5値未満、10値を超える場合には施工が可能か 施工マニュアルを確認してください。", "modal.module.basic.setting.module.setting.info1": "※勾配の 範囲には制限があります。屋根傾斜が2.5値未満、10値を超える場合には施工が可能か 施工マニュアルを確認してください。",
"modal.module.basic.setting.module.setting.info2": "モジュール配置時は、施工マニュアルに記載されている<モジュール配置条件>を必ず確認してください", "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.fitting.height": "設置高さ",
"modal.module.basic.setting.module.standard.wind.speed": "基準風速", "modal.module.basic.setting.module.standard.wind.speed": "基準風速",
"modal.module.basic.setting.module.standard.snowfall.amount": "基準積雪量", "modal.module.basic.setting.module.standard.snowfall.amount": "基準積雪量",
@ -160,7 +163,7 @@
"plan.menu.estimate.docDown": "文書のダウンロード", "plan.menu.estimate.docDown": "文書のダウンロード",
"plan.menu.estimate.save": "保存", "plan.menu.estimate.save": "保存",
"plan.menu.estimate.reset": "初期化", "plan.menu.estimate.reset": "初期化",
"plan.menu.estimate.copy": "コピー", "plan.menu.estimate.copy": "見積書のコピー",
"plan.menu.simulation": "発展シミュレーション", "plan.menu.simulation": "発展シミュレーション",
"plan.menu.simulation.excel": "Excel", "plan.menu.simulation.excel": "Excel",
"plan.menu.simulation.pdf": "PDF", "plan.menu.simulation.pdf": "PDF",
@ -284,7 +287,6 @@
"modal.panel.batch.statistic.total": "合計", "modal.panel.batch.statistic.total": "合計",
"modal.flow.direction.setting": "流れ方向の設定", "modal.flow.direction.setting": "流れ方向の設定",
"modal.flow.direction.setting.info": "流れ方向を選択してください。", "modal.flow.direction.setting.info": "流れ方向を選択してください。",
"modal.image.size.setting": "画像のサイズ変更",
"modal.actual.size.setting": "実測値設定", "modal.actual.size.setting": "実測値設定",
"modal.actual.size.setting.info": "※隅棟・谷・棟の実際の寸法を入力してください。", "modal.actual.size.setting.info": "※隅棟・谷・棟の実際の寸法を入力してください。",
"modal.actual.size.setting.not.exist.auxiliary.line": "실측치 입력할 보조선을 선택해 주세요(JA)", "modal.actual.size.setting.not.exist.auxiliary.line": "실측치 입력할 보조선을 선택해 주세요(JA)",
@ -616,9 +618,9 @@
"stuff.planReqPopup.title": "設計依頼のインポート", "stuff.planReqPopup.title": "設計依頼のインポート",
"stuff.temp.subTitle": "商品情報", "stuff.temp.subTitle": "商品情報",
"stuff.temp.subTitle2": "作図", "stuff.temp.subTitle2": "作図",
"stuff.detail.header.message1": "存在しないものです。", "stuff.detail.header.notExistObjectNo": "存在しないものです。",
"stuff.detail.header.message2": "商品番号がコピーされました。", "stuff.detail.header.successCopy": "商品番号がコピーされました。",
"stuff.detail.header.message3": "存在しないものです。", "stuff.detail.header.failCopy": "存在しないものです。",
"stuff.detail.header.objectNo": "商品番号のコピーに失敗しました。", "stuff.detail.header.objectNo": "商品番号のコピーに失敗しました。",
"stuff.detail.header.specificationConfirmDate": "仕様拡張日", "stuff.detail.header.specificationConfirmDate": "仕様拡張日",
"stuff.detail.header.lastEditDatetime": "更新日時", "stuff.detail.header.lastEditDatetime": "更新日時",
@ -667,6 +669,7 @@
"stuff.detail.btn.save": "保存", "stuff.detail.btn.save": "保存",
"stuff.detail.btn.tempSave": "一時保存", "stuff.detail.btn.tempSave": "一時保存",
"stuff.detail.save": "保存しました", "stuff.detail.save": "保存しました",
"stuff.detail.tempSave": "一時保存されました",
"stuff.detail.noChgData": "変更内容はありません", "stuff.detail.noChgData": "変更内容はありません",
"stuff.detail.save.valierror1": "垂直説説は0より大きい値を入力してください", "stuff.detail.save.valierror1": "垂直説説は0より大きい値を入力してください",
"stuff.detail.save.valierror2": "設置高さ0より大きい値を入力してください", "stuff.detail.save.valierror2": "設置高さ0より大きい値を入力してください",
@ -694,11 +697,12 @@
"stuff.planReqPopup.search.period": "期間検索", "stuff.planReqPopup.search.period": "期間検索",
"stuff.planReqPopup.search.schDateGbnS": "提出日", "stuff.planReqPopup.search.schDateGbnS": "提出日",
"stuff.planReqPopup.search.schDateGbnR": "受付日", "stuff.planReqPopup.search.schDateGbnR": "受付日",
"stuff.planReqPopup.error.message1": "設計依頼を選択してください。", "stuff.planReqPopup.error.message1": "設計依頼を選択してください.",
"stuff.planReqPopup.error.message2": "販売店を選択してください.",
"stuff.search.title": "物件状況", "stuff.search.title": "物件状況",
"stuff.search.btn1": "新規 物件 登録", "stuff.search.btn.register": "新規 物件 登録",
"stuff.search.btn2": "照会", "stuff.search.btn.search": "照会",
"stuff.search.btn3": "初期化", "stuff.search.btn.reset": "初期化",
"stuff.search.schObjectNo": "品番", "stuff.search.schObjectNo": "品番",
"stuff.search.schSaleStoreName": "販売代理店名", "stuff.search.schSaleStoreName": "販売代理店名",
"stuff.search.schAddress": "商品アドレス", "stuff.search.schAddress": "商品アドレス",
@ -727,12 +731,12 @@
"stuff.detail.planGridHeader.moduleModel": "モジュール", "stuff.detail.planGridHeader.moduleModel": "モジュール",
"stuff.detail.planGridHeader.capacity": "システム容量", "stuff.detail.planGridHeader.capacity": "システム容量",
"stuff.detail.planGridHeader.roofMaterialIdMulti": "屋根材", "stuff.detail.planGridHeader.roofMaterialIdMulti": "屋根材",
"stuff.detail.planGridHeader.constructSpecification": "施工方法", "stuff.detail.planGridHeader.constructSpecificationMulti": "施工方法",
"stuff.detail.planGridHeader.supportMethodIdMulti": "架台", "stuff.detail.planGridHeader.supportMethodIdMulti": "架台",
"stuff.detail.planGridHeader.pcTypeNo": "パワーコンディショナー", "stuff.detail.planGridHeader.pcTypeNo": "パワーコンディショナー",
"stuff.detail.planGridHeader.management": "管理", "stuff.detail.planGridHeader.management": "管理",
"stuff.detail.planGrid.btn1": "見積書の照会", "stuff.detail.planGrid.btn1": "見積書の照会",
"stuff.detail.planGrid.btn2": "Excel", "stuff.detail.planGrid.docDownload": "文書のダウンロード",
"stuff.grid.noData": "照会されたデータがありません", "stuff.grid.noData": "照会されたデータがありません",
"length": "長さ", "length": "長さ",
"height": "高さ", "height": "高さ",
@ -822,6 +826,8 @@
"estimate.detail.objectName": "案件名", "estimate.detail.objectName": "案件名",
"estimate.detail.objectRemarks": "メモ", "estimate.detail.objectRemarks": "メモ",
"estimate.detail.estimateType": "注文分類", "estimate.detail.estimateType": "注文分類",
"estimate.detail.estimateType.yjss": "住宅PKG",
"estimate.detail.estimateType.yjod": "積上げ( YJOD )",
"estimate.detail.roofCns": "屋根材・仕様施工", "estimate.detail.roofCns": "屋根材・仕様施工",
"estimate.detail.remarks": "備考", "estimate.detail.remarks": "備考",
"estimate.detail.fileFlg": "後日資料提出", "estimate.detail.fileFlg": "後日資料提出",
@ -830,19 +836,18 @@
"estimate.detail.header.fileList2": "添付ファイル一覧", "estimate.detail.header.fileList2": "添付ファイル一覧",
"estimate.detail.header.specialEstimate": "見積もりの具体的な", "estimate.detail.header.specialEstimate": "見積もりの具体的な",
"estimate.detail.header.specialEstimateProductInfo": "製品情報", "estimate.detail.header.specialEstimateProductInfo": "製品情報",
"estimate.detail.sepcialEstimateProductInfo.totPcs": "数量 (PCS)", "estimate.detail.sepcialEstimateProductInfo.totAmount": "数量 (PCS)",
"estimate.detail.sepcialEstimateProductInfo.vol": "容量 (Kw)", "estimate.detail.sepcialEstimateProductInfo.totVolKw": "容量 (Kw)",
"estimate.detail.sepcialEstimateProductInfo.netAmt": "供給価格", "estimate.detail.sepcialEstimateProductInfo.supplyPrice": "供給価格",
"estimate.detail.sepcialEstimateProductInfo.vat": "付加価値税 (10%)", "estimate.detail.sepcialEstimateProductInfo.vatPrice": "付加価値税 (10%)",
"estimate.detail.sepcialEstimateProductInfo.totPrice": "総額", "estimate.detail.sepcialEstimateProductInfo.totPrice": "総額",
"estimate.detail.sepcialEstimateProductInfo.pkgUnitPrice": "住宅PKG単価 (W)", "estimate.detail.sepcialEstimateProductInfo.pkgUnitPrice": "住宅PKG単価 (W)",
"estimate.detail.sepcialEstimateProductInfo.pkgWeight": "PKG容量 (Kw)", "estimate.detail.sepcialEstimateProductInfo.pkgWeight": "PKG容量 (Kw)",
"estimate.detail.sepcialEstimateProductInfo.pkgPrice": "PKG金額", "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.showPrice": "価格表示",
"estimate.detail.header.unitPrice": "定価", "estimate.detail.header.unitPrice": "定価",
"estimate.detail.showPrice.pricingBtn": "Pricing", "estimate.detail.showPrice.pricingBtn": "Pricing",
"estimate.detail.showPrice.pricingBtn.noItemId": "Pricingが欠落しているアイテムがあります。 Pricingを進めてください.",
"estimate.detail.showPrice.description1": "製品価格 OPEN", "estimate.detail.showPrice.description1": "製品価格 OPEN",
"estimate.detail.showPrice.description2": "追加, 変更資材", "estimate.detail.showPrice.description2": "追加, 変更資材",
"estimate.detail.showPrice.description3": "添付必須", "estimate.detail.showPrice.description3": "添付必須",
@ -859,25 +864,41 @@
"estimate.detail.docPopup.title": "ドキュメントダウンロードオプションの設定", "estimate.detail.docPopup.title": "ドキュメントダウンロードオプションの設定",
"estimate.detail.docPopup.explane": "ダウンロードする文書のオプションを選択したら、 [文書のダウンロード]ボタンをクリックします.", "estimate.detail.docPopup.explane": "ダウンロードする文書のオプションを選択したら、 [文書のダウンロード]ボタンをクリックします.",
"estimate.detail.docPopup.schUnitPriceFlg": "ダウンロードファイル", "estimate.detail.docPopup.schUnitPriceFlg": "ダウンロードファイル",
"estimate.detail.docPopup.schUnitPriceFlg.schUnitPriceFlg0": "見積もり Excel", "estimate.detail.docPopup.schUnitPriceFlg.excelFlg0": "見積もり Excel",
"estimate.detail.docPopup.schUnitPriceFlg.schUnitPriceFlg1": "定価用 Excel", "estimate.detail.docPopup.schUnitPriceFlg.excelFlg1": "定価用 Excel",
"estimate.detail.docPopup.schUnitPriceFlg.schUnitPriceFlg2": "見積もり PDF", "estimate.detail.docPopup.schUnitPriceFlg.pdfFlg0": "見積もり PDF",
"estimate.detail.docPopup.schUnitPriceFlg.schUnitPriceFlg3": "定価用 PDF", "estimate.detail.docPopup.schUnitPriceFlg.pdfFlg1": "定価用 PDF",
"estimate.detail.docPopup.schDisplayFlg": "見積提出先表示名", "estimate.detail.docPopup.schDisplayFlg": "見積提出先表示名",
"estimate.detail.docPopup.schDisplayFlg.schDisplayFlg0": "販売店名", "estimate.detail.docPopup.schDisplayFlg.schDisplayFlg0": "販売店名",
"estimate.detail.docPopup.schDisplayFlg.schDisplayFlg1": "案件名", "estimate.detail.docPopup.schDisplayFlg.schDisplayFlg1": "案件名",
"estimate.detail.docPopup.schWeightFlg": "架台重量表を含む", "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": "図面/シミュレーションファイルを含む",
"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.close": "閉じる",
"estimate.detail.docPopup.docDownload": "文書のダウンロード", "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.title": "製品特異事項",
"estimate.detail.productFeaturesPopup.close": "閉じる", "estimate.detail.productFeaturesPopup.close": "閉じる",
"estimate.detail.productFeaturesPopup.requiredStoreId": "一次販売店は必須です.",
"estimate.detail.productFeaturesPopup.requiredReceiveUser": "担当者は必須です.",
"estimate.detail.save.alertMsg": "保存されている見積書で製品を変更した場合、図面や回路には反映されません.", "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": "保存した見積書情報が初期化され、図面情報が反映されます。本当に初期化しますか?", "estimate.detail.reset.confirmMsg": "保存した見積書情報が初期化され、図面情報が反映されます。本当に初期化しますか?",
"simulator.title.sub1": "物件番号", "simulator.title.sub1": "物件番号",
"simulator.title.sub2": "作成日", "simulator.title.sub2": "作成日",

Some files were not shown because too many files have changed in this diff Show More