feat: Add canvas plan load and save
This commit is contained in:
parent
1c2d3b7968
commit
08ef5a63ca
@ -1,12 +1,30 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useRef } from 'react'
|
||||
|
||||
import { useCanvas } from '@/hooks/useCanvas'
|
||||
import { useRef } from 'react'
|
||||
import { useEvent } from '@/hooks/useEvent'
|
||||
|
||||
export default function CanvasFrame() {
|
||||
export default function CanvasFrame({ plan }) {
|
||||
const canvasRef = useRef(null)
|
||||
useCanvas('canvas')
|
||||
const { canvas } = useCanvas('canvas')
|
||||
useEvent()
|
||||
|
||||
const loadCanvas = () => {
|
||||
if (canvas) {
|
||||
canvas?.clear() // 캔버스를 초기화합니다.
|
||||
if (plan?.canvasStatus) {
|
||||
canvas?.loadFromJSON(JSON.parse(plan.canvasStatus), function () {
|
||||
canvas?.renderAll() // 캔버스를 다시 그립니다.
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
loadCanvas()
|
||||
}, [plan])
|
||||
|
||||
return (
|
||||
<div className="canvas-frame flex justify-center">
|
||||
<canvas ref={canvasRef} id={'canvas'}></canvas>
|
||||
|
||||
@ -1,43 +1,96 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useState } from 'react'
|
||||
import CanvasFrame from './CanvasFrame'
|
||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||
import { currentMenuState, stepState } from '@/store/canvasAtom'
|
||||
import CanvasFrame from './CanvasFrame'
|
||||
import { useAxios } from '@/hooks/useAxios'
|
||||
import { globalLocaleStore } from '@/store/localeAtom'
|
||||
import { currentCanvasPlanState, initCanvasPlansState } from '@/store/canvasAtom'
|
||||
|
||||
export default function CanvasLayout() {
|
||||
const [plans, setPlans] = useState([
|
||||
{ id: 0, name: 'Plan 1', isCurrent: false },
|
||||
{ id: 1, name: 'Plan 2', isCurrent: false },
|
||||
{ id: 2, name: 'Plan 3', isCurrent: false },
|
||||
])
|
||||
const [idxNum, setIdxNum] = useState(null)
|
||||
const [objectNo, setObjectNo] = useState('test123240822001') // 이후 삭제 필요
|
||||
const [addCanvasPlans, setAddCanvasPlans] = useState([])
|
||||
const [idxNum, setIdxNum] = useState(0)
|
||||
const [currentCanvasPlan, setCurrentCanvasPlan] = useRecoilState(currentCanvasPlanState)
|
||||
const [initCanvasPlans, setInitCanvasPlans] = useRecoilState(initCanvasPlansState)
|
||||
const globalLocaleState = useRecoilValue(globalLocaleStore)
|
||||
const { get } = useAxios(globalLocaleState)
|
||||
|
||||
const onClickPlane = (num) => {
|
||||
setIdxNum(num)
|
||||
const handleCurrentPlan = (newCurrentId) => {
|
||||
if (!currentCanvasPlan?.id || currentCanvasPlan.id !== newCurrentId) {
|
||||
setInitCanvasPlans((plans) =>
|
||||
plans.map((plan) => {
|
||||
return { ...plan, isCurrent: plan.id === newCurrentId }
|
||||
}),
|
||||
)
|
||||
setAddCanvasPlans((plans) =>
|
||||
plans.map((plan) => {
|
||||
return { ...plan, isCurrent: plan.id === newCurrentId }
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
useEffect(() => {
|
||||
setCurrentCanvasPlan([...initCanvasPlans, ...addCanvasPlans].find((plan) => plan.isCurrent) || null)
|
||||
}, [initCanvasPlans, addCanvasPlans])
|
||||
|
||||
const handleDeletePlan = (e, id) => {
|
||||
e.stopPropagation() // 이벤트 버블링 방지
|
||||
setPlans(plans.filter((plan) => plan.id !== id)) // 삭제할 아이디와 다른 아이템만 남김
|
||||
|
||||
// 삭제할 아이디와 다른 아이템만 남김
|
||||
const filterInitPlans = initCanvasPlans.filter((plan) => plan.id !== id)
|
||||
setInitCanvasPlans(filterInitPlans)
|
||||
const filterAddPlans = addCanvasPlans.filter((plan) => plan.id !== id)
|
||||
setAddCanvasPlans(filterAddPlans)
|
||||
|
||||
const combinedPlans = [...filterInitPlans, ...filterAddPlans]
|
||||
if (combinedPlans.length === 0) {
|
||||
// 모든 데이터가 삭제된 경우
|
||||
setIdxNum(0)
|
||||
} else {
|
||||
const lastPlanId = combinedPlans.at(-1).id
|
||||
if (id !== lastPlanId) {
|
||||
handleCurrentPlan(lastPlanId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const addNewPlan = () => {
|
||||
setPlans([...plans, { id: plans.length, name: `Plan ${plans.length + 1}` }])
|
||||
setAddCanvasPlans([...addCanvasPlans, { id: idxNum, name: `Plan ${idxNum + 1}`, objectNo: `${objectNo}` }])
|
||||
handleCurrentPlan(idxNum)
|
||||
setIdxNum(idxNum + 1)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (plans.length === 1) {
|
||||
setPlans([{ id: 0, name: 'Plan 1', isCurrent: false }])
|
||||
}
|
||||
get({ url: `/api/canvas-management/canvas-statuses/by-object/${objectNo}` }).then((res) => {
|
||||
// console.log('canvas 목록 ', res)
|
||||
const arrangeData = res.map((item) => {
|
||||
const test = item.canvasStatus.replace(/##/g, '"').replace(/\\/g, '')
|
||||
const test2 = test.substring(1, test.length - 1)
|
||||
return {
|
||||
id: item.id,
|
||||
name: item.objectNo + '-' + item.id, // tab button에 표출될 이름
|
||||
userId: item.userId,
|
||||
canvasStatus: JSON.stringify(test2),
|
||||
isCurrent: false,
|
||||
}
|
||||
})
|
||||
if (arrangeData.length > 0) {
|
||||
setInitCanvasPlans(arrangeData)
|
||||
handleCurrentPlan(arrangeData.at(-1).id) // last 데이터에 포커싱
|
||||
setIdxNum(arrangeData.length)
|
||||
} else {
|
||||
addNewPlan()
|
||||
}
|
||||
})
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="canvas-layout">
|
||||
<div className="canvas-page-list">
|
||||
<div className="canvas-plane-wrap">
|
||||
{plans.map((plan, idx) => (
|
||||
<button key={plan.id} className={`canvas-page-box ${idx === idxNum ? 'on' : ''}`} onClick={() => onClickPlane(idx)}>
|
||||
{[...initCanvasPlans, ...addCanvasPlans].map((plan) => (
|
||||
<button key={plan.id} className={`canvas-page-box ${plan.isCurrent === true ? 'on' : ''}`} onClick={() => handleCurrentPlan(plan.id)}>
|
||||
<span>{plan.name}</span>
|
||||
<i className="close" onClick={(e) => handleDeletePlan(e, plan.id)}></i>
|
||||
</button>
|
||||
@ -47,7 +100,7 @@ export default function CanvasLayout() {
|
||||
<span></span>
|
||||
</button>
|
||||
</div>
|
||||
<CanvasFrame />
|
||||
<CanvasFrame plan={[...initCanvasPlans, ...addCanvasPlans].find((plan) => plan.isCurrent === true)} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -8,7 +8,9 @@ import MenuDepth01 from './MenuDepth01'
|
||||
import QSelectBox from '@/components/common/select/QSelectBox'
|
||||
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { usePlan } from '@/hooks/usePlan'
|
||||
import { canvasState, canvasZoomState, currentMenuState, verticalHorizontalModeState } from '@/store/canvasAtom'
|
||||
import { sessionStore } from '@/store/commonAtom'
|
||||
import { outerLinePointsState } from '@/store/outerLineAtom'
|
||||
import { appMessageStore, globalLocaleStore } from '@/store/localeAtom'
|
||||
import { MENU } from '@/common/common'
|
||||
@ -37,12 +39,14 @@ export default function CanvasMenu(props) {
|
||||
const [appMessageState, setAppMessageState] = useRecoilState(appMessageStore)
|
||||
const setCurrentMenu = useSetRecoilState(currentMenuState)
|
||||
const setPoints = useSetRecoilState(outerLinePointsState)
|
||||
|
||||
const [canvasZoom, setCanvasZoom] = useRecoilState(canvasZoomState)
|
||||
const [sessionState, setSessionState] = useRecoilState(sessionStore)
|
||||
|
||||
const globalLocale = useRecoilValue(globalLocaleStore)
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
|
||||
const { getMessage } = useMessage()
|
||||
const { saveCanvas } = usePlan()
|
||||
|
||||
const SelectOption = [{ name: '瓦53A' }, { name: '瓦53A' }]
|
||||
const onClickNav = (number) => {
|
||||
@ -63,7 +67,9 @@ export default function CanvasMenu(props) {
|
||||
}, [menuNumber, type])
|
||||
|
||||
// 저장버튼(btn08) 클릭 시 호출되는 함수
|
||||
const handleSaveSettings = async () => {}
|
||||
const handleSaveCanvas = () => {
|
||||
saveCanvas(sessionState.userId)
|
||||
}
|
||||
|
||||
const handleClear = () => {
|
||||
setPoints([])
|
||||
@ -201,7 +207,7 @@ export default function CanvasMenu(props) {
|
||||
</div>
|
||||
<div className="btn-from">
|
||||
<button className="btn07" onClick={handleClear}></button>
|
||||
<button className="btn08" onClick={handleSaveSettings}></button>
|
||||
<button className="btn08" onClick={handleSaveCanvas}></button>
|
||||
<button className="btn09"></button>
|
||||
</div>
|
||||
</>
|
||||
|
||||
@ -61,6 +61,10 @@ export function useAxios(lang = '') {
|
||||
.catch(console.error)
|
||||
}
|
||||
|
||||
const promisePut = async ({ url, data }) => {
|
||||
return await getInstances(url).put(url, data)
|
||||
}
|
||||
|
||||
const patch = async ({ url, data }) => {
|
||||
return await getInstances(url)
|
||||
.patch(url, data)
|
||||
@ -75,5 +79,5 @@ export function useAxios(lang = '') {
|
||||
.catch(console.error)
|
||||
}
|
||||
|
||||
return { get, promiseGet, post, promisePost, put, patch, del }
|
||||
return { get, promiseGet, post, promisePost, put, promisePut, patch, del }
|
||||
}
|
||||
|
||||
123
src/hooks/usePlan.js
Normal file
123
src/hooks/usePlan.js
Normal file
@ -0,0 +1,123 @@
|
||||
import { useState } from 'react'
|
||||
import { useRecoilState } from 'recoil'
|
||||
import { canvasState, currentCanvasPlanState, initCanvasPlansState } from '@/store/canvasAtom'
|
||||
import { useAxios } from '@/hooks/useAxios'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { toastUp } from '@/hooks/useToast'
|
||||
|
||||
export function usePlan() {
|
||||
const [canvas, setCanvas] = useRecoilState(canvasState)
|
||||
const [currentCanvasPlan, setcurrentCanvasPlan] = useRecoilState(currentCanvasPlanState)
|
||||
const [initCanvasPlans, setInitCanvasPlans] = useRecoilState(initCanvasPlansState)
|
||||
const { getMessage } = useMessage()
|
||||
const { post, promisePost, put, promisePut } = useAxios()
|
||||
|
||||
/**
|
||||
* 마우스 포인터의 가이드라인을 제거합니다.
|
||||
*/
|
||||
const removeMouseLines = () => {
|
||||
if (canvas?._objects.length > 0) {
|
||||
const mouseLines = canvas?._objects.filter((obj) => obj.name === 'mouseLine')
|
||||
mouseLines.forEach((item) => canvas?.remove(item))
|
||||
}
|
||||
canvas?.renderAll()
|
||||
}
|
||||
|
||||
const addCanvas = () => {
|
||||
const objs = canvas?.toJSON([
|
||||
'selectable',
|
||||
'name',
|
||||
'parentId',
|
||||
'id',
|
||||
'length',
|
||||
'idx',
|
||||
'direction',
|
||||
'lines',
|
||||
'points',
|
||||
'lockMovementX',
|
||||
'lockMovementY',
|
||||
'lockRotation',
|
||||
'lockScalingX',
|
||||
'lockScalingY',
|
||||
'opacity',
|
||||
'cells',
|
||||
'maxX',
|
||||
'maxY',
|
||||
'minX',
|
||||
'minY',
|
||||
'x',
|
||||
'y',
|
||||
'stickeyPoint',
|
||||
])
|
||||
|
||||
const str = JSON.stringify(objs)
|
||||
|
||||
// canvas?.clear()
|
||||
return str
|
||||
|
||||
// setTimeout(() => {
|
||||
// // 역직렬화하여 캔버스에 객체를 다시 추가합니다.
|
||||
// canvas?.loadFromJSON(JSON.parse(str), function () {
|
||||
// // 모든 객체가 로드되고 캔버스에 추가된 후 호출됩니다.
|
||||
// console.log(canvas?.getObjects().filter((obj) => obj.name === 'roof'))
|
||||
// canvas?.renderAll() // 캔버스를 다시 그립니다.
|
||||
// })
|
||||
// }, 1000)
|
||||
}
|
||||
|
||||
/**
|
||||
* 페이지 내 캔버스를 저장하는 함수
|
||||
*
|
||||
* 1. 신규 저장 : POST
|
||||
* param(body) : userId, objectNo, canvasStatus
|
||||
* 2. 수정 저장 : PUT
|
||||
* param(body) : id, canvasStatus
|
||||
*/
|
||||
const saveCanvas = async (userId) => {
|
||||
removeMouseLines()
|
||||
const canvasStatus = addCanvas()
|
||||
|
||||
if (initCanvasPlans.some((plan) => plan.id === currentCanvasPlan.id)) {
|
||||
// canvas 수정
|
||||
const planData = {
|
||||
id: currentCanvasPlan.id,
|
||||
canvasStatus: JSON.stringify(canvasStatus).replace(/"/g, '##'),
|
||||
}
|
||||
|
||||
await promisePut({ url: '/api/canvas-management/canvas-statuses', data: planData })
|
||||
.then((res) => {
|
||||
toastUp({ message: getMessage('res.message'), type: 'success' }) // 성공 시 메세지 없음
|
||||
console.log('[PUT] canvas-statuses res :::::::: %o', res)
|
||||
})
|
||||
.catch((error) => {
|
||||
toastUp({ message: getMessage(error.message), type: 'error' })
|
||||
console.error('[PUT] canvas-statuses error :::::::: %o', error)
|
||||
})
|
||||
} else {
|
||||
// canvas 신규 등록
|
||||
const planData = {
|
||||
userId: 'NEW016610', // userId,
|
||||
imageName: 'image_name', // api 필수항목이여서 임시로 넣음, 이후 삭제 필요
|
||||
objectNo: currentCanvasPlan.objectNo,
|
||||
canvasStatus: JSON.stringify(canvasStatus).replace(/"/g, '##'),
|
||||
}
|
||||
|
||||
await promisePost({ url: '/api/canvas-management/canvas-statuses', data: planData })
|
||||
.then((res) => {
|
||||
toastUp({ message: getMessage('res.message'), type: 'success' }) // 성공 시 메세지 없음
|
||||
console.log('[POST] canvas-statuses response :::::::: %o', res)
|
||||
})
|
||||
.catch((error) => {
|
||||
toastUp({ message: getMessage(error.message), type: 'error' })
|
||||
console.error('[POST] canvas-statuses res error :::::::: %o', error)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
canvas,
|
||||
removeMouseLines,
|
||||
saveCanvas,
|
||||
addCanvas,
|
||||
}
|
||||
}
|
||||
@ -218,3 +218,15 @@ export const adsorptionRangeState = atom({
|
||||
key: 'adsorptionRangeState',
|
||||
default: 50,
|
||||
})
|
||||
|
||||
// canvas plan 초기 목록
|
||||
export const initCanvasPlansState = atom({
|
||||
key: 'initCanvasPlans',
|
||||
default: [],
|
||||
})
|
||||
|
||||
// 현재 canvas plan
|
||||
export const currentCanvasPlanState = atom({
|
||||
key: 'currentCanvasPlan',
|
||||
default: {},
|
||||
})
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user