Merge branch 'dev'

This commit is contained in:
yoosangwook 2024-10-18 17:16:28 +09:00
commit dfde512b6f
52 changed files with 4639 additions and 3386 deletions

12
.env
View File

@ -1,12 +0,0 @@
# Environment variables declared in this file are automatically made available to Prisma.
# See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema
# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB.
# See the documentation for all the connection string options: https://pris.ly/d/connection-strings
# DATABASE_URL="postgresql://johndoe:randompassword@localhost:5432/mydb?schema=public"
# DATABASE_URL="mongodb://yoo32767:GuCtswjLGqUaNL0G@cluster0.vsdtcnb.mongodb.net/sample_mflix?retryWrites=true&w=majority"
#DATABASE_URL = "mongodb%2Bsrv%3A%2F%2Fyoo32767%3AGuCtswjLGqUaNL0G%40cluster0.vsdtcnb.mongodb.net%2F%3FretryWrites%3Dtrue%26w%3Dmajority%26appName%3DCluster0"
# DATABASE_URL = "mongodb+srv://yoo32767:GuCtswjLGqUaNL0G@cluster0.vsdtcnb.mongodb.net/Cluster0?retryWrites=true&w=majority"
# DATABASE_URL="mongodb://yoo32767:GuCtswjLGqUaNL0G@cluster0.vsdtcnb.mongodb.net/sample_mflix?retryWrites=true&w=majority"
DATABASE_URL="mongodb+srv://yoo32767:GuCtswjLGqUaNL0G@cluster0.vsdtcnb.mongodb.net/mytest"

View File

@ -1,14 +1,8 @@
NEXT_PUBLIC_TEST="테스트변수입니다. development"
NEXT_PUBLIC_API_SERVER_PATH="http://1.248.227.176:38080" NEXT_PUBLIC_API_SERVER_PATH="http://1.248.227.176:38080"
# NEXT_PUBLIC_API_SERVER_PATH="http://localhost:8080"
# NEXT_PUBLIC_API_SERVER_PATH="http://172.30.1.60:8080"
DATABASE_URL="sqlserver://mssql.devgrr.kr:1433;database=qcast;user=qcast;password=Qwertqaz12345;trustServerCertificate=true"
SESSION_SECRET="i3iHH1yp2/2SpQSIySQ4bpyc4g0D+zCF9FAn5xUG0+Y=" SESSION_SECRET="i3iHH1yp2/2SpQSIySQ4bpyc4g0D+zCF9FAn5xUG0+Y="
NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_bV5zuYMyyIYFlOb3" NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_bV5zuYMyyIYFlOb3"
NEXT_PUBLIC_Q_ORDER_AUTO_LOGIN_URL="http://q-order-local.q-cells.jp:8120/eos/login/autoLogin" NEXT_PUBLIC_Q_ORDER_AUTO_LOGIN_URL="http://q-order-qa.q-cells.jp:8120/eos/login/autoLogin"
NEXT_PUBLIC_Q_MUSUBI_AUTO_LOGIN_URL="http://q-musubi-local.q-cells.jp:8120/qm/login/autoLogin" NEXT_PUBLIC_Q_MUSUBI_AUTO_LOGIN_URL="http://q-musubi-qa.q-cells.jp:8120/qm/login/autoLogin"

View File

@ -1,12 +1,8 @@
NEXT_PUBLIC_TEST="테스트변수입니다. production"
NEXT_PUBLIC_API_SERVER_PATH="http://1.248.227.176:38080" NEXT_PUBLIC_API_SERVER_PATH="http://1.248.227.176:38080"
DATABASE_URL=""
SESSION_SECRET="i3iHH1yp2/2SpQSIySQ4bpyc4g0D+zCF9FAn5xUG0+Y=" SESSION_SECRET="i3iHH1yp2/2SpQSIySQ4bpyc4g0D+zCF9FAn5xUG0+Y="
NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_bV5zuYMyyIYFlOb3" NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_bV5zuYMyyIYFlOb3"
NEXT_PUBLIC_Q_ORDER_AUTO_LOGIN_URL="http://q-order-local.q-cells.jp:8120/eos/login/autoLogin" NEXT_PUBLIC_Q_ORDER_AUTO_LOGIN_URL="https://q-order.q-cells.jp/eos/login/autoLogin"
NEXT_PUBLIC_Q_MUSUBI_AUTO_LOGIN_URL="http://q-musubi-local.q-cells.jp:8120/qm/login/autoLogin" NEXT_PUBLIC_Q_MUSUBI_AUTO_LOGIN_URL="https://q-musubi.q-cells.jp/qm/login/autoLogin"

View File

@ -44,7 +44,6 @@
"prettier": "^3.3.3", "prettier": "^3.3.3",
"prisma": "^5.18.0", "prisma": "^5.18.0",
"react-color-palette": "^7.2.2", "react-color-palette": "^7.2.2",
"react-dropdown-select": "^4.11.3",
"react-select": "^5.8.1", "react-select": "^5.8.1",
"sass": "^1.77.8", "sass": "^1.77.8",
"tailwindcss": "^3.4.1" "tailwindcss": "^3.4.1"

View File

@ -0,0 +1,4 @@
<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0.998535 1L3.99723 3.99594L6.99593 1" stroke="#697C8F" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M7 6.98376L3.99319 3.99595L1.00263 6.99999" stroke="#697C8F" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 370 B

View File

@ -0,0 +1,12 @@
'use client'
import { createContext, useState } from 'react'
export const SessionContext = createContext({
session: {},
})
export default function SessionProvider({ useSession, children }) {
const [session, setSession] = useState(useSession)
return <SessionContext.Provider value={{ session }}>{children}</SessionContext.Provider>
}

View File

@ -15,6 +15,7 @@ import './globals.css'
import '../styles/style.scss' import '../styles/style.scss'
import '../styles/contents.scss' import '../styles/contents.scss'
import Dimmed from '@/components/ui/Dimmed' import Dimmed from '@/components/ui/Dimmed'
import SessionProvider from './SessionProvider'
// const inter = Inter({ subsets: ['latin'] }) // const inter = Inter({ subsets: ['latin'] })
@ -70,7 +71,9 @@ export default async function RootLayout({ children }) {
<Header userSession={sessionProps} /> <Header userSession={sessionProps} />
<div className="content"> <div className="content">
<Dimmed /> <Dimmed />
<QcastProvider>{children}</QcastProvider> <QcastProvider>
<SessionProvider useSession={sessionProps}>{children}</SessionProvider>
</QcastProvider>
</div> </div>
<footer> <footer>
<div className="footer-inner"> <div className="footer-inner">

View File

@ -66,8 +66,13 @@ export const LINE_TYPE = {
}, },
SUBLINE: { SUBLINE: {
/** /**
* * 추녀 / 마루 / 박공 / 지붕골 / 박공단
*/ */
HIP: 'hip',
RIDGE: 'ridge',
GABLE: 'gable',
VALLEY: 'valley',
VERGE: 'verge',
}, },
} }

View File

@ -30,7 +30,7 @@ export default function Playground() {
const fileRef = useRef(null) const fileRef = useRef(null)
const queryRef = useRef(null) const queryRef = useRef(null)
const [zoom, setZoom] = useState(20) const [zoom, setZoom] = useState(20)
const { get, promisePost } = useAxios() const { get, promiseGet, promisePost } = useAxios()
const testVar = process.env.NEXT_PUBLIC_TEST const testVar = process.env.NEXT_PUBLIC_TEST
const converterUrl = process.env.NEXT_PUBLIC_CONVERTER_API_URL const converterUrl = process.env.NEXT_PUBLIC_CONVERTER_API_URL
const { getMessage } = useMessage() const { getMessage } = useMessage()
@ -43,6 +43,8 @@ export default function Playground() {
const [checkboxInput, setCheckboxInput] = useState([]) const [checkboxInput, setCheckboxInput] = useState([])
const [selectedValue, setSelectedValue] = useState('') const [selectedValue, setSelectedValue] = useState('')
const [users, setUsers] = useState([])
useEffect(() => { useEffect(() => {
console.log('textInput:', textInput) console.log('textInput:', textInput)
}, [textInput]) }, [textInput])
@ -142,6 +144,10 @@ export default function Playground() {
}, },
} }
useEffect(() => {
console.log('users:', users)
}, [users])
return ( return (
<> <>
<div className="container mx-auto p-4 m-4 border"> <div className="container mx-auto p-4 m-4 border">
@ -305,6 +311,32 @@ export default function Playground() {
<div className="my-2"> <div className="my-2">
<QPagination {...paginationProps} /> <QPagination {...paginationProps} />
</div> </div>
<div className="my-2">
<Button
onClick={() => {
promiseGet({ url: 'http://localhost:8080/api/user' }).then((res) => setUsers(res.data))
}}
>
axios get test
</Button>
</div>
<div className="my-2">
<Button
onClick={() => {
const result = promisePost({
url: 'http://localhost:8080/api/user',
data: {
firstName: 'Yoo',
lastName: 'Sangwook',
email: 'yoo1757@naver.com',
age: 46,
},
}).then((res) => console.log('res', res))
}}
>
axios post test
</Button>
</div>
</div> </div>
</> </>
) )

View File

@ -27,7 +27,7 @@ export default function QContextMenu(props) {
const handleContextMenu = (e) => { const handleContextMenu = (e) => {
// e.preventDefault() // contextmenu // e.preventDefault() // contextmenu
setContextMenu({ visible: true, x: e.pageX, y: e.pageY }) setContextMenu({ visible: true, x: e.pageX, y: e.pageY })
console.log(111, canvasProps) // console.log(111, canvasProps)
canvasProps?.upperCanvasEl.removeEventListener('contextmenu', handleContextMenu) // canvasProps?.upperCanvasEl.removeEventListener('contextmenu', handleContextMenu) //
} }

View File

@ -61,7 +61,7 @@ export default function Archive() {
<div className="sub-content"> <div className="sub-content">
<div className="sub-content-inner"> <div className="sub-content-inner">
<div className="sub-table-box"> <div className="sub-table-box">
<Search title={boardType.boardTitle} subTitle={boardType.subTitle} isSelectUse={true} /> <Search title={boardType.boardTitle} subTitle={boardType.subTitle} isSelectUse={false} />
<ArchiveTable clsCode={boardType.clsCode} /> <ArchiveTable clsCode={boardType.clsCode} />
</div> </div>
</div> </div>

View File

@ -45,7 +45,7 @@ export default function ArchiveTable({ clsCode }) {
} }
fetchData() fetchData()
}, [search.searchValue]) }, [search.searchValue, search.searchFlag])
// //
const handleDetailFileListDown = async (noticeNo) => { const handleDetailFileListDown = async (noticeNo) => {
@ -74,30 +74,34 @@ export default function ArchiveTable({ clsCode }) {
return ( return (
<> <>
<div className="file-down-list"> {boardList.length > 0 ? (
{boardList?.map((board) => ( <div className="file-down-list">
<div key={board.noticeNo} className="file-down-item"> {boardList?.map((board) => (
<div className="file-item-info"> <div key={board.noticeNo} className="file-down-item">
<div className="item-num"> <div className="file-item-info">
{/* 번호 */} <div className="item-num">
{board.rowNumber} {/* 번호 */}
{board.rowNumber}
</div>
<div className="item-name">
{/* 제목 */}
{board.title}
</div>
<div className="item-date">
{/* 등록일 */}
{getMessage('board.sub.updDt')} : {board.uptDt ? board.uptDt : board.regDt}
</div>
</div> </div>
<div className="item-name"> <div className="file-down-box">
{/* 제목 */} {/* 첨부파일 */}
{board.title} <button type="button" className="file-down-btn" onClick={() => handleDetailFileListDown(board.noticeNo)}></button>
</div>
<div className="item-date">
{/* 등록일 */}
{getMessage('board.sub.updDt')} : {board.uptDt ? board.uptDt : board.regDt}
</div> </div>
</div> </div>
<div className="file-down-box"> ))}
{/* 첨부파일 */} </div>
<button type="button" className="file-down-btn" onClick={() => handleDetailFileListDown(board.noticeNo)}></button> ) : (
</div> <div className="file-down-nodata">{getMessage('common.message.no.data')}</div>
</div> )}
))}
</div>
</> </>
) )
} }

View File

@ -75,7 +75,7 @@ export const QLine = fabric.util.createClass(fabric.Line, {
const y2 = this.top + this.height * scaleY const y2 = this.top + this.height * scaleY
const dx = x2 - x1 const dx = x2 - x1
const dy = y2 - y1 const dy = y2 - y1
this.length = Number(Math.sqrt(dx * dx + dy * dy).toFixed(0)) this.length = Number(Math.sqrt(dx * dx + dy * dy).toFixed(1))
}, },
addLengthText() { addLengthText() {
@ -150,7 +150,7 @@ export const QLine = fabric.util.createClass(fabric.Line, {
getLength() { getLength() {
//10배 곱해진 값 return //10배 곱해진 값 return
return Number(this.length.toFixed(1) * 10) return Number(this.length.toFixed(0) * 10)
}, },
setViewLengthText(bool) { setViewLengthText(bool) {

View File

@ -199,32 +199,16 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
const end = points[(i + 1) % points.length] const end = points[(i + 1) % points.length]
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 = Number(Math.sqrt(dx * dx + dy * dy).toFixed(0)) * 10
let midPoint let midPoint
switch (this.direction) { midPoint = new fabric.Point((start.x + end.x) / 2, (start.y + end.y) / 2)
case 'north':
midPoint = new fabric.Point((start.x + end.x) / 2, (start.y + end.y) / 2 - 30)
break
case 'west':
midPoint = new fabric.Point((start.x + end.x) / 2 - 30, (start.y + end.y) / 2)
break
case 'south':
midPoint = new fabric.Point((start.x + end.x) / 2, (start.y + end.y) / 2 + 30)
break
case 'east':
midPoint = new fabric.Point((start.x + end.x) / 2 + 30, (start.y + end.y) / 2)
break
default:
midPoint = new fabric.Point((start.x + end.x) / 2, (start.y + end.y) / 2)
break
}
const degree = (Math.atan2(dy, dx) * 180) / Math.PI const degree = (Math.atan2(dy, dx) * 180) / Math.PI
// Create new text object if it doesn't exist // Create new text object if it doesn't exist
const text = new fabric.Text(length.toFixed(0), { const text = new fabric.Text(length.toString(), {
left: midPoint.x, left: midPoint.x,
top: midPoint.y, top: midPoint.y,
fontSize: this.fontSize, fontSize: this.fontSize,

View File

@ -13,11 +13,7 @@ export default function CanvasFrame({ plan }) {
const canvasRef = useRef(null) const canvasRef = useRef(null)
const { canvas } = useCanvas('canvas') const { canvas } = useCanvas('canvas')
const { contextMenu, currentContextMenu, setCurrentContextMenu } = useContextMenu() const { contextMenu, currentContextMenu, setCurrentContextMenu } = useContextMenu()
const currentObject = useRecoilValue(currentObjectState)
useEffect(() => {
console.log(currentObject)
}, [currentObject])
useEvent() useEvent()
const loadCanvas = () => { const loadCanvas = () => {

View File

@ -1,122 +1,29 @@
'use client' 'use client'
import { useEffect, useState } from 'react' import { useContext, useEffect, useState } from 'react'
import { useRecoilState, useRecoilValue } from 'recoil' import { useRecoilValue } from 'recoil'
import CanvasFrame from './CanvasFrame' import CanvasFrame from './CanvasFrame'
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'
import { globalLocaleStore } from '@/store/localeAtom' import { globalLocaleStore } from '@/store/localeAtom'
import { currentCanvasPlanState, initCanvasPlansState, plansState } from '@/store/canvasAtom'
import { sessionStore } from '@/store/commonAtom' import { sessionStore } from '@/store/commonAtom'
import { SessionContext } from '@/app/SessionProvider'
export default function CanvasLayout() { export default function CanvasLayout() {
const { session } = useContext(SessionContext)
console.log('session >>> ', session)
const [objectNo, setObjectNo] = useState('test123240822001') // const [objectNo, setObjectNo] = useState('test123240822001') //
const [planNum, setPlanNum] = useState(0)
const [currentCanvasPlan, setCurrentCanvasPlan] = useRecoilState(currentCanvasPlanState)
const [initCanvasPlans, setInitCanvasPlans] = useRecoilState(initCanvasPlansState)
const [plans, setPlans] = useRecoilState(plansState)
const globalLocaleState = useRecoilValue(globalLocaleStore)
const sessionState = useRecoilValue(sessionStore) const sessionState = useRecoilValue(sessionStore)
const globalLocaleState = useRecoilValue(globalLocaleStore)
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { swalFire } = useSwal() const { swalFire } = useSwal()
const { getCanvasByObjectNo, delCanvasById, checkModifiedCanvasPlan, saveCanvas, currentCanvasData } = usePlan() const { plans, loadCanvasPlanData, handleCurrentPlan, handleAddPlan, handleDeletePlan } = usePlan()
const handleCurrentPlan = (newCurrentId) => {
// console.log('currentPlan newCurrentId: ', newCurrentId)
if (!currentCanvasPlan || currentCanvasPlan.id !== newCurrentId) {
if (currentCanvasPlan?.id && checkModifiedCanvasPlan()) {
swalFire({
html: getMessage('common.message.confirm.save') + `</br>${currentCanvasPlan.name}`,
type: 'confirm',
confirmFn: async () => {
await saveCanvas(sessionState.userId)
updateCurrentPlan(newCurrentId)
},
denyFn: () => {
updateCurrentPlan(newCurrentId)
},
})
} else {
updateCurrentPlan(newCurrentId)
}
}
}
const updateCurrentPlan = (newCurrentId) => {
setPlans((plans) =>
plans.map((plan) => {
return { ...plan, isCurrent: plan.id === newCurrentId }
}),
)
}
useEffect(() => {
setCurrentCanvasPlan(plans.find((plan) => plan.isCurrent) || null)
}, [plans])
const handleDeletePlan = (e, id) => {
e.stopPropagation() //
if (initCanvasPlans.some((plan) => plan.id === id)) {
delCanvasById(id)
.then((res) => {
swalFire({ text: getMessage('common.message.delete') })
// console.log('[DELETE] canvas-statuses res :::::::: %o', res)
setInitCanvasPlans((initCanvasPlans) => initCanvasPlans.filter((plan) => plan.id !== id))
setPlans((plans) => plans.filter((plan) => plan.id !== id))
})
.catch((error) => {
swalFire({ text: error.message, icon: 'error' })
// console.error('[DELETE] canvas-statuses res error :::::::: %o', error)
})
} else {
setPlans((plans) => plans.filter((plan) => plan.id !== id))
swalFire({ text: getMessage('common.message.delete') })
}
// last
const lastPlan = plans.filter((plan) => plan.id !== id).at(-1)
if (!lastPlan) {
setPlanNum(0)
setCurrentCanvasPlan(null)
} else if (id !== lastPlan.id) {
handleCurrentPlan(lastPlan.id)
}
}
const addEmptyPlan = () => {
setPlans([...plans, { id: planNum, name: `Plan ${planNum + 1}`, objectNo: `${objectNo}` }])
handleCurrentPlan(planNum)
setPlanNum(planNum + 1)
}
const addCopyPlan = () => {
const copyPlan = {
id: planNum,
name: `Plan ${planNum + 1}`,
objectNo: `${objectNo}`,
userId: sessionState.userId,
canvasStatus: currentCanvasData(),
}
setPlans((plans) => [...plans, copyPlan])
handleCurrentPlan(planNum)
setPlanNum(planNum + 1)
}
useEffect(() => { useEffect(() => {
if (!currentCanvasPlan) { console.log('loadCanvasPlanData 실행, sessionState.userId >>> ', sessionState.userId)
getCanvasByObjectNo(sessionState.userId, objectNo).then((res) => { loadCanvasPlanData(sessionState.userId, objectNo)
// console.log('canvas ', res)
if (res.length > 0) {
setInitCanvasPlans(res)
setPlans(res)
handleCurrentPlan(res.at(-1).id) // last
setPlanNum(res.length)
} else {
addEmptyPlan()
}
})
}
}, []) }, [])
return ( return (
@ -127,14 +34,14 @@ export default function CanvasLayout() {
<button <button
key={`plan-${plan.id}`} key={`plan-${plan.id}`}
className={`canvas-page-box ${plan.isCurrent === true ? 'on' : ''}`} className={`canvas-page-box ${plan.isCurrent === true ? 'on' : ''}`}
onClick={() => handleCurrentPlan(plan.id)} onClick={() => handleCurrentPlan(sessionState.userId, plan.id)}
> >
<span>{plan.name}</span> <span>{plan.name}</span>
<i <i
className="close" className="close"
onClick={(e) => onClick={(e) =>
swalFire({ swalFire({
html: getMessage('common.message.confirm.delete') + `</br>${plan.name}`, text: `${plan.name} ` + getMessage('plan.message.confirm.delete'),
type: 'confirm', type: 'confirm',
confirmFn: () => { confirmFn: () => {
handleDeletePlan(e, plan.id) handleDeletePlan(e, plan.id)
@ -146,23 +53,7 @@ export default function CanvasLayout() {
))} ))}
</div> </div>
{plans.length < 10 && ( {plans.length < 10 && (
<button <button className="plane-add" onClick={() => handleAddPlan(sessionState.userId, objectNo)}>
className="plane-add"
onClick={() =>
JSON.parse(currentCanvasData()).objects.length > 0
? swalFire({
html: `${currentCanvasPlan.name} 을 복제하시겠습니까?`,
type: 'confirm',
confirmFn: () => {
addCopyPlan()
},
denyFn: () => {
addEmptyPlan()
},
})
: addEmptyPlan()
}
>
<span></span> <span></span>
</button> </button>
)} )}

View File

@ -19,6 +19,7 @@ import { MENU } from '@/common/common'
import KO from '@/locales/ko.json' import KO from '@/locales/ko.json'
import JA from '@/locales/ja.json' import JA from '@/locales/ja.json'
import { settingModalFirstOptionsState } from '@/store/settingAtom' import { settingModalFirstOptionsState } from '@/store/settingAtom'
import { placementShapeDrawingPointsState } from '@/store/placementShapeDrawingAtom'
const canvasMenus = [ const canvasMenus = [
{ index: 0, name: 'plan.menu.plan.drawing', icon: 'con00', title: MENU.PLAN_DRAWING }, { index: 0, name: 'plan.menu.plan.drawing', icon: 'con00', title: MENU.PLAN_DRAWING },
@ -58,7 +59,8 @@ export default function CanvasMenu(props) {
const [verticalHorizontalMode, setVerticalHorizontalMode] = useRecoilState(verticalHorizontalModeState) const [verticalHorizontalMode, setVerticalHorizontalMode] = useRecoilState(verticalHorizontalModeState)
const [appMessageState, setAppMessageState] = useRecoilState(appMessageStore) const [appMessageState, setAppMessageState] = useRecoilState(appMessageStore)
const setCurrentMenu = useSetRecoilState(currentMenuState) const setCurrentMenu = useSetRecoilState(currentMenuState)
const setPoints = useSetRecoilState(outerLinePointsState) const setOuterLinePoints = useSetRecoilState(outerLinePointsState)
const setPlacementPoints = useSetRecoilState(placementShapeDrawingPointsState)
const [canvasZoom, setCanvasZoom] = useRecoilState(canvasZoomState) const [canvasZoom, setCanvasZoom] = useRecoilState(canvasZoomState)
const [currentCanvasPlan, setcurrentCanvasPlan] = useRecoilState(currentCanvasPlanState) const [currentCanvasPlan, setcurrentCanvasPlan] = useRecoilState(currentCanvasPlanState)
@ -121,14 +123,14 @@ export default function CanvasMenu(props) {
}, [menuNumber, type]) }, [menuNumber, type])
// (btn08) // (btn08)
const handleSaveCanvas = () => { const handleSaveCanvas = async () => {
swalFire({ // swalFire({
html: getMessage('common.message.confirm.save') + `</br>${currentCanvasPlan.name}`, // text: `${currentCanvasPlan.name} ` + getMessage('plan.message.confirm.save'),
type: 'confirm', // type: 'confirm',
confirmFn: () => { // confirmFn: async () => {
saveCanvas(sessionState.userId) await saveCanvas(sessionState.userId)
}, // },
}) // })
} }
const onClickPlacementInitialMenu = () => { const onClickPlacementInitialMenu = () => {
@ -140,7 +142,8 @@ export default function CanvasMenu(props) {
} }
const handleClear = () => { const handleClear = () => {
setPoints([]) setOuterLinePoints([])
setPlacementPoints([])
canvas?.clear() canvas?.clear()
} }

View File

@ -1,8 +1,13 @@
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 { globalPitchState } from '@/store/canvasAtom'
import { useRecoilState } from 'recoil'
import { useRef } from 'react'
export default function Slope({ setShowSlopeSettingModal }) { export default function Slope({ setShowSlopeSettingModal }) {
const { getMessage } = useMessage() const { getMessage } = useMessage()
const [globalPitch, setGlobalPitch] = useRecoilState(globalPitchState)
const inputRef = useRef()
return ( return (
<WithDraggable isShow={true} pos={{ x: 50, y: -950 }}> <WithDraggable isShow={true} pos={{ x: 50, y: -950 }}>
<div className={`modal-pop-wrap xxxm`}> <div className={`modal-pop-wrap xxxm`}>
@ -19,13 +24,21 @@ export default function Slope({ setShowSlopeSettingModal }) {
{getMessage('slope')} {getMessage('slope')}
</span> </span>
<div className="input-grid mr5"> <div className="input-grid mr5">
<input type="text" className="input-origin block" defaultValue={300} /> <input type="text" className="input-origin block" defaultValue={globalPitch} ref={inputRef} />
</div> </div>
<span className="thin">{getMessage('size.angle')}</span> <span className="thin">{getMessage('size.angle')}</span>
</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={() => {
setGlobalPitch(inputRef.current.value)
setShowSlopeSettingModal(false)
}}
>
{getMessage('modal.common.save')}
</button>
</div> </div>
</div> </div>
</div> </div>

View File

@ -46,6 +46,7 @@ export default function ObjectSetting({ setShowObjectSettingModal }) {
heightRef: useRef(null), heightRef: useRef(null),
pitchRef: useRef(null), pitchRef: useRef(null),
offsetRef: useRef(null), offsetRef: useRef(null),
offsetWidthRef: useRef(null),
directionRef: useRef(null), directionRef: useRef(null),
} }

View File

@ -1,8 +1,17 @@
import Image from 'next/image' import Image from 'next/image'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { forwardRef, useState } from 'react'
export default function PentagonDormer() { const PentagonDormer = forwardRef((props, refs) => {
const { getMessage } = useMessage() const { getMessage } = useMessage()
const [direction, setDirection] = useState('down')
refs.directionRef.current = direction
const getDirection = (e) => {
setDirection(e.target.value)
refs.directionRef.current = e.target.value
}
return ( return (
<> <>
<div className="discrimination-box mb10"> <div className="discrimination-box mb10">
@ -18,7 +27,7 @@ export default function PentagonDormer() {
<div className="eaves-keraba-td"> <div className="eaves-keraba-td">
<div className="outline-form"> <div className="outline-form">
<div className="input-grid mr5" style={{ width: '60px' }}> <div className="input-grid mr5" style={{ width: '60px' }}>
<input type="text" className="input-origin block" defaultValue={2000} /> <input type="text" className="input-origin block" placeholder={0} ref={refs.heightRef} defaultValue={2000} />
</div> </div>
<span className="thin">mm</span> <span className="thin">mm</span>
</div> </div>
@ -29,7 +38,7 @@ export default function PentagonDormer() {
<div className="eaves-keraba-td"> <div className="eaves-keraba-td">
<div className="outline-form"> <div className="outline-form">
<div className="input-grid mr5" style={{ width: '60px' }}> <div className="input-grid mr5" style={{ width: '60px' }}>
<input type="text" className="input-origin block" defaultValue={1000} /> <input type="text" className="input-origin block" placeholder={0} ref={refs.offsetRef} />
</div> </div>
<span className="thin">mm</span> <span className="thin">mm</span>
</div> </div>
@ -40,18 +49,18 @@ export default function PentagonDormer() {
<div className="eaves-keraba-td"> <div className="eaves-keraba-td">
<div className="outline-form"> <div className="outline-form">
<div className="input-grid mr5" style={{ width: '60px' }}> <div className="input-grid mr5" style={{ width: '60px' }}>
<input type="text" className="input-origin block" defaultValue={4000} /> <input type="text" className="input-origin block" placeholder={0} ref={refs.widthRef} defaultValue={2000} />
</div> </div>
<span className="thin">mm</span> <span className="thin">mm</span>
</div> </div>
</div> </div>
</div> </div>
<div className="eaves-keraba-item"> <div className="eaves-keraba-item">
<div className="eaves-keraba-th">{getMessage('modal.object.setting.offset.depth')}</div> <div className="eaves-keraba-th">{getMessage('modal.object.setting.offset.width')}</div>
<div className="eaves-keraba-td"> <div className="eaves-keraba-td">
<div className="outline-form"> <div className="outline-form">
<div className="input-grid mr5" style={{ width: '60px' }}> <div className="input-grid mr5" style={{ width: '60px' }}>
<input type="text" className="input-origin block" defaultValue={500} /> <input type="text" className="input-origin block" placeholder={0} ref={refs.offsetWidthRef} />
</div> </div>
<span className="thin">mm</span> <span className="thin">mm</span>
</div> </div>
@ -62,7 +71,7 @@ export default function PentagonDormer() {
<div className="eaves-keraba-td"> <div className="eaves-keraba-td">
<div className="outline-form"> <div className="outline-form">
<div className="input-grid mr5" style={{ width: '60px' }}> <div className="input-grid mr5" style={{ width: '60px' }}>
<input type="text" className="input-origin block" defaultValue={4} /> <input type="text" className="input-origin block" placeholder={0} ref={refs.pitchRef} defaultValue={4} />
</div> </div>
<span className="thin"></span> <span className="thin"></span>
</div> </div>
@ -80,13 +89,15 @@ export default function PentagonDormer() {
<span className="right">{getMessage('commons.east')}</span> <span className="right">{getMessage('commons.east')}</span>
<span className="bottom">{getMessage('commons.south')}</span> <span className="bottom">{getMessage('commons.south')}</span>
<span className="left">{getMessage('commons.west')}</span> <span className="left">{getMessage('commons.west')}</span>
<button className="plane-btn up"></button> <button className={`plane-btn up ${direction === 'up' ? ' act' : ''}`} value="up" onClick={getDirection}></button>
<button className="plane-btn right"></button> <button className={`plane-btn right ${direction === 'right' ? ' act' : ''}`} value="right" onClick={getDirection}></button>
<button className="plane-btn down act"></button> <button className={`plane-btn down ${direction === 'down' ? ' act' : ''}`} value="down" onClick={getDirection}></button>
<button className="plane-btn left"></button> <button className={`plane-btn left ${direction === 'left' ? ' act' : ''}`} value="left" onClick={getDirection}></button>
</div> </div>
</div> </div>
</div> </div>
</> </>
) )
} })
export default PentagonDormer

View File

@ -5,7 +5,6 @@ import { forwardRef, useState } from 'react'
const TriangleDormer = forwardRef((props, refs) => { const TriangleDormer = forwardRef((props, refs) => {
const { getMessage } = useMessage() const { getMessage } = useMessage()
const [direction, setDirection] = useState('down') const [direction, setDirection] = useState('down')
refs.directionRef.current = direction refs.directionRef.current = direction
const getDirection = (e) => { const getDirection = (e) => {

View File

@ -66,6 +66,7 @@ export default function Stuff() {
gridColumns: [ gridColumns: [
{ {
field: 'lastEditDatetime', field: 'lastEditDatetime',
minWidth: 200,
headerName: getMessage('stuff.gridHeader.lastEditDatetime'), headerName: getMessage('stuff.gridHeader.lastEditDatetime'),
headerCheckboxSelection: true, headerCheckboxSelection: true,
headerCheckboxSelectionCurrentPageOnly: true, // headerCheckboxSelectionCurrentPageOnly: true, //
@ -244,9 +245,8 @@ export default function Stuff() {
} }
async function fetchData() { async function fetchData() {
// const apiUrl = `/api/object/list?saleStoreId=${sessionState?.storeId}&${queryStringFormatter(params)}` // const apiUrl = `/api/object/list?saleStoreId=T01&${queryStringFormatter(params)}`
const apiUrl = `/api/object/list?saleStoreId=T01&${queryStringFormatter(params)}` const apiUrl = `/api/object/list?saleStoreId=${sessionState?.storeId}&${queryStringFormatter(params)}`
await get({ await get({
url: apiUrl, url: apiUrl,
}).then((res) => { }).then((res) => {
@ -295,8 +295,6 @@ export default function Stuff() {
useEffect(() => { useEffect(() => {
if (stuffSearchParams?.code === 'E') { if (stuffSearchParams?.code === 'E') {
//console.log('::::::::', stuffSearchParams, sessionState) //console.log('::::::::', stuffSearchParams, sessionState)
// stuffSearchParams.startRow = (pageNo - 1) * pageSize + 1
// stuffSearchParams.endRow = pageNo * pageSize
stuffSearchParams.startRow = 1 stuffSearchParams.startRow = 1
stuffSearchParams.endRow = 1 * pageSize stuffSearchParams.endRow = 1 * pageSize
stuffSearchParams.schSortType = defaultSortType stuffSearchParams.schSortType = defaultSortType
@ -304,8 +302,8 @@ export default function Stuff() {
setPageNo(1) setPageNo(1)
async function fetchData() { async function fetchData() {
const apiUrl = `/api/object/list?saleStoreId=T01&${queryStringFormatter(stuffSearchParams)}` // const apiUrl = `/api/object/list?saleStoreId=T01&${queryStringFormatter(stuffSearchParams)}`
// const apiUrl = `/api/object/list?saleStoreId=${sessionState?.storeId}&${queryStringFormatter(stuffSearchParams)}` const apiUrl = `/api/object/list?saleStoreId=${sessionState?.storeId}&${queryStringFormatter(stuffSearchParams)}`
await get({ url: apiUrl }).then((res) => { await get({ url: apiUrl }).then((res) => {
if (!isEmptyArray(res)) { if (!isEmptyArray(res)) {
setGridProps({ ...gridProps, gridData: res, count: res[0].totCnt }) setGridProps({ ...gridProps, gridData: res, count: res[0].totCnt })
@ -334,8 +332,8 @@ export default function Stuff() {
}) })
setPageNo(1) setPageNo(1)
// const apiUrl = `/api/object/list?saleStoreId=${sessionState?.storeId}&${queryStringFormatter(stuffSearchParams)}` // const apiUrl = `/api/object/list?saleStoreId=T01&${queryStringFormatter(stuffSearchParams)}`
const apiUrl = `/api/object/list?saleStoreId=T01&${queryStringFormatter(stuffSearchParams)}` const apiUrl = `/api/object/list?saleStoreId=${sessionState?.storeId}&${queryStringFormatter(stuffSearchParams)}`
get({ url: apiUrl }).then((res) => { get({ url: apiUrl }).then((res) => {
if (!isEmptyArray(res)) { if (!isEmptyArray(res)) {
setGridProps({ ...gridProps, gridData: res, count: res[0].totCnt }) setGridProps({ ...gridProps, gridData: res, count: res[0].totCnt })
@ -365,8 +363,8 @@ export default function Stuff() {
setPageNo(1) setPageNo(1)
const apiUrl = `/api/object/list?saleStoreId=T01&${queryStringFormatter(stuffSearchParams)}` // const apiUrl = `/api/object/list?saleStoreId=T01&${queryStringFormatter(stuffSearchParams)}`
// const apiUrl = `/api/object/list?saleStoreId=${sessionState?.storeId}&${queryStringFormatter(stuffSearchParams)}` const apiUrl = `/api/object/list?saleStoreId=${sessionState?.storeId}&${queryStringFormatter(stuffSearchParams)}`
get({ url: apiUrl }).then((res) => { get({ url: apiUrl }).then((res) => {
if (!isEmptyArray(res)) { if (!isEmptyArray(res)) {
setGridProps({ ...gridProps, gridData: res, count: res[0].totCnt }) setGridProps({ ...gridProps, gridData: res, count: res[0].totCnt })
@ -406,14 +404,14 @@ export default function Stuff() {
<div className="sub-table-box"> <div className="sub-table-box">
<div className="table-box-title-wrap"> <div className="table-box-title-wrap">
<div className="title-wrap"> <div className="title-wrap">
<h3>물건목록</h3> <h3>{getMessage('stuff.search.grid.title')}</h3>
<ul className="info-wrap"> <ul className="info-wrap">
<li> <li>
전체 {getMessage('stuff.search.grid.all')}
<span>{convertNumberToPriceDecimal(totalCount)}</span> <span>{convertNumberToPriceDecimal(totalCount)}</span>
</li> </li>
<li> <li>
선택 {getMessage('stuff.search.grid.selected')}
<span className="red">{convertNumberToPriceDecimal(selectedRowDataCount)}</span> <span className="red">{convertNumberToPriceDecimal(selectedRowDataCount)}</span>
</li> </li>
</ul> </ul>
@ -421,8 +419,8 @@ export default function Stuff() {
<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" name="" id="" onChange={onChangeSortType}>
<option value="R">최근 등록일</option> <option value="R">{getMessage('stuff.search.grid.schSortTypeR')}</option>
<option value="U">최근 수정일</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' }}>

View File

@ -16,6 +16,8 @@ import FindAddressPop from './popup/FindAddressPop'
import PlanRequestPop from './popup/PlanRequestPop' import PlanRequestPop from './popup/PlanRequestPop'
import WindSelectPop from './popup/WindSelectPop' import WindSelectPop from './popup/WindSelectPop'
export default function StuffDetail() { export default function StuffDetail() {
const [selOptions, setSelOptions] = useState('')
const sessionState = useRecoilValue(sessionStore) const sessionState = useRecoilValue(sessionStore)
const router = useRouter() const router = useRouter()
@ -79,7 +81,7 @@ export default function StuffDetail() {
const [detailData, setDetailData] = useState({}) const [detailData, setDetailData] = useState({})
useEffect(() => { useEffect(() => {
console.log('objectNo::', objectNo) // console.log('objectNo::', objectNo)
if (objectNo) { if (objectNo) {
console.log('수정화면') console.log('수정화면')
@ -107,26 +109,28 @@ export default function StuffDetail() {
// 1 saleStoreId=201TES01 // 1 saleStoreId=201TES01
// T01 // T01
//1 : X167 //1 : X167 T01
get({ url: `/api/object/saleStore/T01/list` }).then((res) => { // get({ url: `/api/object/saleStore/TEMP02/list` }).then((res) => {
// get({ url: `/api/object/saleStore/${sessionState?.storeId}/list` }).then((res) => { get({ url: `/api/object/saleStore/${sessionState?.storeId}/list` }).then((res) => {
if (!isEmptyArray(res)) { if (!isEmptyArray(res)) {
const firstList = res.filter((row) => row.saleStoreLevel === '1') const firstList = res.filter((row) => row.saleStoreLevel === '1')
const otherList = res.filter((row) => row.saleStoreLevel !== '1') const otherList = res.filter((row) => row.saleStoreLevel !== '1')
//1 //1
setSaleStoreList(firstList) setSaleStoreList(firstList)
setSelOptions(sessionState?.storeId)
form.setValue('saleStoreId', sessionState?.storeId)
form.setValue('saleStoreLevel', sessionState?.storeLvl)
//1 //1
setOriginOtherSaleStoreList(otherList) setOriginOtherSaleStoreList(otherList)
setOtherSaleStoreList(otherList) setOtherSaleStoreList(otherList)
} }
}) })
} }
}, [objectNo]) }, [objectNo, sessionState])
useEffect(() => { useEffect(() => {
if (isObjectNotEmpty(detailData)) { if (isObjectNotEmpty(detailData)) {
console.log('상세데이타:::::::', detailData)
// API // API
get({ url: '/api/object/prefecture/list' }).then((res) => { get({ url: '/api/object/prefecture/list' }).then((res) => {
if (!isEmptyArray(res)) { if (!isEmptyArray(res)) {
@ -151,6 +155,8 @@ export default function StuffDetail() {
setOtherSaleStoreList(otherList) setOtherSaleStoreList(otherList)
} }
}) })
console.log('상세데이타::세팅:::::', detailData)
} }
}, [detailData]) }, [detailData])
@ -161,12 +167,14 @@ export default function StuffDetail() {
form.setValue('saleStoreId', key.saleStoreId) form.setValue('saleStoreId', key.saleStoreId)
form.setValue('saleStoreName', key.saleStoreName) form.setValue('saleStoreName', key.saleStoreName)
form.setValue('saleStoreLevel', key.saleStoreLevel) form.setValue('saleStoreLevel', key.saleStoreLevel)
setSelOptions(key.saleStoreId)
// 1 2 list // 1 2 list
//  // 
let newOtherSaleStoreList = originOtherSaleStoreList.filter((row) => row.firstAgentId === key.saleStoreId) let newOtherSaleStoreList = originOtherSaleStoreList.filter((row) => row.firstAgentId === key.saleStoreId)
setOtherSaleStoreList(newOtherSaleStoreList) setOtherSaleStoreList(newOtherSaleStoreList)
} else { } else {
//X //X
setSelOptions('')
form.setValue('saleStoreId', '') form.setValue('saleStoreId', '')
form.setValue('saleStoreName', '') form.setValue('saleStoreName', '')
form.setValue('saleStoreLevel', '') form.setValue('saleStoreLevel', '')
@ -209,8 +217,17 @@ export default function StuffDetail() {
form.setValue('zipNo', info.zipNo) form.setValue('zipNo', info.zipNo)
} }
// //
const setPlanReqInfo = (info) => {} const setPlanReqInfo = (info) => {
console.log('팝업에서 넘어온 설계의뢰 정보::: ', info)
//building :
//planReqName :
//zipNo :
// :address1 : address2
// setPrefValue(info.prefId)
// :
// form.setValue('dispCompanyName', info.planReqName)
}
// //
const setWindSppedInfo = (info) => { const setWindSppedInfo = (info) => {
@ -286,7 +303,7 @@ export default function StuffDetail() {
} }
// console.log('::', errors) // console.log('::', errors)
setIsFormValid(Object.keys(errors).length === 0) setIsFormValid(Object.keys(errors).length === 0 ? true : false)
} else { } else {
console.log('상세일때 폼체크') console.log('상세일때 폼체크')
} }
@ -345,25 +362,14 @@ export default function StuffDetail() {
form.setValue('areaId', e.target.value) form.setValue('areaId', e.target.value)
} }
// useEffect(() => {
// if (!isEmptyArray(areaIdList)) {
// let _prefName = form.watch('prefName')
// // console.log(' API', _prefName)
// get({ url: `/api/object/windSpeed/${_prefName}/list` }).then((res) => {
// // console.log('res::', res)
// if (!isEmptyArray(res)) {
// setWindSpeedList(res)
// }
// })
// }
// }, [areaIdList])
// //
const onValid = (data) => { const onValid = (data, e) => {
const formData = form.getValues()
console.log('필수값 통과:::', data, formData)
// console.log(' formData:::', formData)
// PUT // PUT
// console.log(' ') // console.log(' ')
// console.log('data::::::', data) // console.log('data::::::', data)
const formData = form.getValues()
// console.log('formData::::', formData) // console.log('formData::::', formData)
// const _dispCompanyName = watch('dispCompanyName') // const _dispCompanyName = watch('dispCompanyName')
// const _objectStatusId = watch('objectStatusId') // const _objectStatusId = watch('objectStatusId')
@ -391,6 +397,8 @@ export default function StuffDetail() {
// //
const onTempSave = async () => { const onTempSave = async () => {
console.log('임시저장:::::')
return
const formData = form.getValues() const formData = form.getValues()
// console.log('formData::', formData) // console.log('formData::', formData)
const params = { const params = {
@ -441,7 +449,7 @@ export default function StuffDetail() {
let testobj = '10' let testobj = '10'
del({ url: `/api/object/${testobj}` }).then((res) => { del({ url: `/api/object/${testobj}` }).then((res) => {
// console.log(' :::', res) console.log('삭제 결과:::', res)
router.push('/management/stuff') router.push('/management/stuff')
}) })
} }
@ -470,9 +478,9 @@ export default function StuffDetail() {
<div className="input-wrap mr5" style={{ width: '200px' }}> <div className="input-wrap mr5" style={{ width: '200px' }}>
<input type="text" className="input-light" readOnly /> <input type="text" className="input-light" readOnly />
</div> </div>
<button className="btn-origin grey" onClick={onSearchDesignRequestPopOpen}> <Button className="btn-origin grey" onClick={onSearchDesignRequestPopOpen}>
{getMessage('stuff.planReqPopup.title')} {getMessage('stuff.planReqPopup.title')}
</button> </Button>
</div> </div>
</td> </td>
</tr> </tr>
@ -536,13 +544,21 @@ export default function StuffDetail() {
</th> </th>
<td> <td>
<div className="flx-box"> <div className="flx-box">
<div style={{ width: '567px', marginRight: '5px' }}> <div className="select-wrap mr5" style={{ width: '567px' }}>
<Select <Select
id="long-value-select1"
instanceId="long-value-select1"
className="react-select-custom"
classNamePrefix="custom"
placeholder="Select"
options={saleStoreList} options={saleStoreList}
onChange={onSelectionChange} onChange={onSelectionChange}
getOptionLabel={(x) => x.saleStoreName} getOptionLabel={(x) => x.saleStoreName}
getOptionValue={(x) => x.saleStoreId} getOptionValue={(x) => x.saleStoreId}
isClearable={true} isClearable={true}
value={saleStoreList.filter(function (option) {
return option.saleStoreId === selOptions
})}
/> />
</div> </div>
<div className="input-wrap" style={{ width: '216px' }}> <div className="input-wrap" style={{ width: '216px' }}>
@ -562,8 +578,13 @@ export default function StuffDetail() {
</th> </th>
<td> <td>
<div className="flx-box"> <div className="flx-box">
<div style={{ width: '567px', marginRight: '5px' }}> <div className="select-wrap mr5" style={{ width: '567px' }}>
<Select <Select
id="long-value-select2"
instanceId="long-value-select2"
className="react-select-custom"
classNamePrefix="custom"
placeholder="Select"
ref={ref} ref={ref}
options={otherSaleStoreList} options={otherSaleStoreList}
onChange={onSelectionChange2} onChange={onSelectionChange2}
@ -669,9 +690,9 @@ export default function StuffDetail() {
</select> </select>
</div> */} </div> */}
<span className="mr10">{getMessage('stuff.detail.windSpeedSpan')}</span> <span className="mr10">{getMessage('stuff.detail.windSpeedSpan')}</span>
<button className="btn-origin grey" onClick={onSearchWindSpeedPopOpen}> <Button className="btn-origin grey" onClick={onSearchWindSpeedPopOpen}>
{getMessage('stuff.detail.btn.windSpeedPop')} {getMessage('stuff.detail.btn.windSpeedPop')}
</button> </Button>
</div> </div>
</td> </td>
</tr> </tr>
@ -764,18 +785,18 @@ export default function StuffDetail() {
</div> </div>
<div className="sub-table-footer"> <div className="sub-table-footer">
{!isFormValid ? ( {!isFormValid ? (
<button className="btn-origin grey mr5" onClick={onTempSave}> <Button className="btn-origin grey mr5" onClick={onTempSave}>
New화면 임시저장 New화면 임시저장
</button> </Button>
) : ( ) : (
<button type="submit" className="btn-origin navy mr5"> <Button type="submit" className="btn-origin navy mr5">
NEW 화면 저장 NEW 화면 저장
</button> </Button>
)} )}
<Link href="/management/stuff"> <Link href="/management/stuff">
<button type="button" className="btn-origin grey"> <Button type="button" className="btn-origin grey">
NEW화면 물건목록이동 NEW화면 물건목록이동
</button> </Button>
</Link> </Link>
</div> </div>
</div> </div>
@ -801,9 +822,9 @@ export default function StuffDetail() {
<div className="input-wrap mr5" style={{ width: '200px' }}> <div className="input-wrap mr5" style={{ width: '200px' }}>
<input type="text" className="input-light" readOnly /> <input type="text" className="input-light" readOnly />
</div> </div>
<button className="btn-origin grey" onClick={onSearchDesignRequestPopOpen}> <Button className="btn-origin grey" onClick={onSearchDesignRequestPopOpen}>
{getMessage('stuff.planReqPopup.title')} {getMessage('stuff.planReqPopup.title')}
</button> </Button>
</div> </div>
</td> </td>
</tr> </tr>
@ -813,7 +834,7 @@ export default function StuffDetail() {
</th> </th>
<td> <td>
<div className="input-wrap" style={{ width: '500px' }}> <div className="input-wrap" style={{ width: '500px' }}>
{/* <input type="text" className="input-light" {...form.register('dispCompanyName')} value={form.watch('dispCompanyName')} /> */} <input type="text" className="input-light" {...form.register('dispCompanyName')} value={form.watch('dispCompanyName')} />
</div> </div>
</td> </td>
</tr> </tr>
@ -865,7 +886,7 @@ export default function StuffDetail() {
</th> </th>
<td> <td>
<div className="flx-box"> <div className="flx-box">
<div style={{ width: '567px', marginRight: '5px' }}> <div className="select-wrap mr5" style={{ width: '567px' }}>
{/* <Select {/* <Select
options={saleStoreList} options={saleStoreList}
value={form.watch('saleStoreId')} value={form.watch('saleStoreId')}
@ -890,32 +911,32 @@ export default function StuffDetail() {
{objectNo.substring(0, 1) === 'R' ? ( {objectNo.substring(0, 1) === 'R' ? (
<> <>
<Link href="/management/stuff"> <Link href="/management/stuff">
<button type="button" className="btn-origin grey"> <Button type="button" className="btn-origin grey mr5">
R상세:물건목록 R상세:물건목록
</button> </Button>
</Link> </Link>
<button type="submit" className="btn-origin navy mr5"> <Button type="submit" className="btn-origin navy mr5">
R상세:저장 R상세:저장
</button> </Button>
<button type="submit" className="btn-origin navy mr5" onClick={onDelete}> <Button type="submit" className="btn-origin navy" onClick={onDelete}>
R상세:물건삭제 R상세:물건삭제
</button> </Button>
</> </>
) : ( ) : (
<> <>
{!isFormValid ? ( {!isFormValid ? (
<button type="submit" className="btn-origin navy mr5" onClick={onTempSave}> <Button type="submit" className="btn-origin navy mr5" onClick={onTempSave}>
TEMP상세:임시저장 TEMP상세:임시저장
</button> </Button>
) : ( ) : (
<button type="submit" className="btn-origin navy mr5"> <Button type="submit" className="btn-origin navy mr5">
TEMP상세:저장 TEMP상세:저장
</button> </Button>
)} )}
<Link href="/management/stuff"> <Link href="/management/stuff">
<button type="button" className="btn-origin grey"> <Button type="button" className="btn-origin grey">
T상세:물건목록 T상세:물건목록
</button> </Button>
</Link> </Link>
</> </>
)} )}
@ -923,7 +944,14 @@ export default function StuffDetail() {
)} )}
{showAddressButtonValid && <FindAddressPop setShowAddressButtonValid={setShowAddressButtonValid} zipInfo={setZipInfo} />} {showAddressButtonValid && <FindAddressPop setShowAddressButtonValid={setShowAddressButtonValid} zipInfo={setZipInfo} />}
{showDesignRequestButtonValid && ( {showDesignRequestButtonValid && (
<PlanRequestPop setShowDesignRequestButtonValid={setShowDesignRequestButtonValid} planReqInfo={setPlanReqInfo} /> <PlanRequestPop
setShowDesignRequestButtonValid={setShowDesignRequestButtonValid}
saleStoreId={form.watch('saleStoreId')}
saleStoreLevel={form.watch('saleStoreLevel')}
otherSaleStoreId={form.watch('otherSaleStoreId')}
otherSaleStoreLevel={form.watch('otherSaleStoreLevel')}
planReqInfo={setPlanReqInfo}
/>
)} )}
{showWindSpeedButtonValid && ( {showWindSpeedButtonValid && (
<WindSelectPop setShowWindSpeedButtonValid={setShowWindSpeedButtonValid} prefName={form.watch('prefName')} windSpeedInfo={setWindSppedInfo} /> <WindSelectPop setShowWindSpeedButtonValid={setShowWindSpeedButtonValid} prefName={form.watch('prefName')} windSpeedInfo={setWindSppedInfo} />

View File

@ -69,12 +69,6 @@ export default function StuffQGrid(props) {
props.getCellDoubleClicked(event) props.getCellDoubleClicked(event)
}, []) }, [])
const autoSizeStrategy = useMemo(() => {
return {
type: 'fitCellContents',
}
}, [])
// Fetch data & update rowData state // Fetch data & update rowData state
useEffect(() => { useEffect(() => {
gridData ? setRowData(gridData) : '' gridData ? setRowData(gridData) : ''

View File

@ -18,7 +18,6 @@ import { useMessage } from '@/hooks/useMessage'
import { isObjectNotEmpty } from '@/util/common-utils' import { isObjectNotEmpty } from '@/util/common-utils'
export default function StuffSearchCondition() { export default function StuffSearchCondition() {
const sessionState = useRecoilValue(sessionStore) const sessionState = useRecoilValue(sessionStore)
const [appMessageState, setAppMessageState] = useRecoilState(appMessageStore) const [appMessageState, setAppMessageState] = useRecoilState(appMessageStore)
const globalLocaleState = useRecoilValue(globalLocaleStore) const globalLocaleState = useRecoilValue(globalLocaleStore)
const { getMessage } = useMessage() const { getMessage } = useMessage()
@ -74,10 +73,6 @@ export default function StuffSearchCondition() {
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',
selObject: {
label: stuffSearch.selObject.label,
value: stuffSearch.selObject.value,
},
}) })
} }
@ -99,9 +94,8 @@ export default function StuffSearchCondition() {
useEffect(() => { useEffect(() => {
if (isObjectNotEmpty(sessionState)) { if (isObjectNotEmpty(sessionState)) {
// console.log(' ::::::::', sessionState)
// storeId T01 1 // storeId T01 1
// get({ url: `/api/object/saleStore/201TES01/list` }).then((res) => { // get({ url: `/api/object/saleStore/TEMP02/list` }).then((res) => {
get({ url: `/api/object/saleStore/${sessionState?.storeId}/list` }).then((res) => { get({ url: `/api/object/saleStore/${sessionState?.storeId}/list` }).then((res) => {
if (!isEmptyArray(res)) { if (!isEmptyArray(res)) {
res.map((row) => { res.map((row) => {
@ -129,7 +123,6 @@ export default function StuffSearchCondition() {
...stuffSearch, ...stuffSearch,
code: 'S', code: 'S',
schSelSaleStoreId: key.saleStoreId, schSelSaleStoreId: key.saleStoreId,
selObject: { value: key.saleStoreId, label: key.saleStoreName },
}) })
} else { } else {
setSchSelSaleStoreId('') setSchSelSaleStoreId('')
@ -159,7 +152,7 @@ export default function StuffSearchCondition() {
<h3>{getMessage('stuff.search.title')}</h3> <h3>{getMessage('stuff.search.title')}</h3>
</div> </div>
<div className="left-unit-box"> <div className="left-unit-box">
<Link href="/management/stuff/tempdetail"> <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.btn1')}
</button> </button>
@ -190,7 +183,6 @@ export default function StuffSearchCondition() {
<input <input
type="text" type="text"
className="input-light" className="input-light"
// placeholder=" "
value={stuffSearch?.code === 'E' || stuffSearch?.code === 'M' ? stuffSearch.schObjectNo : objectNo} value={stuffSearch?.code === 'E' || stuffSearch?.code === 'M' ? stuffSearch.schObjectNo : objectNo}
onChange={(e) => { onChange={(e) => {
setObjectNo(e.target.value) setObjectNo(e.target.value)
@ -205,7 +197,6 @@ export default function StuffSearchCondition() {
<input <input
type="text" type="text"
className="input-light" className="input-light"
// placeholder=" "
value={stuffSearch?.schSaleStoreName ? stuffSearch.schSaleStoreName : saleStoreName} value={stuffSearch?.schSaleStoreName ? stuffSearch.schSaleStoreName : saleStoreName}
onChange={(e) => { onChange={(e) => {
setSaleStoreName(e.target.value) setSaleStoreName(e.target.value)
@ -220,7 +211,6 @@ export default function StuffSearchCondition() {
<input <input
type="text" type="text"
className="input-light" className="input-light"
// placeholder=" "
value={stuffSearch?.schAddress ? stuffSearch.schAddress : address} value={stuffSearch?.schAddress ? stuffSearch.schAddress : address}
onChange={(e) => { onChange={(e) => {
setAddress(e.target.value) setAddress(e.target.value)
@ -237,7 +227,6 @@ export default function StuffSearchCondition() {
<input <input
type="text" type="text"
className="input-light" className="input-light"
// placeholder=" "
value={stuffSearch?.schObjectName ? stuffSearch.schObjectName : objectName} value={stuffSearch?.schObjectName ? stuffSearch.schObjectName : objectName}
onChange={(e) => { onChange={(e) => {
setobjectName(e.target.value) setobjectName(e.target.value)
@ -252,7 +241,6 @@ export default function StuffSearchCondition() {
<input <input
type="text" type="text"
className="input-light" className="input-light"
// placeholder=" "
value={stuffSearch?.schDispCompanyName ? stuffSearch.schDispCompanyName : dispCompanyName} value={stuffSearch?.schDispCompanyName ? stuffSearch.schDispCompanyName : dispCompanyName}
onChange={(e) => { onChange={(e) => {
setDispCompanyName(e.target.value) setDispCompanyName(e.target.value)
@ -263,18 +251,39 @@ export default function StuffSearchCondition() {
</td> </td>
<th>{getMessage('stuff.search.schSelSaleStoreId')}</th> <th>{getMessage('stuff.search.schSelSaleStoreId')}</th>
<td> <td>
{schSelSaleStoreList?.length > 0 && ( <div className="select-wrap">
<Select {schSelSaleStoreList?.length > 0 && (
ref={ref} <Select
options={schSelSaleStoreList} id="long-value-select1"
onChange={onSelectionChange} instanceId="long-value-select1"
getOptionLabel={(x) => x.saleStoreName} className="react-select-custom"
getOptionValue={(x) => x.saleStoreId} classNamePrefix="custom"
defaultValue={stuffSearch?.selObject?.value ? stuffSearch?.selObject : null} placeholder="Select"
isDisabled={sessionState?.storeLvl === '1' ? false : true} ref={ref}
isClearable={true} options={schSelSaleStoreList}
/> onChange={onSelectionChange}
)} getOptionLabel={(x) => x.saleStoreName}
getOptionValue={(x) => x.saleStoreId}
value={schSelSaleStoreList.filter(function (option) {
if (stuffSearch?.code === 'S' && schSelSaleStoreId === '') {
return false
} else if (stuffSearch?.code === 'S' && schSelSaleStoreId !== '') {
return option.saleStoreId === schSelSaleStoreId
} else if (stuffSearch?.code === 'E' && schSelSaleStoreId !== '') {
return option.saleStoreId === schSelSaleStoreId
} else {
if (stuffSearch?.schSelSaleStoreId !== '') {
return option.saleStoreId === stuffSearch.schSelSaleStoreId
} else {
return false
}
}
})}
isDisabled={sessionState?.storeLvl === '1' ? false : true}
isClearable={true}
/>
)}
</div>
</td> </td>
</tr> </tr>
<tr> <tr>
@ -284,7 +293,6 @@ export default function StuffSearchCondition() {
<input <input
type="text" type="text"
className="input-light" className="input-light"
// placeholder=" "
value={stuffSearch?.schReceiveUser ? stuffSearch.schReceiveUser : receiveUser} value={stuffSearch?.schReceiveUser ? stuffSearch.schReceiveUser : receiveUser}
onChange={(e) => { onChange={(e) => {
setReceiveUser(e.target.value) setReceiveUser(e.target.value)

View File

@ -79,7 +79,6 @@ export default function FindAddressPop(props) {
} }
// //
const applyAddress = () => { const applyAddress = () => {
// console.log(' :::::::::', prefId, address1, address2, address3, zipNo)
if (prefId == null) { if (prefId == null) {
alert(getMessage('stuff.addressPopup.error.message2')) alert(getMessage('stuff.addressPopup.error.message2'))
} else { } else {

View File

@ -1,6 +1,5 @@
import React, { useState, useRef, useEffect } from 'react' import React, { useState, useRef, useEffect } from 'react'
import { useForm } from 'react-hook-form' import { useForm } from 'react-hook-form'
import { queryStringFormatter } from '@/util/common-utils'
import { useAxios } from '@/hooks/useAxios' import { useAxios } from '@/hooks/useAxios'
import { globalLocaleStore } from '@/store/localeAtom' import { globalLocaleStore } from '@/store/localeAtom'
import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil' import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil'
@ -12,14 +11,24 @@ import dayjs from 'dayjs'
import PlanRequestPopQGrid from './PlanRequestPopQGrid' import PlanRequestPopQGrid from './PlanRequestPopQGrid'
import { sessionStore } from '@/store/commonAtom' import { sessionStore } from '@/store/commonAtom'
import { planReqSearchState } from '@/store/planReqAtom' import { planReqSearchState } from '@/store/planReqAtom'
import { isObjectNotEmpty, queryStringFormatter } from '@/util/common-utils'
import Select from 'react-select'
import QPagination from '@/components/common/pagination/QPagination'
export default function PlanRequestPop(props) { export default function PlanRequestPop(props) {
const sessionState = useRecoilValue(sessionStore) const [pageNo, setPageNo] = useState(1) //
const [pageSize, setPageSize] = useState(20) //
const [totalCount, setTotalCount] = useState(0) //
const globalLocaleState = useRecoilValue(globalLocaleStore) const globalLocaleState = useRecoilValue(globalLocaleStore)
const { get } = useAxios(globalLocaleState) const [planReqObject, setPlanReqObject] = useState({})
const { get, promiseGet } = useAxios(globalLocaleState)
const { getMessage } = useMessage() const { getMessage } = useMessage()
//Select ref
const ref = useRef()
// //
const [startDate, setStartDate] = useState(dayjs(new Date()).add(-3, 'month').format('YYYY-MM-DD')) const [startDate, setStartDate] = useState(dayjs(new Date()).add(-3, 'month').format('YYYY-MM-DD'))
const [endDate, setEndDate] = useState(dayjs(new Date()).format('YYYY-MM-DD')) const [endDate, setEndDate] = useState(dayjs(new Date()).format('YYYY-MM-DD'))
@ -34,7 +43,6 @@ export default function PlanRequestPop(props) {
setStartDate: setEndDate, setStartDate: setEndDate,
} }
const ref = useRef()
const resetPlanReqRecoil = useResetRecoilState(planReqSearchState) const resetPlanReqRecoil = useResetRecoilState(planReqSearchState)
const [planReqSearch, setPlanReqSearch] = useRecoilState(planReqSearchState) const [planReqSearch, setPlanReqSearch] = useRecoilState(planReqSearchState)
@ -47,14 +55,41 @@ export default function PlanRequestPop(props) {
const [schDateGbn, setSchDateGbn] = useState('S') //(S/R) const [schDateGbn, setSchDateGbn] = useState('S') //(S/R)
// //
const resetRecoil = () => {} const resetRecoil = () => {
setSchPlanReqNo('')
setSchTitle('')
setSchAddress('')
setSchSaleStoreName('')
setSchPlanReqName('')
setSchDateGbn('S')
setStartDate(dayjs(new Date()).add(-3, 'month').format('YYYY-MM-DD'))
setEndDate(dayjs(new Date()).format('YYYY-MM-DD'))
setSchPlanStatCd('')
handleClear() //
resetPlanReqRecoil()
}
// .. //
const handleClear = () => { const handleClear = () => {
if (ref.current.state.dropDown) { if (ref.current) {
ref.current.methods.dropDown() ref.current.clearValue()
}
}
//
const onSelectionChange = (key) => {
//
// console.log('E::::::::', key)
if (isObjectNotEmpty(key)) {
setSchPlanStatCd(key.value)
setPlanReqSearch({
...planReqSearch,
schPlanStatCd: key.value,
})
} else { } else {
ref.current.state.values = [] //X
setSchPlanStatCd('')
setPlanReqSearch({ ...planReqSearch, schPlanStatCd: '' })
} }
} }
@ -63,6 +98,60 @@ export default function PlanRequestPop(props) {
setEndDate(planReqSearch?.schEndDt ? planReqSearch.schEndDt : dayjs(new Date()).format('YYYY-MM-DD')) setEndDate(planReqSearch?.schEndDt ? planReqSearch.schEndDt : dayjs(new Date()).format('YYYY-MM-DD'))
}, [planReqSearch]) }, [planReqSearch])
//
const onSubmit = (page, type) => {
const params = {
// saleStoreId: 'T100',
// saleStoreLevel: '1',
saleStoreId: props?.otherSaleStoreId ? props.otherSaleStoreId : props.saleStoreId,
saleStoreLevel: props?.otherSaleStoreLevel ? props.otherSaleStoreLevel : props.saleStoreLevel,
schPlanReqNo: planReqSearch?.schPlanReqNo ? planReqSearch.schPlanReqNo : schPlanReqNo,
schTitle: planReqSearch?.schTitle ? planReqSearch.schTitle : schTitle,
schAddress: planReqSearch?.schAddress ? planReqSearch.schAddress : schAddress,
schSaleStoreName: planReqSearch?.schSaleStoreName ? planReqSearch.schSaleStoreName : schSaleStoreName,
schPlanReqName: planReqSearch?.schPlanReqName ? planReqSearch.schPlanReqName : schPlanReqName,
schPlanStatCd: planReqSearch?.schPlanStatCd ? planReqSearch.schPlanStatCd : schPlanStatCd,
schDateGbn: planReqSearch?.schDateGbn ? planReqSearch.schDateGbn : schDateGbn,
schStartDt: dayjs(startDate).format('YYYY-MM-DD'),
schEndDt: dayjs(endDate).format('YYYY-MM-DD'),
startRow: type === 'S' ? (1 - 1) * pageSize + 1 : (page - 1) * pageSize + 1,
endRow: type === 'S' ? 1 * pageSize : page * pageSize,
}
if (type === 'S') {
setPageNo(1)
} else {
setPageNo(page)
}
// console.log(params)
const apiUrl = `/api/object/planReq/list?${queryStringFormatter(params)}`
promiseGet({ url: apiUrl }).then((res) => {
if (res.status === 200) {
if (isNotEmptyArray(res.data.data)) {
setGridProps({ ...gridProps, gridData: res.data.data, gridCount: res.data.data[0].totCnt })
setTotalCount(res.data.data[0].totCnt)
} else {
setGridProps({ ...gridProps, gridData: [], gridCount: 0 })
setTotalCount(0)
}
} else {
setGridProps({ ...gridProps, gridData: [], gridCount: 0 })
setTotalCount(0)
}
})
}
//
const handleChangePage = (page) => {
setPlanReqSearch({
...planReqSearch,
startRow: (page - 1) * pageSize + 1,
endRow: page * pageSize,
})
setPageNo(page)
onSubmit(page, 'P')
}
const [gridProps, setGridProps] = useState({ const [gridProps, setGridProps] = useState({
gridData: [], gridData: [],
isPageable: false, isPageable: false,
@ -117,6 +206,45 @@ export default function PlanRequestPop(props) {
], ],
}) })
//
const getSelectedRowdata = (data) => {
if (isNotEmptyArray(data)) {
setPlanReqObject(data[0])
} else {
setPlanReqObject({})
}
}
//
const applyPlanReq = () => {
if (isObjectNotEmpty(planReqObject)) {
props.planReqInfo(planReqObject)
//
props.setShowDesignRequestButtonValid(false)
} else {
alert(getMessage('stuff.planReqPopup.error.message1'))
}
}
const tempList = [
{
label: '완료',
value: 'C',
},
{
label: '저장',
value: 'I',
},
{
label: '접수',
value: 'R',
},
{
label: '제출',
value: 'S',
},
]
return ( return (
<div className="modal-popup"> <div className="modal-popup">
<div className="modal-dialog big"> <div className="modal-dialog big">
@ -132,8 +260,17 @@ export default function PlanRequestPop(props) {
<div className="design-tit-wrap"> <div className="design-tit-wrap">
<h3>{getMessage('stuff.planReqPopup.popTitle')}</h3> <h3>{getMessage('stuff.planReqPopup.popTitle')}</h3>
<div className="design-search-wrap"> <div className="design-search-wrap">
<button className="btn-origin grey mr5">{getMessage('stuff.planReqPopup.btn1')}</button> <button
<button className="btn-origin navy ">{getMessage('stuff.planReqPopup.btn2')}</button> className="btn-origin navy mr5"
onClick={() => {
onSubmit(pageNo, 'S')
}}
>
{getMessage('stuff.planReqPopup.btn1')}
</button>
<button className="btn-origin grey" onClick={resetRecoil}>
{getMessage('stuff.planReqPopup.btn2')}
</button>
</div> </div>
</div> </div>
<div className="design-request-table"> <div className="design-request-table">
@ -224,12 +361,18 @@ export default function PlanRequestPop(props) {
<th>{getMessage('stuff.planReqPopup.search.planStatName')}</th> <th>{getMessage('stuff.planReqPopup.search.planStatName')}</th>
<td> <td>
<div className="select-wrap"> <div className="select-wrap">
<select className="select-light" name="" id=""> <Select
<option value={''}>All</option> id="long-value-select1"
<option value={'SAVE'}>저장</option> instanceId="long-value-select1"
<option value={'SUBMIT'}>제출</option> className="react-select-custom"
<option value={'RECEIPT'}>접수</option> classNamePrefix="custom"
</select> ref={ref}
options={tempList}
onChange={onSelectionChange}
isSearchable={false}
placeholder="Select"
isClearable={true}
/>
</div> </div>
</td> </td>
</tr> </tr>
@ -285,14 +428,19 @@ export default function PlanRequestPop(props) {
</div> </div>
<div className="design-request-grid"> <div className="design-request-grid">
<div className="design-request-grid-tit">Plan List</div> <div className="design-request-grid-tit">Plan List</div>
<PlanRequestPopQGrid {...gridProps} /> <PlanRequestPopQGrid {...gridProps} getSelectedRowdata={getSelectedRowdata} />
<div className="pagination-wrap">
<QPagination pageNo={pageNo} pageSize={pageSize} pagePerBlock={10} totalCount={totalCount} handleChangePage={handleChangePage} />
</div>
</div> </div>
</div> </div>
<div className="footer-btn-wrap"> <div className="footer-btn-wrap">
<button className="btn-origin grey mr5" onClick={() => props.setShowDesignRequestButtonValid(false)}> <button className="btn-origin grey mr5" onClick={() => props.setShowDesignRequestButtonValid(false)}>
{getMessage('stuff.planReqPopup.btn3')} {getMessage('stuff.planReqPopup.btn3')}
</button> </button>
<button className="btn-origin navy ">{getMessage('stuff.planReqPopup.btn4')}</button> <button className="btn-origin navy" onClick={applyPlanReq}>
{getMessage('stuff.planReqPopup.btn4')}
</button>
</div> </div>
</div> </div>
</div> </div>

View File

@ -39,8 +39,14 @@ export default function PlanRequestPopQGrid(props) {
[gridData], [gridData],
) )
//
const onSelectionChanged = () => {
const selectedData = gridApi.getSelectedRows()
props.getSelectedRowdata(selectedData)
}
return ( return (
<div className="ag-theme-quartz" style={{ height: 500 }}> <div className="ag-theme-quartz" style={{ height: 350 }}>
<AgGridReact <AgGridReact
onGridReady={onGridReady} onGridReady={onGridReady}
rowBuffer={rowBuffer} rowBuffer={rowBuffer}
@ -49,7 +55,7 @@ export default function PlanRequestPopQGrid(props) {
defaultColDef={defaultColDef} defaultColDef={defaultColDef}
rowSelection={'singleRow'} rowSelection={'singleRow'}
pagination={isPageable} pagination={isPageable}
// onSelectionChanged={onSelectionChanged} onSelectionChanged={onSelectionChanged}
/> />
</div> </div>
) )

View File

@ -150,7 +150,7 @@ export default function UserInfoModal({ userId, userInfoModal, setUserInfoModal
<th>{getMessage('myinfo.info.userId')}</th> <th>{getMessage('myinfo.info.userId')}</th>
<td> <td>
<div className="input-wrap"> <div className="input-wrap">
<input type="text" className="input-light" value={userId} readOnly /> <input type="text" className="input-light" value={userId || ''} readOnly />
</div> </div>
</td> </td>
</tr> </tr>
@ -158,7 +158,7 @@ export default function UserInfoModal({ userId, userInfoModal, setUserInfoModal
<th>{getMessage('myinfo.info.nameKana')}</th> <th>{getMessage('myinfo.info.nameKana')}</th>
<td> <td>
<div className="input-wrap"> <div className="input-wrap">
<input type="text" className="input-light" value={info?.nameKana} readOnly /> <input type="text" className="input-light" value={info?.nameKana || ''} readOnly />
</div> </div>
</td> </td>
</tr> </tr>
@ -166,7 +166,7 @@ export default function UserInfoModal({ userId, userInfoModal, setUserInfoModal
<th>{getMessage('myinfo.info.name')}</th> <th>{getMessage('myinfo.info.name')}</th>
<td> <td>
<div className="input-wrap"> <div className="input-wrap">
<input type="text" className="input-light" value={info?.name} readOnly /> <input type="text" className="input-light" value={info?.name || ''} readOnly />
</div> </div>
</td> </td>
</tr> </tr>
@ -235,7 +235,7 @@ export default function UserInfoModal({ userId, userInfoModal, setUserInfoModal
<th>{getMessage('myinfo.info.category')}</th> <th>{getMessage('myinfo.info.category')}</th>
<td> <td>
<div className="input-wrap"> <div className="input-wrap">
<input type="text" className="input-light" value={info?.groupName} readOnly /> <input type="text" className="input-light" value={info?.groupName || ''} readOnly />
</div> </div>
</td> </td>
</tr> </tr>
@ -243,7 +243,7 @@ export default function UserInfoModal({ userId, userInfoModal, setUserInfoModal
<th>{getMessage('myinfo.info.tel')}</th> <th>{getMessage('myinfo.info.tel')}</th>
<td> <td>
<div className="input-wrap"> <div className="input-wrap">
<input type="text" className="input-light" value={info?.tel} readOnly /> <input type="text" className="input-light" value={info?.tel || ''} readOnly />
</div> </div>
</td> </td>
</tr> </tr>
@ -251,7 +251,7 @@ export default function UserInfoModal({ userId, userInfoModal, setUserInfoModal
<th>{getMessage('myinfo.info.fax')}</th> <th>{getMessage('myinfo.info.fax')}</th>
<td> <td>
<div className="input-wrap"> <div className="input-wrap">
<input type="text" className="input-light" value={info?.fax} readOnly /> <input type="text" className="input-light" value={info?.fax || ''} readOnly />
</div> </div>
</td> </td>
</tr> </tr>
@ -259,7 +259,7 @@ export default function UserInfoModal({ userId, userInfoModal, setUserInfoModal
<th>{getMessage('myinfo.info.mail')}</th> <th>{getMessage('myinfo.info.mail')}</th>
<td> <td>
<div className="input-wrap"> <div className="input-wrap">
<input type="text" className="input-light" value={info?.mail} readOnly /> <input type="text" className="input-light" value={info?.mail || ''} readOnly />
</div> </div>
</td> </td>
</tr> </tr>

View File

@ -4,14 +4,7 @@ import { useRecoilState, useRecoilValue } from 'recoil'
import { canvasState } from '@/store/canvasAtom' import { canvasState } from '@/store/canvasAtom'
import { INPUT_TYPE, BATCH_TYPE } from '@/common/common' import { INPUT_TYPE, BATCH_TYPE } from '@/common/common'
import { useEvent } from '@/hooks/useEvent' import { useEvent } from '@/hooks/useEvent'
import { import { polygonToTurfPolygon, rectToPolygon, triangleToPolygon, pointsToTurfPolygon, setSurfaceShapePattern } from '@/util/canvas-util'
polygonToTurfPolygon,
rectToPolygon,
triangleToPolygon,
pointsToTurfPolygon,
splitDormerTriangle,
setSurfaceShapePattern,
} from '@/util/canvas-util'
import { useSwal } from '@/hooks/useSwal' import { useSwal } from '@/hooks/useSwal'
import * as turf from '@turf/turf' import * as turf from '@turf/turf'
import { QPolygon } from '@/components/fabric/QPolygon' import { QPolygon } from '@/components/fabric/QPolygon'
@ -211,11 +204,13 @@ export function useObjectBatch() {
const dormerName = buttonAct === 3 ? BATCH_TYPE.TRIANGLE_DORMER : BATCH_TYPE.PENTAGON_DORMER const dormerName = buttonAct === 3 ? BATCH_TYPE.TRIANGLE_DORMER : BATCH_TYPE.PENTAGON_DORMER
const dormerTempName = buttonAct === 3 ? BATCH_TYPE.TRIANGLE_DORMER_TEMP : BATCH_TYPE.PENTAGON_DORMER_TEMP const dormerTempName = buttonAct === 3 ? BATCH_TYPE.TRIANGLE_DORMER_TEMP : BATCH_TYPE.PENTAGON_DORMER_TEMP
const height = dormerPlacement.heightRef.current.value / 10 const height = dormerPlacement.heightRef.current.value / 10
const width = dormerPlacement.widthRef.current.value / 10
const pitch = dormerPlacement.pitchRef.current.value const pitch = dormerPlacement.pitchRef.current.value
const directionRef = dormerPlacement.directionRef.current
const offsetRef = dormerPlacement.offsetRef.current.value === '' ? 0 : parseInt(dormerPlacement.offsetRef.current.value) / 10 const offsetRef = dormerPlacement.offsetRef.current.value === '' ? 0 : parseInt(dormerPlacement.offsetRef.current.value) / 10
const offsetWidthRef = dormerPlacement.offsetWidthRef.current.value === '' ? 0 : parseInt(dormerPlacement.offsetWidthRef.current.value) / 10
const directionRef = dormerPlacement.directionRef.current
let dormer, dormerOffset, isDown, selectedSurface let dormer, dormerOffset, isDown, selectedSurface, pentagonPoints, pentagonOffsetPoints
console.log('dormerPlacement', dormerPlacement) console.log('dormerPlacement', dormerPlacement)
@ -229,8 +224,6 @@ export function useObjectBatch() {
const bottomLength = height / (pitch * 0.25) const bottomLength = height / (pitch * 0.25)
const bottomOffsetLength = (height + offsetRef) / (pitch * 0.25) const bottomOffsetLength = (height + offsetRef) / (pitch * 0.25)
console.log(bottomOffsetLength)
addCanvasMouseEventListener('mouse:move', (e) => { addCanvasMouseEventListener('mouse:move', (e) => {
isDown = true isDown = true
if (!isDown) return if (!isDown) return
@ -305,16 +298,16 @@ export function useObjectBatch() {
addCanvasMouseEventListener('mouse:up', (e) => { addCanvasMouseEventListener('mouse:up', (e) => {
if (dormer) { if (dormer) {
// const trianglePolygon = pointsToTurfPolygon(triangleToPolygon(dormer)) const trianglePolygon = pointsToTurfPolygon(triangleToPolygon(dormer))
// const selectedSurfacePolygon = polygonToTurfPolygon(selectedSurface) const selectedSurfacePolygon = polygonToTurfPolygon(selectedSurface)
// //지붕 밖으로 그렸을때 //지붕 밖으로 그렸을때
// if (!turf.booleanWithin(trianglePolygon, selectedSurfacePolygon)) { if (!turf.booleanWithin(trianglePolygon, selectedSurfacePolygon)) {
// swalFire({ text: '개구를 배치할 수 없습니다.', icon: 'error' }) swalFire({ text: '개구를 배치할 수 없습니다.', icon: 'error' })
// //일단 지워 //일단 지워
// deleteTempObjects() deleteTempObjects()
// return return
// } }
//각도 추가 //각도 추가
let originAngle = 0 //기본 남쪽 let originAngle = 0 //기본 남쪽
@ -387,6 +380,183 @@ export function useObjectBatch() {
drawDirectionArrow(leftTriangle) drawDirectionArrow(leftTriangle)
drawDirectionArrow(rightTriangle) drawDirectionArrow(rightTriangle)
isDown = false
initEvent()
}
})
} else if (buttonAct === 4) {
const heightLength = height - (width / 2) * (pitch * 0.25)
//(동의길이 깊이)+출폭(깊이)-[(입력한 폭값)/2+출폭(폭)]*(0.25*입력한 寸)
const heightOffsetLength = height + offsetRef - (width / 2 + offsetWidthRef) * (pitch * 0.25)
addCanvasMouseEventListener('mouse:move', (e) => {
isDown = true
if (!isDown) return
canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === dormerTempName)) //움직일때 일단 지워가면서 움직임
const pointer = canvas.getPointer(e.e)
surfaceShapePolygons.forEach((surface) => {
if (surface.inPolygon({ x: pointer.x, y: pointer.y })) {
selectedSurface = surface
}
})
let angle = 0
if (directionRef === 'left') {
//서
angle = 90
} else if (directionRef === 'right') {
//동
angle = 270
} else if (directionRef === 'up') {
//북
angle = 180
}
pentagonPoints = [
{ x: pointer.x, y: pointer.y },
{ x: pointer.x - width / 2, y: pointer.y + (height - heightLength) },
{ x: pointer.x - width / 2, y: pointer.y + height },
{ x: pointer.x + width / 2, y: pointer.y + height },
{ x: pointer.x + width / 2, y: pointer.y + (height - heightLength) },
]
pentagonOffsetPoints = [
{ x: pointer.x, y: pointer.y },
{ x: pointer.x - width / 2 - offsetWidthRef, y: pointer.y + height + offsetRef - heightOffsetLength },
{ x: pointer.x - width / 2 - offsetWidthRef, y: pointer.y + height + offsetRef },
{ x: pointer.x + width / 2 + offsetWidthRef, y: pointer.y + height + offsetRef },
{ x: pointer.x + width / 2 + offsetWidthRef, y: pointer.y + height + offsetRef - heightOffsetLength },
]
dormer = new QPolygon(pentagonPoints, {
fill: 'white',
stroke: 'red',
strokeDashArray: [5, 5],
strokeWidth: 1,
selectable: true,
lockMovementX: true,
lockMovementY: true,
lockRotation: true,
lockScalingX: true,
lockScalingY: true,
name: dormerTempName,
originX: 'center',
originY: 'top',
angle: angle,
})
canvas?.add(dormer)
if (offsetRef > 0 || offsetWidthRef > 0) {
dormerOffset = new QPolygon(pentagonOffsetPoints, {
fill: 'gray',
stroke: 'red',
strokeDashArray: [5, 5],
strokeWidth: 1,
selectable: true,
lockMovementX: true,
lockMovementY: true,
lockRotation: true,
lockScalingX: true,
lockScalingY: true,
name: dormerTempName,
originX: 'center',
originY: 'top',
angle: angle,
})
canvas?.add(dormerOffset)
}
})
addCanvasMouseEventListener('mouse:up', (e) => {
if (dormer) {
// const trianglePolygon = pointsToTurfPolygon(triangleToPolygon(dormer))
// const selectedSurfacePolygon = polygonToTurfPolygon(selectedSurface)
// //지붕 밖으로 그렸을때
// if (!turf.booleanWithin(trianglePolygon, selectedSurfacePolygon)) {
// swalFire({ text: '개구를 배치할 수 없습니다.', icon: 'error' })
// //일단 지워
// deleteTempObjects()
// return
// }
//각도 추가
let originAngle = 0 //기본 남쪽
let direction = 'south'
if (directionRef === 'left') {
//서
originAngle = 90
direction = 'west'
} else if (directionRef === 'right') {
//동
originAngle = 270
direction = 'east'
} else if (directionRef === 'up') {
//북
originAngle = 180
direction = 'north'
}
const offsetMode = offsetRef > 0 || offsetWidthRef > 0 ? 'offset' : 'normal'
let splitedPentagon =
offsetRef > 0 || offsetWidthRef > 0
? splitDormerPentagon(dormerOffset, directionRef, offsetMode)
: splitDormerPentagon(dormer, directionRef, offsetMode)
canvas?.remove(offsetRef > 0 || offsetWidthRef > 0 ? dormerOffset : dormer)
if (offsetRef > 0)
dormer.set({
name: dormerName,
stroke: 'black',
strokeWidth: 1,
strokeDashArray: [0],
}) //오프셋이 있을땐 같이 도머로 만든다
const leftPentagon = new QPolygon(splitedPentagon[0], {
fill: 'transparent',
stroke: 'red',
strokeWidth: 1,
selectable: true,
lockMovementX: true, // X 축 이동 잠금
lockMovementY: true, // Y 축 이동 잠금
lockRotation: true, // 회전 잠금
viewLengthText: true,
fontSize: 14,
direction: direction,
originX: 'center',
originY: 'center',
name: dormerName,
})
const rightPentagon = new QPolygon(splitedPentagon[1], {
fill: 'transparent',
stroke: 'red',
strokeWidth: 1,
selectable: true,
lockMovementX: true, // X 축 이동 잠금
lockMovementY: true, // Y 축 이동 잠금
lockRotation: true, // 회전 잠금
viewLengthText: true,
fontSize: 14,
direction: direction,
originX: 'center',
originY: 'center',
name: dormerName,
})
canvas?.add(leftPentagon)
canvas?.add(rightPentagon)
//패턴
setSurfaceShapePattern(leftPentagon)
setSurfaceShapePattern(rightPentagon)
//방향
drawDirectionArrow(leftPentagon)
drawDirectionArrow(rightPentagon)
isDown = false isDown = false
initEvent() initEvent()
} }
@ -408,8 +578,140 @@ export function useObjectBatch() {
initEvent() //이벤트 초기화 initEvent() //이벤트 초기화
} }
const splitDormerTriangle = (triangle, direction) => {
const halfWidth = triangle.width / 2
let leftPoints = []
let rightPoints = []
if (direction === 'down') {
leftPoints = [
{ x: triangle.left, y: triangle.top },
{ x: triangle.left - halfWidth, y: triangle.top + triangle.height },
{ x: triangle.left, y: triangle.top + triangle.height },
]
rightPoints = [
{ x: triangle.left, y: triangle.top },
{ x: triangle.left, y: triangle.top + triangle.height },
{ x: triangle.left + halfWidth, y: triangle.top + triangle.height },
]
} else if (direction === 'up') {
leftPoints = [
{ x: triangle.left, y: triangle.top },
{ x: triangle.left - halfWidth, y: triangle.top - triangle.height },
{ x: triangle.left, y: triangle.top - triangle.height },
]
rightPoints = [
{ x: triangle.left, y: triangle.top },
{ x: triangle.left, y: triangle.top - triangle.height },
{ x: triangle.left + halfWidth, y: triangle.top - triangle.height },
]
} else if (direction === 'left') {
leftPoints = [
{ x: triangle.left, y: triangle.top },
{ x: triangle.left - triangle.height, y: triangle.top - halfWidth },
{ x: triangle.left - triangle.height, y: triangle.top },
]
rightPoints = [
{ x: triangle.left, y: triangle.top },
{ x: triangle.left - triangle.height, y: triangle.top },
{ x: triangle.left - triangle.height, y: triangle.top + halfWidth },
]
} else if (direction === 'right') {
leftPoints = [
{ x: triangle.left, y: triangle.top },
{ x: triangle.left + triangle.height, y: triangle.top },
{ x: triangle.left + triangle.height, y: triangle.top + triangle.height },
]
rightPoints = [
{ x: triangle.left, y: triangle.top },
{ x: triangle.left + triangle.height, y: triangle.top },
{ x: triangle.left + triangle.height, y: triangle.top - triangle.height },
]
}
return [leftPoints, rightPoints]
}
const splitDormerPentagon = (pentagon, direction, offsetMode) => {
const points = pentagon.points
console.log(pentagon.points)
let leftPoints = []
let rightPoints = []
if (direction === 'down') {
leftPoints = [
{ x: points[0].x, y: points[0].y },
{ x: points[1].x, y: points[1].y },
{ x: points[2].x, y: points[2].y },
{ x: points[0].x, y: points[3].y },
]
rightPoints = [
{ x: points[0].x, y: points[0].y },
{ x: points[0].x, y: points[2].y },
{ x: points[3].x, y: points[3].y },
{ x: points[4].x, y: points[4].y },
]
} else if (direction === 'up') {
leftPoints = [
{ x: points[0].x, y: points[0].y },
{ x: points[1].x, y: points[0].y - (points[1].y - points[0].y) },
{ x: points[2].x, y: points[0].y - (points[2].y - points[0].y) },
{ x: points[0].x, y: points[0].y - (points[2].y - points[0].y) },
]
rightPoints = [
{ x: points[0].x, y: points[0].y },
{ x: points[3].x, y: points[0].y - (points[1].y - points[0].y) },
{ x: points[3].x, y: points[0].y - (points[2].y - points[0].y) },
{ x: points[0].x, y: points[0].y - (points[2].y - points[0].y) },
]
} else if (direction === 'left') {
leftPoints = [
{ x: points[0].x, y: points[0].y },
{ x: points[0].x - (points[1].y - points[0].y), y: points[0].y - (points[0].x - points[1].x) },
{ x: points[0].x - (points[1].y - points[0].y) - (points[2].y - points[1].y), y: points[0].y - (points[0].x - points[1].x) },
{ x: points[0].x - (points[1].y - points[0].y) - (points[2].y - points[1].y), y: points[0].y },
]
rightPoints = [
{ x: points[0].x, y: points[0].y },
{ x: points[0].x - (points[1].y - points[0].y), y: points[0].y + (points[0].x - points[1].x) },
{ x: points[0].x - (points[1].y - points[0].y) - (points[2].y - points[1].y), y: points[0].y + (points[0].x - points[1].x) },
{ x: points[0].x - (points[1].y - points[0].y) - (points[2].y - points[1].y), y: points[0].y },
]
} else if (direction === 'right') {
leftPoints = [
{ x: points[0].x, y: points[0].y },
{ x: points[0].x + (points[1].y - points[0].y), y: points[0].y + (points[0].x - points[1].x) },
{ x: points[0].x + (points[1].y - points[0].y) + (points[2].y - points[1].y), y: points[0].y + (points[0].x - points[1].x) },
{ x: points[0].x + (points[1].y - points[0].y) + (points[2].y - points[1].y), y: points[0].y },
]
rightPoints = [
{ x: points[0].x, y: points[0].y },
{ x: points[0].x + (points[1].y - points[0].y), y: points[0].y - (points[0].x - points[1].x) },
{ x: points[0].x + (points[1].y - points[0].y) + (points[2].y - points[1].y), y: points[0].y - (points[0].x - points[1].x) },
{ x: points[0].x + (points[1].y - points[0].y) + (points[2].y - points[1].y), y: points[0].y },
]
}
console.log(leftPoints, rightPoints)
return [leftPoints, rightPoints]
}
return { return {
applyOpeningAndShadow, applyOpeningAndShadow,
applyDormers, applyDormers,
splitDormerTriangle,
splitDormerPentagon,
} }
} }

View File

@ -23,10 +23,11 @@ import {
outerLineLength2State, outerLineLength2State,
outerLineTypeState, outerLineTypeState,
} from '@/store/outerLineAtom' } from '@/store/outerLineAtom'
import { calculateDistance, calculateIntersection, distanceBetweenPoints, findClosestPoint } from '@/util/canvas-util' import { calculateDistance, calculateIntersection, distanceBetweenPoints, findClosestPoint, polygonToTurfPolygon } 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'
// 보조선 작성 // 보조선 작성
export function useAuxiliaryDrawing(setShowAuxiliaryModal) { export function useAuxiliaryDrawing(setShowAuxiliaryModal) {
@ -76,6 +77,8 @@ export function useAuxiliaryDrawing(setShowAuxiliaryModal) {
useEffect(() => { useEffect(() => {
typeRef.current = type typeRef.current = type
clear()
addDocumentEventListener('keydown', document, keydown[type])
}, [type]) }, [type])
useEffect(() => { useEffect(() => {
@ -103,12 +106,8 @@ export function useAuxiliaryDrawing(setShowAuxiliaryModal) {
} }
}, []) }, [])
useEffect(() => {
clear()
addDocumentEventListener('keydown', document, keydown[type])
}, [type])
const clear = () => { const clear = () => {
addCanvasMouseEventListener('mouse:move', mouseMove)
setLength1(0) setLength1(0)
setLength2(0) setLength2(0)
@ -459,8 +458,9 @@ export function useAuxiliaryDrawing(setShowAuxiliaryModal) {
} }
const mouseDown = (e) => { const mouseDown = (e) => {
addCanvasMouseEventListener('mouse:move', mouseMove) canvas.renderAll()
const pointer = getIntersectMousePoint(e) const pointer = getIntersectMousePoint(e)
console.log(pointer)
mousePointerArr.current.push(pointer) mousePointerArr.current.push(pointer)
if (mousePointerArr.current.length === 2) { if (mousePointerArr.current.length === 2) {
@ -538,59 +538,66 @@ export function useAuxiliaryDrawing(setShowAuxiliaryModal) {
// 보조선 절삭 // 보조선 절삭
const cutAuxiliary = (e) => { const cutAuxiliary = (e) => {
const auxiliaryLines = canvas.getObjects().filter((obj) => obj.name === 'auxiliaryLine' && !obj.isFixed) const auxiliaryLines = canvas.getObjects().filter((obj) => obj.name === 'auxiliaryLine')
if (auxiliaryLines.length === 0) { if (auxiliaryLines.length === 0) {
return return
} }
const roofBases = canvas.getObjects().filter((obj) => obj.name === 'roofBase')
const allLines = [...auxiliaryLines]
roofBases.forEach((roofBase) => {
roofBase.lines.forEach((line) => {
allLines.push(line)
})
})
auxiliaryLines.forEach((line1) => { auxiliaryLines.forEach((line1) => {
auxiliaryLines.forEach((line2) => { allLines.forEach((line2) => {
const lines = [line1, line2]
if (line1 === line2) { if (line1 === line2) {
return return
} }
const intersectionPoint = calculateIntersection(line1, line2) const intersectionPoint = calculateIntersection(line1, line2)
if (!intersectionPoint || intersectionPoints.current.some((point) => point.x === intersectionPoint.x && point.y === intersectionPoint.y)) { if (!intersectionPoint) {
return return
} }
roofAdsorptionPoints.current.push(intersectionPoint) roofAdsorptionPoints.current.push(intersectionPoint)
intersectionPoints.current.push(intersectionPoint) intersectionPoints.current.push(intersectionPoint)
lines.forEach((line) => {
const distance1 = distanceBetweenPoints({ x: line.x1, y: line.y1 }, intersectionPoint)
const distance2 = distanceBetweenPoints({ x: line.x2, y: line.y2 }, intersectionPoint)
if (distance1 === 0 || distance2 === 0) { const distance1 = distanceBetweenPoints({ x: line1.x1, y: line1.y1 }, intersectionPoint)
return const distance2 = distanceBetweenPoints({ x: line1.x2, y: line1.y2 }, intersectionPoint)
}
//historyLine에서 기존 line을 제거한다.
lineHistory.current = lineHistory.current.filter((history) => history !== line)
let newLine if (distance1 === 0 || distance2 === 0) {
return
}
//historyLine에서 기존 line을 제거한다.
lineHistory.current = lineHistory.current.filter((history) => history !== line1)
if (distance1 >= distance2) { let newLine
newLine = addLine([line.x1, line.y1, intersectionPoint.x, intersectionPoint.y], {
stroke: 'black', if (distance1 >= distance2) {
strokeWidth: 1, newLine = addLine([line1.x1, line1.y1, intersectionPoint.x, intersectionPoint.y], {
selectable: false, stroke: 'black',
name: 'auxiliaryLine', strokeWidth: 1,
isFixed: true, selectable: false,
intersectionPoint, name: 'auxiliaryLine',
}) isFixed: true,
} else { intersectionPoint,
newLine = addLine([line.x2, line.y2, intersectionPoint.x, intersectionPoint.y], { })
stroke: 'black', } else {
strokeWidth: 1, newLine = addLine([line1.x2, line1.y2, intersectionPoint.x, intersectionPoint.y], {
selectable: false, stroke: 'black',
name: 'auxiliaryLine', strokeWidth: 1,
isFixed: true, selectable: false,
intersectionPoint, name: 'auxiliaryLine',
}) isFixed: true,
} intersectionPoint,
lineHistory.current.push(newLine) })
removeLine(line) }
}) lineHistory.current.push(newLine)
removeLine(line1)
}) })
}) })
addCanvasMouseEventListener('mouse:move', mouseMove) addCanvasMouseEventListener('mouse:move', mouseMove)
@ -600,10 +607,15 @@ export function useAuxiliaryDrawing(setShowAuxiliaryModal) {
* 일변전으로 돌아가기 * 일변전으로 돌아가기
*/ */
const handleRollback = () => { const handleRollback = () => {
const lastLine = lineHistory.current.pop() const innerPoint = canvas.getObjects().find((obj) => obj.name === 'innerPoint')
mousePointerArr.current = [] if (innerPoint) {
canvas.remove(...canvas.getObjects().filter((obj) => obj.name === 'innerPoint')) mousePointerArr.current = []
canvas.remove(innerPoint)
canvas.renderAll()
return
}
const lastLine = lineHistory.current.pop()
if (lastLine) { if (lastLine) {
roofAdsorptionPoints.current = roofAdsorptionPoints.current.filter( roofAdsorptionPoints.current = roofAdsorptionPoints.current.filter(
(point) => point.x !== lastLine.intersectionPoint?.x && point.y !== lastLine.intersectionPoint?.y, (point) => point.x !== lastLine.intersectionPoint?.x && point.y !== lastLine.intersectionPoint?.y,
@ -621,10 +633,25 @@ export function useAuxiliaryDrawing(setShowAuxiliaryModal) {
return return
} }
const roofBases = canvas.getObjects().find((obj) => obj.name === 'roofBase') const roofBases = canvas.getObjects().filter((obj) => obj.name === 'roofBase')
const innerLines = [...lineHistory.current] const innerLines = [...lineHistory.current]
roofBases.innerLines = [...innerLines] roofBases.forEach((roofBase) => {
const roofInnerLines = innerLines.filter((line) => {
const turfPolygon = polygonToTurfPolygon(roofBase)
// innerLines의 두 점이 모두 polygon 안에 있는지 확인
const inPolygon1 = booleanPointInPolygon([line.x1, line.y1], turfPolygon)
const inPolygon2 = booleanPointInPolygon([line.x2, line.y2], turfPolygon)
if (inPolygon1 && inPolygon2) {
line.attributes = { ...line.attributes, roofId: roofBase.id }
return true
}
})
roofBase.innerLines = [...roofInnerLines]
})
setShowAuxiliaryModal(false) setShowAuxiliaryModal(false)
} }

View File

@ -29,12 +29,19 @@ import {
} from '@/store/outerLineAtom' } from '@/store/outerLineAtom'
import { calculateAngle } from '@/util/qpolygon-utils' import { calculateAngle } from '@/util/qpolygon-utils'
import { fabric } from 'fabric' import { fabric } from 'fabric'
import { QLine } from '@/components/fabric/QLine'
//외벽선 그리기 //외벽선 그리기
export function useOuterLineWall(setShowOutlineModal) { export function useOuterLineWall(setShowOutlineModal) {
const canvas = useRecoilValue(canvasState) const canvas = useRecoilValue(canvasState)
const { addCanvasMouseEventListener, addDocumentEventListener, removeAllMouseEventListeners, removeAllDocumentEventListeners, removeMouseEvent } = const {
useEvent() initEvent,
addCanvasMouseEventListener,
addDocumentEventListener,
removeAllMouseEventListeners,
removeAllDocumentEventListeners,
removeMouseEvent,
} = useEvent()
const { getIntersectMousePoint } = useMouse() const { getIntersectMousePoint } = useMouse()
const { addLine, removeLine } = useLine() const { addLine, removeLine } = useLine()
const { tempGridMode } = useTempGrid() const { tempGridMode } = useTempGrid()
@ -75,6 +82,9 @@ export function useOuterLineWall(setShowOutlineModal) {
addCanvasMouseEventListener('mouse:down', mouseDown) addCanvasMouseEventListener('mouse:down', mouseDown)
clear() clear()
return () => {
initEvent()
}
}, [verticalHorizontalMode, points, adsorptionPointAddMode, adsorptionPointMode, adsorptionRange, interval, tempGridMode]) }, [verticalHorizontalMode, points, adsorptionPointAddMode, adsorptionPointMode, adsorptionRange, interval, tempGridMode])
useEffect(() => { useEffect(() => {
@ -215,11 +225,11 @@ export function useOuterLineWall(setShowOutlineModal) {
return return
} }
/*if (lastPoint.x === firstPoint.x && lastPoint.y === firstPoint.y) { if (Math.abs(lastPoint.x - firstPoint.x) < 1 && Math.abs(lastPoint.y - firstPoint.y) < 1) {
return return
} }
if (lastPoint.x === firstPoint.x || lastPoint.y === firstPoint.y) { if (Math.abs(lastPoint.x - firstPoint.x) < 1 || Math.abs(lastPoint.y - firstPoint.y) < 1) {
let isAllRightAngle = true let isAllRightAngle = true
const firstPoint = points[0] const firstPoint = points[0]
@ -238,38 +248,27 @@ export function useOuterLineWall(setShowOutlineModal) {
if (isAllRightAngle) { if (isAllRightAngle) {
return return
} }
const line = new QLine([lastPoint.x, lastPoint.y, firstPoint.x, firstPoint.y], { const line = addLine([lastPoint.x, lastPoint.y, firstPoint.x, firstPoint.y], {
stroke: 'grey', stroke: 'grey',
strokeWidth: 1, strokeWidth: 1,
selectable: false, selectable: false,
name: 'helpGuideLine', name: 'helpGuideLine',
}) })
canvas?.add(line)
addLineText(line)
} else { } else {
const guideLine1 = new QLine([lastPoint.x, lastPoint.y, lastPoint.x, firstPoint.y], { const guideLine1 = addLine([lastPoint.x, lastPoint.y, lastPoint.x, firstPoint.y], {
stroke: 'grey', stroke: 'grey',
strokeWidth: 1, strokeWidth: 1,
strokeDashArray: [1, 1, 1], strokeDashArray: [1, 1, 1],
name: 'helpGuideLine', name: 'helpGuideLine',
}) })
const guideLine2 = new QLine([guideLine1.x2, guideLine1.y2, firstPoint.x, firstPoint.y], { const guideLine2 = addLine([guideLine1.x2, guideLine1.y2, firstPoint.x, firstPoint.y], {
stroke: 'grey', stroke: 'grey',
strokeWidth: 1, strokeWidth: 1,
strokeDashArray: [1, 1, 1], strokeDashArray: [1, 1, 1],
name: 'helpGuideLine', name: 'helpGuideLine',
}) })
if (guideLine1.length > 0) { }
canvas?.add(guideLine1)
addLineText(guideLine1)
}
canvas?.add(guideLine2)
addLineText(guideLine2)
}*/
} }
}, [points]) }, [points])

View File

@ -95,7 +95,11 @@ export function useRoofAllocationSetting(setShowRoofAllocationSettingModal) {
const roofBases = canvas.getObjects().filter((obj) => obj.name === 'roofBase') const roofBases = canvas.getObjects().filter((obj) => obj.name === 'roofBase')
const wallLines = canvas.getObjects().filter((obj) => obj.name === 'wallLine') const wallLines = canvas.getObjects().filter((obj) => obj.name === 'wallLine')
roofBases.forEach((roofBase) => { roofBases.forEach((roofBase) => {
splitPolygonWithLines(roofBase) try {
splitPolygonWithLines(roofBase)
} catch (e) {
return
}
roofBase.innerLines.forEach((line) => { roofBase.innerLines.forEach((line) => {
canvas.remove(line) canvas.remove(line)

View File

@ -33,6 +33,11 @@ export function useRoofShapePassivitySetting(setShowRoofShapePassivitySettingMod
const [type, setType] = useState(TYPES.EAVES) const [type, setType] = useState(TYPES.EAVES)
const isFix = useRef(false)
const initLines = useRef([])
const [isLoading, setIsLoading] = useState(false)
const buttons = [ const buttons = [
{ id: 1, name: getMessage('eaves'), type: TYPES.EAVES }, { id: 1, name: getMessage('eaves'), type: TYPES.EAVES },
{ id: 2, name: getMessage('gable'), type: TYPES.GABLE }, { id: 2, name: getMessage('gable'), type: TYPES.GABLE },
@ -46,15 +51,20 @@ export function useRoofShapePassivitySetting(setShowRoofShapePassivitySettingMod
if (!outerLineFix || outerLines.length === 0) { if (!outerLineFix || outerLines.length === 0) {
swalFire({ text: '외벽선이 없습니다.' }) swalFire({ text: '외벽선이 없습니다.' })
setShowRoofShapePassivitySettingModal(false) setShowRoofShapePassivitySettingModal(false)
return
} }
setIsLoading(true)
}, []) }, [])
useEffect(() => { useEffect(() => {
if (!isLoading) return
addCanvasMouseEventListener('mouse:down', mouseDown) addCanvasMouseEventListener('mouse:down', mouseDown)
const wallLines = canvas.getObjects().filter((obj) => obj.name === 'wallLine') const wallLines = canvas.getObjects().filter((obj) => obj.name === 'wallLine')
canvas.remove(wallLines)
canvas?.remove(...wallLines)
const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
initLines.current = outerLines.map((line) => ({ ...line }))
outerLines.forEach((outerLine, idx) => { outerLines.forEach((outerLine, idx) => {
if (idx === 0) { if (idx === 0) {
currentLineRef.current = outerLine currentLineRef.current = outerLine
@ -66,10 +76,11 @@ export function useRoofShapePassivitySetting(setShowRoofShapePassivitySettingMod
canvas?.renderAll() canvas?.renderAll()
return () => { return () => {
handleLineToPolygon()
canvas?.discardActiveObject() canvas?.discardActiveObject()
initEvent() initEvent()
} }
}, []) }, [isLoading])
useEffect(() => { useEffect(() => {
const lines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') const lines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
@ -166,6 +177,14 @@ export function useRoofShapePassivitySetting(setShowRoofShapePassivitySettingMod
} }
const handleSave = () => { const handleSave = () => {
isFix.current = true
handleLineToPolygon()
setShowRoofShapePassivitySettingModal(false)
}
const handleLineToPolygon = () => {
const roofBases = canvas.getObjects().filter((obj) => obj.name === 'roofBase')
const exceptObjs = canvas.getObjects().filter((obj) => obj.name !== 'outerLine' && obj.parent?.name !== 'outerLine') const exceptObjs = canvas.getObjects().filter((obj) => obj.name !== 'outerLine' && obj.parent?.name !== 'outerLine')
const lines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') const lines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
exceptObjs.forEach((obj) => { exceptObjs.forEach((obj) => {
@ -176,13 +195,28 @@ export function useRoofShapePassivitySetting(setShowRoofShapePassivitySettingMod
hideLine(line) hideLine(line)
}) })
const wall = addPolygonByLines(lines, { name: 'wallLine', fill: 'transparent', stroke: 'black' }) let wall
if (isFix.current) {
wall = addPolygonByLines(lines, { name: 'wallLine', fill: 'transparent', stroke: 'black' })
} else {
// 그냥 닫을 경우 처리
wall = addPolygonByLines([...initLines.current], { name: 'wallLine', fill: 'transparent', stroke: 'black' })
lines.forEach((line, idx) => {
line.attributes = initLines.current[idx].attributes
})
}
wall.lines = [...lines] wall.lines = [...lines]
// 기존 그려진 지붕이 없다면
if (isFix.current) {
// 완료 한 경우에는 지붕까지 그려줌
const roof = drawRoofPolygon(wall)
}
const roof = drawRoofPolygon(wall)
canvas.renderAll() canvas.renderAll()
setShowRoofShapePassivitySettingModal(false)
} }
return { handleSave, handleConfirm, buttons, type, setType, TYPES, offsetRef, pitchRef, handleRollback } return { handleSave, handleConfirm, buttons, type, setType, TYPES, offsetRef, pitchRef, handleRollback }
} }

View File

@ -244,8 +244,13 @@ export function useRoofShapeSetting(setShowRoofShapeSettingModal) {
} }
} }
// 기존 wallLine 제거 // 기존 wallLine, roofBase 제거
canvas?.remove(canvas.getObjects().filter((obj) => obj.name === 'wallLine')) canvas
.getObjects()
.filter((obj) => obj.name === 'wallLine' || obj.name === 'roofBase')
.forEach((line) => {
canvas.remove(line)
})
const polygon = addPolygonByLines(outerLines, { name: 'wallLine' }) const polygon = addPolygonByLines(outerLines, { name: 'wallLine' })
polygon.lines = [...outerLines] polygon.lines = [...outerLines]

View File

@ -1,4 +1,4 @@
import { canvasState } from '@/store/canvasAtom' import { canvasState, currentObjectState } from '@/store/canvasAtom'
import { useRecoilValue } from 'recoil' import { useRecoilValue } from 'recoil'
import { useEffect, useRef, useState } from 'react' import { useEffect, useRef, useState } from 'react'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
@ -21,6 +21,8 @@ export function useWallLineOffsetSetting(setShowWallLineOffsetSettingModal) {
const arrow1Ref = useRef(null) const arrow1Ref = useRef(null)
const arrow2Ref = useRef(null) const arrow2Ref = useRef(null)
const currentObject = useRecoilValue(currentObjectState)
const [isLoading, setIsLoading] = useState(false) const [isLoading, setIsLoading] = useState(false)
const drawLine = (point1, point2, idx, direction = currentWallLineRef.current.direction) => { const drawLine = (point1, point2, idx, direction = currentWallLineRef.current.direction) => {
@ -87,6 +89,7 @@ export function useWallLineOffsetSetting(setShowWallLineOffsetSettingModal) {
if (!isLoading) { if (!isLoading) {
return return
} }
canvas?.discardActiveObject()
removeOuterLineEditCircle() removeOuterLineEditCircle()
addCanvasMouseEventListener('mouse:down', mouseDown) addCanvasMouseEventListener('mouse:down', mouseDown)
if (type === TYPES.WALL_LINE_EDIT) { if (type === TYPES.WALL_LINE_EDIT) {
@ -94,6 +97,20 @@ export function useWallLineOffsetSetting(setShowWallLineOffsetSettingModal) {
} }
}, [type]) }, [type])
useEffect(() => {
canvas
.getObjects()
.filter((obj) => obj.name === 'outerLine')
.forEach((line) => {
line.set({ stroke: 'black' })
})
if (currentObject?.name === 'outerLine') {
currentObject.set({ stroke: '#EA10AC' })
canvas.renderAll()
}
}, [currentObject])
const removeOuterLineEditCircle = () => { const removeOuterLineEditCircle = () => {
canvas.remove(...canvas.getObjects().filter((obj) => obj.name === 'outerLineEditCircleStart' || obj.name === 'outerLineEditCircleEnd')) canvas.remove(...canvas.getObjects().filter((obj) => obj.name === 'outerLineEditCircleStart' || obj.name === 'outerLineEditCircleEnd'))
} }
@ -106,7 +123,6 @@ export function useWallLineOffsetSetting(setShowWallLineOffsetSettingModal) {
} }
currentWallLineRef.current = e.target currentWallLineRef.current = e.target
console.log(currentWallLineRef.current.idx, currentWallLineRef.current.direction)
if (type === TYPES.WALL_LINE_EDIT) { if (type === TYPES.WALL_LINE_EDIT) {
addCircleByLine(currentWallLineRef.current) addCircleByLine(currentWallLineRef.current)
} }

View File

@ -232,11 +232,11 @@ export function usePlacementShapeDrawing(setShowPlaceShapeDrawingModal) {
return return
} }
/*if (lastPoint.x === firstPoint.x && lastPoint.y === firstPoint.y) { if (Math.abs(lastPoint.x - firstPoint.x) < 1 && Math.abs(lastPoint.y - firstPoint.y) < 1) {
return return
} }
if (lastPoint.x === firstPoint.x || lastPoint.y === firstPoint.y) { if (Math.abs(lastPoint.x - firstPoint.x) < 1 || Math.abs(lastPoint.y - firstPoint.y) < 1) {
let isAllRightAngle = true let isAllRightAngle = true
const firstPoint = points[0] const firstPoint = points[0]
@ -255,38 +255,27 @@ export function usePlacementShapeDrawing(setShowPlaceShapeDrawingModal) {
if (isAllRightAngle) { if (isAllRightAngle) {
return return
} }
const line = new QLine([lastPoint.x, lastPoint.y, firstPoint.x, firstPoint.y], { const line = addLine([lastPoint.x, lastPoint.y, firstPoint.x, firstPoint.y], {
stroke: 'grey', stroke: 'grey',
strokeWidth: 1, strokeWidth: 1,
selectable: false, selectable: false,
name: 'helpGuideLine', name: 'helpGuideLine',
}) })
canvas?.add(line)
addLineText(line)
} else { } else {
const guideLine1 = new QLine([lastPoint.x, lastPoint.y, lastPoint.x, firstPoint.y], { const guideLine1 = addLine([lastPoint.x, lastPoint.y, lastPoint.x, firstPoint.y], {
stroke: 'grey', stroke: 'grey',
strokeWidth: 1, strokeWidth: 1,
strokeDashArray: [1, 1, 1], strokeDashArray: [1, 1, 1],
name: 'helpGuideLine', name: 'helpGuideLine',
}) })
const guideLine2 = new QLine([guideLine1.x2, guideLine1.y2, firstPoint.x, firstPoint.y], { const guideLine2 = addLine([guideLine1.x2, guideLine1.y2, firstPoint.x, firstPoint.y], {
stroke: 'grey', stroke: 'grey',
strokeWidth: 1, strokeWidth: 1,
strokeDashArray: [1, 1, 1], strokeDashArray: [1, 1, 1],
name: 'helpGuideLine', name: 'helpGuideLine',
}) })
if (guideLine1.length > 0) { }
canvas?.add(guideLine1)
addLineText(guideLine1)
}
canvas?.add(guideLine2)
addLineText(guideLine2)
}*/
} }
}, [points]) }, [points])

View File

@ -40,59 +40,59 @@ export function useAxios(lang = '') {
// response 추가 로직 // response 추가 로직
axios.interceptors.request.use(undefined, (error) => {}) axios.interceptors.request.use(undefined, (error) => {})
const get = async ({ url }) => { const get = async ({ url, option = {} }) => {
return await getInstances(url) return await getInstances(url)
.get(url) .get(url, option)
.then((res) => res.data) .then((res) => res.data)
.catch(console.error) .catch(console.error)
} }
const promiseGet = async ({ url }) => { const promiseGet = async ({ url, option = {} }) => {
return await getInstances(url).get(url) return await getInstances(url).get(url, option)
} }
const post = async ({ url, data }) => { const post = async ({ url, data, option = {} }) => {
return await getInstances(url) return await getInstances(url)
.post(url, data) .post(url, data, option)
.then((res) => res.data) .then((res) => res.data)
.catch(console.error) .catch(console.error)
} }
const promisePost = async ({ url, data }) => { const promisePost = async ({ url, data, option = {} }) => {
return await getInstances(url).post(url, data) return await getInstances(url).post(url, data, option)
} }
const put = async ({ url, data }) => { const put = async ({ url, data, option = {} }) => {
return await getInstances(url) return await getInstances(url)
.put(url, data) .put(url, data, option)
.then((res) => res.data) .then((res) => res.data)
.catch(console.error) .catch(console.error)
} }
const promisePut = async ({ url, data }) => { const promisePut = async ({ url, data, option = {} }) => {
return await getInstances(url).put(url, data) return await getInstances(url).put(url, data, option)
} }
const patch = async ({ url, data }) => { const patch = async ({ url, data, option = {} }) => {
return await getInstances(url) return await getInstances(url)
.patch(url, data) .patch(url, data, option)
.then((res) => res.data) .then((res) => res.data)
.catch(console.error) .catch(console.error)
} }
const promisePatch = async ({ url, data }) => { const promisePatch = async ({ url, data, option = {} }) => {
return await getInstances(url).patch(url, data) return await getInstances(url).patch(url, data, option)
} }
const del = async ({ url }) => { const del = async ({ url, option = {} }) => {
return await getInstances(url) return await getInstances(url)
.delete(url) .delete(url, option)
.then((res) => res.data) .then((res) => res.data)
.catch(console.error) .catch(console.error)
} }
const promiseDel = async ({ url }) => { const promiseDel = async ({ url, option = {} }) => {
return await getInstances(url).delete(url) return await getInstances(url).delete(url, option)
} }
return { get, promiseGet, post, promisePost, put, promisePut, patch, promisePatch, del, promiseDel } return { get, promiseGet, post, promisePost, put, promisePut, patch, promisePatch, del, promiseDel }

View File

@ -2,7 +2,7 @@ import { useEffect, useRef } from 'react'
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil' import { useRecoilState, 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'
import { calculateDistance, distanceBetweenPoints, findClosestPoint } from '@/util/canvas-util' import { calculateDistance, calculateIntersection, distanceBetweenPoints, findClosestPoint, polygonToTurfPolygon } from '@/util/canvas-util'
import { useAdsorptionPoint } from '@/hooks/useAdsorptionPoint' import { useAdsorptionPoint } from '@/hooks/useAdsorptionPoint'
import { useDotLineGrid } from '@/hooks/useDotLineGrid' import { useDotLineGrid } from '@/hooks/useDotLineGrid'
import { useTempGrid } from '@/hooks/useTempGrid' import { useTempGrid } from '@/hooks/useTempGrid'
@ -43,7 +43,6 @@ export function useEvent() {
//default Event 추가 //default Event 추가
addCanvasMouseEventListener('mouse:move', defaultMouseMoveEvent) addCanvasMouseEventListener('mouse:move', defaultMouseMoveEvent)
addCanvasMouseEventListener('mouse:out', defaultMouseOutEvent) addCanvasMouseEventListener('mouse:out', defaultMouseOutEvent)
addDocumentEventListener('keydown', document, defaultKeyboardEvent)
addDocumentEventListener('contextmenu', document, defaultContextMenuEvent) addDocumentEventListener('contextmenu', document, defaultContextMenuEvent)
if (adsorptionPointAddMode) { if (adsorptionPointAddMode) {
addCanvasMouseEventListener('mouse:down', adsorptionPointAddModeStateEvent) addCanvasMouseEventListener('mouse:down', adsorptionPointAddModeStateEvent)
@ -152,6 +151,17 @@ export function useEvent() {
} }
} }
try {
const helpGuideLines = canvas.getObjects().filter((obj) => obj.name === 'helpGuideLine')
if (helpGuideLines.length === 2) {
const guideIntersectionPoint = calculateIntersection(helpGuideLines[0], helpGuideLines[1])
if (guideIntersectionPoint && distanceBetweenPoints(guideIntersectionPoint, pointer) <= adsorptionRange) {
arrivalPoint = guideIntersectionPoint
}
}
} catch (e) {}
const horizontalLine = new fabric.Line([-1 * canvas.width, arrivalPoint.y, 2 * canvas.width, arrivalPoint.y], { const horizontalLine = new fabric.Line([-1 * canvas.width, arrivalPoint.y, 2 * canvas.width, arrivalPoint.y], {
stroke: 'red', stroke: 'red',
strokeWidth: 1, strokeWidth: 1,

View File

@ -14,7 +14,7 @@ export const useLine = () => {
fontFamily: fontFamily, fontFamily: fontFamily,
}) })
if (line.length === 0) { if (line.length < 1) {
return null return null
} }

View File

@ -1,3 +1,4 @@
import { useEffect, useState } from 'react'
import { useRecoilState } from 'recoil' import { useRecoilState } from 'recoil'
import { canvasState, currentCanvasPlanState, initCanvasPlansState, plansState } from '@/store/canvasAtom' import { canvasState, currentCanvasPlanState, initCanvasPlansState, plansState } from '@/store/canvasAtom'
import { useAxios } from '@/hooks/useAxios' import { useAxios } from '@/hooks/useAxios'
@ -5,10 +6,13 @@ import { useMessage } from '@/hooks/useMessage'
import { useSwal } from '@/hooks/useSwal' import { useSwal } from '@/hooks/useSwal'
export function usePlan() { export function usePlan() {
const [planNum, setPlanNum] = useState(0)
const [canvas, setCanvas] = useRecoilState(canvasState) const [canvas, setCanvas] = useRecoilState(canvasState)
const [currentCanvasPlan, setCurrentCanvasPlan] = useRecoilState(currentCanvasPlanState) const [currentCanvasPlan, setCurrentCanvasPlan] = useRecoilState(currentCanvasPlanState)
const [initCanvasPlans, setInitCanvasPlans] = useRecoilState(initCanvasPlansState) const [initCanvasPlans, setInitCanvasPlans] = useRecoilState(initCanvasPlansState) // DB에 저장된 plan
const [plans, setPlans] = useRecoilState(plansState) const [plans, setPlans] = useRecoilState(plansState) // 전체 plan (DB에 저장된 plan + 저장 안된 새로운 plan)
const { swalFire } = useSwal() const { swalFire } = useSwal()
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { get, promisePost, promisePut, promiseDel } = useAxios() const { get, promisePost, promisePut, promiseDel } = useAxios()
@ -72,6 +76,9 @@ export function usePlan() {
// }, 1000) // }, 1000)
} }
/**
* 현재 캔버스에 그려진 데이터를 추출
*/
const currentCanvasData = () => { const currentCanvasData = () => {
removeMouseLines() removeMouseLines()
return addCanvas() return addCanvas()
@ -81,8 +88,7 @@ export function usePlan() {
* 실시간 캔버스 상태와 DB에 저장된 캔버스 상태를 비교하여 수정 여부를 판단 * 실시간 캔버스 상태와 DB에 저장된 캔버스 상태를 비교하여 수정 여부를 판단
*/ */
const checkModifiedCanvasPlan = () => { const checkModifiedCanvasPlan = () => {
removeMouseLines() const canvasStatus = currentCanvasData()
const canvasStatus = addCanvas()
const initPlanData = initCanvasPlans.find((plan) => plan.id === currentCanvasPlan.id) const initPlanData = initCanvasPlans.find((plan) => plan.id === currentCanvasPlan.id)
if (!initPlanData) { if (!initPlanData) {
@ -90,15 +96,10 @@ export function usePlan() {
return JSON.parse(canvasStatus).objects.length > 0 return JSON.parse(canvasStatus).objects.length > 0
} else { } else {
// 저장된 캔버스 // 저장된 캔버스
if (canvasStatus === initPlanData.canvasStatus) { // 각각 object들의 id 목록을 추출하여 비교
return false const canvasObjsIds = getObjectIds(JSON.parse(canvasStatus).objects)
} else { const dbObjsIds = getObjectIds(JSON.parse(initPlanData.canvasStatus).objects)
// 각각 object들의 id 목록을 추출하여 비교 return canvasObjsIds.length !== dbObjsIds.length || !canvasObjsIds.every((id, index) => id === dbObjsIds[index])
const canvasObjsIds = getObjectIds(JSON.parse(canvasStatus).objects)
const dbObjsIds = getObjectIds(JSON.parse(initPlanData.canvasStatus).objects)
return canvasObjsIds.length !== dbObjsIds.length || !canvasObjsIds.every((id, index) => id === dbObjsIds[index])
}
} }
} }
const getObjectIds = (objects) => { const getObjectIds = (objects) => {
@ -112,9 +113,6 @@ export function usePlan() {
* DB에 저장된 데이터를 canvas에서 사용할 있도록 포맷화 * DB에 저장된 데이터를 canvas에서 사용할 있도록 포맷화
*/ */
const dbToCanvasFormat = (cs) => { const dbToCanvasFormat = (cs) => {
// return JSON.stringify(cs.replace(/##/g, '"')) //.replace(/\\/g, ''))//.slice(1, -1))
// JSON.stringify()를 사용하면 "가 \"로 바뀐다. 따라서, JSON.stringify를 제거
// canvasToDbFormat()에서 \\의 상황을 없앴으므로 replace(/\\/g, '')를 제거
return cs.replace(/##/g, '"') return cs.replace(/##/g, '"')
} }
@ -122,92 +120,24 @@ export function usePlan() {
* canvas의 데이터를 DB에 저장할 있도록 포맷화 * canvas의 데이터를 DB에 저장할 있도록 포맷화
*/ */
const canvasToDbFormat = (cs) => { const canvasToDbFormat = (cs) => {
// return JSON.stringify(cs).replace(/"/g, '##')
// addCanvas()에서 JSON.stringify()를 거쳐서 나오는데, 또 감싸버려서 \가 \\로 된다. 따라서, JSON.stringify를 제거
return cs.replace(/"/g, '##') return cs.replace(/"/g, '##')
} }
/** /**
* 페이지 캔버스를 저장하는 함수 * 페이지 캔버스를 저장
*
* 1. 신규 저장 : POST
* param(body) : userId, objectNo, canvasStatus
* 2. 수정 저장 : PUT
* param(body) : id, canvasStatus
*/ */
const saveCanvas = async (userId) => { const saveCanvas = async (userId) => {
removeMouseLines() const canvasStatus = currentCanvasData()
const canvasStatus = addCanvas() initCanvasPlans.some((plan) => plan.id === currentCanvasPlan.id)
? await putCanvasStatus(canvasStatus)
if (initCanvasPlans.some((plan) => plan.id === currentCanvasPlan.id)) { : await postCanvasStatus(userId, canvasStatus)
// canvas 수정
const planData = {
id: currentCanvasPlan.id,
canvasStatus: canvasToDbFormat(canvasStatus),
}
return await promisePut({ url: '/api/canvas-management/canvas-statuses', data: planData })
.then((res) => {
swalFire({ text: getMessage('common.message.save') })
// console.log('[PUT] canvas-statuses res :::::::: %o', res)
setInitCanvasPlans((initCanvasPlans) =>
initCanvasPlans.map((plan) => (plan.id === currentCanvasPlan.id ? { ...plan, canvasStatus: canvasStatus } : plan)),
)
setPlans((plans) => plans.map((plan) => (plan.id === currentCanvasPlan.id ? { ...plan, canvasStatus: canvasStatus } : plan)))
})
.catch((error) => {
swalFire({ text: error.message, icon: 'error' })
// console.error('[PUT] canvas-statuses error :::::::: %o', error)
})
} else {
// canvas 신규 등록
const planData = {
userId: userId,
imageName: 'image_name', // api 필수항목이여서 임시로 넣음, 이후 삭제 필요
objectNo: currentCanvasPlan.objectNo,
canvasStatus: canvasToDbFormat(canvasStatus),
}
return await promisePost({ url: '/api/canvas-management/canvas-statuses', data: planData })
.then((res) => {
swalFire({ text: getMessage('common.message.save') })
// console.log('[POST] canvas-statuses response :::::::: %o', res)
setInitCanvasPlans((initCanvasPlans) => [
...initCanvasPlans,
{
id: res.data,
name: currentCanvasPlan.objectNo + '-' + res.data,
userId: userId,
canvasStatus: canvasStatus,
isNew: currentCanvasPlan.id,
},
])
setPlans((plans) =>
plans.map((plan) =>
plan.id === currentCanvasPlan.id
? {
...plan,
id: res.data,
name: currentCanvasPlan.objectNo + '-' + res.data,
userId: userId,
canvasStatus: canvasStatus,
isNew: currentCanvasPlan.id,
}
: plan,
),
)
})
.catch((error) => {
swalFire({ text: error.message, icon: 'error' })
// console.error('[POST] canvas-statuses res error :::::::: %o', error)
})
}
} }
/** /**
* objectNo에 해당하는 canvas 목록을 조회하는 함수 * objectNo에 해당하는 canvas 목록을 조회
*/ */
const getCanvasByObjectNo = async (userId, objectNo) => { const getCanvasByObjectNo = async (userId, objectNo) => {
// console.log(`[GET] objectNo: ${objectNo} / userId: ${userId}`)
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) => ({
id: item.id, id: item.id,
@ -220,27 +150,192 @@ export function usePlan() {
} }
/** /**
* id에 해당하는 canvas 데이터를 삭제하는 함수 * canvas 데이터를 추가
*/
const postCanvasStatus = async (userId, canvasStatus) => {
const planData = {
userId: userId,
imageName: 'image_name', // api 필수항목이여서 임시로 넣음, 이후 삭제 필요
objectNo: currentCanvasPlan.objectNo,
canvasStatus: canvasToDbFormat(canvasStatus),
}
await promisePost({ url: '/api/canvas-management/canvas-statuses', data: planData })
.then((res) => {
swalFire({ text: getMessage('plan.message.save') })
setInitCanvasPlans((initCanvasPlans) => [...initCanvasPlans, { id: res.data, canvasStatus: canvasStatus }])
setPlans((plans) =>
plans.map((plan) =>
plan.id === currentCanvasPlan.id
? {
...plan,
id: res.data,
name: currentCanvasPlan.objectNo + '-' + res.data,
canvasStatus: canvasStatus,
}
: plan,
),
)
})
.catch((error) => {
swalFire({ text: error.message, icon: 'error' })
})
}
/**
* id에 해당하는 canvas 데이터를 수정
*/
const putCanvasStatus = async (canvasStatus) => {
const planData = {
id: currentCanvasPlan.id,
canvasStatus: canvasToDbFormat(canvasStatus),
}
await promisePut({ url: '/api/canvas-management/canvas-statuses', data: planData })
.then((res) => {
swalFire({ text: getMessage('plan.message.save') })
setInitCanvasPlans((initCanvasPlans) =>
initCanvasPlans.map((plan) => (plan.id === currentCanvasPlan.id ? { ...plan, canvasStatus: canvasStatus } : plan)),
)
setPlans((plans) => plans.map((plan) => (plan.id === currentCanvasPlan.id ? { ...plan, canvasStatus: canvasStatus } : plan)))
})
.catch((error) => {
swalFire({ text: error.message, icon: 'error' })
})
}
/**
* id에 해당하는 canvas 데이터를 삭제
*/ */
const delCanvasById = (id) => { const delCanvasById = (id) => {
return promiseDel({ url: `/api/canvas-management/canvas-statuses/by-id/${id}` }) return promiseDel({ url: `/api/canvas-management/canvas-statuses/by-id/${id}` })
} }
/** /**
* objectNo에 해당하는 canvas 데이터들을 삭제하는 함수 * objectNo에 해당하는 canvas 데이터들을 삭제
*/ */
const delCanvasByObjectNo = (objectNo) => { const delCanvasByObjectNo = (objectNo) => {
return promiseDel({ url: `/api/canvas-management/canvas-statuses/by-object/${objectNo}` }) return promiseDel({ url: `/api/canvas-management/canvas-statuses/by-object/${objectNo}` })
} }
/**
* plan 이동
* 현재 plan의 작업상태를 확인, 저장 이동
*/
const handleCurrentPlan = (userId, newCurrentId) => {
if (!currentCanvasPlan || currentCanvasPlan.id !== newCurrentId) {
if (currentCanvasPlan?.id && checkModifiedCanvasPlan()) {
swalFire({
text: `${currentCanvasPlan.name} ` + getMessage('plan.message.confirm.save'),
type: 'confirm',
confirmFn: async () => {
await saveCanvas(userId)
updateCurrentPlan(newCurrentId)
},
denyFn: () => {
updateCurrentPlan(newCurrentId)
},
})
} else {
updateCurrentPlan(newCurrentId)
}
}
}
const updateCurrentPlan = (newCurrentId) => {
setPlans((plans) =>
plans.map((plan) => {
return { ...plan, isCurrent: plan.id === newCurrentId }
}),
)
}
useEffect(() => {
setCurrentCanvasPlan(plans.find((plan) => plan.isCurrent) || null)
}, [plans])
/**
* 새로운 plan 생성
* 현재 plan의 데이터가 있을 경우 복제 여부를 확인
*/
const handleAddPlan = (userId, objectNo) => {
JSON.parse(currentCanvasData()).objects.length > 0
? swalFire({
text: `${currentCanvasPlan.name} ` + getMessage('plan.message.confirm.copy'),
type: 'confirm',
confirmFn: () => {
addPlan(userId, objectNo, currentCanvasData())
},
denyFn: () => {
addPlan(userId, objectNo)
},
})
: addPlan(userId, objectNo)
}
const addPlan = (userId, objectNo, canvasStatus = '') => {
const newPlan = {
id: planNum,
name: `Plan ${planNum + 1}`,
objectNo: objectNo,
userId: userId,
canvasStatus: canvasStatus,
}
setPlans([...plans, newPlan])
handleCurrentPlan(userId, planNum)
setPlanNum(planNum + 1)
}
/**
* plan 삭제
*/
const handleDeletePlan = (e, id) => {
e.stopPropagation() // 이벤트 버블링 방지
if (initCanvasPlans.some((plan) => plan.id === id)) {
delCanvasById(id)
.then((res) => {
swalFire({ text: getMessage('plan.message.delete') })
setInitCanvasPlans((initCanvasPlans) => initCanvasPlans.filter((plan) => plan.id !== id))
setPlans((plans) => plans.filter((plan) => plan.id !== id))
})
.catch((error) => {
swalFire({ text: error.message, icon: 'error' })
})
} else {
setPlans((plans) => plans.filter((plan) => plan.id !== id))
swalFire({ text: getMessage('plan.message.delete') })
}
// 삭제 후 last 데이터에 포커싱
const lastPlan = plans.filter((plan) => plan.id !== id).at(-1)
if (!lastPlan) {
setPlanNum(0)
setCurrentCanvasPlan(null)
} else if (id !== lastPlan.id) {
updateCurrentPlan(lastPlan.id)
}
}
/**
* plan 조회
*/
const loadCanvasPlanData = (userId, objectNo) => {
getCanvasByObjectNo(userId, objectNo).then((res) => {
// console.log('canvas 목록 ', res)
if (res.length > 0) {
setInitCanvasPlans(res)
setPlans(res)
updateCurrentPlan(res.at(-1).id) // last 데이터에 포커싱
setPlanNum(res.length)
} else {
addPlan(userId, objectNo)
}
})
}
return { return {
canvas, canvas,
removeMouseLines, plans,
currentCanvasData,
saveCanvas, saveCanvas,
addCanvas, handleCurrentPlan,
checkModifiedCanvasPlan, handleAddPlan,
getCanvasByObjectNo, handleDeletePlan,
delCanvasById, loadCanvasPlanData,
} }
} }

View File

@ -247,6 +247,11 @@
"modal.object.setting.direction.select": "方向の選択", "modal.object.setting.direction.select": "方向の選択",
"modal.placement.surface.setting.info": "ⓘ ①の長さ入力後に対角線の長さを入力すると、②の長さを自動計算します。", "modal.placement.surface.setting.info": "ⓘ ①の長さ入力後に対角線の長さを入力すると、②の長さを自動計算します。",
"modal.placement.surface.setting.diagonal.length": "斜めの長さ", "modal.placement.surface.setting.diagonal.length": "斜めの長さ",
"plan.message.confirm.save": "PLAN을 저장하시겠습니까?",
"plan.message.confirm.copy": "PLAN을 복사하시겠습니까?",
"plan.message.confirm.delete": "PLAN을 삭제하시겠습니까?",
"plan.message.save": "저장되었습니다.",
"plan.message.delete": "삭제되었습니다.",
"setting": "設定", "setting": "設定",
"common.message.no.data": "No data", "common.message.no.data": "No data",
"common.message.no.dataDown": "ダウンロードするデータがありません", "common.message.no.dataDown": "ダウンロードするデータがありません",
@ -335,7 +340,7 @@
"common.message.writeToConfirm": "作成解除を実行しますか?", "common.message.writeToConfirm": "作成解除を実行しますか?",
"common.message.password.init.success": "パスワード [{0}] に初期化されました。", "common.message.password.init.success": "パスワード [{0}] に初期化されました。",
"common.message.no.edit.save": "この文書は変更できません。", "common.message.no.edit.save": "この文書は変更できません。",
"common.require": "필수", "common.require": "必須",
"commons.west": "立つ", "commons.west": "立つ",
"commons.east": "ドン", "commons.east": "ドン",
"commons.south": "立つ", "commons.south": "立つ",
@ -497,6 +502,7 @@
"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.search.title": "物件状況", "stuff.search.title": "物件状況",
"stuff.search.btn1": "物件登録", "stuff.search.btn1": "物件登録",
"stuff.search.btn2": "照会", "stuff.search.btn2": "照会",
@ -511,6 +517,11 @@
"stuff.search.period": "期間検索", "stuff.search.period": "期間検索",
"stuff.search.schDateTypeU": "更新日", "stuff.search.schDateTypeU": "更新日",
"stuff.search.schDateTypeR": "登録日", "stuff.search.schDateTypeR": "登録日",
"stuff.search.grid.title": "商品リスト",
"stuff.search.grid.all": "全体",
"stuff.search.grid.selected": "選択",
"stuff.search.grid.schSortTypeR": "最近の登録日",
"stuff.search.grid.schSortTypeU": "最近の更新日",
"stuff.windSelectPopup.title": "風速選択", "stuff.windSelectPopup.title": "風速選択",
"stuff.windSelectPopup.table.selected": "選択", "stuff.windSelectPopup.table.selected": "選択",
"stuff.windSelectPopup.table.windspeed": "風速", "stuff.windSelectPopup.table.windspeed": "風速",

View File

@ -252,6 +252,11 @@
"modal.object.setting.direction.select": "방향 선택", "modal.object.setting.direction.select": "방향 선택",
"modal.placement.surface.setting.info": "ⓘ ①의 길이 입력 후 대각선 길이를 입력하면 ②의 길이를 자동 계산합니다.", "modal.placement.surface.setting.info": "ⓘ ①의 길이 입력 후 대각선 길이를 입력하면 ②의 길이를 자동 계산합니다.",
"modal.placement.surface.setting.diagonal.length": "대각선 길이", "modal.placement.surface.setting.diagonal.length": "대각선 길이",
"plan.message.confirm.save": "PLAN을 저장하시겠습니까?",
"plan.message.confirm.copy": "PLAN을 복사하시겠습니까?",
"plan.message.confirm.delete": "PLAN을 삭제하시겠습니까?",
"plan.message.save": "저장되었습니다.",
"plan.message.delete": "삭제되었습니다.",
"setting": "설정", "setting": "설정",
"common.message.no.data": "No data", "common.message.no.data": "No data",
"common.message.no.dataDown": "No data to download", "common.message.no.dataDown": "No data to download",
@ -502,6 +507,7 @@
"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.search.title": "물건현황", "stuff.search.title": "물건현황",
"stuff.search.btn1": "신규등록", "stuff.search.btn1": "신규등록",
"stuff.search.btn2": "조회", "stuff.search.btn2": "조회",
@ -516,6 +522,11 @@
"stuff.search.period": "기간검색", "stuff.search.period": "기간검색",
"stuff.search.schDateTypeU": "갱신일", "stuff.search.schDateTypeU": "갱신일",
"stuff.search.schDateTypeR": "등록일", "stuff.search.schDateTypeR": "등록일",
"stuff.search.grid.title": "물건목록",
"stuff.search.grid.all": "전체",
"stuff.search.grid.selected": "선택",
"stuff.search.grid.schSortTypeR": "최근 등록일",
"stuff.search.grid.schSortTypeU": "최근 갱신일",
"stuff.windSelectPopup.title": "풍속선택", "stuff.windSelectPopup.title": "풍속선택",
"stuff.windSelectPopup.table.selected": "선택", "stuff.windSelectPopup.table.selected": "선택",
"stuff.windSelectPopup.table.windspeed": "풍속", "stuff.windSelectPopup.table.windspeed": "풍속",

View File

@ -289,3 +289,8 @@ export const canGridOptionSeletor = selector({
return points.length === 0 || outerLineFix return points.length === 0 || outerLineFix
}, },
}) })
export const globalPitchState = atom({
key: 'globalPitch',
default: 4,
})

View File

@ -16,7 +16,7 @@ export const planReqSearchState = atom({
schStartDt: dayjs(new Date()).add(-3, 'month').format('YYYY-MM-DD'), //시작일 schStartDt: dayjs(new Date()).add(-3, 'month').format('YYYY-MM-DD'), //시작일
schEndDt: dayjs(new Date()).format('YYYY-MM-DD'), //종료일 schEndDt: dayjs(new Date()).format('YYYY-MM-DD'), //종료일
startRow: 1, startRow: 1,
endRow: 100, endRow: 20,
}, },
dangerouslyAllowMutability: true, dangerouslyAllowMutability: true,
}) })

View File

@ -18,10 +18,6 @@ export const stuffSearchState = atom({
startRow: 1, startRow: 1,
endRow: 100, endRow: 100,
schSortType: 'R', //정렬조건 (R:최근등록일 U:최근수정일) schSortType: 'R', //정렬조건 (R:최근등록일 U:최근수정일)
selObject: {
value: '',
label: '',
},
}, },
dangerouslyAllowMutability: true, dangerouslyAllowMutability: true,
}) })

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -8,8 +8,10 @@ export const handleFileDown = async (file) => {
const params = new URLSearchParams({ const params = new URLSearchParams({
encodeFileNo: file.encodeFileNo, encodeFileNo: file.encodeFileNo,
}) })
const options = { responseType: 'blob' }
const apiUrl = `${url}?${params.toString()}` const apiUrl = `${url}?${params.toString()}`
await promiseGet({ url: apiUrl, responseType: 'blob' })
await promiseGet({ url: apiUrl, option: options })
.then((resultData) => { .then((resultData) => {
if (resultData) { if (resultData) {
const blob = new Blob([resultData.data], { type: resultData.headers['content-type'] || 'application/octet-stream' }) const blob = new Blob([resultData.data], { type: resultData.headers['content-type'] || 'application/octet-stream' })
@ -25,7 +27,7 @@ export const handleFileDown = async (file) => {
} }
}) })
.catch((error) => { .catch((error) => {
alert(error.response.data.message) alert('File does not exist.')
}) })
} }

View File

@ -744,67 +744,6 @@ export const polygonToTurfPolygon = (polygon) => {
) )
} }
export const splitDormerTriangle = (triangle, direction) => {
const halfWidth = triangle.width / 2
let leftPoints = []
let rightPoints = []
let leftPointOffset = []
let rightPointOffset = []
if (direction === 'down') {
leftPoints = [
{ x: triangle.left, y: triangle.top },
{ x: triangle.left - halfWidth, y: triangle.top + triangle.height },
{ x: triangle.left, y: triangle.top + triangle.height },
]
rightPoints = [
{ x: triangle.left, y: triangle.top },
{ x: triangle.left, y: triangle.top + triangle.height },
{ x: triangle.left + halfWidth, y: triangle.top + triangle.height },
]
} else if (direction === 'up') {
leftPoints = [
{ x: triangle.left, y: triangle.top },
{ x: triangle.left - halfWidth, y: triangle.top - triangle.height },
{ x: triangle.left, y: triangle.top - triangle.height },
]
rightPoints = [
{ x: triangle.left, y: triangle.top },
{ x: triangle.left, y: triangle.top - triangle.height },
{ x: triangle.left + halfWidth, y: triangle.top - triangle.height },
]
} else if (direction === 'left') {
leftPoints = [
{ x: triangle.left, y: triangle.top },
{ x: triangle.left - triangle.height, y: triangle.top - halfWidth },
{ x: triangle.left - triangle.height, y: triangle.top },
]
rightPoints = [
{ x: triangle.left, y: triangle.top },
{ x: triangle.left - triangle.height, y: triangle.top },
{ x: triangle.left - triangle.height, y: triangle.top + halfWidth },
]
} else if (direction === 'right') {
leftPoints = [
{ x: triangle.left, y: triangle.top },
{ x: triangle.left + triangle.height, y: triangle.top },
{ x: triangle.left + triangle.height, y: triangle.top + triangle.height },
]
rightPoints = [
{ x: triangle.left, y: triangle.top },
{ x: triangle.left + triangle.height, y: triangle.top },
{ x: triangle.left + triangle.height, y: triangle.top - triangle.height },
]
}
return [leftPoints, rightPoints]
}
export const triangleToPolygon = (triangle) => { export const triangleToPolygon = (triangle) => {
const points = [] const points = []
const halfWidth = triangle.width / 2 const halfWidth = triangle.width / 2
@ -853,7 +792,7 @@ export function setSurfaceShapePattern(polygon) {
patternSourceCanvas.width = polygon.width * ratio patternSourceCanvas.width = polygon.width * ratio
patternSourceCanvas.height = polygon.height * ratio patternSourceCanvas.height = polygon.height * ratio
const ctx = patternSourceCanvas.getContext('2d') const ctx = patternSourceCanvas.getContext('2d')
const offset = roofStyle === 1 ? 0 : patternSize.width / 2 let offset = roofStyle === 1 ? 0 : patternSize.width / 2
const rows = Math.floor(patternSourceCanvas.height / patternSize.height) const rows = Math.floor(patternSourceCanvas.height / patternSize.height)
const cols = Math.floor(patternSourceCanvas.width / patternSize.width) const cols = Math.floor(patternSourceCanvas.width / patternSize.width)
@ -861,23 +800,46 @@ export function setSurfaceShapePattern(polygon) {
ctx.strokeStyle = 'green' ctx.strokeStyle = 'green'
ctx.lineWidth = 0.4 ctx.lineWidth = 0.4
for (let row = 0; row <= rows; row++) { if (polygon.direction === 'east' || polygon.direction === 'west') {
const y = row * patternSize.height offset = roofStyle === 1 ? 0 : patternSize.height / 2
ctx.beginPath()
ctx.moveTo(0, y) // 선 시작점
ctx.lineTo(patternSourceCanvas.width, y) // 선 끝점
ctx.stroke()
for (let col = 0; col <= cols; col++) { for (let col = 0; col <= cols; col++) {
const x = col * patternSize.width + (row % 2 === 0 ? 0 : offset) const x = col * patternSize.width
const yStart = row * patternSize.height const yStart = 0
const yEnd = yStart + patternSize.height const yEnd = patternSourceCanvas.height
ctx.beginPath() ctx.beginPath()
ctx.moveTo(x, yStart) // 선 시작점 ctx.moveTo(x, yStart) // 선 시작점
ctx.lineTo(x, yEnd) // 선 끝점 ctx.lineTo(x, yEnd) // 선 끝점
ctx.stroke() ctx.stroke()
for (let row = 0; row <= rows; row++) {
const y = row * patternSize.height + (col % 2 === 0 ? 0 : offset)
const xStart = col * patternSize.width
const xEnd = xStart + patternSize.width
ctx.beginPath()
ctx.moveTo(xStart, y) // 선 시작점
ctx.lineTo(xEnd, y) // 선 끝점
ctx.stroke()
}
}
} else {
for (let row = 0; row <= rows; row++) {
const y = row * patternSize.height
ctx.beginPath()
ctx.moveTo(0, y) // 선 시작점
ctx.lineTo(patternSourceCanvas.width, y) // 선 끝점
ctx.stroke()
for (let col = 0; col <= cols; col++) {
const x = col * patternSize.width + (row % 2 === 0 ? 0 : offset)
const yStart = row * patternSize.height
const yEnd = yStart + patternSize.height
ctx.beginPath()
ctx.moveTo(x, yStart) // 선 시작점
ctx.lineTo(x, yEnd) // 선 끝점
ctx.stroke()
}
} }
} }

View File

@ -273,7 +273,7 @@
resolved "https://registry.npmjs.org/@bedrock-layout/use-stateful-ref/-/use-stateful-ref-1.4.1.tgz" resolved "https://registry.npmjs.org/@bedrock-layout/use-stateful-ref/-/use-stateful-ref-1.4.1.tgz"
integrity sha512-4eKO2KdQEXcR5LI4QcxqlJykJUDQJWDeWYAukIn6sRQYoabcfI5kDl61PUi6FR6o8VFgQ8IEP7HleKqWlSe8SQ== integrity sha512-4eKO2KdQEXcR5LI4QcxqlJykJUDQJWDeWYAukIn6sRQYoabcfI5kDl61PUi6FR6o8VFgQ8IEP7HleKqWlSe8SQ==
"@emotion/babel-plugin@^11.11.0", "@emotion/babel-plugin@^11.12.0": "@emotion/babel-plugin@^11.12.0":
version "11.12.0" version "11.12.0"
resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.12.0.tgz#7b43debb250c313101b3f885eba634f1d723fcc2" resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.12.0.tgz#7b43debb250c313101b3f885eba634f1d723fcc2"
integrity sha512-y2WQb+oP8Jqvvclh8Q55gLUyb7UFvgv7eJfsj7td5TToBrIUtPay2kMrZi4xjq9qw2vD0ZR5fSho0yqoFgX7Rw== integrity sha512-y2WQb+oP8Jqvvclh8Q55gLUyb7UFvgv7eJfsj7td5TToBrIUtPay2kMrZi4xjq9qw2vD0ZR5fSho0yqoFgX7Rw==
@ -290,7 +290,7 @@
source-map "^0.5.7" source-map "^0.5.7"
stylis "4.2.0" stylis "4.2.0"
"@emotion/cache@^11.11.0", "@emotion/cache@^11.13.0", "@emotion/cache@^11.4.0": "@emotion/cache@^11.13.0", "@emotion/cache@^11.4.0":
version "11.13.1" version "11.13.1"
resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.13.1.tgz#fecfc54d51810beebf05bf2a161271a1a91895d7" resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.13.1.tgz#fecfc54d51810beebf05bf2a161271a1a91895d7"
integrity sha512-iqouYkuEblRcXmylXIwwOodiEK5Ifl7JcX7o6V4jI3iW4mLXX3dmt5xwBtIkJiQEXFAI+pC8X0i67yiPkH9Ucw== integrity sha512-iqouYkuEblRcXmylXIwwOodiEK5Ifl7JcX7o6V4jI3iW4mLXX3dmt5xwBtIkJiQEXFAI+pC8X0i67yiPkH9Ucw==
@ -306,32 +306,11 @@
resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.9.2.tgz#ff9221b9f58b4dfe61e619a7788734bd63f6898b" resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.9.2.tgz#ff9221b9f58b4dfe61e619a7788734bd63f6898b"
integrity sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g== integrity sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==
"@emotion/is-prop-valid@^1.2.1":
version "1.3.1"
resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-1.3.1.tgz#8d5cf1132f836d7adbe42cf0b49df7816fc88240"
integrity sha512-/ACwoqx7XQi9knQs/G0qKvv5teDMhD7bXYns9N/wM8ah8iNb8jZ2uNO0YOgiq2o2poIvVtJS2YALasQuMSQ7Kw==
dependencies:
"@emotion/memoize" "^0.9.0"
"@emotion/memoize@^0.9.0": "@emotion/memoize@^0.9.0":
version "0.9.0" version "0.9.0"
resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.9.0.tgz#745969d649977776b43fc7648c556aaa462b4102" resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.9.0.tgz#745969d649977776b43fc7648c556aaa462b4102"
integrity sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ== integrity sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==
"@emotion/react@11.11.0":
version "11.11.0"
resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.11.0.tgz#408196b7ef8729d8ad08fc061b03b046d1460e02"
integrity sha512-ZSK3ZJsNkwfjT3JpDAWJZlrGD81Z3ytNDsxw1LKq1o+xkmO5pnWfr6gmCC8gHEFf3nSSX/09YrG67jybNPxSUw==
dependencies:
"@babel/runtime" "^7.18.3"
"@emotion/babel-plugin" "^11.11.0"
"@emotion/cache" "^11.11.0"
"@emotion/serialize" "^1.1.2"
"@emotion/use-insertion-effect-with-fallbacks" "^1.0.1"
"@emotion/utils" "^1.2.1"
"@emotion/weak-memoize" "^0.3.1"
hoist-non-react-statics "^3.3.1"
"@emotion/react@^11.8.1": "@emotion/react@^11.8.1":
version "11.13.3" version "11.13.3"
resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.13.3.tgz#a69d0de2a23f5b48e0acf210416638010e4bd2e4" resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.13.3.tgz#a69d0de2a23f5b48e0acf210416638010e4bd2e4"
@ -346,7 +325,7 @@
"@emotion/weak-memoize" "^0.4.0" "@emotion/weak-memoize" "^0.4.0"
hoist-non-react-statics "^3.3.1" hoist-non-react-statics "^3.3.1"
"@emotion/serialize@^1.1.2", "@emotion/serialize@^1.2.0", "@emotion/serialize@^1.3.1": "@emotion/serialize@^1.2.0", "@emotion/serialize@^1.3.1":
version "1.3.2" version "1.3.2"
resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.3.2.tgz#e1c1a2e90708d5d85d81ccaee2dfeb3cc0cccf7a" resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.3.2.tgz#e1c1a2e90708d5d85d81ccaee2dfeb3cc0cccf7a"
integrity sha512-grVnMvVPK9yUVE6rkKfAJlYZgo0cu3l9iMC77V7DW6E1DUIrU68pSEXRmFZFOFB1QFo57TncmOcvcbMDWsL4yA== integrity sha512-grVnMvVPK9yUVE6rkKfAJlYZgo0cu3l9iMC77V7DW6E1DUIrU68pSEXRmFZFOFB1QFo57TncmOcvcbMDWsL4yA==
@ -362,38 +341,21 @@
resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.4.0.tgz#c9299c34d248bc26e82563735f78953d2efca83c" resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.4.0.tgz#c9299c34d248bc26e82563735f78953d2efca83c"
integrity sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg== integrity sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==
"@emotion/styled@11.11.0":
version "11.11.0"
resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-11.11.0.tgz#26b75e1b5a1b7a629d7c0a8b708fbf5a9cdce346"
integrity sha512-hM5Nnvu9P3midq5aaXj4I+lnSfNi7Pmd4EWk1fOZ3pxookaQTNew6bp4JaCBYM4HVFZF9g7UjJmsUmC2JlxOng==
dependencies:
"@babel/runtime" "^7.18.3"
"@emotion/babel-plugin" "^11.11.0"
"@emotion/is-prop-valid" "^1.2.1"
"@emotion/serialize" "^1.1.2"
"@emotion/use-insertion-effect-with-fallbacks" "^1.0.1"
"@emotion/utils" "^1.2.1"
"@emotion/unitless@^0.10.0": "@emotion/unitless@^0.10.0":
version "0.10.0" version "0.10.0"
resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.10.0.tgz#2af2f7c7e5150f497bdabd848ce7b218a27cf745" resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.10.0.tgz#2af2f7c7e5150f497bdabd848ce7b218a27cf745"
integrity sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg== integrity sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==
"@emotion/use-insertion-effect-with-fallbacks@^1.0.1", "@emotion/use-insertion-effect-with-fallbacks@^1.1.0": "@emotion/use-insertion-effect-with-fallbacks@^1.1.0":
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.1.0.tgz#1a818a0b2c481efba0cf34e5ab1e0cb2dcb9dfaf" resolved "https://registry.yarnpkg.com/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.1.0.tgz#1a818a0b2c481efba0cf34e5ab1e0cb2dcb9dfaf"
integrity sha512-+wBOcIV5snwGgI2ya3u99D7/FJquOIniQT1IKyDsBmEgwvpxMNeS65Oib7OnE2d2aY+3BU4OiH+0Wchf8yk3Hw== integrity sha512-+wBOcIV5snwGgI2ya3u99D7/FJquOIniQT1IKyDsBmEgwvpxMNeS65Oib7OnE2d2aY+3BU4OiH+0Wchf8yk3Hw==
"@emotion/utils@^1.2.1", "@emotion/utils@^1.4.0", "@emotion/utils@^1.4.1": "@emotion/utils@^1.4.0", "@emotion/utils@^1.4.1":
version "1.4.1" version "1.4.1"
resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.4.1.tgz#b3adbb43de12ee2149541c4f1337d2eb7774f0ad" resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.4.1.tgz#b3adbb43de12ee2149541c4f1337d2eb7774f0ad"
integrity sha512-BymCXzCG3r72VKJxaYVwOXATqXIZ85cuvg0YOUDxMGNrKc1DJRZk8MgV5wyXRyEayIMd4FuXJIUgTBXvDNW5cA== integrity sha512-BymCXzCG3r72VKJxaYVwOXATqXIZ85cuvg0YOUDxMGNrKc1DJRZk8MgV5wyXRyEayIMd4FuXJIUgTBXvDNW5cA==
"@emotion/weak-memoize@^0.3.1":
version "0.3.1"
resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz#d0fce5d07b0620caa282b5131c297bb60f9d87e6"
integrity sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==
"@emotion/weak-memoize@^0.4.0": "@emotion/weak-memoize@^0.4.0":
version "0.4.0" version "0.4.0"
resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz#5e13fac887f08c44f76b0ccaf3370eb00fec9bb6" resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz#5e13fac887f08c44f76b0ccaf3370eb00fec9bb6"
@ -5955,14 +5917,6 @@ react-draggable@^4.4.6:
clsx "^1.1.1" clsx "^1.1.1"
prop-types "^15.8.1" prop-types "^15.8.1"
react-dropdown-select@^4.11.3:
version "4.11.3"
resolved "https://registry.yarnpkg.com/react-dropdown-select/-/react-dropdown-select-4.11.3.tgz#b23b8906f3bedc9d6a1a2125af936b34d4057158"
integrity sha512-/mOGSqqhmKsxxrmotLM+qn1Ss3nxGN6QnYusyQ7f0wizsWrc7ZmbcZhGRtwkJwpL6JYDQVTn19EYxJU1XfXrDA==
dependencies:
"@emotion/react" "11.11.0"
"@emotion/styled" "11.11.0"
react-hook-form@^7.53.0: react-hook-form@^7.53.0:
version "7.53.0" version "7.53.0"
resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.53.0.tgz#3cf70951bf41fa95207b34486203ebefbd3a05ab" resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.53.0.tgz#3cf70951bf41fa95207b34486203ebefbd3a05ab"