Merge branch 'dev' into feature/jaeyoung

This commit is contained in:
Jaeyoung Lee 2025-01-07 14:06:35 +09:00
commit 81ea49ccdc
82 changed files with 7528 additions and 4631 deletions

4
.gitignore vendored
View File

@ -40,4 +40,6 @@ next-env.d.ts
#lock files #lock files
yarn.lock yarn.lock
package-lock.json package-lock.json
pnpm-lock.yaml
certificates

1
MainLayout.codediagram Normal file

File diff suppressed because one or more lines are too long

View File

@ -6,13 +6,15 @@
"dev": "next dev", "dev": "next dev",
"build": "next build", "build": "next build",
"start": "next start", "start": "next start",
"lint": "next lint" "lint": "next lint",
"serve": "node server.js"
}, },
"dependencies": { "dependencies": {
"@nextui-org/react": "^2.4.2", "@nextui-org/react": "^2.4.2",
"ag-grid-react": "^32.0.2", "ag-grid-react": "^32.0.2",
"axios": "^1.7.8", "axios": "^1.7.8",
"chart.js": "^4.4.6", "chart.js": "^4.4.6",
"dayjs": "^1.11.13",
"fabric": "^5.3.0", "fabric": "^5.3.0",
"framer-motion": "^11.2.13", "framer-motion": "^11.2.13",
"fs": "^0.0.1-security", "fs": "^0.0.1-security",
@ -20,7 +22,7 @@
"js-cookie": "^3.0.5", "js-cookie": "^3.0.5",
"mathjs": "^13.0.2", "mathjs": "^13.0.2",
"mssql": "^11.0.1", "mssql": "^11.0.1",
"next": "14.2.14", "next": "14.2.21",
"next-international": "^1.2.4", "next-international": "^1.2.4",
"react": "^18", "react": "^18",
"react-chartjs-2": "^5.2.0", "react-chartjs-2": "^5.2.0",
@ -32,12 +34,12 @@
"react-icons": "^5.3.0", "react-icons": "^5.3.0",
"react-loading-skeleton": "^3.5.0", "react-loading-skeleton": "^3.5.0",
"react-responsive-modal": "^6.4.2", "react-responsive-modal": "^6.4.2",
"react-select": "^5.8.1",
"recoil": "^0.7.7", "recoil": "^0.7.7",
"sweetalert2": "^11.14.1", "sweetalert2": "^11.14.1",
"sweetalert2-react-content": "^5.0.7", "sweetalert2-react-content": "^5.0.7",
"uuid": "^10.0.0", "usehooks-ts": "^3.1.0",
"dayjs": "^1.11.13", "uuid": "^10.0.0"
"react-select": "^5.8.1"
}, },
"devDependencies": { "devDependencies": {
"@turf/turf": "^7.0.0", "@turf/turf": "^7.0.0",

40
server.js Normal file
View File

@ -0,0 +1,40 @@
const http = require('http')
const { parse } = require('url')
const next = require('next')
const https = require('https')
const fs = require('fs')
const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()
const PORT = 3000
const httpsOptions = {
key: fs.readFileSync('./certificates/key.pem'),
cert: fs.readFileSync('./certificates/cert.pem'),
}
app.prepare().then(() => {
http
.createServer((req, res) => {
const parsedUrl = parse(req.url, true)
handle(req, res, parsedUrl)
})
.listen(PORT, (err) => {
if (err) throw err
console.log(`> Ready on http://localhost:${PORT}`)
})
// https 서버 추가
https
.createServer(httpsOptions, (req, res) => {
const parsedUrl = parse(req.url, true)
handle(req, res, parsedUrl)
})
.listen(PORT + 1, (err) => {
if (err) throw err
console.log(`> HTTPS: Ready on https://localhost:${PORT + 1}`)
})
})

View File

@ -0,0 +1,24 @@
'use client'
import { createContext, useEffect, useState } from 'react'
import { useLocalStorage } from 'usehooks-ts'
export const GlobalDataContext = createContext({
managementState: {},
setManagementState: () => {},
managementStateLoaded: null,
})
const GlobalDataProvider = ({ children }) => {
const [managementState, setManagementState] = useState({})
// TODO: 임시 조치이며 개발 완료시 삭제 예정 -> 잊지말기...
const [managementStateLoaded, setManagementStateLoaded] = useLocalStorage('managementStateLoaded', null)
useEffect(() => {
setManagementStateLoaded(managementState)
}, [managementState])
return <GlobalDataContext.Provider value={{ managementState, setManagementState, managementStateLoaded }}>{children}</GlobalDataContext.Provider>
}
export default GlobalDataProvider

View File

@ -1,9 +1,8 @@
'use client' 'use client'
import { createContext, useEffect, useState } from 'react' import { createContext, useState } from 'react'
import { ErrorBoundary } from 'next/dist/client/components/error-boundary' import { ErrorBoundary } from 'next/dist/client/components/error-boundary'
import { useCommonCode } from '@/hooks/common/useCommonCode' import { useCommonCode } from '@/hooks/common/useCommonCode'
import { usePlan } from '@/hooks/usePlan'
import ServerError from './error' import ServerError from './error'
import '@/styles/common.scss' import '@/styles/common.scss'
@ -19,7 +18,6 @@ export const QcastContext = createContext({
export const QcastProvider = ({ children }) => { export const QcastProvider = ({ children }) => {
const [planSave, setPlanSave] = useState(false) const [planSave, setPlanSave] = useState(false)
const [isGlobalLoading, setIsGlobalLoading] = useState(false) const [isGlobalLoading, setIsGlobalLoading] = useState(false)
const { currentCanvasPlan, modifiedPlans, checkUnsavedCanvasPlan } = usePlan()
const { commonCode, findCommonCode } = useCommonCode() const { commonCode, findCommonCode } = useCommonCode()
const [qcastState, setQcastState] = useState({ const [qcastState, setQcastState] = useState({
@ -30,16 +28,6 @@ export const QcastProvider = ({ children }) => {
businessChargerMail: null, businessChargerMail: null,
}) })
useEffect(() => {
const targetElement = document.getElementById('canvas')
if (!targetElement && currentCanvasPlan?.id && planSave) {
setPlanSave((prev) => !prev)
checkUnsavedCanvasPlan()
} else if (targetElement && currentCanvasPlan?.id) {
setPlanSave(true)
}
}, [modifiedPlans])
// useEffect(() => { // useEffect(() => {
// console.log('commonCode', commonCode) // console.log('commonCode', commonCode)
// console.log(findCommonCode(113600)) // console.log(findCommonCode(113600))

View File

@ -4,10 +4,11 @@ import { getSession } from '@/lib/authActions'
import RecoilRootWrapper from './RecoilWrapper' import RecoilRootWrapper from './RecoilWrapper'
import { QcastProvider } from './QcastProvider' import { QcastProvider } from './QcastProvider'
import SessionProvider from './SessionProvider'
import GlobalDataProvider from './GlobalDataProvider'
import Header from '@/components/header/Header' import Header from '@/components/header/Header'
import QModal from '@/components/common/modal/QModal' import QModal from '@/components/common/modal/QModal'
import Dimmed from '@/components/ui/Dimmed' import Dimmed from '@/components/ui/Dimmed'
import SessionProvider from './SessionProvider'
import PopupManager from '@/components/common/popupManager/PopupManager' import PopupManager from '@/components/common/popupManager/PopupManager'
import './globals.css' import './globals.css'
@ -50,7 +51,6 @@ export default async function RootLayout({ children }) {
isLoggedIn: session.isLoggedIn, isLoggedIn: session.isLoggedIn,
} }
} }
if (!headerPathname.includes('/login') && !session.isLoggedIn) { if (!headerPathname.includes('/login') && !session.isLoggedIn) {
redirect('/login') redirect('/login')
} }
@ -61,26 +61,28 @@ export default async function RootLayout({ children }) {
return ( return (
<RecoilRootWrapper> <RecoilRootWrapper>
<html lang="en"> <GlobalDataProvider>
<body> <html lang="en">
{headerPathname === '/login' || headerPathname === '/join' ? ( <body>
<QcastProvider>{children}</QcastProvider> {headerPathname === '/login' || headerPathname === '/join' ? (
) : ( <QcastProvider>{children}</QcastProvider>
<QcastProvider> ) : (
<div className="wrap"> <QcastProvider>
<Header userSession={sessionProps} /> <div className="wrap">
<div className="content"> <Header userSession={sessionProps} />
<Dimmed /> <div className="content">
<SessionProvider useSession={sessionProps}>{children}</SessionProvider> <Dimmed />
<SessionProvider useSession={sessionProps}>{children}</SessionProvider>
</div>
<Footer />
</div> </div>
<Footer /> </QcastProvider>
</div> )}
</QcastProvider> <QModal />
)} <PopupManager />
<QModal /> </body>
<PopupManager /> </html>
</body> </GlobalDataProvider>
</html>
</RecoilRootWrapper> </RecoilRootWrapper>
) )
} }

View File

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

View File

@ -112,6 +112,7 @@ export const POLYGON_TYPE = {
WALL: 'wall', WALL: 'wall',
TRESTLE: 'trestle', TRESTLE: 'trestle',
MODULE_SETUP_SURFACE: 'moduleSetupSurface', MODULE_SETUP_SURFACE: 'moduleSetupSurface',
MODULE: 'module',
} }
export const SAVE_KEY = [ export const SAVE_KEY = [

View File

@ -41,12 +41,12 @@ export default function MainPage(mainPageProps) {
if (searchRadioType === 'object') { if (searchRadioType === 'object') {
setStuffSearch({ setStuffSearch({
...stuffSearch, ...stuffSearch,
schObjectNo: searchTxt, schObjectNo: searchTxt.trim(),
code: 'M', code: 'M',
}) })
router.push('/management/stuff', { scroll: false }) router.push('/management/stuff', { scroll: false })
} else { } else {
setSearchForm({ ...searchForm, searchValue: searchTxt, mainFlag: 'Y' }) setSearchForm({ ...searchForm, searchValue: searchTxt.trim(), mainFlag: 'Y' })
router.push('/community/faq') router.push('/community/faq')
} }
} }

View File

@ -1,27 +1,28 @@
'use client' 'use client'
import { useRef, useState, useEffect } from 'react' import { useRef, useState, useEffect, useContext } from 'react'
import Image from 'next/image'
import { useRecoilState } from 'recoil' import { useRecoilState } from 'recoil'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
import { FaAnglesUp } from 'react-icons/fa6' import { FaAnglesUp } from 'react-icons/fa6'
import { FaAnglesDown } from 'react-icons/fa6' import { FaAnglesDown } from 'react-icons/fa6'
import { Button } from '@nextui-org/react'
import ColorPicker from './common/color-picker/ColorPicker'
import { cadFileNameState, googleMapFileNameState, useCadFileState, useGoogleMapFileState } from '@/store/canvasAtom'
import { useAxios } from '@/hooks/useAxios' import { useAxios } from '@/hooks/useAxios'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { useMasterController } from '@/hooks/common/useMasterController' import { useMasterController } from '@/hooks/common/useMasterController'
import { convertDwgToPng } from '@/lib/cadAction'
import { cadFileNameState, googleMapFileNameState, useCadFileState, useGoogleMapFileState } from '@/store/canvasAtom'
import { Button } from '@nextui-org/react'
import ColorPicker from './common/color-picker/ColorPicker'
import { useSwal } from '@/hooks/useSwal' import { useSwal } from '@/hooks/useSwal'
import { convertDwgToPng } from '@/lib/cadAction'
import styles from './playground.module.css' import { GlobalDataContext } from '@/app/GlobalDataProvider'
import Image from 'next/image'
import QInput from './common/input/Qinput' import QInput from './common/input/Qinput'
import QSelect from './common/select/QSelect' import QSelect from './common/select/QSelect'
import QPagination from './common/pagination/QPagination' import QPagination from './common/pagination/QPagination'
import QSelectBox from './common/select/QSelectBox'
import SampleReducer from './sample/SampleReducer'
import styles from './playground.module.css'
export default function Playground() { export default function Playground() {
const [useCadFile, setUseCadFile] = useRecoilState(useCadFileState) const [useCadFile, setUseCadFile] = useRecoilState(useCadFileState)
@ -36,7 +37,7 @@ export default function Playground() {
const converterUrl = process.env.NEXT_PUBLIC_CONVERTER_API_URL const converterUrl = process.env.NEXT_PUBLIC_CONVERTER_API_URL
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { swalFire } = useSwal() const { swalFire } = useSwal()
const { getRoofMaterialList, getModuleTypeItemList } = useMasterController() const { getRoofMaterialList, getModuleTypeItemList, getTrestleList, getConstructionList, getTrestleDetailList } = useMasterController()
const [color, setColor] = useState('#ff0000') const [color, setColor] = useState('#ff0000')
@ -48,6 +49,8 @@ export default function Playground() {
const [users, setUsers] = useState([]) const [users, setUsers] = useState([])
const { managementState, setManagementState, managementStateLoaded } = useContext(GlobalDataContext)
useEffect(() => { useEffect(() => {
console.log('textInput:', textInput) console.log('textInput:', textInput)
}, [textInput]) }, [textInput])
@ -154,6 +157,75 @@ export default function Playground() {
console.log('users:', users) console.log('users:', users)
}, [users]) }, [users])
const codes = [
{
clHeadCd: '203800',
clCode: 'HEI_455',
clCodeNm: '세로 455mm이하',
clPriority: 1,
name: '세로 455mm이하',
id: 'HEI_455',
},
{
clHeadCd: '203800',
clCode: 'HEI_500',
clCodeNm: '세로 500mm이하',
clPriority: 2,
name: '세로 500mm이하',
id: 'HEI_500',
},
{
clHeadCd: '203800',
clCode: 'HEI_606',
clCodeNm: '세로 606mm이하',
clPriority: 3,
name: '세로 606mm이하',
id: 'HEI_606',
},
{
clHeadCd: '203800',
clCode: 'WID_606',
clCodeNm: '가로 606mm이하',
clPriority: 4,
name: '가로 606mm이하',
id: 'WID_606',
},
{
clHeadCd: '203800',
clCode: 'ETC',
clCodeNm: '기타',
clPriority: 5,
name: '기타',
id: 'ETC',
},
]
const [myData, setMyData] = useState({
roofMatlCd: 'ROOF_ID_WA_53A',
roofMatlNm: '화와 A',
roofMatlNmJp: '和瓦A',
widAuth: 'R',
widBase: '265.000',
lenAuth: 'R',
lenBase: '235.000',
roofPchAuth: null,
roofPchBase: null,
raftAuth: 'C',
raftBaseCd: 'HEI_455',
id: 'ROOF_ID_WA_53A',
name: '화와 A',
selected: true,
nameJp: '和瓦A',
length: 235,
width: 265,
layout: 'P',
hajebichi: null,
})
const handleChangeMyData = () => {
setMyData({ ...myData, raftBaseCd: 'HEI_500' })
}
return ( return (
<> <>
<div className="container mx-auto p-4 m-4 border"> <div className="container mx-auto p-4 m-4 border">
@ -166,16 +238,70 @@ export default function Playground() {
}} }}
> >
지붕재 목록 조회 API 호출 지붕재 목록 조회 API 호출
</button> </button>{' '}
<button <button
className="btn-frame deepgray" className="btn-frame deepgray"
onClick={() => { onClick={() => {
getModuleTypeItemList('ROOF_ID_HIRA_SEME') getModuleTypeItemList(['ROOF_ID_HIRA_SEME', 'ROOF_ID_ROOGA'])
}} }}
> >
모듈 타입별 아이템 목록 조회 API 호출 모듈 타입별 아이템 목록 조회 API 호출
</button>{' '}
<button
className="btn-frame deepgray"
onClick={() => {
getTrestleList({ moduleTpCd: '', roofMatlCd: '', raftBaseCd: '', trestleMkrCd: '', constMthdCd: '', roofBaseCd: '' }) //
}}
>
가대 목록 조회 API 호출
</button>{' '}
<button
className="btn-frame deepgray"
onClick={() => {
getConstructionList({
//
moduleTpCd: 'testData_1',
roofMatlCd: 'testData_2',
trestleMkrCd: 'testData_3',
constMthdCd: 'testData_4',
roofBaseCd: 'testData_5',
illuminationTp: 'testData_6',
instHt: 'testData_7',
stdWindSpeed: 'testData_8',
stdSnowLd: 'testData_9',
inclCd: 'testData_10',
raftBaseCd: '',
roofPitch: 30,
})
}}
>
시공법 목록 조회 API 호출
</button>{' '}
<button
className="btn-frame deepgray"
onClick={() => {
getTrestleDetailList({
//
moduleTpCd: 'testData_1',
roofMatlCd: 'testData_2',
trestleMkrCd: 'testData_3',
constMthdCd: 'testData_4',
roofBaseCd: 'testData_5',
illuminationTp: 'testData_6',
instHt: 'testData_7',
stdWindSpeed: 'testData_8',
stdSnowLd: 'testData_9',
inclCd: 'testData_10',
constTp: 'testData_11',
mixMatlNo: 30,
roofPitch: 0,
})
}}
>
가대 상세 조회 API 호출
</button> </button>
</div> </div>
<div className="m-2"> <div className="m-2">
<button <button
className="btn-frame deepgray" className="btn-frame deepgray"
@ -372,6 +498,29 @@ export default function Playground() {
axios post test axios post test
</Button> </Button>
</div> </div>
<div className="my-2">
<QSelectBox options={codes} value={myData} sourceKey="id" targetKey="raftBaseCd" showKey="clCodeNm" />
</div>
<div className="my-2">
<Button onClick={handleChangeMyData}>QSelectBox value change!!</Button>
</div>
<div className="my-2">
<SampleReducer />
</div>
<div className="my-2">
<Button onClick={() => setManagementState({ ...managementState, objectNo: '1234567890' })}>GlobalDataProvider 테스트</Button>
</div>
<div className="my-2">
<Button onClick={() => setManagementState({})}>GlobalDataProvider 초기화</Button>
</div>
<div className="my-2">
<p>{managementStateLoaded?.objectNo}</p>
</div>
<div className="my-2">
<Button onClick={() => swalFire({ text: 'alert 테스트입니다.', type: 'alert', confirmFn: () => console.log('Alert!!!') })}>
Sweetalert - alert
</Button>
</div>
</div> </div>
</> </>
) )

View File

@ -5,7 +5,7 @@ import Image from 'next/image'
import Link from 'next/link' import Link from 'next/link'
import { useRecoilState } from 'recoil' import { useRecoilState } from 'recoil'
import { useAxios } from '@/hooks/useAxios' import { useAxios } from '@/hooks/useAxios'
import { setSession } from '@/lib/authActions' import { setSession, login } from '@/lib/authActions'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { globalLocaleStore } from '@/store/localeAtom' import { globalLocaleStore } from '@/store/localeAtom'
import { sessionStore } from '@/store/commonAtom' import { sessionStore } from '@/store/commonAtom'
@ -36,7 +36,7 @@ export default function Login() {
const result = { ...response, storeLvl: response.groupId === '60000' ? '1' : '2', pwdInitYn: 'Y' } const result = { ...response, storeLvl: response.groupId === '60000' ? '1' : '2', pwdInitYn: 'Y' }
setSession(result) setSession(result)
setSessionState(result) setSessionState(result)
router.push('/') login()
} else { } else {
router.push('/login') router.push('/login')
} }
@ -97,7 +97,8 @@ export default function Login() {
} else { } else {
Cookies.remove('chkLoginId') Cookies.remove('chkLoginId')
} }
router.push('/') // router.push('/')
login()
} else { } else {
alert(res.data.result.resultMsg) alert(res.data.result.resultMsg)
} }

View File

@ -66,7 +66,7 @@ export default function ColorPickerModal(props) {
// //
if (name !== 'DimensionLineColor') if (name !== 'DimensionLineColor')
setSettingsData({ setSettingsDataSave({
...settingsData, ...settingsData,
color: originColor, color: originColor,
}) })

View File

@ -1,8 +1,11 @@
'use client' 'use client'
import { Fragment } from 'react'
import { useRecoilValue } from 'recoil' import { useRecoilValue } from 'recoil'
import { popupState } from '@/store/popupAtom' import { popupState } from '@/store/popupAtom'
import { Fragment } from 'react'
/**
* 팝업 관리자
*/
export default function PopupManager() { export default function PopupManager() {
const popup = useRecoilValue(popupState) const popup = useRecoilValue(popupState)

View File

@ -1,26 +1,81 @@
'use client' 'use client'
import { useEffect, useState } from 'react' import { useEffect, useRef, useState } from 'react'
import { useOnClickOutside } from 'usehooks-ts'
/**
*
* @param {string} title - 선택 제목 (선택이 없을때 보여질 )
* @param {array} options - 선택 옵션 객체 {}
* @param {function} onChange - 선택 변경 함수
* @param {object} value - 선택 객체 {}
* @param {boolean} disabled - 선택 비활성화 여부
* @param {string} sourceKey - options에 있는
* @param {string} targetKey - value에 있는
* @param {string} showKey - options 있는 키중 보여줄
* @param {object} params - 추가 파라미터
* @returns
*/
export default function QSelectBox({
title = '',
options,
onChange,
value,
disabled = false,
sourceKey = '',
targetKey = '',
showKey = '',
params = {},
}) {
/**
* 초기 상태 처리
* useState 초기 값으로 사용해야 해서 useState 보다 위에 작성
* @returns {string} 초기 상태
*/
const handleInitState = () => {
//title ( )
if (title !== '') {
return title
}
//value showKey
if (showKey !== '' && !value) {
return options[0][showKey]
}
//value sourceKey targetKey
if (showKey !== '' && value) {
const option = options.find((option) => option[sourceKey] === value[targetKey])
return option[showKey]
}
}
export default function QSelectBox({ title = '', options, onChange, value, disabled = false, params = {} }) {
const [openSelect, setOpenSelect] = useState(false) const [openSelect, setOpenSelect] = useState(false)
const [selected, setSelected] = useState(title === '' ? options[0].name : title) const [selected, setSelected] = useState(handleInitState())
const ref = useRef(null)
const handleClickSelectOption = (option) => { const handleClickSelectOption = (option) => {
setSelected(option.name) setSelected(showKey !== '' ? option[showKey] : option.name)
onChange?.(option, params) onChange?.(option, params)
} }
const handleClose = () => {
setOpenSelect(false)
}
useEffect(() => { useEffect(() => {
value && handleClickSelectOption(value) // value && handleClickSelectOption(value)
}, [value]) setSelected(handleInitState())
}, [value, sourceKey, targetKey, showKey])
useOnClickOutside(ref, handleClose)
return ( return (
<div className={`sort-select ${openSelect ? 'active' : ''}`} onClick={disabled ? () => {} : () => setOpenSelect(!openSelect)}> <div className={`sort-select ${openSelect ? 'active' : ''}`} ref={ref} onClick={disabled ? () => {} : () => setOpenSelect(!openSelect)}>
<p>{selected}</p> <p>{selected}</p>
<ul className="select-item-wrap"> <ul className="select-item-wrap">
{options?.map((option, index) => ( {options?.map((option, index) => (
<li key={option.id || index} className="select-item" onClick={() => handleClickSelectOption(option)}> <li key={option.id || index} className="select-item" onClick={() => handleClickSelectOption(option)}>
<button key={option.id + 'btn'}>{option.name}</button> <button key={option.id + 'btn'}>{showKey !== '' ? option[showKey] : option.name}</button>
</li> </li>
))} ))}
</ul> </ul>

View File

@ -35,10 +35,6 @@ export const QLine = fabric.util.createClass(fabric.Line, {
this.startPoint = { x: this.x1, y: this.y1 } this.startPoint = { x: this.x1, y: this.y1 }
this.endPoint = { x: this.x2, y: this.y2 } this.endPoint = { x: this.x2, y: this.y2 }
if (canvas) {
this.canvas = canvas
}
}, },
init: function () { init: function () {

View File

@ -1,32 +1,28 @@
'use client' 'use client'
import { useContext, useEffect, useRef } from 'react' import { useEffect, useRef } from 'react'
import { useRecoilValue } from 'recoil' import { useRecoilValue } from 'recoil'
import QContextMenu from '@/components/common/context-menu/QContextMenu'
import PanelBatchStatistics from '@/components/floor-plan/modal/panelBatch/PanelBatchStatistics'
import ImgLoad from '@/components/floor-plan/modal/ImgLoad'
import { useCanvas } from '@/hooks/useCanvas' import { useCanvas } from '@/hooks/useCanvas'
import { useEvent } from '@/hooks/useEvent'
import { usePlan } from '@/hooks/usePlan' import { usePlan } from '@/hooks/usePlan'
import { useContextMenu } from '@/hooks/useContextMenu' import { useContextMenu } from '@/hooks/useContextMenu'
import { currentMenuState } from '@/store/canvasAtom'
import QContextMenu from '@/components/common/context-menu/QContextMenu'
import { useCanvasConfigInitialize } from '@/hooks/common/useCanvasConfigInitialize' import { useCanvasConfigInitialize } from '@/hooks/common/useCanvasConfigInitialize'
import { MENU } from '@/common/common' import { currentMenuState } from '@/store/canvasAtom'
import PanelBatchStatistics from '@/components/floor-plan/modal/panelBatch/PanelBatchStatistics'
import { totalDisplaySelector } from '@/store/settingAtom' import { totalDisplaySelector } from '@/store/settingAtom'
import ImgLoad from '@/components/floor-plan/modal/ImgLoad' import { MENU } from '@/common/common'
export default function CanvasFrame() { export default function CanvasFrame() {
const canvasRef = useRef(null) const canvasRef = useRef(null)
const { canvas, handleBackImageLoadToCanvas } = useCanvas('canvas') const { canvas } = useCanvas('canvas')
const { canvasLoadInit, gridInit } = useCanvasConfigInitialize() const { canvasLoadInit, gridInit } = useCanvasConfigInitialize()
const currentMenu = useRecoilValue(currentMenuState) const currentMenu = useRecoilValue(currentMenuState)
const { contextMenu, handleClick } = useContextMenu() const { contextMenu, handleClick } = useContextMenu()
const { selectedPlan, modifiedPlanFlag, checkCanvasObjectEvent, resetModifiedPlans, currentCanvasPlan } = usePlan() const { selectedPlan } = usePlan()
const totalDisplay = useRecoilValue(totalDisplaySelector) // const totalDisplay = useRecoilValue(totalDisplaySelector) //
// useEvent()
// const { initEvent } = useContext(EventContext)
// initEvent()
const loadCanvas = () => { const loadCanvas = () => {
if (canvas) { if (canvas) {
@ -41,21 +37,8 @@ export default function CanvasFrame() {
} }
} }
useEffect(() => {
if (modifiedPlanFlag && selectedPlan?.id) {
checkCanvasObjectEvent(selectedPlan.id)
}
}, [modifiedPlanFlag])
useEffect(() => {
return () => {
resetModifiedPlans()
}
}, [])
useEffect(() => { useEffect(() => {
loadCanvas() loadCanvas()
resetModifiedPlans()
}, [selectedPlan, canvas]) }, [selectedPlan, canvas])
return ( return (

View File

@ -3,12 +3,12 @@
import { useContext, useEffect } from 'react' import { useContext, useEffect } from 'react'
import { useRecoilValue } from 'recoil' import { useRecoilValue } from 'recoil'
import { FloorPlanContext } from '@/app/floor-plan/FloorPlanProvider' import { FloorPlanContext } from '@/app/floor-plan/FloorPlanProvider'
import { SessionContext } from '@/app/SessionProvider'
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 { SessionContext } from '@/app/SessionProvider'
import { useCanvasMenu } from '@/hooks/common/useCanvasMenu' import { useCanvasMenu } from '@/hooks/common/useCanvasMenu'
import { globalLocaleStore } from '@/store/localeAtom'
export default function CanvasLayout({ children }) { export default function CanvasLayout({ children }) {
// const { menuNumber } = props // const { menuNumber } = props
@ -20,7 +20,7 @@ export default function CanvasLayout({ children }) {
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { swalFire } = useSwal() const { swalFire } = useSwal()
const { plans, modifiedPlans, loadCanvasPlanData, handleCurrentPlan, handleAddPlan, handleDeletePlan } = usePlan() const { plans, loadCanvasPlanData, handleCurrentPlan, handleAddPlan, handleDeletePlan } = usePlan()
useEffect(() => { useEffect(() => {
loadCanvasPlanData(session.userId, objectNo, pid) loadCanvasPlanData(session.userId, objectNo, pid)
@ -36,27 +36,31 @@ export default function CanvasLayout({ children }) {
className={`canvas-page-box ${plan.isCurrent === true ? 'on' : ''}`} className={`canvas-page-box ${plan.isCurrent === true ? 'on' : ''}`}
onClick={() => handleCurrentPlan(plan.id)} onClick={() => handleCurrentPlan(plan.id)}
> >
<span> <span>{`Plan ${plan.ordering}`}</span>
{`Plan ${plan.ordering}`} {plan.ordering !== 1 && (
{modifiedPlans.some((modifiedPlan) => modifiedPlan === plan.id) && ' [ M ]'} <i
</span> className="close"
<i onClick={(e) =>
className="close" swalFire({
onClick={(e) => text: `Plan ${plan.ordering} ` + getMessage('plan.message.confirm.delete'),
swalFire({ type: 'confirm',
text: `Plan ${plan.ordering} ` + getMessage('plan.message.confirm.delete'), confirmFn: () => {
type: 'confirm', handleDeletePlan(e, plan.id)
confirmFn: () => { },
handleDeletePlan(e, plan.id) })
}, }
}) ></i>
} )}
></i>
</button> </button>
))} ))}
</div> </div>
{plans.length < 10 && ( {plans.length < 10 && (
<button className="plane-add" onClick={() => handleAddPlan(session.userId, objectNo)}> <button
className="plane-add"
onClick={async () => {
await handleAddPlan(session.userId, objectNo)
}}
>
<span></span> <span></span>
</button> </button>
)} )}

View File

@ -2,47 +2,47 @@
import { useContext, useEffect, useState } from 'react' import { useContext, useEffect, useState } from 'react'
import { usePathname, useRouter } from 'next/navigation'
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil' import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
import { usePathname, useRouter } from 'next/navigation' import { v4 as uuidv4 } from 'uuid'
import MenuDepth01 from './MenuDepth01' import MenuDepth01 from './MenuDepth01'
import QSelectBox from '@/components/common/select/QSelectBox' import QSelectBox from '@/components/common/select/QSelectBox'
import { v4 as uuidv4 } from 'uuid' import SettingModal01 from '@/components/floor-plan/modal/setting01/SettingModal01'
import PlacementShapeSetting from '@/components/floor-plan/modal/placementShape/PlacementShapeSetting'
import EstimateCopyPop from '../estimate/popup/EstimateCopyPop'
import DocDownOptionPop from '../estimate/popup/DocDownOptionPop'
import { FloorPlanContext } from '@/app/floor-plan/FloorPlanProvider'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { usePlan } from '@/hooks/usePlan' import { usePlan } from '@/hooks/usePlan'
import { useSwal } from '@/hooks/useSwal' import { useSwal } from '@/hooks/useSwal'
import { useEvent } from '@/hooks/useEvent' import { useEvent } from '@/hooks/useEvent'
import { usePopup } from '@/hooks/usePopup'
import { useCanvasEvent } from '@/hooks/useCanvasEvent'
import { useCommonUtils } from '@/hooks/common/useCommonUtils'
import useMenu from '@/hooks/common/useMenu'
import { useEstimateController } from '@/hooks/floorPlan/estimate/useEstimateController'
import { useAxios } from '@/hooks/useAxios'
import { useModuleBasicSetting } from '@/hooks/module/useModuleBasicSetting'
import { canvasSettingState, canvasState, canvasZoomState, currentMenuState, verticalHorizontalModeState } from '@/store/canvasAtom' import { canvasSettingState, canvasState, canvasZoomState, currentMenuState, verticalHorizontalModeState } from '@/store/canvasAtom'
import { sessionStore } from '@/store/commonAtom' import { sessionStore } from '@/store/commonAtom'
import { outerLinePointsState } from '@/store/outerLineAtom' import { outerLinePointsState } from '@/store/outerLineAtom'
import { appMessageStore, globalLocaleStore } from '@/store/localeAtom' import { appMessageStore, globalLocaleStore } from '@/store/localeAtom'
import { settingModalFirstOptionsState } from '@/store/settingAtom' import { addedRoofsState, basicSettingState, selectedRoofMaterialSelector, settingModalFirstOptionsState } from '@/store/settingAtom'
import { placementShapeDrawingPointsState } from '@/store/placementShapeDrawingAtom'
import { commonUtilsState } from '@/store/commonUtilsAtom'
import { menusState, menuTypeState } from '@/store/menuAtom'
import { estimateState, floorPlanObjectState } from '@/store/floorPlanObjectAtom'
import { pwrGnrSimTypeState } from '@/store/simulatorAtom'
import { isObjectNotEmpty } from '@/util/common-utils'
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 { useCanvasEvent } from '@/hooks/useCanvasEvent'
import SettingModal01 from '@/components/floor-plan/modal/setting01/SettingModal01'
import { usePopup } from '@/hooks/usePopup'
import { placementShapeDrawingPointsState } from '@/store/placementShapeDrawingAtom'
import PlacementShapeSetting from '@/components/floor-plan/modal/placementShape/PlacementShapeSetting'
import { useCommonUtils } from '@/hooks/common/useCommonUtils'
import { commonUtilsState } from '@/store/commonUtilsAtom'
import { menusState, menuTypeState } from '@/store/menuAtom'
import useMenu from '@/hooks/common/useMenu'
import { MENU } from '@/common/common' import { MENU } from '@/common/common'
import { useEstimateController } from '@/hooks/floorPlan/estimate/useEstimateController'
import { estimateState, floorPlanObjectState } from '@/store/floorPlanObjectAtom'
import DocDownOptionPop from '../estimate/popup/DocDownOptionPop'
import { FloorPlanContext } from '@/app/floor-plan/FloorPlanProvider'
import EstimateCopyPop from '../estimate/popup/EstimateCopyPop'
import { pwrGnrSimTypeState } from '@/store/simulatorAtom'
import { useAxios } from '@/hooks/useAxios'
import { useModuleBasicSetting } from '@/hooks/module/useModuleBasicSetting'
import { isObjectNotEmpty } from '@/util/common-utils'
export default function CanvasMenu(props) { export default function CanvasMenu(props) {
const { menuNumber, setMenuNumber } = props const { menuNumber, setMenuNumber } = props
const pathname = usePathname() const pathname = usePathname()
@ -51,8 +51,8 @@ export default function CanvasMenu(props) {
const canvasMenus = useRecoilValue(menusState) const canvasMenus = useRecoilValue(menusState)
const [type, setType] = useRecoilState(menuTypeState) const [type, setType] = useRecoilState(menuTypeState)
const [verticalHorizontalMode, setVerticalHorizontalMode] = useRecoilState(verticalHorizontalModeState) const [verticalHorizontalMode, setVerticalHorizontalMode] = useRecoilState(verticalHorizontalModeState)
const [appMessageState, setAppMessageState] = useRecoilState(appMessageStore) const setAppMessageState = useSetRecoilState(appMessageStore)
const [currentMenu, setCurrentMenu] = useRecoilState(currentMenuState) const setCurrentMenu = useSetRecoilState(currentMenuState)
const setOuterLinePoints = useSetRecoilState(outerLinePointsState) const setOuterLinePoints = useSetRecoilState(outerLinePointsState)
const setPlacementPoints = useSetRecoilState(placementShapeDrawingPointsState) const setPlacementPoints = useSetRecoilState(placementShapeDrawingPointsState)
const canvasSetting = useRecoilValue(canvasSettingState) const canvasSetting = useRecoilValue(canvasSettingState)
@ -75,10 +75,14 @@ export default function CanvasMenu(props) {
// const { initEvent, addCanvasMouseEventListener, addDocumentEventListener } = useContext(EventContext) // const { initEvent, addCanvasMouseEventListener, addDocumentEventListener } = useContext(EventContext)
const commonUtils = useRecoilValue(commonUtilsState) const commonUtils = useRecoilValue(commonUtilsState)
const { commonFunctions } = useCommonUtils() const { commonFunctions } = useCommonUtils()
const SelectOption = [{ name: '瓦53A' }, { name: '瓦53A' }]
const { floorPlanState, setFloorPlanState } = useContext(FloorPlanContext) const { floorPlanState, setFloorPlanState } = useContext(FloorPlanContext)
const { restoreModuleInstArea } = useModuleBasicSetting() const { restoreModuleInstArea } = useModuleBasicSetting()
const [addedRoofs, setAddedRoofsState] = useRecoilState(addedRoofsState)
const [basicSetting, setBasicSetting] = useRecoilState(basicSettingState)
const selectedRoofMaterial = useRecoilValue(selectedRoofMaterialSelector)
// //
const [buttonStyle, setButtonStyle] = useState('') const [buttonStyle, setButtonStyle] = useState('')
@ -112,6 +116,20 @@ export default function CanvasMenu(props) {
if (pathname !== '/floor-plan') router.push('/floor-plan') if (pathname !== '/floor-plan') router.push('/floor-plan')
} }
const changeSelectedRoofMaterial = (e) => {
setBasicSetting({ ...basicSetting, selectedRoofMaterial: e })
const newAddedRoofs = addedRoofs.map((roof) => {
if (roof.index === e.index) {
return { ...roof, selected: true }
} else {
return { ...roof, selected: false }
}
})
setAddedRoofsState(newAddedRoofs)
}
const settingsModalOptions = useRecoilState(settingModalFirstOptionsState) const settingsModalOptions = useRecoilState(settingModalFirstOptionsState)
useEffect(() => { useEffect(() => {
@ -194,7 +212,7 @@ export default function CanvasMenu(props) {
}, [canvasSetting]) }, [canvasSetting])
const checkMenuState = (menu) => { const checkMenuState = (menu) => {
return ([2, 3].some((num) => num === canvasSetting?.roofSizeSet) && menu.index === 2) || (menuNumber === 4 && menu.index === 2) return (['2', '3'].includes(canvasSetting?.roofSizeSet) && menu.index === 2) || (menuNumber === 4 && menu.index === 2)
} }
// Excel/PDF // Excel/PDF
@ -265,7 +283,7 @@ export default function CanvasMenu(props) {
key={`canvas-menu-${menu.index}`} key={`canvas-menu-${menu.index}`}
className={`canvas-menu-item ${menuNumber === menu.index ? 'active' : ''}`} className={`canvas-menu-item ${menuNumber === menu.index ? 'active' : ''}`}
onClick={() => { onClick={() => {
if ([2, 3].some((num) => num === canvasSetting?.roofSizeSet) && menu.index === 2) return if (['2', '3'].includes(canvasSetting?.roofSizeSet) && menu.index === 2) return
if (menuNumber === 4 && menu.index === 2) return if (menuNumber === 4 && menu.index === 2) return
onClickNav(menu) onClickNav(menu)
}} }}
@ -292,9 +310,20 @@ export default function CanvasMenu(props) {
<button className={`btn02 ${commonUtils.dimension ? 'active' : ''} `} onClick={() => commonFunctions('dimension')}></button> <button className={`btn02 ${commonUtils.dimension ? 'active' : ''} `} onClick={() => commonFunctions('dimension')}></button>
<button className={`btn03 ${commonUtils.distance ? 'active' : ''} `} onClick={() => commonFunctions('distance')}></button> <button className={`btn03 ${commonUtils.distance ? 'active' : ''} `} onClick={() => commonFunctions('distance')}></button>
</div> </div>
<div className="select-box"> {isObjectNotEmpty(selectedRoofMaterial) && addedRoofs.length > 0 && (
<QSelectBox title={'瓦53A'} option={SelectOption} /> <div className="select-box">
</div> {
<QSelectBox
showKey={'roofMatlNm'}
options={addedRoofs}
value={selectedRoofMaterial}
onChange={changeSelectedRoofMaterial}
sourceKey={'index'}
targetKey={'index'}
/>
}
</div>
)}
<div className="btn-from"> <div className="btn-from">
<button <button
className={`btn10 ${floorPlanState.refFileModalOpen && 'active'}`} className={`btn10 ${floorPlanState.refFileModalOpen && 'active'}`}

View File

@ -1,6 +1,6 @@
'use client' 'use client'
import { useContext, useEffect } from 'react' import { useEffect } from 'react'
//import { useRecoilState } from 'recoil' //import { useRecoilState } from 'recoil'
import CanvasMenu from '@/components/floor-plan/CanvasMenu' import CanvasMenu from '@/components/floor-plan/CanvasMenu'
import { useCanvasMenu } from '@/hooks/common/useCanvasMenu' import { useCanvasMenu } from '@/hooks/common/useCanvasMenu'

View File

@ -1,11 +1,12 @@
'use client' 'use client'
import { useEffect } from 'react'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import useMenu from '@/hooks/common/useMenu'
import { canvasState, currentMenuState } from '@/store/canvasAtom' import { canvasState, currentMenuState } from '@/store/canvasAtom'
import { useRecoilState, useRecoilValue } from 'recoil' import { useRecoilState, useRecoilValue } from 'recoil'
import { menuTypeState, subMenusState } from '@/store/menuAtom' import { menuTypeState, subMenusState } from '@/store/menuAtom'
import useMenu from '@/hooks/common/useMenu'
import { useEffect } from 'react'
export default function MenuDepth01() { export default function MenuDepth01() {
const type = useRecoilValue(menuTypeState) const type = useRecoilValue(menuTypeState)

View File

@ -1,11 +1,12 @@
'use client' 'use client'
import { useMessage } from '@/hooks/useMessage'
import { useRecoilState, useSetRecoilState } from 'recoil' import { useRecoilState, useSetRecoilState } from 'recoil'
import { currentMenuState } from '@/store/canvasAtom'
import { MENU } from '@/common/common' import { MENU } from '@/common/common'
import { modalState } from '@/store/modalAtom'
import { ToggleonMouse } from '@/components/header/Header' import { ToggleonMouse } from '@/components/header/Header'
import { useMessage } from '@/hooks/useMessage'
import { currentMenuState } from '@/store/canvasAtom'
import { modalState } from '@/store/modalAtom'
export default function RoofCoveringMenu() { export default function RoofCoveringMenu() {
const { getMessage } = useMessage() const { getMessage } = useMessage()

View File

@ -2,6 +2,7 @@ import { forwardRef, useImperativeHandle, useState } from 'react'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { useOrientation } from '@/hooks/module/useOrientation' import { useOrientation } from '@/hooks/module/useOrientation'
import { getDegreeInOrientation } from '@/util/canvas-util' import { getDegreeInOrientation } from '@/util/canvas-util'
import { numberCheck } from '@/util/common-utils'
export const Orientation = forwardRef(({ tabNum }, ref) => { export const Orientation = forwardRef(({ tabNum }, ref) => {
const { getMessage } = useMessage() const { getMessage } = useMessage()
@ -18,6 +19,14 @@ export const Orientation = forwardRef(({ tabNum }, ref) => {
nextStep() nextStep()
} }
const checkDegree = (e) => {
if (numberCheck(Number(e)) && Number(e) >= -180 && Number(e) <= 180) {
setCompasDeg(Number(e))
} else {
setCompasDeg(compasDeg)
}
}
return ( return (
<> <>
<div className="properties-setting-wrap"> <div className="properties-setting-wrap">
@ -65,10 +74,15 @@ export const Orientation = forwardRef(({ tabNum }, ref) => {
className="input-origin block" className="input-origin block"
value={compasDeg} value={compasDeg}
readOnly={hasAnglePassivity} readOnly={hasAnglePassivity}
onChange={(e) => placeholder={0}
setCompasDeg( onChange={
e.target.value !== '' && parseInt(e.target.value) <= 360 && parseInt(e.target.value) >= 0 ? Number.parseInt(e.target.value) : 0, (e) => checkDegree(e.target.value)
) // setCompasDeg(
// e.target.value === '-' || (e.target.value !== '' && parseInt(e.target.value) <= 180 && parseInt(e.target.value) >= -180)
// ? e.target.value
// : 0,
// )
} }
/> />
</div> </div>

View File

@ -57,8 +57,6 @@ const PitchPlacement = forwardRef((props, refs) => {
} else { } else {
const moduleSetupSurfaces = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE) // const moduleSetupSurfaces = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE) //
moduleSetupSurfaces.forEach((surface, index) => { moduleSetupSurfaces.forEach((surface, index) => {
console.log(`surface ${index} : `, surface)
const excretaLine = surface.lines const excretaLine = surface.lines
excretaLine.forEach((line) => { excretaLine.forEach((line) => {

View File

@ -20,7 +20,7 @@ export default function Eaves({ pitchRef, offsetRef, widthRef, radioTypeRef, pit
{getMessage('slope')} {getMessage('slope')}
</span> </span>
<div className="input-grid mr5" style={{ width: '100px' }}> <div className="input-grid mr5" style={{ width: '100px' }}>
<input type="number" className="input-origin block" defaultValue={currentAngleType === ANGLE_TYPE.SLOPE ? 4 : 21.8} ref={pitchRef} /> <input type="text" className="input-origin block" defaultValue={currentAngleType === ANGLE_TYPE.SLOPE ? 4 : 21.8} ref={pitchRef} />
</div> </div>
<span className="thin">{pitchText}</span> <span className="thin">{pitchText}</span>
</div> </div>
@ -29,7 +29,7 @@ export default function Eaves({ pitchRef, offsetRef, widthRef, radioTypeRef, pit
{getMessage('offset')} {getMessage('offset')}
</span> </span>
<div className="input-grid mr5" style={{ width: '100px' }}> <div className="input-grid mr5" style={{ width: '100px' }}>
<input type="number" className="input-origin block" defaultValue={500} ref={offsetRef} /> <input type="text" className="input-origin block" defaultValue={500} ref={offsetRef} />
</div> </div>
<span className="thin">mm</span> <span className="thin">mm</span>
</div> </div>

View File

@ -3,18 +3,71 @@ import { useRecoilValue } from 'recoil'
import { contextPopupPositionState } from '@/store/popupAtom' import { contextPopupPositionState } from '@/store/popupAtom'
import { usePopup } from '@/hooks/usePopup' import { usePopup } from '@/hooks/usePopup'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { useState } from 'react' import { useEffect, useState } from 'react'
import { polygonToTurfPolygon } from '@/util/canvas-util'
import { deepCopyArray } from '@/util/common-utils'
import { canvasState } from '@/store/canvasAtom'
import * as turf from '@turf/turf'
import { POLYGON_TYPE } from '@/common/common'
import { useModal } from '@nextui-org/react'
import { useModule } from '@/hooks/module/useModule'
export const PANEL_EDIT_TYPE = {
MOVE: 'move',
MOVE_ALL: 'moveAll',
COPY: 'copy',
COPY_ALL: 'copyAll',
COLUMN_MOVE: 'columnMove',
COLUMN_COPY: 'columnCopy',
ROW_MOVE: 'rowMove',
ROW_COPY: 'rowCopy',
}
export default function PanelEdit(props) { export default function PanelEdit(props) {
const contextPopupPosition = useRecoilValue(contextPopupPositionState) const contextPopupPosition = useRecoilValue(contextPopupPositionState)
const { id, pos = contextPopupPosition, type = 'move', apply } = props const { id, pos = contextPopupPosition, type = PANEL_EDIT_TYPE.MOVE, apply } = props
const { closePopup } = usePopup() const { closePopup } = usePopup()
const [length, setLength] = useState(0) const [length, setLength] = useState(0)
const [direction, setDirection] = useState('') const [direction, setDirection] = useState('up')
const { getMessage } = useMessage() const { getMessage } = useMessage()
const canvas = useRecoilValue(canvasState)
const { moduleMove, moduleCopy, moduleMultiMove, moduleMultiCopy, moduleMoveAll, moduleCopyAll } = useModule()
useEffect(() => {
if (canvas) {
const isSetupModules = canvas.getObjects().filter((obj) => obj.name === 'module') // selectedObj
isSetupModules.forEach((obj) => obj.set({ lockMovementX: false, lockMovementY: false }))
}
}, [])
//
const handleApply = () => { const handleApply = () => {
apply() switch (type) {
case PANEL_EDIT_TYPE.MOVE:
moduleMove(length, direction)
break
case PANEL_EDIT_TYPE.MOVE_ALL:
moduleMoveAll(length, direction)
break
case PANEL_EDIT_TYPE.COPY:
moduleCopy(length, direction)
break
case PANEL_EDIT_TYPE.COPY_ALL:
moduleCopyAll(length, direction)
break
case PANEL_EDIT_TYPE.COLUMN_MOVE:
moduleMultiMove('column', length, direction)
break
case PANEL_EDIT_TYPE.COLUMN_COPY:
moduleMultiCopy('column', length, direction)
break
case PANEL_EDIT_TYPE.ROW_MOVE:
moduleMultiMove('row', length, direction)
break
case PANEL_EDIT_TYPE.ROW_COPY:
moduleMultiCopy('row', length, direction)
break
}
closePopup(id) closePopup(id)
} }
@ -22,45 +75,49 @@ export default function PanelEdit(props) {
<WithDraggable isShow={true} pos={pos}> <WithDraggable isShow={true} pos={pos}>
<div className={`modal-pop-wrap xm mount`}> <div className={`modal-pop-wrap xm mount`}>
<div className="modal-head"> <div className="modal-head">
<h1 className="title">{getMessage(type === 'move' ? 'modal.move.setting' : 'modal.copy.setting')} </h1> <h1 className="title">
{getMessage([PANEL_EDIT_TYPE.MOVE, PANEL_EDIT_TYPE.COLUMN_MOVE].includes(type) ? 'modal.move.setting' : 'modal.copy.setting')}{' '}
</h1>
<button className="modal-close" onClick={() => closePopup(id)}> <button className="modal-close" onClick={() => closePopup(id)}>
닫기 닫기
</button> </button>
</div> </div>
<div className="modal-body"> <div className="modal-body">
<div className="grid-option-tit">{getMessage(type === 'move' ? 'modal.move.setting.info' : 'modal.copy.setting.info')}</div> <div className="grid-option-tit">
{getMessage([PANEL_EDIT_TYPE.MOVE, PANEL_EDIT_TYPE.COLUMN_MOVE].includes(type) ? 'modal.move.setting.info' : 'modal.copy.setting.info')}
</div>
<div className="grid-option-wrap"> <div className="grid-option-wrap">
<div className="grid-option-box"> <div className="grid-option-box">
<div className="grid-input-form"> <div className="grid-input-form">
<span className="mr10">{getMessage('margin')}</span> <span className="mr10">{getMessage('margin')}</span>
<div className="input-grid mr5"> <div className="input-grid mr5">
<input type="text" className="input-origin" defaultValue={0} onClick={(e) => setLength(e.target.value)} /> <input type="text" className="input-origin" defaultValue={0} onKeyUp={(e) => setLength(e.target.value)} />
</div> </div>
<span>mm</span> <span>mm</span>
</div> </div>
<div className="grid-direction"> <div className="grid-direction">
<button <button
className={`direction up ${direction === '' ? 'act' : ''}`} className={`direction up ${direction === 'up' ? 'act' : ''}`}
onClick={() => { onClick={() => {
setDirection('') setDirection('up')
}} }}
></button> ></button>
<button <button
className={`direction down ${direction === '' ? 'act' : ''}`} className={`direction down ${direction === 'down' ? 'act' : ''}`}
onClick={() => { onClick={() => {
setDirection('') setDirection('down')
}} }}
></button> ></button>
<button <button
className={`direction left ${direction === '' ? 'act' : ''}`} className={`direction left ${direction === 'left' ? 'act' : ''}`}
onClick={() => { onClick={() => {
setDirection('') setDirection('left')
}} }}
></button> ></button>
<button <button
className={`direction right ${direction === '' ? 'act' : ''}`} className={`direction right ${direction === 'right' ? 'act' : ''}`}
onClick={() => { onClick={() => {
setDirection('') setDirection('right')
}} }}
></button> ></button>
</div> </div>

View File

@ -5,20 +5,22 @@ import { usePopup } from '@/hooks/usePopup'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { useState } from 'react' import { useState } from 'react'
import Image from 'next/image' import Image from 'next/image'
import { MODULE_INSERT_TYPE, useModule } from '@/hooks/module/useModule'
export default function ColumnInsert(props) { export default function ColumnInsert(props) {
const contextPopupPosition = useRecoilValue(contextPopupPositionState) const contextPopupPosition = useRecoilValue(contextPopupPositionState)
const { id, pos = contextPopupPosition, apply } = props const { id, pos = contextPopupPosition, apply } = props
const { closePopup } = usePopup() const { closePopup } = usePopup()
const [selectedType, setSelectedType] = useState(1) const [selectedType, setSelectedType] = useState(MODULE_INSERT_TYPE.LEFT)
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { moduleColumnInsert } = useModule()
const handleApply = () => { const handleApply = () => {
if (apply) apply() moduleColumnInsert(selectedType)
closePopup(id) closePopup(id)
} }
const HandleRadioChange = (e) => { const handleRadioChange = (e) => {
setSelectedType(Number(e.target.value)) setSelectedType(e.target.value)
} }
return ( return (
@ -36,16 +38,30 @@ export default function ColumnInsert(props) {
<div className="additional-wrap"> <div className="additional-wrap">
<div className="additional-radio-wrap"> <div className="additional-radio-wrap">
<div className="d-check-radio pop"> <div className="d-check-radio pop">
<input type="radio" name="radio01" id="ra01" onChange={HandleRadioChange} value={1} checked={selectedType === 1} /> <input
type="radio"
name="radio01"
id="ra01"
onChange={handleRadioChange}
value={MODULE_INSERT_TYPE.LEFT}
checked={selectedType === MODULE_INSERT_TYPE.LEFT}
/>
<label htmlFor="ra01">{getMessage('modal.panel.column.insert.type.left')}</label> <label htmlFor="ra01">{getMessage('modal.panel.column.insert.type.left')}</label>
</div> </div>
<div className="d-check-radio pop"> <div className="d-check-radio pop">
<input type="radio" name="radio01" id="ra02" onChange={HandleRadioChange} value={2} checked={selectedType === 2} /> <input
type="radio"
name="radio01"
id="ra02"
onChange={handleRadioChange}
value={MODULE_INSERT_TYPE.RIGHT}
checked={selectedType === MODULE_INSERT_TYPE.RIGHT}
/>
<label htmlFor="ra02">{getMessage('modal.panel.column.insert.type.right')}</label> <label htmlFor="ra02">{getMessage('modal.panel.column.insert.type.right')}</label>
</div> </div>
</div> </div>
<div className="additional-img-wrap"> <div className="additional-img-wrap">
{selectedType === 1 && ( {selectedType === MODULE_INSERT_TYPE.LEFT && (
<Image <Image
src="/static/images/canvas/additional-edit01.svg" src="/static/images/canvas/additional-edit01.svg"
alt="react" alt="react"
@ -54,7 +70,7 @@ export default function ColumnInsert(props) {
style={{ width: 'auto', height: 'auto' }} style={{ width: 'auto', height: 'auto' }}
/> />
)} )}
{selectedType === 2 && ( {selectedType === MODULE_INSERT_TYPE.RIGHT && (
<Image <Image
src="/static/images/canvas/additional-edit02.svg" src="/static/images/canvas/additional-edit02.svg"
alt="react" alt="react"

View File

@ -5,21 +5,24 @@ import { usePopup } from '@/hooks/usePopup'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { useState } from 'react' import { useState } from 'react'
import Image from 'next/image' import Image from 'next/image'
import { MODULE_REMOVE_TYPE, useModule } from '@/hooks/module/useModule'
export default function ColumnRemove(props) { export default function ColumnRemove(props) {
const contextPopupPosition = useRecoilValue(contextPopupPositionState) const contextPopupPosition = useRecoilValue(contextPopupPositionState)
const { id, pos = contextPopupPosition, apply } = props const { id, pos = contextPopupPosition, apply } = props
const { closePopup } = usePopup() const { closePopup } = usePopup()
const [selectedType, setSelectedType] = useState(1) const [selectedType, setSelectedType] = useState(MODULE_REMOVE_TYPE.LEFT)
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { moduleColumnRemove } = useModule()
const types = [ const types = [
{ name: getMessage('modal.panel.column.remove.type.left'), value: 1 }, { name: getMessage('modal.panel.column.remove.type.left'), value: MODULE_REMOVE_TYPE.LEFT },
{ name: getMessage('modal.panel.column.remove.type.right'), value: 2 }, { name: getMessage('modal.panel.column.remove.type.right'), value: MODULE_REMOVE_TYPE.RIGHT },
{ name: getMessage('modal.panel.column.remove.type.side'), value: 3 }, { name: getMessage('modal.panel.column.remove.type.side'), value: MODULE_REMOVE_TYPE.HORIZONTAL_SIDE },
{ name: getMessage('modal.panel.column.remove.type.none'), value: 4 }, { name: getMessage('modal.panel.column.remove.type.none'), value: MODULE_REMOVE_TYPE.NONE },
] ]
const handleApply = () => { const handleApply = () => {
if (apply) apply() // if (apply) apply()
moduleColumnRemove(selectedType)
closePopup(id) closePopup(id)
} }
@ -39,12 +42,12 @@ export default function ColumnRemove(props) {
<div className="additional-radio-wrap"> <div className="additional-radio-wrap">
{types.map((type, index) => { {types.map((type, index) => {
return ( return (
<div className="d-check-radio pop"> <div className="d-check-radio pop" key={index}>
<input <input
type="radio" type="radio"
name="radio01" name="radio01"
id={`ra0${index + 1}`} id={`ra0${index + 1}`}
onClick={(e) => setSelectedType(Number(e.target.value))} onClick={(e) => setSelectedType(e.target.value)}
value={type.value} value={type.value}
checked={selectedType === type.value} checked={selectedType === type.value}
/> />
@ -54,7 +57,7 @@ export default function ColumnRemove(props) {
})} })}
</div> </div>
<div className="additional-img-wrap"> <div className="additional-img-wrap">
{selectedType === 1 && ( {selectedType === MODULE_REMOVE_TYPE.LEFT && (
<Image <Image
src="/static/images/canvas/additional_del01.svg" src="/static/images/canvas/additional_del01.svg"
alt="react" alt="react"
@ -63,7 +66,7 @@ export default function ColumnRemove(props) {
style={{ width: 'auto', height: 'auto' }} style={{ width: 'auto', height: 'auto' }}
/> />
)} )}
{selectedType === 2 && ( {selectedType === MODULE_REMOVE_TYPE.RIGHT && (
<Image <Image
src="/static/images/canvas/additional_del02.svg" src="/static/images/canvas/additional_del02.svg"
alt="react" alt="react"
@ -72,7 +75,7 @@ export default function ColumnRemove(props) {
style={{ width: 'auto', height: 'auto' }} style={{ width: 'auto', height: 'auto' }}
/> />
)} )}
{selectedType === 3 && ( {selectedType === MODULE_REMOVE_TYPE.HORIZONTAL_SIDE && (
<Image <Image
src="/static/images/canvas/additional_del03.svg" src="/static/images/canvas/additional_del03.svg"
alt="react" alt="react"
@ -81,7 +84,7 @@ export default function ColumnRemove(props) {
style={{ width: 'auto', height: 'auto' }} style={{ width: 'auto', height: 'auto' }}
/> />
)} )}
{selectedType === 4 && ( {selectedType === MODULE_REMOVE_TYPE.NONE && (
<Image <Image
src="/static/images/canvas/additional_del04.svg" src="/static/images/canvas/additional_del04.svg"
alt="react" alt="react"

View File

@ -5,20 +5,22 @@ import { useRecoilValue } from 'recoil'
import { contextPopupPositionState } from '@/store/popupAtom' import { contextPopupPositionState } from '@/store/popupAtom'
import { usePopup } from '@/hooks/usePopup' import { usePopup } from '@/hooks/usePopup'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { MODULE_INSERT_TYPE, useModule } from '@/hooks/module/useModule'
export default function RowInsert(props) { export default function RowInsert(props) {
const contextPopupPosition = useRecoilValue(contextPopupPositionState) const contextPopupPosition = useRecoilValue(contextPopupPositionState)
const { id, pos = contextPopupPosition, apply } = props const { id, pos = contextPopupPosition, apply } = props
const { closePopup } = usePopup() const { closePopup } = usePopup()
const [selectedType, setSelectedType] = useState(1) const [selectedType, setSelectedType] = useState(MODULE_INSERT_TYPE.TOP)
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { muduleRowInsert } = useModule()
const handleApply = () => { const handleApply = () => {
if (apply) apply() muduleRowInsert(selectedType)
closePopup(id) closePopup(id)
} }
const HandleRadioChange = (e) => { const HandleRadioChange = (e) => {
setSelectedType(Number(e.target.value)) setSelectedType(e.target.value)
} }
return ( return (
@ -36,16 +38,30 @@ export default function RowInsert(props) {
<div className="additional-wrap"> <div className="additional-wrap">
<div className="additional-radio-wrap"> <div className="additional-radio-wrap">
<div className="d-check-radio pop"> <div className="d-check-radio pop">
<input type="radio" name="radio01" id="ra01" onChange={HandleRadioChange} value={1} checked={selectedType === 1} /> <input
type="radio"
name="radio01"
id="ra01"
onChange={HandleRadioChange}
value={MODULE_INSERT_TYPE.TOP}
checked={selectedType === MODULE_INSERT_TYPE.TOP}
/>
<label htmlFor="ra01">{getMessage('modal.row.insert.type.up')}</label> <label htmlFor="ra01">{getMessage('modal.row.insert.type.up')}</label>
</div> </div>
<div className="d-check-radio pop"> <div className="d-check-radio pop">
<input type="radio" name="radio01" id="ra02" onChange={HandleRadioChange} value={2} checked={selectedType === 2} /> <input
type="radio"
name="radio01"
id="ra02"
onChange={HandleRadioChange}
value={MODULE_INSERT_TYPE.BOTTOM}
checked={selectedType === MODULE_INSERT_TYPE.BOTTOM}
/>
<label htmlFor="ra02">{getMessage('modal.row.insert.type.down')}</label> <label htmlFor="ra02">{getMessage('modal.row.insert.type.down')}</label>
</div> </div>
</div> </div>
<div className="additional-img-wrap"> <div className="additional-img-wrap">
{selectedType === 1 && ( {selectedType === MODULE_INSERT_TYPE.TOP && (
<Image <Image
src="/static/images/canvas/additional_bundle-edit01.svg" src="/static/images/canvas/additional_bundle-edit01.svg"
alt="react" alt="react"
@ -54,7 +70,7 @@ export default function RowInsert(props) {
style={{ width: 'auto', height: 'auto' }} style={{ width: 'auto', height: 'auto' }}
/> />
)} )}
{selectedType === 2 && ( {selectedType === MODULE_INSERT_TYPE.BOTTOM && (
<Image <Image
src="/static/images/canvas/additional_bundle-edit02.svg" src="/static/images/canvas/additional_bundle-edit02.svg"
alt="react" alt="react"

View File

@ -5,21 +5,24 @@ import { useRecoilValue } from 'recoil'
import { contextPopupPositionState } from '@/store/popupAtom' import { contextPopupPositionState } from '@/store/popupAtom'
import { usePopup } from '@/hooks/usePopup' import { usePopup } from '@/hooks/usePopup'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { MODULE_REMOVE_TYPE, useModule } from '@/hooks/module/useModule'
export default function RowRemove(props) { export default function RowRemove(props) {
const contextPopupPosition = useRecoilValue(contextPopupPositionState) const contextPopupPosition = useRecoilValue(contextPopupPositionState)
const { id, pos = contextPopupPosition, apply } = props const { id, pos = contextPopupPosition, apply } = props
const { closePopup } = usePopup() const { closePopup } = usePopup()
const [selectedType, setSelectedType] = useState(1) const [selectedType, setSelectedType] = useState(MODULE_REMOVE_TYPE.TOP)
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { moduleRowRemove } = useModule()
const types = [ const types = [
{ name: getMessage('modal.row.remove.type.up'), value: 1 }, { name: getMessage('modal.row.remove.type.up'), value: MODULE_REMOVE_TYPE.TOP },
{ name: getMessage('modal.row.remove.type.down'), value: 2 }, { name: getMessage('modal.row.remove.type.down'), value: MODULE_REMOVE_TYPE.BOTTOM },
{ name: getMessage('modal.row.remove.type.side'), value: 3 }, { name: getMessage('modal.row.remove.type.side'), value: MODULE_REMOVE_TYPE.VERTICAL_SIDE },
{ name: getMessage('modal.row.remove.type.none'), value: 4 }, { name: getMessage('modal.row.remove.type.none'), value: MODULE_REMOVE_TYPE.NONE },
] ]
const handleApply = () => { const handleApply = () => {
if (apply) apply() // if (apply) apply()
moduleRowRemove(selectedType)
closePopup(id) closePopup(id)
} }
@ -39,22 +42,22 @@ export default function RowRemove(props) {
<div className="additional-radio-wrap"> <div className="additional-radio-wrap">
{types.map((type, index) => { {types.map((type, index) => {
return ( return (
<div className="d-check-radio pop"> <div className="d-check-radio pop" key={index}>
<input <input
type="radio" type="radio"
name="radio01" name="radio01"
id={`ra0${index + 1}`} id={`ra0${index + 1}`}
onClick={() => setSelectedType(Number(e.target.value))} onClick={(e) => setSelectedType(e.target.value)}
value={type.value} value={type.value}
checked={selectedType === type.value} checked={selectedType === type.value}
/> />
<label htmlFor="ra01">{getMessage(type.name)}</label> <label htmlFor={`ra0${index + 1}`}>{type.name}</label>
</div> </div>
) )
})} })}
</div> </div>
<div className="additional-img-wrap"> <div className="additional-img-wrap">
{selectedType === 1 && ( {selectedType === MODULE_REMOVE_TYPE.TOP && (
<Image <Image
src="/static/images/canvas/additional_bundle-del01.svg" src="/static/images/canvas/additional_bundle-del01.svg"
alt="react" alt="react"
@ -63,7 +66,7 @@ export default function RowRemove(props) {
style={{ width: 'auto', height: 'auto' }} style={{ width: 'auto', height: 'auto' }}
/> />
)} )}
{selectedType === 2 && ( {selectedType === MODULE_REMOVE_TYPE.BOTTOM && (
<Image <Image
src="/static/images/canvas/additional_bundle-del02.svg" src="/static/images/canvas/additional_bundle-del02.svg"
alt="react" alt="react"
@ -72,7 +75,7 @@ export default function RowRemove(props) {
style={{ width: 'auto', height: 'auto' }} style={{ width: 'auto', height: 'auto' }}
/> />
)} )}
{selectedType === 3 && ( {selectedType === MODULE_REMOVE_TYPE.VERTICAL_SIDE && (
<Image <Image
src="/static/images/canvas/additional_bundle-del03.svg" src="/static/images/canvas/additional_bundle-del03.svg"
alt="react" alt="react"
@ -81,7 +84,7 @@ export default function RowRemove(props) {
style={{ width: 'auto', height: 'auto' }} style={{ width: 'auto', height: 'auto' }}
/> />
)} )}
{selectedType === 4 && ( {selectedType === MODULE_REMOVE_TYPE.NONE && (
<Image <Image
src="/static/images/canvas/additional_bundle-del04.svg" src="/static/images/canvas/additional_bundle-del04.svg"
alt="react" alt="react"

View File

@ -1,4 +1,4 @@
import { useEffect, useState } from 'react' import { useEffect, useRef, useState } from 'react'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { usePopup } from '@/hooks/usePopup' import { usePopup } from '@/hooks/usePopup'
@ -8,10 +8,18 @@ import MaterialGuide from '@/components/floor-plan/modal/placementShape/Material
import WithDraggable from '@/components/common/draggable/WithDraggable' import WithDraggable from '@/components/common/draggable/WithDraggable'
import { useCanvasSetting } from '@/hooks/option/useCanvasSetting' import { useCanvasSetting } from '@/hooks/option/useCanvasSetting'
import { useRecoilState, useRecoilValue } from 'recoil' import { useRecoilValue } from 'recoil'
import { roofMaterialsAtom } from '@/store/settingAtom' import { roofMaterialsAtom } from '@/store/settingAtom'
import { isObjectNotEmpty } from '@/util/common-utils' import { useCommonCode } from '@/hooks/common/useCommonCode'
import QSelectBox from '@/components/common/select/QSelectBox'
import { globalLocaleStore } from '@/store/localeAtom'
import { onlyNumberInputChange } from '@/util/input-utils'
export const ROOF_MATERIAL_LAYOUT = {
PARALLEL: 'P',
STAIRS: 'S',
}
export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, setShowPlaceShapeModal }) { export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, setShowPlaceShapeModal }) {
const [showSizeGuideModal, setShowSizeGuidModal] = useState(false) const [showSizeGuideModal, setShowSizeGuidModal] = useState(false)
const [showMaterialGuideModal, setShowMaterialGuidModal] = useState(false) const [showMaterialGuideModal, setShowMaterialGuidModal] = useState(false)
@ -19,31 +27,113 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, set
const { getMessage } = useMessage() const { getMessage } = useMessage()
const roofMaterials = useRecoilValue(roofMaterialsAtom) const roofMaterials = useRecoilValue(roofMaterialsAtom)
const { basicSetting, setBasicSettings, fetchBasicSettings, basicSettingSave } = useCanvasSetting() const globalLocale = useRecoilValue(globalLocaleStore)
const [currentRoofMaterial, setCurrentRoofMaterial] = useState( const { basicSetting, setBasicSettings, basicSettingSave, addedRoofs, setAddedRoofs } = useCanvasSetting()
isObjectNotEmpty(basicSetting.selectedRoofMaterial) ? basicSetting.selectedRoofMaterial : roofMaterials[0], const { findCommonCode } = useCommonCode()
) const [raftCodes, setRaftCodes] = useState([]) //
const [currentRoof, setCurrentRoof] = useState(addedRoofs[0]) //
const roofRef = {
roofCd: useRef(null),
width: useRef(null),
length: useRef(null),
rafter: useRef(null),
hajebichi: useRef(null),
}
// //
useEffect(() => { useEffect(() => {
fetchBasicSettings() const raftCodeList = findCommonCode('203800')
setRaftCodes(raftCodeList)
}, []) }, [])
useEffect(() => { useEffect(() => {
console.log(currentRoofMaterial) setBasicSettings({
}, [roofMaterials]) ...basicSetting,
roofsData: {
roofApply: true,
roofSeq: 0,
roofMatlCd: currentRoof.roofMatlCd,
roofWidth: currentRoof.width,
roofHeight: currentRoof.length,
roofHajebichi: currentRoof.hajebichi,
roofGap: currentRoof.raft,
roofLayout: currentRoof.layout,
},
})
}, [basicSetting.roofSizeSet, basicSetting.roofAngleSet, currentRoof])
// Function to update the roofType and corresponding values // Function to update the roofType and corresponding values
const handleRoofTypeChange = (value) => { const handleRoofTypeChange = (value) => {
const selectedRoofMaterial = roofMaterials.find((roof) => roof.roofMatlCd === value) const selectedRoofMaterial = roofMaterials.find((roof) => roof.roofMatlCd === value)
setCurrentRoofMaterial(selectedRoofMaterial) setCurrentRoof({...selectedRoofMaterial, index: 0})
/*const newBasicSetting = { ...basicSetting } }
setBasicSettings({ ...newBasicSetting, selectedRoofMaterial: selectedRoofMaterial })*/
const changeInput = (value, e) => {
const { name } = e.target
setCurrentRoof({...currentRoof, [name]: Number(value)})
}
const handleRafterChange = (value) => {
setCurrentRoof({...currentRoof, raft: value})
}
const handleRoofLayoutChange = (value) => {
setCurrentRoof({...currentRoof, layout: value})
}
const handleSaveBtn = () => {
const roofInfo = {
...currentRoof,
roofCd: roofRef.roofCd.current?.value,
width: roofRef.width.current?.value,
length: roofRef.length.current?.value,
hajebichi: roofRef.hajebichi.current?.value,
raft: roofRef.rafter.current?.value,
selected: true,
layout: currentRoof.layout,
index: 0,
}
const newAddedRoofs = [...addedRoofs]
if (addedRoofs.length === 1) {
newAddedRoofs[0] = { ...roofInfo }
setAddedRoofs(newAddedRoofs)
}
console.log('save Info', {
...basicSetting,
selectedRoofMaterial: {
//
roofInfo,
}
})
setBasicSettings({
...basicSetting,
selectedRoofMaterial: {
//
...roofInfo,
},
//roofs: addedRoofs,
roofsData: {
roofApply: true,
roofSeq: 0,
roofMatlCd: currentRoof.roofMatlCd,
roofWidth: currentRoof.width,
roofHeight: currentRoof.length,
roofHajebichi: currentRoof.hajebichi,
roofGap: currentRoof.raft,
roofLayout: currentRoof.layout,
},
})
basicSettingSave()
} }
return ( return (
<WithDraggable isShow={true} pos={pos}> <WithDraggable isShow={true} pos={pos}>
<div className={`modal-pop-wrap l mount`}> <div className={`modal-pop-wrap ll mount`}>
<div className="modal-head"> <div className="modal-head">
<h1 className="title">{getMessage('plan.menu.placement.surface.initial.setting')}</h1> <h1 className="title">{getMessage('plan.menu.placement.surface.initial.setting')}</h1>
<button className="modal-close" onClick={() => closePopup(id)}> <button className="modal-close" onClick={() => closePopup(id)}>
@ -145,12 +235,28 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, set
</th> </th>
<td> <td>
<div className="placement-option"> <div className="placement-option">
<div className="select-wrap" style={{ width: '171px' }}> <div className="grid-select no-flx" style={{ width: '171px' }}>
<select <QSelectBox
title={basicSetting.roofSizeSet == 3 ? getMessage('modal.placement.initial.setting.size.none.pitch') : currentRoof.roofMatlNm}
ref={roofRef.roofCd}
options={roofMaterials.map((roof, index) => {
return { ...roof, name: globalLocale === 'ko' ? roof.roofMatlNm : roof.roofMatlNmJp }
})}
value={basicSetting.roofSizeSet == 3 ? null : currentRoof.roofMatlCd}
onChange={(e) => handleRoofTypeChange(e.roofMatlCd)}
sourceKey="id"
targetKey="id"
showKey="name"
disabled={basicSetting.roofSizeSet == 3}
/>
{/* <select
className="select-light dark" className="select-light dark"
name="roofType" name="roofMatlCd"
ref={roofRef.roofCd}
value={currentRoofMaterial.roofMatlCd} value={currentRoofMaterial.roofMatlCd}
onChange={(e) => handleRoofTypeChange(e.target.value)} onChange={(e) => {
handleRoofTypeChange(e.target.value)
}}
> >
{roofMaterials.map((roof, index) => { {roofMaterials.map((roof, index) => {
return ( return (
@ -159,86 +265,131 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, set
</option> </option>
) )
})} })}
</select> </select> */}
</div> </div>
{['R', 'C'].includes(currentRoofMaterial.widAuth) && ( {basicSetting && ['R', 'C'].includes(currentRoof.widAuth) && (
<> <div className="flex-ment">
<div className="flex-ment"> <span>W</span>
<span>W</span> <div className="input-grid" style={{ width: '84px' }}>
<div className="select-wrap" style={{ width: '84px' }}> <input
<select type="text"
className="select-light dark" className="input-origin block"
name="roofWidth" name={`width`}
onChange={(e) => { ref={roofRef.width}
// value={parseInt(currentRoof.width)}
setCurrentRoofMaterial({ ...currentRoofMaterial, roofWidth: e.target.value }) onChange={(e) => onlyNumberInputChange(e, changeInput)}
}} readOnly={currentRoof.widAuth === 'R'}
> disabled={basicSetting.roofSizeSet == 3}
<option value={parseInt(currentRoofMaterial.widBase)}>{parseInt(currentRoofMaterial.widBase)}</option> />
</select>
</div>
</div> </div>
</> {/* <div className="input-grid" style={{ width: '63px' }}>
<input
type="text"
className="input-origin block"
ref={roofRef.width}
defaultValue={parseInt(currentRoofMaterial.width)}
/>
</div> */}
</div>
)} )}
{['R', 'C'].includes(currentRoofMaterial.lenAuth) && ( {basicSetting && ['R', 'C'].includes(currentRoof.lenAuth) && (
<div className="flex-ment"> <div className="flex-ment">
<span>L</span> <span>L</span>
<div className="select-wrap" style={{ width: '84px' }}> <div className="input-grid" style={{ width: '84px' }}>
<select <input
className="select-light dark" type="text"
name="roofHeight" className="input-origin block"
onChange={(e) => { name={`length`}
// ref={roofRef.length}
setCurrentRoofMaterial({ ...currentRoofMaterial, roofHeight: e.target.value }) value={parseInt(currentRoof.length)}
}} onChange={(e) => onlyNumberInputChange(e, changeInput)}
> readOnly={currentRoof.lenAuth === 'R'}
<option value={parseInt(currentRoofMaterial.lenBase)}>{parseInt(currentRoofMaterial.lenBase)}</option> disabled={basicSetting.roofSizeSet == 3}
</select> />
</div> </div>
{/* <div className="input-grid" style={{ width: '63px' }}>
<input
type="text"
className="input-origin block"
ref={roofRef.length}
defaultValue={parseInt(currentRoofMaterial.length)}
/>
</div> */}
</div> </div>
)} )}
{['C', 'R'].includes(currentRoofMaterial.raftAuth) && ( {basicSetting && ['C', 'R'].includes(currentRoof.raftAuth) && (
<div className="flex-ment"> <div className="flex-ment">
<span>{getMessage('modal.placement.initial.setting.rafter')}</span> <span>{getMessage('modal.placement.initial.setting.rafter')}</span>
<div className="select-wrap" style={{ width: '84px' }}> {raftCodes.length > 0 && (
<select <div className="select-wrap" style={{ width: '160px' }}>
className="select-light dark" <QSelectBox
name="roofGap" options={raftCodes}
onChange={(e) => { ref={roofRef.rafter}
// title={raftCodes.find((r) => r.clCode === (currentRoof.raft === undefined ? currentRoof.raftBaseCd : currentRoof.raft)).clCodeNm}
setCurrentRoofMaterial({ ...currentRoofMaterial, roofGap: e.target.value }) value={currentRoof.raft === undefined ? currentRoof.raftBaseCd : currentRoof.raft}
}} onChange={(e) => handleRafterChange(e.clCode)}
> sourceKey="clCode"
<option>455</option> targetKey={currentRoof.raft ? 'raft' : 'raftBaseCd'}
</select> showKey="clCodeNm"
disabled={basicSetting.roofSizeSet == 3}
/>
{/* <select className="select-light dark" name="roofGap" ref={roofRef.rafter}>
{raftCodes.map((raft, index) => {
return (
<option key={index} value={raft.clCode} selected={currentRoofMaterial.raft === raft.clCode}>
{raft.clCodeNm}
</option>
)
})}
</select> */}
</div> </div>
)}
</div> </div>
)} )}
{['C', 'R'].includes(currentRoofMaterial.roofPchAuth) && ( {basicSetting && ['C', 'R'].includes(currentRoof.roofPchAuth) && (
<div className="flex-ment"> <div className="flex-ment">
<span>{getMessage('hajebichi')}</span> <span>{getMessage('hajebichi')}</span>
<div className="grid-select no-flx" style={{ width: '84px' }}> <div className="input-grid" style={{ width: '84px' }}>
<select <input
className="select-light dark" type="text"
name="roofHajebichi" className="input-origin block"
onChange={(e) => { name={`hajebichi`}
// ref={roofRef.hajebichi}
setCurrentRoofMaterial({ ...currentRoofMaterial, roofHajebichi: e.target.value }) value={parseInt(currentRoof.hajebichi)}
}} onChange={(e) => onlyNumberInputChange(e, changeInput)}
> readOnly={currentRoof.roofPchAuth === 'R'}
<option value={parseInt(currentRoofMaterial.roofPchBase)}>{parseInt(currentRoofMaterial.roofPchBase)}</option> disabled={basicSetting.roofSizeSet == 3}
</select> />
</div> </div>
</div> </div>
)} )}
</div> </div>
<div className="placement-roof-btn-wrap">
<div className="icon-btn-wrap mt10">
<button
className={`${currentRoof.layout === ROOF_MATERIAL_LAYOUT.PARALLEL && 'act'}`}
value={ROOF_MATERIAL_LAYOUT.PARALLEL}
onClick={() => handleRoofLayoutChange(ROOF_MATERIAL_LAYOUT.PARALLEL)}
>
{getMessage('modal.roof.alloc.select.parallel')}
<i className="allocation01"></i>
</button>
<button
className={`${currentRoof.layout === ROOF_MATERIAL_LAYOUT.STAIRS && 'act'}`}
value={ROOF_MATERIAL_LAYOUT.STAIRS}
onClick={() => handleRoofLayoutChange(ROOF_MATERIAL_LAYOUT.STAIRS)}
>
{getMessage('modal.roof.alloc.select.stairs')} <i className="allocation02"></i>
</button>
</div>
</div>
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div> </div>
<div className="grid-btn-wrap"> <div className="grid-btn-wrap">
<button className="btn-frame modal act" onClick={() => basicSettingSave()}> <button className="btn-frame modal act" onClick={handleSaveBtn}>
{getMessage('modal.common.save')} {getMessage('modal.common.save')}
</button> </button>
</div> </div>

View File

@ -0,0 +1,199 @@
import { useMessage } from '@/hooks/useMessage'
import WithDraggable from '@/components/common/draggable/WithDraggable'
import QSelectBox from '@/components/common/select/QSelectBox'
import { useRoofAllocationSetting } from '@/hooks/roofcover/useRoofAllocationSetting'
import { usePopup } from '@/hooks/usePopup'
import { useRecoilState, useRecoilValue } from 'recoil'
import { contextPopupPositionState } from '@/store/popupAtom'
import { useEffect, useState } from 'react'
import { basicSettingState } from '@/store/settingAtom'
import { ROOF_MATERIAL_LAYOUT } from '@/components/floor-plan/modal/placementShape/PlacementShapeSetting'
import { useCanvasSetting } from '@/hooks/option/useCanvasSetting'
import { useCommonCode } from '@/hooks/common/useCommonCode'
export default function ContextRoofAllocationSetting(props) {
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
const { id, pos = contextPopupPosition } = props
const { getMessage } = useMessage()
const { closePopup } = usePopup()
const {
handleSave,
onAddRoofMaterial,
onDeleteRoofMaterial,
roofMaterials,
setCurrentRoofMaterial,
roofList,
handleDefaultRoofMaterial,
handleChangeRoofMaterial,
handleChangeRaft,
handleChangeLayout,
handleSaveContext,
currentRoofList,
} = useRoofAllocationSetting(id)
const { findCommonCode } = useCommonCode()
const [raftCodes, setRaftCodes] = useState([])
useEffect(() => {
const raftCodeList = findCommonCode('203800')
setRaftCodes(raftCodeList.map((raft) => ({ ...raft, value: raft.clCode, name: raft.clCodeNm })))
}, [])
return (
<WithDraggable isShow={true} pos={pos}>
<div className={`modal-pop-wrap lr mount`}>
<div className="modal-head">
<h1 className="title">{getMessage('plan.menu.estimate.roof.alloc')}</h1>
<button className="modal-close" onClick={() => closePopup(id)}>
닫기
</button>
</div>
<div className="modal-body">
<div className="properties-guide">{getMessage('modal.roof.alloc.info')}</div>
<div className="allocation-select-wrap">
<span>{getMessage('modal.roof.alloc.select.roof.material')}</span>
<div className="grid-select">
<QSelectBox
options={roofMaterials}
value={roofMaterials[0]}
onChange={(e) => {
// const selected = roofMaterials.find((roofMaterial) => roofMaterial.roofMatlCd === e.id)
setCurrentRoofMaterial(e)
}}
showKey={'roofMatlNm'}
sourceKey={'roofMatlCd'}
targetKey={'roofMatlCd'}
/>
</div>
<button
className="allocation-edit"
onClick={() => {
onAddRoofMaterial()
}}
>
<i className="edit-ico"></i>
{getMessage('modal.common.add')}
</button>
</div>
<div className="grid-option-wrap">
{currentRoofList.map((roof, index) => {
return (
<div className="grid-option-box" key={index}>
<div className="d-check-radio pop no-text">
<input type="radio" name="radio01" checked={roof.selected && 'checked'} readOnly={true} />
<label
htmlFor="ra01"
onClick={(e) => {
handleDefaultRoofMaterial(index)
}}
></label>
</div>
<div className="grid-option-block-form">
<div className="block-box">
<div className="flex-ment">
<div className="grid-select" style={{ width: '248px' }}>
<QSelectBox
options={roofMaterials}
value={roof}
showKey={'roofMatlNm'}
sourceKey={'roofMatlCd'}
targetKey={'roofMatlCd'}
onChange={(e) => handleChangeRoofMaterial(e, index)}
/>
</div>
{index === 0 && <span className="dec">{getMessage('modal.roof.alloc.default.roof.material')}</span>}
{index !== 0 && <button className="delete" onClick={() => onDeleteRoofMaterial(index)}></button>}
</div>
</div>
{(roof.widAuth || roof.lenAuth) && (
<div className="block-box">
{roof.widAuth && (
<div className="flex-ment">
<span>W</span>
<div className="input-grid" style={{ width: '100px' }}>
<input type="text" className="input-origin block" defaultValue={roof.width} readOnly={roof.widAuth === 'R'} />
</div>
</div>
)}
{roof.lenAuth && (
<div className="flex-ment">
<span>L</span>
<div className="input-grid" style={{ width: '100px' }}>
<input type="text" className="input-origin block" defaultValue={roof.length} readOnly={roof.lenAuth === 'R'} />
</div>
</div>
)}
</div>
)}
{(roof.raftAuth || roof.roofPchAuth) && (
<div className="block-box">
{roof.raftAuth && (
<div className="block-box">
<div className="flex-ment">
<span>{getMessage('modal.placement.initial.setting.rafter')}</span>
{raftCodes.length > 0 && (
<div className="grid-select" style={{ width: '160px' }}>
<QSelectBox
options={raftCodes}
value={roof}
showKey={'clCodeNm'}
sourceKey={'clCode'}
targetKey={roof.raft ? 'raft' : 'raftBaseCd'}
/>
</div>
)}
</div>
</div>
)}
{roof.roofPchAuth && (
<div className="block-box">
<div className="flex-ment">
<span>{getMessage('hajebichi')}</span>
<div className="input-grid" style={{ width: '84px' }}>
<input
type="text"
className="input-origin block"
value={parseInt(roof.hajebichi)}
readOnly={roof.roofPchAuth === 'R'}
/>
</div>
</div>
</div>
)}
</div>
)}
<div className="block-box">
<div className="icon-btn-wrap">
<button
className={roof.layout === ROOF_MATERIAL_LAYOUT.PARALLEL ? 'act' : ''}
onClick={() => {
handleChangeLayout(ROOF_MATERIAL_LAYOUT.PARALLEL, index)
}}
>
{getMessage('modal.roof.alloc.select.parallel')}
<i className="allocation01"></i>
</button>
<button
className={roof.layout === ROOF_MATERIAL_LAYOUT.STAIRS ? 'act' : ''}
onClick={() => {
handleChangeLayout(ROOF_MATERIAL_LAYOUT.STAIRS, index)
}}
>
{getMessage('modal.roof.alloc.select.stairs')} <i className="allocation02"></i>
</button>
</div>
</div>
</div>
</div>
)
})}
</div>
<div className="grid-btn-wrap">
<button className="btn-frame modal act" onClick={handleSaveContext}>
{getMessage('modal.roof.alloc.apply')}
</button>
</div>
</div>
</div>
</WithDraggable>
)
}

View File

@ -5,18 +5,41 @@ import { useRoofAllocationSetting } from '@/hooks/roofcover/useRoofAllocationSet
import { usePopup } from '@/hooks/usePopup' import { usePopup } from '@/hooks/usePopup'
import { useRecoilValue } from 'recoil' import { useRecoilValue } from 'recoil'
import { contextPopupPositionState } from '@/store/popupAtom' import { contextPopupPositionState } from '@/store/popupAtom'
import { useEffect, useState } from 'react'
import { ROOF_MATERIAL_LAYOUT } from '@/components/floor-plan/modal/placementShape/PlacementShapeSetting'
import { useCanvasSetting } from '@/hooks/option/useCanvasSetting'
import { useCommonCode } from '@/hooks/common/useCommonCode'
import { globalLocaleStore } from '@/store/localeAtom'
export default function RoofAllocationSetting(props) { export default function RoofAllocationSetting(props) {
const contextPopupPosition = useRecoilValue(contextPopupPositionState) const contextPopupPosition = useRecoilValue(contextPopupPositionState)
const { id, pos = contextPopupPosition } = props const { id, pos = contextPopupPosition } = props
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { closePopup } = usePopup() const { closePopup } = usePopup()
const { handleSave, onAddRoofMaterial, onDeleteRoofMaterial, values, roofMaterials, selectedRoofMaterial, setSelectedRoofMaterial } = const {
useRoofAllocationSetting(id) handleSave,
onAddRoofMaterial,
onDeleteRoofMaterial,
roofMaterials,
setCurrentRoofMaterial,
roofList,
handleDefaultRoofMaterial,
handleChangeRoofMaterial,
handleChangeRaft,
handleChangeLayout,
currentRoofList,
} = useRoofAllocationSetting(id)
const { findCommonCode } = useCommonCode()
const [raftCodes, setRaftCodes] = useState([])
const globalLocale = useRecoilValue(globalLocaleStore)
useEffect(() => {
const raftCodeList = findCommonCode('203800')
setRaftCodes(raftCodeList.map((raft) => ({ ...raft, name: raft.clCodeNm })))
}, [])
return ( return (
<WithDraggable isShow={true} pos={pos}> <WithDraggable isShow={true} pos={pos}>
<div className={`modal-pop-wrap ml mount`}> <div className={`modal-pop-wrap lr mount`}>
<div className="modal-head"> <div className="modal-head">
<h1 className="title">{getMessage('plan.menu.estimate.roof.alloc')}</h1> <h1 className="title">{getMessage('plan.menu.estimate.roof.alloc')}</h1>
<button className="modal-close" onClick={() => closePopup(id)}> <button className="modal-close" onClick={() => closePopup(id)}>
@ -28,7 +51,17 @@ export default function RoofAllocationSetting(props) {
<div className="allocation-select-wrap"> <div className="allocation-select-wrap">
<span>{getMessage('modal.roof.alloc.select.roof.material')}</span> <span>{getMessage('modal.roof.alloc.select.roof.material')}</span>
<div className="grid-select"> <div className="grid-select">
<QSelectBox options={roofMaterials} value={selectedRoofMaterial} onChange={(e) => setSelectedRoofMaterial(e)} /> <QSelectBox
options={roofMaterials}
value={roofMaterials[0]}
onChange={(e) => {
// const selected = roofMaterials.find((roofMaterial) => roofMaterial.roofMatlCd === e.id)
setCurrentRoofMaterial(e)
}}
showKey={'roofMatlNm'}
sourceKey={'roofMatlCd'}
targetKey={'roofMatlCd'}
/>
</div> </div>
<button <button
className="allocation-edit" className="allocation-edit"
@ -40,118 +73,121 @@ export default function RoofAllocationSetting(props) {
{getMessage('modal.common.add')} {getMessage('modal.common.add')}
</button> </button>
</div> </div>
<div className="grid-option-wrap"> <div className="grid-option-overflow">
{values.map((value, index) => ( <div className="grid-option-wrap">
<div className="grid-option-box" key={index}> {currentRoofList.map((roof, index) => {
<div className="d-check-radio pop no-text"> return (
<input type="radio" name="radio01" /> <div className="grid-option-box" key={index}>
<label htmlFor="ra01"></label> <div className="d-check-radio pop no-text">
</div> <input type="radio" name="radio01" checked={roof.selected && 'checked'} readOnly={true} />
<div className="grid-option-block-form"> <label
<div className="block-box"> htmlFor="ra01"
<div className="flex-ment"> onClick={(e) => {
<div className="grid-select" style={{ width: '248px' }}> handleDefaultRoofMaterial(index)
<QSelectBox options={roofMaterials} value={value} /> }}
></label>
</div>
<div className="grid-option-block-form">
<div className="block-box">
<div className="flex-ment">
<div className="grid-select" style={{ width: '248px' }}>
<QSelectBox
options={roofMaterials}
value={roof}
showKey={'roofMatlNm'}
sourceKey={'roofMatlCd'}
targetKey={'roofMatlCd'}
onChange={(e) => handleChangeRoofMaterial(e, index)}
/>
</div>
{index === 0 && <span className="dec">{getMessage('modal.roof.alloc.default.roof.material')}</span>}
{index !== 0 && <button className="delete" onClick={() => onDeleteRoofMaterial(index)}></button>}
</div>
</div>
{(roof.widAuth || roof.lenAuth) && (
<div className="block-box">
{roof.widAuth && (
<div className="flex-ment">
<span>W</span>
<div className="input-grid" style={{ width: '100px' }}>
<input type="text" className="input-origin block" defaultValue={roof.width} readOnly={roof.widAuth === 'R'} />
</div>
</div>
)}
{roof.lenAuth && (
<div className="flex-ment">
<span>L</span>
<div className="input-grid" style={{ width: '100px' }}>
<input type="text" className="input-origin block" defaultValue={roof.length} readOnly={roof.lenAuth === 'R'} />
</div>
</div>
)}
</div>
)}
{(roof.raftAuth || roof.roofPchAuth) && (
<div className="block-box">
{roof.raftAuth && (
<div className="block-box">
<div className="flex-ment">
<span>{getMessage('modal.placement.initial.setting.rafter')}</span>
{raftCodes.length > 0 && (
<div className="grid-select" style={{ width: '160px' }}>
<QSelectBox
options={raftCodes}
value={roof}
showKey={'clCodeNm'}
sourceKey={'clCode'}
targetKey={roof.raft ? 'raft' : 'raftBaseCd'}
onChange={(e) => handleChangeRaft(e, index)}
/>
</div>
)}
</div>
</div>
)}
{roof.roofPchAuth && (
<div className="block-box">
<div className="flex-ment">
<span>{getMessage('hajebichi')}</span>
<div className="input-grid" style={{ width: '84px' }}>
<input
type="text"
className="input-origin block"
value={parseInt(roof.hajebichi)}
readOnly={roof.roofPchAuth === 'R'}
/>
</div>
</div>
</div>
)}
</div>
)}
<div className="block-box">
<div className="icon-btn-wrap">
<button
className={roof.layout === ROOF_MATERIAL_LAYOUT.PARALLEL ? 'act' : ''}
onClick={() => {
handleChangeLayout(ROOF_MATERIAL_LAYOUT.PARALLEL, index)
}}
>
{getMessage('modal.roof.alloc.select.parallel')}
<i className="allocation01"></i>
</button>
<button
className={roof.layout === ROOF_MATERIAL_LAYOUT.STAIRS ? 'act' : ''}
onClick={() => {
handleChangeLayout(ROOF_MATERIAL_LAYOUT.STAIRS, index)
}}
>
{getMessage('modal.roof.alloc.select.stairs')} <i className="allocation02"></i>
</button>
</div>
</div> </div>
{index === 0 && <span className="dec">基本屋根材</span>}
{index !== 0 && <button className="delete" onClick={() => onDeleteRoofMaterial(value.id)}></button>}
</div> </div>
</div> </div>
<div className="block-box"> )
{value.type === 'A' ? ( })}
<> </div>
<div className="flex-ment">
<span>W</span>
<div className="select-wrap" style={{ width: '84px' }}>
<select className="select-light dark" name="" id="">
<option>265</option>
</select>
</div>
</div>
<div className="flex-ment">
<span>L</span>
<div className="select-wrap" style={{ width: '84px' }}>
<select className="select-light dark" name="" id="">
<option>235</option>
</select>
</div>
</div>
<div className="flex-ment">
<span>{getMessage('modal.placement.initial.setting.rafter')}</span>
<div className="select-wrap" style={{ width: '84px' }}>
<select className="select-light dark" name="" id="">
<option>455</option>
</select>
</div>
</div>
</>
) : value.type === 'B' ? (
<>
<div className="flex-ment">
<span>{getMessage('hajebichi')}</span>
<div className="grid-select no-flx" style={{ width: '84px' }}>
<select className="select-light dark" name="" id="">
<option>265</option>
</select>
</div>
</div>
<div className="flex-ment">
<span>{getMessage('modal.placement.initial.setting.rafter')}</span>
<div className="grid-select no-flx right" style={{ width: '84px' }}>
<select className="select-light dark" name="" id="">
<option>265</option>
</select>
</div>
</div>
</>
) : value.type === 'C' ? (
<>
<div className="flex-ment">
<span>{getMessage('hajebichi')}</span>
<div className="grid-select no-flx" style={{ width: '84px' }}>
<select className="select-light dark" name="" id="">
<option>265</option>
</select>
</div>
</div>
</>
) : value.type === 'D' ? (
<>
<div className="flex-ment">
<span>L</span>
<div className="grid-select no-flx" style={{ width: '84px' }}>
<select className="select-light dark" name="" id="">
<option>265</option>
</select>
</div>
</div>
<div className="flex-ment">
<span>{getMessage('modal.placement.initial.setting.rafter')}</span>
<div className="grid-select no-flx right" style={{ width: '84px' }}>
<select className="select-light dark" name="" id="">
<option>265</option>
</select>
</div>
</div>
</>
) : (
''
)}
</div>
<div className="block-box">
<div className="icon-btn-wrap">
<button className={value.alignType === 'parallel' ? 'act' : ''}>
{getMessage('modal.roof.alloc.select.parallel')}
<i className="allocation01"></i>
</button>
<button className={value.alignType === 'stairs' ? 'act' : ''}>
{getMessage('modal.roof.alloc.select.stairs')} <i className="allocation02"></i>
</button>
</div>
</div>
</div>
</div>
))}
</div> </div>
<div className="grid-btn-wrap"> <div className="grid-btn-wrap">
<button className="btn-frame modal act" onClick={handleSave}> <button className="btn-frame modal act" onClick={handleSave}>

View File

@ -1,9 +1,8 @@
import React, { useEffect, useState } from 'react' import React, { useEffect, useState } from 'react'
import { useCanvasSetting } from '@/hooks/option/useCanvasSetting'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { POLYGON_TYPE } from '@/common/common' import { POLYGON_TYPE } from '@/common/common'
import { setSurfaceShapePattern } from '@/util/canvas-util'
import { useEvent } from '@/hooks/useEvent' import { useEvent } from '@/hooks/useEvent'
import { useRoofFn } from '@/hooks/common/useRoofFn'
export default function FirstOption(props) { export default function FirstOption(props) {
const { getMessage } = useMessage() const { getMessage } = useMessage()
@ -11,6 +10,7 @@ export default function FirstOption(props) {
let { canvas, settingModalFirstOptions, setSettingModalFirstOptions, settingsData, setSettingsData, settingsDataSave, setSettingsDataSave } = props let { canvas, settingModalFirstOptions, setSettingModalFirstOptions, settingsData, setSettingsData, settingsDataSave, setSettingsDataSave } = props
const { option1, option2, dimensionDisplay } = settingModalFirstOptions const { option1, option2, dimensionDisplay } = settingModalFirstOptions
const { initEvent } = useEvent() const { initEvent } = useEvent()
const { setSurfaceShapePattern } = useRoofFn()
// //
useEffect(() => { useEffect(() => {

View File

@ -60,24 +60,33 @@ export default function Header(props) {
const qOrderUrl = process.env.NEXT_PUBLIC_Q_ORDER_AUTO_LOGIN_URL const qOrderUrl = process.env.NEXT_PUBLIC_Q_ORDER_AUTO_LOGIN_URL
const qMusubiUrl = process.env.NEXT_PUBLIC_Q_MUSUBI_AUTO_LOGIN_URL const qMusubiUrl = process.env.NEXT_PUBLIC_Q_MUSUBI_AUTO_LOGIN_URL
const [SelectOptions, setSelectOptions] = useState( const [SelectOptions, setSelectOptions] = useState([])
userSession.groupId === '60000' ? [{ id: 0, name: 'Q.ORDER', link: `${qOrderUrl}` }] : [{ id: 1, name: 'Q.Musubi', link: `${qMusubiUrl}` }],
)
const getAutoLoginParam = async () => { const getAutoLoginParam = async () => {
await promisePost({ url: '/api/login/v1.0/user/login/autoLoginEncryptData', data: { loginId: userSession.userId } }) await promisePost({ url: '/api/login/v1.0/user/login/autoLoginEncryptData', data: { loginId: userSession.userId } })
.then((res) => { .then((res) => {
if (res) { if (res) {
setSelectOptions( setSelectOptions(
userSession.groupId === '60000' userSession.storeId === 'T01'
? [{ id: 0, name: 'Q.ORDER', link: `${qOrderUrl}?autoLoginParam1=${encodeURIComponent(res.data)}` }] ? [
: [{ id: 1, name: 'Q.Musubi', link: `${qMusubiUrl}?autoLoginParam1=${encodeURIComponent(res.data)}` }], { id: 0, name: getMessage('site.header.link1') },
) { id: 1, name: 'Q.ORDER', link: `${qOrderUrl}?autoLoginParam1=${encodeURIComponent(res.data)}` },
setSelected( { id: 2, name: 'Q.Musubi', link: `${qMusubiUrl}?autoLoginParam1=${encodeURIComponent(res.data)}` },
userSession.groupId === '60000' { id: 3, name: getMessage('site.header.link2'), link: `https://q-warranty.q-cells.jp/seller_login`, target: '_blank' },
? { id: 0, name: 'Q.ORDER', link: `${qOrderUrl}?autoLoginParam1=${encodeURIComponent(res.data)}` } ]
: { id: 1, name: 'Q.Musubi', link: `${qMusubiUrl}?autoLoginParam1=${encodeURIComponent(res.data)}` }, : userSession.groupId === '60000'
? [
{ id: 0, name: getMessage('site.header.link1') },
{ id: 1, name: 'Q.ORDER', link: `${qOrderUrl}?autoLoginParam1=${encodeURIComponent(res.data)}` },
{ id: 2, name: getMessage('site.header.link2'), link: `https://q-warranty.q-cells.jp/seller_login`, target: '_blank' },
]
: [
{ id: 0, name: getMessage('site.header.link1') },
{ id: 1, name: 'Q.Musubi', link: `${qMusubiUrl}?autoLoginParam1=${encodeURIComponent(res.data)}` },
{ id: 2, name: getMessage('site.header.link2'), link: `https://q-warranty.q-cells.jp/seller_login`, target: '_blank' },
],
) )
onChangeSelect({ id: 0, name: getMessage('site.header.link1') })
} }
}) })
.catch((error) => { .catch((error) => {
@ -124,18 +133,31 @@ export default function Header(props) {
const onChangeSelect = (option) => { const onChangeSelect = (option) => {
setSelected(option) setSelected(option)
} }
const navPage = () => { const navPage = () => {
if (selected.link) { if (selected.link) {
location.href = selected.link if (selected.target === '_blank') {
window.open(selected.link)
} else {
location.href = selected.link
}
} }
} }
// Home //
const moveHome = () => { const removeStuffRecoil = (menu) => {
setStuffSearch({ //menu.id 0 HOME
...stuffSearch, //menu.id 3
code: 'DELETE', //menu.id 4
}) //menu.id 5
//menu.id 6 FAQ
//menu.id 7
if (menu.id === 0 || menu.id === 5 || menu.id === 6 || menu.id === 7) {
setStuffSearch({
...stuffSearch,
code: 'DELETE',
})
}
} }
const getMenuTemplate = (menus) => { const getMenuTemplate = (menus) => {
@ -152,7 +174,8 @@ export default function Header(props) {
key={`${menu.id}`} key={`${menu.id}`}
href={menu.url} href={menu.url}
onClick={() => { onClick={() => {
moveHome() // moveHome()
removeStuffRecoil(menu)
}} }}
> >
{getMessage(menu.name)} {getMessage(menu.name)}
@ -169,7 +192,13 @@ export default function Header(props) {
onMouseEnter={(e) => ToggleonMouse(e, 'add', 'li > ul')} onMouseEnter={(e) => ToggleonMouse(e, 'add', 'li > ul')}
onMouseLeave={(e) => ToggleonMouse(e, 'remove', 'li > ul')} onMouseLeave={(e) => ToggleonMouse(e, 'remove', 'li > ul')}
> >
<Link scroll={false} href={m.url}> <Link
scroll={false}
href={m.url}
onClick={() => {
removeStuffRecoil(m)
}}
>
{getMessage(m.name)} {getMessage(m.name)}
</Link> </Link>
</li> </li>
@ -190,7 +219,15 @@ export default function Header(props) {
<div className="header-inner"> <div className="header-inner">
<div className="header-right"> <div className="header-right">
<h1 className="logo"> <h1 className="logo">
<Link href={'/'}></Link> <Link
href={'/'}
onClick={() => {
setStuffSearch({
...stuffSearch,
code: 'DELETE',
})
}}
></Link>
</h1> </h1>
<nav> <nav>
<ul className="nav-list ">{getMenuTemplate(menus)}</ul> <ul className="nav-list ">{getMenuTemplate(menus)}</ul>
@ -223,7 +260,7 @@ export default function Header(props) {
</button> </button>
</div> </div>
<div className="select-box"> <div className="select-box">
<QSelectBox options={SelectOptions} onChange={onChangeSelect} /> <QSelectBox options={SelectOptions} onChange={onChangeSelect} title={getMessage('site.header.link1')} />
</div> </div>
<div className="btn-wrap"> <div className="btn-wrap">
<button className="btn-frame small dark" onClick={() => navPage()}> <button className="btn-frame small dark" onClick={() => navPage()}>

View File

@ -11,12 +11,13 @@ import { useRouter } from 'next/navigation'
import { globalLocaleStore } from '@/store/localeAtom' import { globalLocaleStore } from '@/store/localeAtom'
import { queryStringFormatter } from '@/util/common-utils' import { queryStringFormatter } from '@/util/common-utils'
import MainSkeleton from '../ui/MainSkeleton' import MainSkeleton from '../ui/MainSkeleton'
import { SessionContext } from '@/app/SessionProvider'
import { useMainContentsController } from '@/hooks/main/useMainContentsController' import { useMainContentsController } from '@/hooks/main/useMainContentsController'
import { QcastContext } from '@/app/QcastProvider' import { QcastContext } from '@/app/QcastProvider'
import { useSwal } from '@/hooks/useSwal'
export default function MainContents() { export default function MainContents() {
const { session } = useContext(SessionContext) const { swalFire } = useSwal()
const { getMessage } = useMessage() const { getMessage } = useMessage()
const router = useRouter() const router = useRouter()
const globalLocaleState = useRecoilValue(globalLocaleStore) const globalLocaleState = useRecoilValue(globalLocaleStore)
@ -107,8 +108,8 @@ export default function MainContents() {
> >
<div className="item-inner"> <div className="item-inner">
<span className="time">{dayjs(row.lastEditDatetime).format('YYYY.MM.DD HH:mm:ss')}</span> <span className="time">{dayjs(row.lastEditDatetime).format('YYYY.MM.DD HH:mm:ss')}</span>
<span>{row.objectNo}</span> <span>{row.tempFlg === '0' ? row.objectNo : getMessage('stuff.gridData.tempObjectNo')}</span>
<span>{row.objectName}</span> <span>{row.objectName ? row.objectName : '-'}</span>
<span>{row.saleStoreName}</span> <span>{row.saleStoreName}</span>
</div> </div>
</li> </li>
@ -116,51 +117,60 @@ export default function MainContents() {
})} })}
</ul> </ul>
) : ( ) : (
<MainSkeleton count={6} /> <div className="recently-no-data">
<h3>{getMessage('main.content.objectList.noData1')}</h3>
<p>{getMessage('main.content.objectList.noData2')}</p>
<button type="button" className="btn-origin navy" onClick={() => router.push('/management/stuff/tempReg')}>
+ {getMessage('stuff.search.btn.register')}
</button>
</div>
)} )}
</ProductItem> </ProductItem>
<ProductItem num={2} name={getMessage('main.content.notice')}> <ProductItem num={2} name={getMessage('main.content.notice')}>
<div className="notice-box"> {recentNoticeList.length > 0 ? (
{recentNoticeList.length > 0 ? ( <div className="notice-box">
<> <div className="notice-day pre">{dayjs(recentNoticeList[0]?.regDt).format('YYYY.MM.DD')}</div>
<div className="notice-day pre">{dayjs(recentNoticeList[0]?.regDt).format('YYYY.MM.DD')}</div> <div className="notice-title">{recentNoticeList[0]?.title}</div>
<div className="notice-title">{recentNoticeList[0]?.title}</div> <div
<div className="notice-contents">{recentNoticeList[0]?.contents}</div> className="notice-contents"
</> dangerouslySetInnerHTML={{ __html: recentNoticeList[0]?.contents ? recentNoticeList[0].contents.replaceAll('\n', '<br/>') : '' }}
) : ( ></div>
<MainSkeleton count={5} /> </div>
)} ) : (
</div> <div className="recently-no-data">
<h3>{getMessage('main.content.noBusiness')}</h3>
</div>
)}
</ProductItem> </ProductItem>
</div> </div>
<div className="main-product-list"> <div className="main-product-list">
<ProductItem num={3} name={getMessage('main.faq')}> <ProductItem num={3} name={getMessage('main.faq')}>
<ul className="faq-list"> {recentFaqList.length > 0 ? (
{recentFaqList.length > 0 ? ( <ul className="faq-list">
<> {recentFaqList.map((row) => {
{recentFaqList.map((row) => { return (
return ( <li key={row.rowNumber} className="faq-item">
<li key={row.rowNumber} className="faq-item"> <div className="faq-item-inner">
<div className="faq-item-inner"> <div className="faq-num pre">FAQ {row.totCnt - row.rowNumber}</div>
<div className="faq-num pre">FAQ {row.noticeNo}</div> <div className="faq-title pre">{row.title}</div>
<div className="faq-title pre">{row.title}</div> <div className="faq-day pre">{dayjs(row.regDt).format('YYYY.MM.DD')}</div>
<div className="faq-day pre">{dayjs(row.regDt).format('YYYY.MM.DD')}</div> </div>
</div> </li>
</li> )
) })}
})} </ul>
</> ) : (
) : ( <div className="recently-no-data">
<MainSkeleton count={2} /> <h3>{getMessage('main.content.noBusiness')}</h3>
)} </div>
</ul> )}
</ProductItem> </ProductItem>
<ProductItem num={4} name={'Data Download'}> <ProductItem num={4} name={'Data Download'}>
<div className="data-download-wrap"> <div className="data-download-wrap">
<button className="data-down"> <button className="data-down" type="button" onClick={() => swalFire({ text: getMessage('main.content.alert.noFile'), type: 'alert' })}>
<span>{getMessage('main.content.download1')}</span> <span>{getMessage('main.content.download1')}</span>
</button> </button>
<button className="data-down"> <button className="data-down" type="button" onClick={() => swalFire({ text: getMessage('main.content.alert.noFile'), type: 'alert' })}>
<span>{getMessage('main.content.download2')}</span> <span>{getMessage('main.content.download2')}</span>
</button> </button>
</div> </div>

View File

@ -9,8 +9,10 @@ export default function ProductItem({ num, name, children }) {
router.push('/management/stuff', { scroll: false }) router.push('/management/stuff', { scroll: false })
} else if (num === 2) { } else if (num === 2) {
router.push('/community/notice') router.push('/community/notice')
} else { } else if (num === 3) {
router.push('/community/faq') router.push('/community/faq')
} else {
router.push('/community/archive')
} }
} }
return ( return (
@ -20,7 +22,7 @@ export default function ProductItem({ num, name, children }) {
<span className={`item-logo ico0${num}`}></span> <span className={`item-logo ico0${num}`}></span>
{name} {name}
</h2> </h2>
{num !== 4 && num !== 5 && ( {num !== 5 && (
<button <button
className="more-btn" className="more-btn"
onClick={() => { onClick={() => {

View File

@ -30,7 +30,7 @@ export default function Stuff() {
const [pageNo, setPageNo] = useState(1) // const [pageNo, setPageNo] = useState(1) //
const [pageSize, setPageSize] = useState(100) // const [pageSize, setPageSize] = useState(100) //
const [totalCount, setTotalCount] = useState(0) // const [totalCount, setTotalCount] = useState(0) //
const [defaultSortType, setDefaultSortType] = useState('R') const [defaultSortType, setDefaultSortType] = useState('U')
const globalLocaleState = useRecoilValue(globalLocaleStore) const globalLocaleState = useRecoilValue(globalLocaleStore)
const { get } = useAxios(globalLocaleState) const { get } = useAxios(globalLocaleState)
@ -41,11 +41,42 @@ export default function Stuff() {
// //
const copyNo = async (value) => { const copyNo = async (value) => {
try { // try {
await navigator.clipboard.writeText(value) // await navigator.clipboard.writeText(value)
alert(getMessage('stuff.detail.header.successCopy')) // alert(getMessage('stuff.detail.header.successCopy'))
} catch (error) { // } catch (error) {
alert(getMessage('stuff.detail.header.failCopy')) // alert(getMessage('stuff.detail.header.failCopy'))
// }
// Navigator clipboard api needs a secure context (https)
if (navigator.clipboard && window.isSecureContext) {
await navigator.clipboard
.writeText(value)
.then(() => {
alert(getMessage('stuff.detail.header.successCopy'))
})
.catch(() => {
alert(getMessage('stuff.detail.header.failCopy'))
})
} else {
// Use the 'out of viewport hidden text area' trick
const textArea = document.createElement('textArea')
textArea.value = value
// Move textarea out of the viewport so it's not visible
textArea.style.position = 'absolute'
textArea.style.left = '-999999px'
document.body.prepend(textArea)
textArea.select()
try {
document.execCommand('copy')
alert(getMessage('stuff.detail.header.successCopy'))
} catch (err) {
alert(getMessage('stuff.detail.header.failCopy'))
} finally {
textArea.remove()
}
} }
} }
@ -223,7 +254,7 @@ export default function Stuff() {
startRow: (pageNo - 1) * pageSize + 1, startRow: (pageNo - 1) * pageSize + 1,
endRow: pageNo * pageSize, endRow: pageNo * pageSize,
schSelSaleStoreId: stuffSearchParams?.schOtherSelSaleStoreId ? stuffSearchParams.schOtherSelSaleStoreId : stuffSearchParams.schSelSaleStoreId, schSelSaleStoreId: stuffSearchParams?.schOtherSelSaleStoreId ? stuffSearchParams.schOtherSelSaleStoreId : stuffSearchParams.schSelSaleStoreId,
schSortType: 'R', schSortType: 'U',
code: 'E', code: 'E',
pageNo: 1, pageNo: 1,
pageSize: 100, pageSize: 100,
@ -297,7 +328,7 @@ export default function Stuff() {
endRow: 100, endRow: 100,
schSelSaleStoreId: '', schSelSaleStoreId: '',
schOtherSelSaleStoreId: '', schOtherSelSaleStoreId: '',
schSortType: 'R', schSortType: 'U',
code: 'S', code: 'S',
pageNo: 1, pageNo: 1,
pageSize: 100, pageSize: 100,
@ -407,8 +438,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" onChange={onChangeSortType} value={stuffSearch.schSortType}> <select className="select-light black" onChange={onChangeSortType} value={stuffSearch.schSortType}>
<option value="R">{getMessage('stuff.search.grid.schSortTypeR')}</option>
<option value="U">{getMessage('stuff.search.grid.schSortTypeU')}</option> <option value="U">{getMessage('stuff.search.grid.schSortTypeU')}</option>
<option value="R">{getMessage('stuff.search.grid.schSortTypeR')}</option>
</select> </select>
</div> </div>
<div className="select-box" style={{ width: '80px' }}> <div className="select-box" style={{ width: '80px' }}>

View File

@ -1,7 +1,7 @@
'use client' 'use client'
import { useState, useEffect, useRef, useContext } from 'react' import { useState, useEffect, useRef, useContext } from 'react'
import { useRouter, useSearchParams, usePathname } from 'next/navigation' import { useRouter, useSearchParams } from 'next/navigation'
import { Button } from '@nextui-org/react' import { Button } from '@nextui-org/react'
import Select, { components } from 'react-select' import Select, { components } from 'react-select'
import Link from 'next/link' import Link from 'next/link'
@ -18,7 +18,8 @@ import WindSelectPop from './popup/WindSelectPop'
import { useCommonCode } from '@/hooks/common/useCommonCode' import { useCommonCode } from '@/hooks/common/useCommonCode'
import StuffPlanQGrid from './StuffPlanQGrid' import StuffPlanQGrid from './StuffPlanQGrid'
import { floorPlanObjectState } from '@/store/floorPlanObjectAtom' import { floorPlanObjectState } from '@/store/floorPlanObjectAtom'
import { ManagementContext } from '@/app/management/ManagementProvider' import { GlobalDataContext } from '@/app/GlobalDataProvider'
// import { ManagementContext } from '@/app/management/ManagementProvider'
import DocDownOptionPop from '../estimate/popup/DocDownOptionPop' import DocDownOptionPop from '../estimate/popup/DocDownOptionPop'
import { stuffSearchState } from '@/store/stuffAtom' import { stuffSearchState } from '@/store/stuffAtom'
import { QcastContext } from '@/app/QcastProvider' import { QcastContext } from '@/app/QcastProvider'
@ -33,6 +34,8 @@ export default function StuffDetail() {
const [popPlanNo, setPopPlanNo] = useState('1') //default 1 const [popPlanNo, setPopPlanNo] = useState('1') //default 1
const [showButton, setShowButton] = useState('') //, ,
// //
const { commonCode, findCommonCode } = useCommonCode() const { commonCode, findCommonCode } = useCommonCode()
const [selOptions, setSelOptions] = useState('') // 1 const [selOptions, setSelOptions] = useState('') // 1
@ -50,7 +53,7 @@ export default function StuffDetail() {
const formInitValue = { const formInitValue = {
// T...() R...() // T...() R...()
planReqNo: '', //No planReqNo: '', //No
receiveUser: '', // receiveUser: session?.userNm, //
objectStatusId: '0', //(:0 : 1) objectStatusId: '0', //(:0 : 1)
objectName: '', // objectName: '', //
objectNameOmit: '', // objectNameOmit: '', //
@ -106,7 +109,7 @@ export default function StuffDetail() {
const objectNo = searchParams.get('objectNo') //url set const objectNo = searchParams.get('objectNo') //url set
const [editMode, setEditMode] = useState('NEW') const [editMode, setEditMode] = useState('NEW')
const { managementState, setManagementState } = useContext(ManagementContext) const { managementState, setManagementState } = useContext(GlobalDataContext)
const [planGridProps, setPlanGridProps] = useState({ const [planGridProps, setPlanGridProps] = useState({
planGridData: [], planGridData: [],
isPageable: false, isPageable: false,
@ -146,7 +149,8 @@ export default function StuffDetail() {
let capacity let capacity
if (origin) { if (origin) {
capacity = origin / 1000 capacity = origin / 1000
return capacity.toFixed(3) //#400 2
return capacity.toFixed(2) + 'kW'
} else { } else {
return null return null
} }
@ -264,7 +268,10 @@ export default function StuffDetail() {
display: 'none', display: 'none',
} }
} }
if (managementState?.createUser === 'T01' && session?.userId !== 'T01') {
//createUser T01 T01
buttonStyle = { display: 'none' }
}
return ( return (
<> <>
<div className="grid-cell-btn"> <div className="grid-cell-btn">
@ -317,8 +324,21 @@ export default function StuffDetail() {
} }
promiseGet({ url: `/api/object/${objectNo}/detail` }).then((res) => { promiseGet({ url: `/api/object/${objectNo}/detail` }).then((res) => {
if (res.status === 200) { if (res.status === 200) {
// console.log(res.data.createUser)
// console.log(session)
if (res?.data?.createUser === 'T01' && session?.userId !== 'T01') {
//createUser T01 T01
setShowButton('none')
}
if (isObjectNotEmpty(res.data)) { if (isObjectNotEmpty(res.data)) {
setManagementState(res.data) let surfaceTypeValue
if (res.data.surfaceType === 'Ⅲ・Ⅳ') {
surfaceTypeValue = '3'
} else if (res.data.surfaceType === 'Ⅱ') {
surfaceTypeValue = '2'
}
setManagementState({ ...res.data, surfaceTypeValue: surfaceTypeValue })
} else { } else {
setManagementState({}) setManagementState({})
alert(getMessage('stuff.detail.header.notExistObjectNo')) alert(getMessage('stuff.detail.header.notExistObjectNo'))
@ -1287,12 +1307,16 @@ export default function StuffDetail() {
delete params_sort.workNo delete params_sort.workNo
delete params_sort.workName delete params_sort.workName
// console.log(JSON.stringify(detail_sort) === JSON.stringify(params_sort))
// console.log(Object.entries(detail_sort).toString() === Object.entries(params_sort).toString())
if (Object.entries(detail_sort).toString() === Object.entries(params_sort).toString()) { if (Object.entries(detail_sort).toString() === Object.entries(params_sort).toString()) {
return alert(getMessage('stuff.detail.noChgData')) return alert(getMessage('stuff.detail.noChgData'))
} }
if (params?.receiveUser !== '') {
if (params?.receiveUser.trim().length > 10) {
return alert(getMessage('stuff.detail.tempSave.message2'))
}
}
if (editMode === 'NEW') { if (editMode === 'NEW') {
await promisePost({ url: apiUrl, data: params }) await promisePost({ url: apiUrl, data: params })
.then((res) => { .then((res) => {
@ -1364,6 +1388,13 @@ export default function StuffDetail() {
params.saleStoreLevel = session.storeLvl params.saleStoreLevel = session.storeLvl
} }
//
if (params?.receiveUser !== '') {
if (params?.receiveUser.trim().length > 10) {
return alert(getMessage('stuff.detail.tempSave.message2'))
}
}
const apiUrl = '/api/object/save-object' const apiUrl = '/api/object/save-object'
if (objectNo) { if (objectNo) {
await promisePut({ url: apiUrl, data: params }) await promisePut({ url: apiUrl, data: params })
@ -1443,14 +1474,43 @@ export default function StuffDetail() {
) )
} }
//
const getCellDoubleClicked = (params) => {
if (params?.column?.colId !== 'estimateDate') {
if (params?.data?.planNo && params?.data?.objectNo) {
let objectNo = params?.data?.objectNo
let planNo = params?.data?.planNo
router.push(`/floor-plan?pid=${planNo}&objectNo=${objectNo}`)
}
}
}
return ( return (
<> <>
{(editMode === 'NEW' && ( {(editMode === 'NEW' && (
<form onSubmit={handleSubmit(onValid)}> <form onSubmit={handleSubmit(onValid)}>
<div className="sub-table-box"> <div className="sub-table-box">
<div className="promise-gudie"> <div className="promise-title-wrap">
<span className="important">*</span> <div className="promise-gudie">
{getMessage('stuff.detail.required')} <span className="important">*</span>
{getMessage('stuff.detail.required')}
</div>
<div className="left-unit-box">
{!isFormValid ? (
<Button className="btn-origin grey mr5" onPress={onTempSave} style={{ display: showButton }}>
{getMessage('stuff.detail.btn.tempSave')}
</Button>
) : (
<Button type="submit" className="btn-origin navy mr5" style={{ display: showButton }}>
{getMessage('stuff.detail.btn.save')}
</Button>
)}
<Link href="/management/stuff" scroll={false}>
<button type="button" className="btn-origin grey">
{getMessage('stuff.detail.btn.moveList')}
</button>
</Link>
</div>
</div> </div>
<div className="infomation-table"> <div className="infomation-table">
<table> <table>
@ -1476,7 +1536,7 @@ export default function StuffDetail() {
)) || )) ||
null} null}
</div> </div>
<Button type="button" className="btn-origin grey" onPress={onSearchDesignRequestPopOpen}> <Button type="button" className="btn-origin grey" onPress={onSearchDesignRequestPopOpen} style={{ display: showButton }}>
{getMessage('stuff.planReqPopup.title')} {getMessage('stuff.planReqPopup.title')}
</Button> </Button>
</div> </div>
@ -1685,7 +1745,7 @@ export default function StuffDetail() {
onChange={onSelectionChange2} onChange={onSelectionChange2}
getOptionLabel={(x) => x.saleStoreName} getOptionLabel={(x) => x.saleStoreName}
getOptionValue={(x) => x.saleStoreId} getOptionValue={(x) => x.saleStoreId}
isDisabled={otherSaleStoreList.length > 0 ? false : true} isDisabled={otherSaleStoreList != null && otherSaleStoreList.length > 0 ? false : true}
isClearable={true} isClearable={true}
value={otherSaleStoreList.filter(function (option) { value={otherSaleStoreList.filter(function (option) {
return option.saleStoreId === otherSelOptions return option.saleStoreId === otherSelOptions
@ -1713,7 +1773,7 @@ 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" disabled value={form.watch('zipNo') || ''} /> <input type="text" className="input-light" disabled value={form.watch('zipNo') || ''} />
</div> </div>
<Button className="btn-origin grey" onPress={onSearchPostNumberPopOpen}> <Button className="btn-origin grey" onPress={onSearchPostNumberPopOpen} style={{ display: showButton }}>
{getMessage('stuff.detail.btn.addressPop')} {getMessage('stuff.detail.btn.addressPop')}
</Button> </Button>
<div className="guide">{getMessage('stuff.detail.btn.addressPop.guide')}</div> <div className="guide">{getMessage('stuff.detail.btn.addressPop.guide')}</div>
@ -1803,7 +1863,7 @@ export default function StuffDetail() {
></Select> ></Select>
</div> </div>
<span className="mr10">{getMessage('stuff.detail.standardWindSpeedIdSpan')}</span> <span className="mr10">{getMessage('stuff.detail.standardWindSpeedIdSpan')}</span>
<Button type="button" className="btn-origin grey" onPress={onSearchWindSpeedPopOpen}> <Button type="button" className="btn-origin grey" onPress={onSearchWindSpeedPopOpen} style={{ display: showButton }}>
{getMessage('stuff.detail.btn.windSpeedPop')} {getMessage('stuff.detail.btn.windSpeedPop')}
</Button> </Button>
</div> </div>
@ -1925,11 +1985,11 @@ export default function StuffDetail() {
</div> </div>
<div className="sub-right-footer"> <div className="sub-right-footer">
{!isFormValid ? ( {!isFormValid ? (
<Button className="btn-origin grey mr5" onPress={onTempSave}> <Button className="btn-origin grey mr5" onPress={onTempSave} style={{ display: showButton }}>
{getMessage('stuff.detail.btn.tempSave')} {getMessage('stuff.detail.btn.tempSave')}
</Button> </Button>
) : ( ) : (
<Button type="submit" className="btn-origin navy mr5"> <Button type="submit" className="btn-origin navy mr5" style={{ display: showButton }}>
{getMessage('stuff.detail.btn.save')} {getMessage('stuff.detail.btn.save')}
</Button> </Button>
)} )}
@ -1945,8 +2005,46 @@ export default function StuffDetail() {
<> <>
<form onSubmit={handleSubmit(onValid)}> <form onSubmit={handleSubmit(onValid)}>
<div className="sub-table-box"> <div className="sub-table-box">
<div className="promise-gudie"> <div className="promise-title-wrap">
<span className="important">*</span> {getMessage('stuff.detail.required')} <div className="promise-gudie">
<span className="important">*</span> {getMessage('stuff.detail.required')}
</div>
{managementState?.tempFlg === '0' ? (
<>
<div className="left-unit-box">
<Link href="/management/stuff" scroll={false}>
<button type="button" className="btn-origin grey mr5">
{getMessage('stuff.detail.btn.moveList')}
</button>
</Link>
<Button type="submit" className="btn-origin navy mr5" style={{ display: showButton }}>
{getMessage('stuff.detail.btn.save')}
</Button>
<Button type="button" className="btn-origin grey" onPress={onDelete} style={{ display: showButton }}>
{getMessage('stuff.detail.btn.delete')}
</Button>
</div>
</>
) : (
<>
<div className="left-unit-box">
{!isFormValid ? (
<Button className="btn-origin grey mr5" onPress={onTempSave} style={{ display: showButton }}>
{getMessage('stuff.detail.btn.tempSave')}
</Button>
) : (
<Button type="submit" className="btn-origin navy mr5" style={{ display: showButton }}>
{getMessage('stuff.detail.btn.save')}
</Button>
)}
<Link href="/management/stuff" scroll={false}>
<button type="button" className="btn-origin grey">
{getMessage('stuff.detail.btn.moveList')}
</button>
</Link>
</div>
</>
)}
</div> </div>
<div className="infomation-wrap"> <div className="infomation-wrap">
<div className="infomation-table"> <div className="infomation-table">
@ -1969,12 +2067,13 @@ export default function StuffDetail() {
onClick={() => { onClick={() => {
form.setValue('planReqNo', '') form.setValue('planReqNo', '')
}} }}
style={{ display: showButton }}
></button> ></button>
) : null} ) : null}
</div> </div>
{managementState?.tempFlg === '1' ? ( {managementState?.tempFlg === '1' ? (
<> <>
<Button className="btn-origin grey" onPress={onSearchDesignRequestPopOpen}> <Button className="btn-origin grey" onPress={onSearchDesignRequestPopOpen} style={{ display: showButton }}>
{getMessage('stuff.planReqPopup.title')} {getMessage('stuff.planReqPopup.title')}
</Button> </Button>
</> </>
@ -2230,7 +2329,7 @@ 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" disabled value={form.watch('zipNo') || ''} /> <input type="text" className="input-light" disabled value={form.watch('zipNo') || ''} />
</div> </div>
<Button className="btn-origin grey" onPress={onSearchPostNumberPopOpen}> <Button className="btn-origin grey" onPress={onSearchPostNumberPopOpen} style={{ display: showButton }}>
{getMessage('stuff.detail.btn.addressPop')} {getMessage('stuff.detail.btn.addressPop')}
</Button> </Button>
<div className="guide">{getMessage('stuff.detail.btn.addressPop.guide')}</div> <div className="guide">{getMessage('stuff.detail.btn.addressPop.guide')}</div>
@ -2325,7 +2424,7 @@ export default function StuffDetail() {
></Select> ></Select>
</div> </div>
<span className="mr10">{getMessage('stuff.detail.standardWindSpeedIdSpan')}</span> <span className="mr10">{getMessage('stuff.detail.standardWindSpeedIdSpan')}</span>
<Button type="button" className="btn-origin grey" onPress={onSearchWindSpeedPopOpen}> <Button type="button" className="btn-origin grey" onPress={onSearchWindSpeedPopOpen} style={{ display: showButton }}>
{getMessage('stuff.detail.btn.windSpeedPop')} {getMessage('stuff.detail.btn.windSpeedPop')}
</Button> </Button>
</div> </div>
@ -2485,7 +2584,7 @@ export default function StuffDetail() {
</div> </div>
<div className="information-grid"> <div className="information-grid">
<div className="q-grid no-cols"> <div className="q-grid no-cols">
<StuffPlanQGrid {...planGridProps} /> <StuffPlanQGrid {...planGridProps} getCellDoubleClicked={getCellDoubleClicked} />
</div> </div>
</div> </div>
{/* 진짜R 플랜끝 */} {/* 진짜R 플랜끝 */}
@ -2495,10 +2594,10 @@ export default function StuffDetail() {
{getMessage('stuff.detail.btn.moveList')} {getMessage('stuff.detail.btn.moveList')}
</button> </button>
</Link> </Link>
<Button type="submit" className="btn-origin navy mr5"> <Button type="submit" className="btn-origin navy mr5" style={{ display: showButton }}>
{getMessage('stuff.detail.btn.save')} {getMessage('stuff.detail.btn.save')}
</Button> </Button>
<Button type="button" className="btn-origin grey" onPress={onDelete}> <Button type="button" className="btn-origin grey" onPress={onDelete} style={{ display: showButton }}>
{getMessage('stuff.detail.btn.delete')} {getMessage('stuff.detail.btn.delete')}
</Button> </Button>
</div> </div>
@ -2507,11 +2606,11 @@ export default function StuffDetail() {
<> <>
<div className="sub-right-footer"> <div className="sub-right-footer">
{!isFormValid ? ( {!isFormValid ? (
<Button className="btn-origin grey mr5" onPress={onTempSave}> <Button className="btn-origin grey mr5" onPress={onTempSave} style={{ display: showButton }}>
{getMessage('stuff.detail.btn.tempSave')} {getMessage('stuff.detail.btn.tempSave')}
</Button> </Button>
) : ( ) : (
<Button type="submit" className="btn-origin navy mr5"> <Button type="submit" className="btn-origin navy mr5" style={{ display: showButton }}>
{getMessage('stuff.detail.btn.save')} {getMessage('stuff.detail.btn.save')}
</Button> </Button>
)} )}

View File

@ -3,19 +3,53 @@
import { useContext } from 'react' import { useContext } from 'react'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { ManagementContext } from '@/app/management/ManagementProvider' import { GlobalDataContext } from '@/app/GlobalDataProvider'
// import { ManagementContext } from '@/app/management/ManagementProvider'
export default function StuffHeader() { export default function StuffHeader() {
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { managementState } = useContext(ManagementContext) const { managementState } = useContext(GlobalDataContext)
// //
// const copyObjectNo = async (objectNo) => {
// await navigator.clipboard.writeText(objectNo)
// alert(getMessage('stuff.detail.header.successCopy'))
// try {
// } catch (error) {
// alert(getMessage('stuff.detail.header.failCopy'))
// }
// }
const copyObjectNo = async (objectNo) => { const copyObjectNo = async (objectNo) => {
await navigator.clipboard.writeText(objectNo) if (navigator.clipboard && window.isSecureContext) {
alert(getMessage('stuff.detail.header.successCopy')) await navigator.clipboard
try { .writeText(objectNo)
} catch (error) { .then(() => {
alert(getMessage('stuff.detail.header.failCopy')) alert(getMessage('stuff.detail.header.successCopy'))
})
.catch(() => {
alert(getMessage('stuff.detail.header.failCopy'))
})
} else {
// Use the 'out of viewport hidden text area' trick
const textArea = document.createElement('textArea')
textArea.value = objectNo
// Move textarea out of the viewport so it's not visible
textArea.style.position = 'absolute'
textArea.style.left = '-999999px'
document.body.prepend(textArea)
textArea.select()
try {
document.execCommand('copy')
alert(getMessage('stuff.detail.header.successCopy'))
} catch (err) {
alert(getMessage('stuff.detail.header.failCopy'))
} finally {
textArea.remove()
}
} }
} }

View File

@ -27,13 +27,10 @@ export default function StuffPlanQGrid(props) {
planGridData ? setRowData(planGridData) : '' planGridData ? setRowData(planGridData) : ''
}, [planGridData]) }, [planGridData])
// const onGridReady = useCallback( //
// (params) => { const onCellDoubleClicked = useCallback((params) => {
// setGridApi(params.api) props.getCellDoubleClicked(params)
// planGridData ? setRowData(planGridData) : '' }, [])
// },
// [planGridData],
// )
return ( return (
<div className="ag-theme-quartz" style={{ height: '100%' }}> <div className="ag-theme-quartz" style={{ height: '100%' }}>
@ -47,6 +44,7 @@ export default function StuffPlanQGrid(props) {
pagination={isPageable} pagination={isPageable}
domLayout="autoHeight" domLayout="autoHeight"
suppressCellFocus={true} suppressCellFocus={true}
onCellDoubleClicked={onCellDoubleClicked}
overlayNoRowsTemplate={`<span className="ag-overlay-loading-center">${getMessage('stuff.grid.noData')}</span>`} overlayNoRowsTemplate={`<span className="ag-overlay-loading-center">${getMessage('stuff.grid.noData')}</span>`}
/> />
</div> </div>

View File

@ -86,165 +86,164 @@ export default function StuffSearchCondition() {
if (stuffSearch.code === 'S') { if (stuffSearch.code === 'S') {
if (stuffSearch.pageNo !== 1) { if (stuffSearch.pageNo !== 1) {
setStuffSearch({ setStuffSearch({
schObjectNo: objectNo ? objectNo : stuffSearch.schObjectNo, schObjectNo: objectNo ? objectNo.trim() : stuffSearch.schObjectNo.trim(),
schSaleStoreName: saleStoreName ? saleStoreName : '', schSaleStoreName: saleStoreName ? saleStoreName.trim() : '',
schAddress: address ? address : '', schAddress: address ? address.trim() : '',
schObjectName: objectName ? objectName : '', schObjectName: objectName ? objectName.trim() : '',
schDispCompanyName: dispCompanyName ? dispCompanyName : '', schDispCompanyName: dispCompanyName ? dispCompanyName.trim() : '',
schSelSaleStoreId: stuffSearch?.schSelSaleStoreId ? stuffSearch.schSelSaleStoreId : '', schSelSaleStoreId: stuffSearch?.schSelSaleStoreId ? stuffSearch.schSelSaleStoreId : '',
schOtherSelSaleStoreId: stuffSearch?.schOtherSelSaleStoreId ? stuffSearch.schOtherSelSaleStoreId : '', schOtherSelSaleStoreId: stuffSearch?.schOtherSelSaleStoreId ? stuffSearch.schOtherSelSaleStoreId : '',
schReceiveUser: receiveUser ? receiveUser : '', schReceiveUser: receiveUser ? receiveUser.trim() : '',
schDateType: dateType, schDateType: dateType,
schFromDt: startDate ? dayjs(startDate).format('YYYY-MM-DD') : '', schFromDt: startDate ? dayjs(startDate).format('YYYY-MM-DD') : '',
schToDt: endDate ? dayjs(endDate).format('YYYY-MM-DD') : '', schToDt: endDate ? dayjs(endDate).format('YYYY-MM-DD') : '',
code: 'E', code: 'E',
startRow: 1, startRow: 1,
endRow: 1 * stuffSearch?.pageSize, endRow: 1 * stuffSearch?.pageSize,
schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'R', schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'U',
pageNo: 1, pageNo: 1,
pageSize: stuffSearch?.pageSize, pageSize: stuffSearch?.pageSize,
}) })
} else { } else {
setStuffSearch({ setStuffSearch({
schObjectNo: objectNo ? objectNo : stuffSearch.schObjectNo, schObjectNo: objectNo ? objectNo.trim() : stuffSearch.schObjectNo.trim(),
schSaleStoreName: saleStoreName ? saleStoreName : '', schSaleStoreName: saleStoreName ? saleStoreName.trim() : '',
schAddress: address ? address : '', schAddress: address ? address.trim() : '',
schObjectName: objectName ? objectName : '', schObjectName: objectName ? objectName.trim() : '',
schDispCompanyName: dispCompanyName ? dispCompanyName : '', schDispCompanyName: dispCompanyName ? dispCompanyName.trim() : '',
schSelSaleStoreId: stuffSearch?.schSelSaleStoreId ? stuffSearch.schSelSaleStoreId : '', schSelSaleStoreId: stuffSearch?.schSelSaleStoreId ? stuffSearch.schSelSaleStoreId : '',
schOtherSelSaleStoreId: stuffSearch?.schOtherSelSaleStoreId ? stuffSearch.schOtherSelSaleStoreId : '', schOtherSelSaleStoreId: stuffSearch?.schOtherSelSaleStoreId ? stuffSearch.schOtherSelSaleStoreId : '',
schReceiveUser: receiveUser ? receiveUser : '', schReceiveUser: receiveUser ? receiveUser.trim() : '',
schDateType: dateType, schDateType: dateType,
schFromDt: startDate ? dayjs(startDate).format('YYYY-MM-DD') : '', schFromDt: startDate ? dayjs(startDate).format('YYYY-MM-DD') : '',
schToDt: endDate ? dayjs(endDate).format('YYYY-MM-DD') : '', schToDt: endDate ? dayjs(endDate).format('YYYY-MM-DD') : '',
code: 'E', code: 'E',
startRow: stuffSearch?.startRow ? stuffSearch.startRow : 1, startRow: stuffSearch?.startRow ? stuffSearch.startRow : 1,
endRow: stuffSearch?.endRow ? stuffSearch.endRow : 100, endRow: stuffSearch?.endRow ? stuffSearch.endRow : 100,
schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'R', schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'U',
pageNo: stuffSearch?.pageNo, pageNo: stuffSearch?.pageNo,
pageSize: stuffSearch?.pageSize, pageSize: stuffSearch?.pageSize,
}) })
} }
} else if (stuffSearch.code === 'FINISH') { } else if (stuffSearch.code === 'FINISH') {
setStuffSearch({ setStuffSearch({
schObjectNo: objectNo, schObjectNo: objectNo.trim(),
schSaleStoreName: saleStoreName, schSaleStoreName: saleStoreName.trim(),
schAddress: address, schAddress: address.trim(),
schObjectName: objectName, schObjectName: objectName.trim(),
schDispCompanyName: dispCompanyName, schDispCompanyName: dispCompanyName.trim(),
schSelSaleStoreId: schSelSaleStoreId, schSelSaleStoreId: schSelSaleStoreId,
schOtherSelSaleStoreId: otherSaleStoreId, schOtherSelSaleStoreId: otherSaleStoreId,
schReceiveUser: receiveUser, schReceiveUser: receiveUser.trim(),
schDateType: dateType, schDateType: dateType,
schFromDt: startDate ? dayjs(startDate).format('YYYY-MM-DD') : '', schFromDt: startDate ? dayjs(startDate).format('YYYY-MM-DD') : '',
schToDt: endDate ? dayjs(endDate).format('YYYY-MM-DD') : '', schToDt: endDate ? dayjs(endDate).format('YYYY-MM-DD') : '',
code: 'E', code: 'E',
startRow: 1, startRow: 1,
endRow: 100, endRow: 100,
schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'R', schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'U',
}) })
} else if (stuffSearch.code === 'E') { } else if (stuffSearch.code === 'E') {
if (session.storeId !== 'T01' && session.storeLvl === '1') { if (session.storeId !== 'T01' && session.storeLvl === '1') {
setStuffSearch({ setStuffSearch({
schObjectNo: stuffSearch?.schObjectNo ? stuffSearch.schObjectNo : objectNo, schObjectNo: stuffSearch?.schObjectNo ? stuffSearch.schObjectNo.trim() : objectNo.trim(),
schSaleStoreName: stuffSearch?.schSaleStoreName ? stuffSearch.schSaleStoreName : saleStoreName, schSaleStoreName: stuffSearch?.schSaleStoreName ? stuffSearch.schSaleStoreName.trim() : saleStoreName.trim(),
schAddress: stuffSearch?.schAddress ? stuffSearch.schAddress : address, schAddress: stuffSearch?.schAddress ? stuffSearch.schAddress.trim() : address.trim(),
schObjectName: stuffSearch?.schObjectName ? stuffSearch.schObjectName : objectName, schObjectName: stuffSearch?.schObjectName ? stuffSearch.schObjectName.trim() : objectName.trim(),
schDispCompanyName: stuffSearch?.schDispCompanyName ? stuffSearch.schDispCompanyName : dispCompanyName, schDispCompanyName: stuffSearch?.schDispCompanyName ? stuffSearch.schDispCompanyName.trim() : dispCompanyName.trim(),
schSelSaleStoreId: otherSaleStoreId ? schSelSaleStoreId : '', schSelSaleStoreId: otherSaleStoreId ? schSelSaleStoreId : '',
schOtherSelSaleStoreId: otherSaleStoreId, schOtherSelSaleStoreId: otherSaleStoreId,
schReceiveUser: stuffSearch?.schReceiveUser ? stuffSearch.schReceiveUser : receiveUser, schReceiveUser: stuffSearch?.schReceiveUser ? stuffSearch.schReceiveUser.trim() : receiveUser.trim(),
schDateType: dateType, schDateType: dateType,
schFromDt: startDate ? dayjs(startDate).format('YYYY-MM-DD') : '', schFromDt: startDate ? dayjs(startDate).format('YYYY-MM-DD') : '',
schToDt: endDate ? dayjs(endDate).format('YYYY-MM-DD') : '', schToDt: endDate ? dayjs(endDate).format('YYYY-MM-DD') : '',
code: 'E', code: 'E',
startRow: stuffSearch?.startRow ? stuffSearch.startRow : 1, startRow: stuffSearch?.startRow ? stuffSearch.startRow : 1,
endRow: stuffSearch?.endRow ? stuffSearch.endRow : 100, endRow: stuffSearch?.endRow ? stuffSearch.endRow : 100,
schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'R', schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'U',
pageNo: stuffSearch?.pageNo, pageNo: stuffSearch?.pageNo,
pageSize: stuffSearch?.pageSize, pageSize: stuffSearch?.pageSize,
}) })
} else if (session.storeId === 'T01') { } else if (session.storeId === 'T01') {
if (stuffSearch.pageNo !== 1) { if (stuffSearch.pageNo !== 1) {
setStuffSearch({ setStuffSearch({
schObjectNo: objectNo ? objectNo : stuffSearch.schObjectNo, schObjectNo: objectNo ? objectNo.trim() : stuffSearch.schObjectNo.trim(),
schSaleStoreName: saleStoreName ? saleStoreName : '', schSaleStoreName: saleStoreName ? saleStoreName.trim() : '',
schAddress: address ? address : '', schAddress: address ? address.trim() : '',
schObjectName: objectName ? objectName : '', schObjectName: objectName ? objectName.trim() : '',
schDispCompanyName: dispCompanyName ? dispCompanyName : '', schDispCompanyName: dispCompanyName ? dispCompanyName.trim() : '',
schSelSaleStoreId: stuffSearch?.schSelSaleStoreId ? stuffSearch.schSelSaleStoreId : '', schSelSaleStoreId: stuffSearch?.schSelSaleStoreId ? stuffSearch.schSelSaleStoreId : '',
schOtherSelSaleStoreId: stuffSearch?.schOtherSelSaleStoreId ? stuffSearch.schOtherSelSaleStoreId : '', schOtherSelSaleStoreId: stuffSearch?.schOtherSelSaleStoreId ? stuffSearch.schOtherSelSaleStoreId : '',
schReceiveUser: receiveUser ? receiveUser : '', schReceiveUser: receiveUser ? receiveUser.trim() : '',
schDateType: dateType, schDateType: dateType,
schFromDt: startDate ? dayjs(startDate).format('YYYY-MM-DD') : '', schFromDt: startDate ? dayjs(startDate).format('YYYY-MM-DD') : '',
schToDt: endDate ? dayjs(endDate).format('YYYY-MM-DD') : '', schToDt: endDate ? dayjs(endDate).format('YYYY-MM-DD') : '',
code: 'E', code: 'E',
startRow: 1, startRow: 1,
endRow: 1 * stuffSearch?.pageSize, endRow: 1 * stuffSearch?.pageSize,
// schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'R', schSortType: 'U',
schSortType: 'R',
pageNo: 1, pageNo: 1,
pageSize: stuffSearch?.pageSize, pageSize: stuffSearch?.pageSize,
}) })
} else { } else {
setStuffSearch({ setStuffSearch({
schObjectNo: stuffSearch?.schObjectNo ? stuffSearch.schObjectNo : objectNo, schObjectNo: stuffSearch?.schObjectNo ? stuffSearch.schObjectNo.trim() : objectNo.trim(),
schSaleStoreName: stuffSearch?.schSaleStoreName ? stuffSearch.schSaleStoreName : saleStoreName, schSaleStoreName: stuffSearch?.schSaleStoreName ? stuffSearch.schSaleStoreName.trim() : saleStoreName.trim(),
schAddress: stuffSearch?.schAddress ? stuffSearch.schAddress : address, schAddress: stuffSearch?.schAddress ? stuffSearch.schAddress.trim() : address.trim(),
schObjectName: stuffSearch?.schObjectName ? stuffSearch.schObjectName : objectName, schObjectName: stuffSearch?.schObjectName ? stuffSearch.schObjectName.trim() : objectName.trim(),
schDispCompanyName: stuffSearch?.schDispCompanyName ? stuffSearch.schDispCompanyName : dispCompanyName, schDispCompanyName: stuffSearch?.schDispCompanyName ? stuffSearch.schDispCompanyName.trim() : dispCompanyName.trim(),
schSelSaleStoreId: schSelSaleStoreId, schSelSaleStoreId: schSelSaleStoreId,
schOtherSelSaleStoreId: otherSaleStoreId, schOtherSelSaleStoreId: otherSaleStoreId,
schReceiveUser: stuffSearch?.schReceiveUser ? stuffSearch.schReceiveUser : receiveUser, schReceiveUser: stuffSearch?.schReceiveUser ? stuffSearch.schReceiveUser.trim() : receiveUser.trim(),
schDateType: dateType, schDateType: dateType,
schFromDt: startDate ? dayjs(startDate).format('YYYY-MM-DD') : '', schFromDt: startDate ? dayjs(startDate).format('YYYY-MM-DD') : '',
schToDt: endDate ? dayjs(endDate).format('YYYY-MM-DD') : '', schToDt: endDate ? dayjs(endDate).format('YYYY-MM-DD') : '',
code: 'E', code: 'E',
startRow: stuffSearch?.startRow ? stuffSearch.startRow : 1, startRow: stuffSearch?.startRow ? stuffSearch.startRow : 1,
endRow: stuffSearch?.endRow ? stuffSearch.endRow : 100, endRow: stuffSearch?.endRow ? stuffSearch.endRow : 100,
schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'R', schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'U',
pageNo: stuffSearch?.pageNo, pageNo: stuffSearch?.pageNo,
pageSize: stuffSearch?.pageSize, pageSize: stuffSearch?.pageSize,
}) })
} }
} else { } else {
setStuffSearch({ setStuffSearch({
schObjectNo: stuffSearch?.schObjectNo ? stuffSearch.schObjectNo : objectNo, schObjectNo: stuffSearch?.schObjectNo ? stuffSearch.schObjectNo.trim() : objectNo.trim(),
schSaleStoreName: stuffSearch?.schSaleStoreName ? stuffSearch.schSaleStoreName : saleStoreName, schSaleStoreName: stuffSearch?.schSaleStoreName ? stuffSearch.schSaleStoreName.trim() : saleStoreName.trim(),
schAddress: stuffSearch?.schAddress ? stuffSearch.schAddress : address, schAddress: stuffSearch?.schAddress ? stuffSearch.schAddress.trim() : address.trim(),
schObjectName: stuffSearch?.schObjectName ? stuffSearch.schObjectName : objectName, schObjectName: stuffSearch?.schObjectName ? stuffSearch.schObjectName.trim() : objectName.trim(),
schDispCompanyName: stuffSearch?.schDispCompanyName ? stuffSearch.schDispCompanyName : dispCompanyName, schDispCompanyName: stuffSearch?.schDispCompanyName ? stuffSearch.schDispCompanyName.trim() : dispCompanyName.trim(),
schSelSaleStoreId: schSelSaleStoreId, schSelSaleStoreId: schSelSaleStoreId,
schOtherSelSaleStoreId: otherSaleStoreId, schOtherSelSaleStoreId: otherSaleStoreId,
schReceiveUser: stuffSearch?.schReceiveUser ? stuffSearch.schReceiveUser : receiveUser, schReceiveUser: stuffSearch?.schReceiveUser ? stuffSearch.schReceiveUser.trim() : receiveUser.trim(),
schDateType: dateType, schDateType: dateType,
schFromDt: startDate ? dayjs(startDate).format('YYYY-MM-DD') : '', schFromDt: startDate ? dayjs(startDate).format('YYYY-MM-DD') : '',
schToDt: endDate ? dayjs(endDate).format('YYYY-MM-DD') : '', schToDt: endDate ? dayjs(endDate).format('YYYY-MM-DD') : '',
code: 'E', code: 'E',
startRow: stuffSearch?.startRow ? stuffSearch.startRow : 1, startRow: stuffSearch?.startRow ? stuffSearch.startRow : 1,
endRow: stuffSearch?.endRow ? stuffSearch.endRow : 100, endRow: stuffSearch?.endRow ? stuffSearch.endRow : 100,
schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'R', schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'U',
pageNo: stuffSearch?.pageNo, pageNo: stuffSearch?.pageNo,
pageSize: stuffSearch?.pageSize, pageSize: stuffSearch?.pageSize,
}) })
} }
} else { } else {
setStuffSearch({ setStuffSearch({
schObjectNo: objectNo, schObjectNo: objectNo.trim(),
schSaleStoreName: saleStoreName, schSaleStoreName: saleStoreName.trim(),
schAddress: address, schAddress: address.trim(),
schObjectName: objectName, schObjectName: objectName.trim(),
schDispCompanyName: dispCompanyName, schDispCompanyName: dispCompanyName.trim(),
schSelSaleStoreId: schSelSaleStoreId, schSelSaleStoreId: schSelSaleStoreId,
schOtherSelSaleStoreId: otherSaleStoreId, schOtherSelSaleStoreId: otherSaleStoreId,
schReceiveUser: receiveUser, schReceiveUser: receiveUser.trim(),
schDateType: dateType, schDateType: dateType,
schFromDt: startDate ? dayjs(startDate).format('YYYY-MM-DD') : '', schFromDt: startDate ? dayjs(startDate).format('YYYY-MM-DD') : '',
schToDt: endDate ? dayjs(endDate).format('YYYY-MM-DD') : '', schToDt: endDate ? dayjs(endDate).format('YYYY-MM-DD') : '',
code: 'E', code: 'E',
startRow: stuffSearch?.startRow ? stuffSearch.startRow : 1, startRow: stuffSearch?.startRow ? stuffSearch.startRow : 1,
endRow: stuffSearch?.endRow ? stuffSearch.endRow : 100, endRow: stuffSearch?.endRow ? stuffSearch.endRow : 100,
schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'R', schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'U',
pageNo: stuffSearch?.pageNo, pageNo: stuffSearch?.pageNo,
pageSize: stuffSearch?.pageSize, pageSize: stuffSearch?.pageSize,
}) })
@ -288,10 +287,9 @@ export default function StuffSearchCondition() {
schDateType: 'U', schDateType: 'U',
startRow: 1, startRow: 1,
endRow: 100, endRow: 100,
schSortType: 'R', schSortType: 'U',
pageNo: 1, pageNo: 1,
pageSize: 100, pageSize: 100,
// code: 'S',
}) })
} else { } else {
if (otherSaleStoreList.length > 1) { if (otherSaleStoreList.length > 1) {
@ -307,7 +305,7 @@ export default function StuffSearchCondition() {
stuffSearch.startRow = 1 stuffSearch.startRow = 1
stuffSearch.endRow = 100 stuffSearch.endRow = 100
stuffSearch.schSortType = 'R' stuffSearch.schSortType = 'U'
stuffSearch.pageNo = 1 stuffSearch.pageNo = 1
stuffSearch.pageSize = 100 stuffSearch.pageSize = 100
} else { } else {
@ -321,7 +319,7 @@ export default function StuffSearchCondition() {
stuffSearch.startRow = 1 stuffSearch.startRow = 1
stuffSearch.endRow = 100 stuffSearch.endRow = 100
stuffSearch.schSortType = 'R' stuffSearch.schSortType = 'U'
stuffSearch.pageNo = 1 stuffSearch.pageNo = 1
stuffSearch.pageSize = 100 stuffSearch.pageSize = 100
} }
@ -347,6 +345,8 @@ export default function StuffSearchCondition() {
get({ url: url }).then((res) => { get({ url: url }).then((res) => {
if (!isEmptyArray(res)) { if (!isEmptyArray(res)) {
res.map((row) => { res.map((row) => {
//#399
row.saleStoreName = row.saleStoreName + ' - ' + row.saleStoreId
row.value = row.saleStoreId row.value = row.saleStoreId
row.label = row.saleStoreName row.label = row.saleStoreName
}) })
@ -367,6 +367,8 @@ export default function StuffSearchCondition() {
get({ url: url }).then((res) => { get({ url: url }).then((res) => {
if (!isEmptyArray(res)) { if (!isEmptyArray(res)) {
res.map((row) => { res.map((row) => {
//#399
row.saleStoreName = row.saleStoreName + ' - ' + row.saleStoreId
row.value = row.saleStoreId row.value = row.saleStoreId
row.label = row.saleStoreName row.label = row.saleStoreName
}) })
@ -456,6 +458,8 @@ export default function StuffSearchCondition() {
get({ url: url }).then((res) => { get({ url: url }).then((res) => {
if (!isEmptyArray(res)) { if (!isEmptyArray(res)) {
res.map((row) => { res.map((row) => {
//#399
row.saleStoreName = row.saleStoreName + ' - ' + row.saleStoreId
row.value = row.saleStoreId row.value = row.saleStoreId
row.label = row.saleStoreName row.label = row.saleStoreName
}) })
@ -474,6 +478,10 @@ export default function StuffSearchCondition() {
if (stuffSearch.code === 'S') { if (stuffSearch.code === 'S') {
stuffSearch.schSelSaleStoreId = '' stuffSearch.schSelSaleStoreId = ''
stuffSearch.schOtherSelSaleStoreId = '' stuffSearch.schOtherSelSaleStoreId = ''
} else if (stuffSearch.code === 'E') {
//#401
stuffSearch.schSelSaleStoreId = ''
stuffSearch.schOtherSelSaleStoreId = ''
} }
//2 //2
@ -540,7 +548,7 @@ export default function StuffSearchCondition() {
stuffSearch.endRow = 100 stuffSearch.endRow = 100
stuffSearch.schSelSaleStoreId = '' stuffSearch.schSelSaleStoreId = ''
stuffSearch.schOtherSelSaleStoreId = '' stuffSearch.schOtherSelSaleStoreId = ''
stuffSearch.schSortType = 'R' stuffSearch.schSortType = 'U'
stuffSearch.pageNo = 1 stuffSearch.pageNo = 1
stuffSearch.pageSize = 100 stuffSearch.pageSize = 100
@ -572,7 +580,7 @@ export default function StuffSearchCondition() {
stuffSearch.endRow = 100 stuffSearch.endRow = 100
stuffSearch.schSelSaleStoreId = '' stuffSearch.schSelSaleStoreId = ''
stuffSearch.schOtherSelSaleStoreId = '' stuffSearch.schOtherSelSaleStoreId = ''
stuffSearch.schSortType = 'R' stuffSearch.schSortType = 'U'
stuffSearch.pageNo = 1 stuffSearch.pageNo = 1
stuffSearch.pageSize = 100 stuffSearch.pageSize = 100
setSchSelSaleStoreId('') setSchSelSaleStoreId('')

View File

@ -13,7 +13,8 @@ import { useMessage } from '@/hooks/useMessage'
import { floorPlanObjectState } from '@/store/floorPlanObjectAtom' import { floorPlanObjectState } from '@/store/floorPlanObjectAtom'
import { isObjectNotEmpty, queryStringFormatter } from '@/util/common-utils' import { isObjectNotEmpty, queryStringFormatter } from '@/util/common-utils'
import { ManagementContext } from '@/app/management/ManagementProvider' import { GlobalDataContext } from '@/app/GlobalDataProvider'
// import { ManagementContext } from '@/app/management/ManagementProvider'
import { SessionContext } from '@/app/SessionProvider' import { SessionContext } from '@/app/SessionProvider'
export default function StuffSubHeader({ type }) { export default function StuffSubHeader({ type }) {
@ -25,7 +26,7 @@ export default function StuffSubHeader({ type }) {
const { isGlobalLoading } = useContext(QcastContext) const { isGlobalLoading } = useContext(QcastContext)
const { managementState } = useContext(ManagementContext) const { managementState } = useContext(GlobalDataContext)
const [buttonStyle, setButtonStyle] = useState('') const [buttonStyle, setButtonStyle] = useState('')
useEffect(() => { useEffect(() => {

View File

@ -87,7 +87,7 @@ export default function PlanRequestPop(props) {
const onSubmit = (page, type) => { const onSubmit = (page, type) => {
//2 201X112 //2 201X112
const params = { const params = {
// saleStoreId: 'T100', // saleStoreId: 'X112',
// saleStoreLevel: '1', // saleStoreLevel: '1',
saleStoreId: props?.otherSaleStoreId ? props.otherSaleStoreId : props.saleStoreId, saleStoreId: props?.otherSaleStoreId ? props.otherSaleStoreId : props.saleStoreId,
saleStoreLevel: props?.otherSaleStoreLevel ? props.otherSaleStoreLevel : props.saleStoreLevel, saleStoreLevel: props?.otherSaleStoreLevel ? props.otherSaleStoreLevel : props.saleStoreLevel,

View File

@ -0,0 +1,115 @@
import { Card, CardBody, Input, Tab, Tabs } from '@nextui-org/react'
import { useEffect, useReducer } from 'react'
const reducer = (prevState, nextState) => {
return { ...prevState, ...nextState }
}
const defaultData = {
commonData: 'common',
tabs: [
{
id: 1,
name: 'tab1',
range: 10,
maker: 'maker1',
law: 'law1',
basis: 'basis1',
},
{
id: 2,
name: 'tab2',
range: 20,
maker: 'maker2',
law: 'law2',
basis: 'basis2',
},
{
id: 3,
name: 'tab3',
range: 30,
maker: 'maker3',
law: 'law3',
basis: 'basis3',
},
{
id: 4,
name: 'tab4',
range: 40,
maker: 'maker4',
law: 'law4',
basis: 'basis4',
},
],
}
export default function SampleReducer() {
const [sampleState, setSampleState] = useReducer(reducer, defaultData)
const handleChangeTabsData = (newTab) => {
const newTabs = sampleState.tabs.map((t) => {
if (t.id === newTab.id) {
return newTab
} else {
return t
}
})
setSampleState({ tabs: newTabs })
}
useEffect(() => {
console.log('🚀 ~ SampleReducer ~ sampleState:', sampleState)
}, [sampleState])
return (
<>
<div>공통: {sampleState.commonData}</div>
<div className="flex w-full flex-col">
<Tabs aria-label="Options">
{sampleState.tabs.map((s) => (
<Tab key={s.id} title={s.name}>
<Card>
<CardBody>
<div className="flex w-full flex-wrap md:flex-nowrap gap-4">
<Input
label="range"
type="text"
value={s.range}
onChange={(e) => {
handleChangeTabsData({ ...s, range: e.target.value })
}}
/>
<Input
label="maker"
type="text"
value={s.maker}
onChange={(e) => {
handleChangeTabsData({ ...s, maker: e.target.value })
}}
/>
<Input
label="law"
type="text"
value={s.law}
onChange={(e) => {
handleChangeTabsData({ ...s, law: e.target.value })
}}
/>
<Input
label="basis"
type="text"
value={s.basis}
onChange={(e) => {
handleChangeTabsData({ ...s, basis: e.target.value })
}}
/>
</div>
</CardBody>
</Card>
</Tab>
))}
</Tabs>
</div>
</>
)
}

View File

@ -2,12 +2,13 @@ import { useEffect } from 'react'
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil' import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
import { basicSettingState, roofDisplaySelector, settingModalFirstOptionsState } from '@/store/settingAtom' import { basicSettingState, roofDisplaySelector, settingModalFirstOptionsState } from '@/store/settingAtom'
import { canvasState, dotLineGridSettingState, pitchText, pitchTextSelector, showAngleUnitSelector } from '@/store/canvasAtom' import { canvasState, dotLineGridSettingState, pitchText, pitchTextSelector, showAngleUnitSelector } from '@/store/canvasAtom'
import { getChonByDegree, getDegreeByChon, setSurfaceShapePattern } from '@/util/canvas-util' import { getChonByDegree, getDegreeByChon } from '@/util/canvas-util'
import { useFont } from '@/hooks/common/useFont' import { useFont } from '@/hooks/common/useFont'
import { useGrid } from '@/hooks/common/useGrid' import { useGrid } from '@/hooks/common/useGrid'
import { globalFontAtom } from '@/store/fontAtom' import { globalFontAtom } from '@/store/fontAtom'
import { useRoof } from '@/hooks/common/useRoof' import { useRoof } from '@/hooks/common/useRoof'
import { usePolygon } from '@/hooks/usePolygon' import { usePolygon } from '@/hooks/usePolygon'
import { useRoofFn } from '@/hooks/common/useRoofFn'
export function useCanvasConfigInitialize() { export function useCanvasConfigInitialize() {
const canvas = useRecoilValue(canvasState) const canvas = useRecoilValue(canvasState)
@ -18,6 +19,7 @@ export function useCanvasConfigInitialize() {
const setDotLineGridSetting = useSetRecoilState(dotLineGridSettingState) const setDotLineGridSetting = useSetRecoilState(dotLineGridSettingState)
const pitchText = useRecoilValue(pitchTextSelector) const pitchText = useRecoilValue(pitchTextSelector)
const angleUnit = useRecoilValue(showAngleUnitSelector) const angleUnit = useRecoilValue(showAngleUnitSelector)
const { setSurfaceShapePattern } = useRoofFn()
const {} = useFont() const {} = useFont()
const {} = useGrid() const {} = useGrid()
const {} = useRoof() const {} = useRoof()
@ -63,6 +65,7 @@ export function useCanvasConfigInitialize() {
roofInit() //화면표시 초기화 roofInit() //화면표시 초기화
groupDimensionInit() groupDimensionInit()
reGroupInit() //그룹 객체 재그룹 reGroupInit() //그룹 객체 재그룹
moduleInit()
} }
const gridInit = () => { const gridInit = () => {
@ -194,5 +197,19 @@ export function useCanvasConfigInitialize() {
}) })
} }
const moduleInit = () => {
canvas
.getObjects()
.filter((obj) => obj.name === 'module')
.forEach((obj) => {
obj.set({
selectable: true,
lockMovementX: false,
lockMovementY: false,
})
obj.setViewLengthText(false)
})
}
return { canvasLoadInit, gridInit } return { canvasLoadInit, gridInit }
} }

View File

@ -39,10 +39,6 @@ export const useCommonCode = () => {
return resultCodes return resultCodes
} }
useEffect(() => {
findCommonCode()
}, [globalLocale])
useEffect(() => { useEffect(() => {
const getCommonCode = async () => { const getCommonCode = async () => {
await promiseGet({ url: '/api/commcode/qc-comm-code' }).then((res) => { await promiseGet({ url: '/api/commcode/qc-comm-code' }).then((res) => {

View File

@ -603,8 +603,12 @@ export function useCommonUtils() {
} }
const deleteObject = () => { const deleteObject = () => {
const obj = canvas?.getActiveObject() const selectedObj = canvas?.getActiveObjects()
commonDeleteText(obj) if (selectedObj) {
selectedObj.forEach((obj) => {
commonDeleteText(obj)
})
}
} }
const moveObject = () => { const moveObject = () => {

View File

@ -28,11 +28,7 @@ export function useGrid() {
// 1. 점.선 그리드 설정으로 만들어진 기존 오브젝트 제거 // 1. 점.선 그리드 설정으로 만들어진 기존 오브젝트 제거
canvas canvas
?.getObjects() ?.getObjects()
.filter((obj) => obj.name === 'lineGrid') .filter((obj) => ['lineGrid', 'dotGrid'].includes(obj.name))
.forEach((obj) => canvas?.remove(obj))
canvas
?.getObjects()
.filter((obj) => obj.name === 'dotGrid')
.forEach((obj) => canvas?.remove(obj)) .forEach((obj) => canvas?.remove(obj))
//const horizontalInterval = interval.horizontalInterval //const horizontalInterval = interval.horizontalInterval
@ -181,8 +177,17 @@ export function useGrid() {
canvas.renderAll() canvas.renderAll()
} }
const removeGrid = () => {
canvas
.getObjects()
.filter((obj) => ['lineGrid', 'dotGrid', 'tempGrid'].includes(obj.name))
.forEach((obj) => canvas.remove(obj))
canvas.renderAll()
}
return { return {
move, move,
copy, copy,
removeGrid,
} }
} }

View File

@ -2,7 +2,7 @@ import { useAxios } from '@/hooks/useAxios'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { useSwal } from '@/hooks/useSwal' import { useSwal } from '@/hooks/useSwal'
import { getQueryString } from '@/util/common-utils' import { getQueryString } from '@/util/common-utils'
import axios from 'axios' import { trestleRequest, constructionRequest, trestleDetailRequest } from '@/models/apiModels'
/** /**
* 마스터 컨트롤러 * 마스터 컨트롤러
@ -26,16 +26,15 @@ export function useMasterController() {
/** /**
* 모듈 타입별 아이템 목록 조회 * 모듈 타입별 아이템 목록 조회
* @param {지붕재 코드} roofMatlCd * @param {지붕재 코드 목록} arrRoofMatlCd
* @returns * @returns
*/ */
const getModuleTypeItemList = async (roofMatlCd) => { const getModuleTypeItemList = async (paramArr) => {
if (!roofMatlCd || roofMatlCd.trim() === '') { if (!Array.isArray(paramArr) || paramArr.length === 0 || paramArr.length > 4 || paramArr.some((item) => !item || item.trim() === '')) {
swalFire({ text: getMessage('master.moduletypeitem.message.error'), type: 'alert', icon: 'error' }) swalFire({ text: getMessage('master.moduletypeitem.message.error'), type: 'alert', icon: 'error' })
return null return null
} }
const param = { roofMatlCd: roofMatlCd } const paramString = `?${paramArr.map((item) => `arrRoofMatlCd=${item}`).join('&')}`
const paramString = getQueryString(param)
return await get({ url: `/api/v1/master/getModuleTypeItemList${paramString}` }).then((res) => { return await get({ url: `/api/v1/master/getModuleTypeItemList${paramString}` }).then((res) => {
console.log('🚀🚀 ~ getModuleTypeItemList ~ res:', res) console.log('🚀🚀 ~ getModuleTypeItemList ~ res:', res)
return res return res
@ -44,11 +43,18 @@ export function useMasterController() {
/** /**
* 가대 목록 조회 * 가대 목록 조회
* @param * @param {모듈타입코드} moduleTpCd
* @param {지붕재코드} roofMatlCd
* @param {서까래기초코드} raftBaseCd
* @param {가대메이커코드} trestleMkrCd
* @param {공법코드} constMthdCd
* @param {지붕기초코드} roofBaseCd
* @returns * @returns
*/ */
const getTrestleList = async (params) => { const getTrestleList = async (params) => {
return await get({ url: `/api/v1/master/getTrestleList/${params}` }).then((res) => { const paramString = getQueryString(params)
console.log('🚀🚀 ~ getTrestleList ~ paramString:', paramString)
return await get({ url: '/api/v1/master/getTrestleList' + paramString }).then((res) => {
console.log('🚀🚀 ~ getTrestleList ~ res:', res) console.log('🚀🚀 ~ getTrestleList ~ res:', res)
return res return res
}) })
@ -56,11 +62,25 @@ export function useMasterController() {
/** /**
* 모듈 시공법 목록 조회 * 모듈 시공법 목록 조회
* @param * @param {모듈타입코드} moduleTpCd
* @param {지붕재코드} roofMatlCd
* @param {가대메이커코드} trestleMkrCd
* @param {공법코드} constMthdCd
* @param {지붕기초코드} roofBaseCd
* @param {면조도} illuminationTp
* @param {설치높이} instHt
* @param {풍속} stdWindSpeed
* @param {적설량} stdSnowLd
* @param {경사도코드} inclCd
* @param {서까래기초코드} raftBaseCd
* @param {하제(망둥어)피치} roofPitch
*
* @returns * @returns
*/ */
const getConstructionList = async (params) => { const getConstructionList = async (params) => {
return await get({ url: `/api/v1/master/getConstructionList/${params}` }).then((res) => { const paramString = getQueryString(params)
console.log('🚀🚀 ~ getConstructionList ~ paramString:', paramString)
return await get({ url: '/api/v1/master/getConstructionList' + paramString }).then((res) => {
console.log('🚀🚀 ~ getConstructionList ~ res:', res) console.log('🚀🚀 ~ getConstructionList ~ res:', res)
return res return res
}) })
@ -68,11 +88,25 @@ export function useMasterController() {
/** /**
* 가대 상세 조회 * 가대 상세 조회
* @param * @param {모듈타입코드} moduleTpCd
* @param {지붕재코드} roofMatlCd
* @param {가대메이커코드} trestleMkrCd
* @param {공법코드} constMthdCd
* @param {지붕기초코드} roofBaseCd
* @param {면조도} illuminationTp
* @param {설치높이} instHt
* @param {풍속} stdWindSpeed
* @param {적설량} stdSnowLd
* @param {경사도코드} inclCd
* @param {시공법} constTp
* @param {혼합모듈번호} mixMatlNo
* @param {하제(망둥어)피치}roofPitch
* @returns * @returns
*/ */
const getTrestleDetailList = async (params) => { const getTrestleDetailList = async (params) => {
return await get({ url: `/api/v1/master/getTrestleDetailList/${params}` }).then((res) => { const paramString = getQueryString(params)
console.log('🚀🚀 ~ getTrestleDetailList ~ paramString:', paramString)
return await get({ url: '/api/v1/master/getTrestleDetailList' + paramString }).then((res) => {
console.log('🚀🚀 ~ getTrestleDetailList ~ res:', res) console.log('🚀🚀 ~ getTrestleDetailList ~ res:', res)
return res return res
}) })

View File

@ -2,12 +2,13 @@ import { canvasState } from '@/store/canvasAtom'
import { allocDisplaySelector, roofDisplaySelector } from '@/store/settingAtom' import { allocDisplaySelector, roofDisplaySelector } from '@/store/settingAtom'
import { useRecoilValue } from 'recoil' import { useRecoilValue } from 'recoil'
import { useEffect } from 'react' import { useEffect } from 'react'
import { useRoofFn } from '@/hooks/common/useRoofFn'
export function useRoof() { export function useRoof() {
const canvas = useRecoilValue(canvasState) const canvas = useRecoilValue(canvasState)
const allocDisplay = useRecoilValue(allocDisplaySelector) const allocDisplay = useRecoilValue(allocDisplaySelector)
const roofDisplay = useRecoilValue(roofDisplaySelector) const roofDisplay = useRecoilValue(roofDisplaySelector)
const { setSurfaceShapePattern } = useRoofFn()
useEffect(() => { useEffect(() => {
if (!canvas) return if (!canvas) return
canvas canvas
@ -23,7 +24,7 @@ export function useRoof() {
canvas.renderAll() canvas.renderAll()
}, [allocDisplay]) }, [allocDisplay])
const setSurfaceShapePattern = (polygon, mode = 'onlyBorder') => { /*const setSurfaceShapePattern = (polygon, mode = 'onlyBorder') => {
const ratio = window.devicePixelRatio || 1 const ratio = window.devicePixelRatio || 1
let width = 265 / 10 let width = 265 / 10
@ -145,7 +146,7 @@ export function useRoof() {
polygon.set('fill', null) polygon.set('fill', null)
polygon.set('fill', pattern) polygon.set('fill', pattern)
polygon.canvas?.renderAll() polygon.canvas?.renderAll()
} }*/
return {} return {}
} }

View File

@ -0,0 +1,170 @@
import { useRecoilValue } from 'recoil'
import { canvasState, currentObjectState } from '@/store/canvasAtom'
import { selectedRoofMaterialSelector } from '@/store/settingAtom'
import { ROOF_MATERIAL_LAYOUT } from '@/components/floor-plan/modal/placementShape/PlacementShapeSetting'
import { POLYGON_TYPE } from '@/common/common'
const ROOF_COLOR = {
0: 'rgb(199,240,213)',
1: 'rgb(178,238,255)',
2: 'rgb(187,204,255)',
3: 'rgb(228,202,255)',
}
export function useRoofFn() {
const canvas = useRecoilValue(canvasState)
const selectedRoofMaterial = useRecoilValue(selectedRoofMaterialSelector)
const currentObject = useRecoilValue(currentObjectState)
//면형상 선택 클릭시 지붕 패턴 입히기
function setSurfaceShapePattern(polygon, mode = 'onlyBorder', trestleMode = false, roofMaterial = selectedRoofMaterial) {
const ratio = window.devicePixelRatio || 1
const layout = roofMaterial.layout
let width = (roofMaterial.width ?? 226) / 10
let height = (roofMaterial.length ?? 158) / 10
const index = roofMaterial.index ?? 0
let roofStyle = 2
const inputPatternSize = { width: width, height: height } //임시 사이즈
const patternSize = { ...inputPatternSize } // 입력된 값을 뒤집기 위해
if (polygon.direction === 'east' || polygon.direction === 'west') {
//세로형이면 width height를 바꿈
;[patternSize.width, patternSize.height] = [inputPatternSize.height, patternSize.width]
}
// 패턴 소스를 위한 임시 캔버스 생성
const patternSourceCanvas = document.createElement('canvas')
patternSourceCanvas.width = polygon.width * ratio
patternSourceCanvas.height = polygon.height * ratio
const ctx = patternSourceCanvas.getContext('2d')
let offset = roofStyle === 1 ? 0 : patternSize.width / 2
const rows = Math.floor(patternSourceCanvas.height / patternSize.height)
const cols = Math.floor(patternSourceCanvas.width / patternSize.width)
ctx.strokeStyle = mode === 'allPainted' ? 'black' : ROOF_COLOR[index]
ctx.lineWidth = 2
ctx.fillStyle = mode === 'allPainted' ? 'rgba(0, 159, 64, 0.7)' : 'white'
if (trestleMode) {
ctx.strokeStyle = 'black'
ctx.lineWidth = 0.2
ctx.fillStyle = 'rgba(0, 0, 0, 0.1)'
} else {
ctx.fillStyle = 'rgba(255, 255, 255, 1)'
}
if (polygon.direction === 'east' || polygon.direction === 'west') {
offset = roofStyle === 1 ? 0 : patternSize.height / 2
for (let col = 0; col <= cols; col++) {
const x = col * patternSize.width
const yStart = 0
const yEnd = patternSourceCanvas.height
ctx.beginPath()
ctx.moveTo(x, yStart) // 선 시작점
ctx.lineTo(x, yEnd) // 선 끝점
ctx.stroke()
if (mode === 'allPainted' || trestleMode) {
ctx.fillRect(x, yStart, patternSize.width, yEnd - yStart)
}
for (let row = 0; row <= rows; row++) {
const y = layout === ROOF_MATERIAL_LAYOUT.STAIRS ? row * patternSize.height + (col % 2 === 0 ? 0 : offset) : row * patternSize.height
const xStart = col * patternSize.width
const xEnd = xStart + patternSize.width
ctx.beginPath()
ctx.moveTo(xStart, y) // 선 시작점
ctx.lineTo(xEnd, y) // 선 끝점
ctx.stroke()
if (mode === 'allPainted' || trestleMode) {
ctx.fillRect(xStart, y, xEnd - xStart, patternSize.height)
}
}
}
} 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()
if (mode === 'allPainted' || trestleMode) {
ctx.fillRect(0, y, patternSourceCanvas.width, patternSize.height)
}
for (let col = 0; col <= cols; col++) {
const x = layout === ROOF_MATERIAL_LAYOUT.STAIRS ? col * patternSize.width + (row % 2 === 0 ? 0 : offset) : col * patternSize.width
const yStart = row * patternSize.height
const yEnd = yStart + patternSize.height
ctx.beginPath()
ctx.moveTo(x, yStart) // 선 시작점
ctx.lineTo(x, yEnd) // 선 끝점
ctx.stroke()
if (mode === 'allPainted' || trestleMode) {
ctx.fillRect(x, yStart, patternSize.width, yEnd - yStart)
}
}
}
}
const hachingPatternSourceCanvas = document.createElement('canvas')
if (mode === 'lineHatch') {
hachingPatternSourceCanvas.width = polygon.width * ratio
hachingPatternSourceCanvas.height = polygon.height * ratio
const ctx1 = hachingPatternSourceCanvas.getContext('2d')
const gap = 10
ctx1.strokeStyle = 'green' // 선 색상
ctx1.lineWidth = 0.3 // 선 두께
for (let x = 0; x < hachingPatternSourceCanvas.width + hachingPatternSourceCanvas.height; x += gap) {
ctx1.beginPath()
ctx1.moveTo(x, 0) // 선 시작점
ctx1.lineTo(0, x) // 선 끝점
ctx1.stroke()
}
}
const combinedPatternCanvas = document.createElement('canvas')
combinedPatternCanvas.width = polygon.width * ratio
combinedPatternCanvas.height = polygon.height * ratio
const combinedCtx = combinedPatternCanvas.getContext('2d')
// 첫 번째 패턴을 그린 후 두 번째 패턴을 덧입힘
combinedCtx.drawImage(patternSourceCanvas, 0, 0)
combinedCtx.drawImage(hachingPatternSourceCanvas, 0, 0)
// 패턴 생성
const pattern = new fabric.Pattern({
source: combinedPatternCanvas,
repeat: 'repeat',
})
polygon.set('fill', null)
polygon.set('fill', pattern)
polygon.roofMaterial = roofMaterial
polygon.canvas?.renderAll()
}
function removeRoofMaterial(roof = currentObject) {
if (roof === null || roof.name !== POLYGON_TYPE.ROOF) {
return
}
roof.set('fill', null)
roof.roofMaterial = null
canvas?.renderAll()
}
function removeAllRoofMaterial() {
const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
roofBases.forEach((roofBase) => {
removeRoofMaterial(roofBase)
})
}
return { setSurfaceShapePattern, removeRoofMaterial, removeAllRoofMaterial }
}

View File

@ -0,0 +1,940 @@
import { BATCH_TYPE, POLYGON_TYPE } from '@/common/common'
import { canvasState } from '@/store/canvasAtom'
import { isOverlap, polygonToTurfPolygon, rectToPolygon } from '@/util/canvas-util'
import { useRecoilValue } from 'recoil'
import { v4 as uuidv4 } from 'uuid'
import * as turf from '@turf/turf'
import { useSwal } from '../useSwal'
import { useModuleBasicSetting } from './useModuleBasicSetting'
import { useMessage } from '../useMessage'
export const MODULE_REMOVE_TYPE = {
LEFT: 'left',
RIGHT: 'right',
HORIZONTAL_SIDE: 'horizontalSide',
TOP: 'top',
BOTTOM: 'bottom',
VERTICAL_SIDE: 'verticalSide',
NONE: 'none',
}
export const MODULE_INSERT_TYPE = {
LEFT: 'left',
RIGHT: 'right',
TOP: 'up',
BOTTOM: 'down',
}
export const MODULE_ALIGN_TYPE = {
VERTICAL: 'vertical',
HORIZONTAL: 'horizontal',
}
export function useModule() {
const canvas = useRecoilValue(canvasState)
const { swalFire } = useSwal()
const { getMessage } = useMessage()
const { checkModuleDisjointObjects } = useModuleBasicSetting()
const moduleMove = (length, direction) => {
const selectedObj = canvas.getActiveObjects() //선택된 객체들을 가져옴
const selectedIds = selectedObj.map((obj) => obj.id) // selectedObj의 ID 추출
canvas.discardActiveObject() //선택해제
const isSetupModules = getOtherModules(selectedObj)
const selectedModules = canvas.getObjects().filter((obj) => selectedIds.includes(obj.id) && obj.name === 'module') //선택했던 객체들만 가져옴
const setupSurface = canvas
.getObjects()
.filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE && obj.id === selectedModules[0].surfaceId)[0]
let isWarning = false
const objects = getObjects()
if (selectedModules) {
selectedModules.forEach((module) => {
const { top, left } = getPosotion(module, direction, length, false)
module.originCoords = {
left: module.left,
top: module.top,
fill: module.fill,
}
module.set({ top, left })
module.setCoords()
if (isOverlapOtherModules(module, isSetupModules) || isOverlapObjects(module, objects) || isOutsideSurface(module, setupSurface)) {
isWarning = true
module.set({ fill: 'red' })
}
canvas.renderAll()
})
if (isWarning) {
swalFire({
title: getMessage('can.not.move.module'),
icon: 'error',
type: 'alert',
confirmFn: () => {
selectedModules.forEach((module) => {
module.set({ left: module.originCoords.left, top: module.originCoords.top, fill: module.originCoords.fill })
module.setCoords()
})
canvas.renderAll()
},
})
}
}
}
const moduleMultiMove = (type, length, direction) => {
if (canvas.getActiveObjects().length === 0) return
if (canvas.getActiveObjects().length > 1) {
swalFire({
title: '여러 개의 모듈을 선택할 수 없습니다.',
icon: 'error',
type: 'alert',
})
canvas.discardActiveObject()
return
}
const activeModule = canvas.getObjects().filter((obj) => canvas.getActiveObjects()[0].id === obj.id)[0]
const modules = type === 'row' ? getRowModules(activeModule) : getColumnModules(activeModule)
const otherModules = getOtherModules(modules)
const objects = getObjects()
const moduleSetupSurface = canvas
.getObjects()
.filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE && obj.id === activeModule.surfaceId)[0]
let isWarning = false
modules.forEach((module) => {
const { top, left } = getPosotion(module, direction, length, false)
module.originPos = {
top: module.top,
left: module.left,
fill: module.fill,
}
module.set({ top, left })
module.setCoords()
canvas.renderAll()
if (otherModules.length > 0) {
if (isOverlapOtherModules(module, otherModules) || isOverlapObjects(module, objects) || isOutsideSurface(module, moduleSetupSurface)) {
isWarning = true
module.set({ fill: 'red' })
}
}
})
canvas.renderAll()
if (isWarning) {
swalFire({
title: getMessage('can.not.move.module'),
icon: 'error',
type: 'alert',
confirmFn: () => {
modules.forEach((module) => {
module.set({ top: module.originPos.top, left: module.originPos.left, fill: module.originPos.fill })
module.setCoords()
})
canvas.renderAll()
},
})
}
}
const moduleMoveAll = (length, direction) => {
const moduleSetupSurface = canvas.getObjects().filter((obj) => canvas.getActiveObjects()[0].id === obj.id)[0]
const modules = canvas.getObjects().filter((obj) => obj.surfaceId === moduleSetupSurface.id && obj.name === POLYGON_TYPE.MODULE)
const objects = getObjects()
let isWarning = false
modules.forEach((module) => {
const { top, left } = getPosotion(module, direction, length, false)
module.originPos = {
top: module.top,
left: module.left,
fill: module.fill,
}
module.set({ top, left })
module.setCoords()
canvas.renderAll()
if (isOverlapObjects(module, objects) || isOutsideSurface(module, moduleSetupSurface)) {
isWarning = true
module.set({ fill: 'red' })
}
})
canvas.renderAll()
if (isWarning) {
swalFire({
title: getMessage('can.not.move.module'),
icon: 'error',
type: 'alert',
confirmFn: () => {
modules.forEach((module) => {
module.set({ top: module.originPos.top, left: module.originPos.left, fill: module.originPos.fill })
module.setCoords()
})
canvas.renderAll()
},
})
}
}
const moduleCopyAll = (length, direction) => {
const moduleSetupSurface = canvas.getObjects().filter((obj) => canvas.getActiveObjects()[0].id === obj.id)[0]
const modules = canvas.getObjects().filter((obj) => obj.surfaceId === moduleSetupSurface.id && obj.name === POLYGON_TYPE.MODULE)
const objects = getObjects()
const copyModules = []
let copyModule = null
let isWarning = false
let moduleLength = 0
if (['up', 'down'].includes(direction)) {
modules.sort((a, b) => a.top - b.top)
moduleLength = Number(modules[modules.length - 1].top) + Number(modules[modules.length - 1].height) - Number(modules[0].top)
} else if (['left', 'right'].includes(direction)) {
modules.sort((a, b) => a.left - b.left)
moduleLength = Number(modules[modules.length - 1].left) + Number(modules[modules.length - 1].width) - Number(modules[0].left)
}
modules.forEach((module) => {
const { top, left } = getPosotion(module, direction, Number(length) + Number(moduleLength), false)
module.clone((obj) => {
obj.set({
parentId: module.parentId,
initOptions: module.initOptions,
direction: module.direction,
arrow: module.arrow,
name: module.name,
type: module.type,
length: module.length,
points: module.points,
surfaceId: module.surfaceId,
left,
top,
id: uuidv4(),
})
copyModule = obj
canvas.add(obj)
copyModules.push(obj)
obj.setCoords()
})
if (isOverlapObjects(copyModule, objects) || isOutsideSurface(copyModule, moduleSetupSurface)) {
isWarning = true
copyModule.set({ fill: 'red' })
}
canvas.renderAll()
})
if (isWarning) {
swalFire({
title: getMessage('can.not.copy.module'),
icon: 'error',
type: 'alert',
confirmFn: () => {
canvas.remove(...copyModules)
canvas.renderAll()
},
})
}
}
const moduleCopy = (length, direction) => {
if (canvas.getActiveObjects().length === 0) return
const activeModuleIds = canvas.getActiveObjects().map((obj) => obj.id)
const modules = canvas.getObjects().filter((obj) => activeModuleIds.includes(obj.id))
const objects = getObjects()
const otherModules = canvas.getObjects().filter((obj) => obj.surfaceId === modules[0].surfaceId && obj.name === POLYGON_TYPE.MODULE)
const moduleSetupSurface = canvas
.getObjects()
.filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE && obj.id === modules[0].surfaceId)[0]
let isWarning = false
let copyModules = []
let copyModule = null
canvas.discardActiveObject() //선택해제
modules.forEach((module) => {
const { top, left } = getPosotion(module, direction, length, true)
module.clone((obj) => {
obj.set({
parentId: module.parentId,
initOptions: module.initOptions,
direction: module.direction,
arrow: module.arrow,
name: module.name,
type: module.type,
length: module.length,
points: module.points,
surfaceId: module.surfaceId,
left,
top,
id: uuidv4(),
})
copyModules.push(obj)
copyModule = obj
canvas.add(obj)
canvas.renderAll()
})
if (
isOverlapOtherModules(copyModule, otherModules) ||
isOverlapObjects(copyModule, objects) ||
isOutsideSurface(copyModule, moduleSetupSurface)
) {
isWarning = true
copyModule.set({ fill: 'red' })
canvas.renderAll()
}
})
if (isWarning) {
swalFire({
title: getMessage('can.not.copy.module'),
icon: 'error',
type: 'alert',
confirmFn: () => {
canvas.remove(...copyModules)
canvas.renderAll()
},
})
}
}
const moduleMultiCopy = (type, length, direction) => {
if (canvas.getActiveObjects().length === 0) return
if (canvas.getActiveObjects().length > 1) {
swalFire({
title: '여러 개의 모듈을 선택할 수 없습니다.',
icon: 'error',
type: 'alert',
})
canvas.discardActiveObject()
return
}
const activeModule = canvas.getObjects().filter((obj) => canvas.getActiveObjects()[0].id === obj.id)[0]
const modules = type === 'row' ? getRowModules(activeModule) : getColumnModules(activeModule)
const otherModules = canvas.getObjects().filter((obj) => obj.surfaceId === modules[0].surfaceId && obj.name === POLYGON_TYPE.MODULE)
const objects = getObjects()
const copyModules = []
let copyModule = null
const moduleSetupSurface = canvas
.getObjects()
.filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE && obj.id === modules[0].surfaceId)[0]
let isWarning = false
let moduleLength = 0
if (['up', 'down'].includes(direction)) {
moduleLength = Number(modules[modules.length - 1].top) + Number(modules[modules.length - 1].height) - Number(modules[0].top)
} else if (['left', 'right'].includes(direction)) {
moduleLength = Number(modules[modules.length - 1].left) + Number(modules[modules.length - 1].width) - Number(modules[0].left)
}
modules.forEach((module) => {
const { top, left } = getPosotion(module, direction, Number(length) + Number(moduleLength), false)
module.clone((obj) => {
obj.set({
parentId: module.parentId,
initOptions: module.initOptions,
direction: module.direction,
arrow: module.arrow,
name: module.name,
type: module.type,
length: module.length,
points: module.points,
surfaceId: module.surfaceId,
left,
top,
id: uuidv4(),
})
copyModule = obj
canvas.add(obj)
copyModules.push(obj)
obj.setCoords()
})
if (
isOverlapOtherModules(copyModule, otherModules) ||
isOverlapObjects(copyModule, objects) ||
isOutsideSurface(copyModule, moduleSetupSurface)
) {
isWarning = true
copyModule.set({ fill: 'red' })
}
canvas.renderAll()
})
if (isWarning) {
swalFire({
title: getMessage('can.not.copy.module'),
icon: 'error',
type: 'alert',
confirmFn: () => {
canvas.remove(...copyModules)
canvas.renderAll()
},
})
}
}
const moduleColumnRemove = (type) => {
const activeModule = canvas.getObjects().filter((obj) => canvas.getActiveObjects()[0].id === obj.id)[0]
const columnModules = getColumnModules(activeModule)
const otherModules = getOtherModules(columnModules)
const objects = getObjects()
let targetModules = []
const rightModules = otherModules.filter((module) => activeModule.left < module.left).sort((a, b) => a.left - b.left)
const leftModules = otherModules.filter((module) => activeModule.left > module.left).sort((a, b) => b.left - a.left)
let width = -1
const moduleSetupSurface = canvas
.getObjects()
.filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE && obj.id === activeModule.surfaceId)[0]
let isWarning = false
canvas.discardActiveObject()
canvas.remove(...columnModules)
canvas.renderAll()
if (type === MODULE_REMOVE_TYPE.LEFT) {
rightModules.forEach((module) => {
module.originPos = {
left: module.left,
top: module.top,
fill: module.fill,
}
if (width === -1) width = module.left - activeModule.left
module.set({ left: module.left - width })
module.setCoords()
canvas.renderAll()
if (isOverlapOtherModules(module, leftModules) || isOverlapObjects(module, objects) || isOutsideSurface(module, moduleSetupSurface)) {
module.set({ fill: 'red' })
isWarning = true
}
})
canvas.renderAll()
targetModules = rightModules
} else if (type === MODULE_REMOVE_TYPE.RIGHT) {
leftModules.forEach((module) => {
module.originPos = {
left: module.left,
top: module.top,
fill: module.fill,
}
if (width === -1) width = activeModule.left - module.left
module.set({ left: module.left + width })
module.setCoords()
canvas.renderAll()
if (isOverlapOtherModules(module, rightModules) || isOverlapObjects(module, objects) || isOutsideSurface(module, moduleSetupSurface)) {
module.set({ fill: 'red' })
isWarning = true
}
})
canvas.renderAll()
targetModules = leftModules
} else if (type === MODULE_REMOVE_TYPE.HORIZONTAL_SIDE) {
const sideModules = [...leftModules, ...rightModules]
leftModules.forEach((module) => {
module.originPos = {
left: module.left,
top: module.top,
fill: module.fill,
}
if (width === -1) width = activeModule.left - module.left
module.set({ left: module.left + width / 2 })
module.setCoords()
canvas.renderAll()
})
rightModules.forEach((module) => {
module.originPos = {
left: module.left,
top: module.top,
fill: module.fill,
}
if (width === -1) width = module.left - activeModule.left
module.set({ left: module.left - width / 2 })
module.setCoords()
canvas.renderAll()
})
sideModules.forEach((module) => {
if (
isOverlapOtherModules(
module,
sideModules.filter((m) => m.id !== module.id),
) ||
isOverlapObjects(module, objects) ||
isOutsideSurface(module, moduleSetupSurface)
) {
isWarning = true
module.set({ fill: 'red' })
}
})
targetModules = sideModules
}
canvas.renderAll()
if (isWarning) {
swalFire({
title: getMessage('can.not.remove.module'),
icon: 'error',
type: 'alert',
confirmFn: () => {
canvas.add(...columnModules)
targetModules.forEach((module) => {
module.set({ top: module.originPos.top, left: module.originPos.left, fill: module.originPos.fill })
module.setCoords()
})
canvas.renderAll()
},
})
}
}
const moduleRowRemove = (type) => {
const activeModule = canvas.getObjects().filter((obj) => canvas.getActiveObjects()[0].id === obj.id)[0]
const rowModules = getRowModules(activeModule)
const otherModules = getOtherModules(rowModules)
const objects = getObjects()
let targetModules = []
const topModules = otherModules.filter((module) => activeModule.top > module.top).sort((a, b) => b.top - a.top)
const bottomModules = otherModules.filter((module) => activeModule.top < module.top).sort((a, b) => a.top - b.top)
let height = -1
const moduleSetupSurface = canvas
.getObjects()
.filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE && obj.id === activeModule.surfaceId)[0]
let isWarning = false
canvas.discardActiveObject()
canvas.remove(...rowModules)
canvas.renderAll()
if (type === MODULE_REMOVE_TYPE.TOP) {
bottomModules.forEach((module) => {
module.originPos = {
left: module.left,
top: module.top,
fill: module.fill,
}
if (height === -1) height = module.top - activeModule.top
module.set({ top: module.top - height })
module.setCoords()
canvas.renderAll()
if (isOverlapOtherModules(module, topModules) || isOverlapObjects(module, objects) || isOutsideSurface(module, moduleSetupSurface)) {
isWarning = true
module.set({ fill: 'red' })
}
})
canvas.renderAll()
targetModules = bottomModules
} else if (type === MODULE_REMOVE_TYPE.BOTTOM) {
topModules.forEach((module) => {
module.originPos = {
left: module.left,
top: module.top,
fill: module.fill,
}
if (height === -1) height = activeModule.top - module.top
module.set({ top: module.top + activeModule.height })
module.setCoords()
canvas.renderAll()
if (isOverlapOtherModules(module, bottomModules) || isOverlapObjects(module, objects) || isOutsideSurface(module, moduleSetupSurface)) {
isWarning = true
module.set({ fill: 'red' })
}
})
targetModules = topModules
} else if (type === MODULE_REMOVE_TYPE.VERTICAL_SIDE) {
topModules.forEach((module) => {
module.originPos = {
left: module.left,
top: module.top,
fill: module.fill,
}
// if (height === -1) height = activeModule.top - module.top
if (height === -1) height = activeModule.height
module.set({ top: module.top + height / 2 })
module.setCoords()
})
bottomModules.forEach((module) => {
module.originPos = {
left: module.left,
top: module.top,
fill: module.fill,
}
// if (height === -1) height = module.top - activeModule.top
if (height === -1) height = activeModule.height
module.set({ top: module.top - height / 2 })
module.setCoords()
})
canvas.renderAll()
const sideModules = [...topModules, ...bottomModules]
sideModules.forEach((module) => {
if (
isOverlapOtherModules(
module,
sideModules.filter((m) => m.id !== module.id),
) ||
isOverlapObjects(module, objects) ||
isOutsideSurface(module, moduleSetupSurface)
) {
isWarning = true
module.set({ fill: 'red' })
}
})
targetModules = sideModules
}
canvas.renderAll()
if (isWarning && type !== MODULE_REMOVE_TYPE.NONE) {
targetModules.forEach((rect) => rect.set({ fill: 'red' }))
swalFire({
title: getMessage('can.not.remove.module'),
icon: 'error',
type: 'alert',
confirmFn: () => {
canvas.add(...rowModules)
targetModules.forEach((module) => {
module.set({ top: module.originPos.top, left: module.originPos.left, fill: module.originPos.fill })
module.setCoords()
})
canvas.renderAll()
},
})
}
}
const moduleColumnInsert = (type) => {
const activeModule = canvas.getObjects().filter((obj) => canvas.getActiveObjects()[0].id === obj.id)[0]
const columnModules = getColumnModules(activeModule)
let otherModules = getOtherModules(columnModules)
const targetModules =
type === MODULE_INSERT_TYPE.LEFT
? otherModules.filter((module) => module.left < activeModule.left).sort((a, b) => a.left - b.left)
: otherModules.filter((module) => module.left > activeModule.left).sort((a, b) => a.left - b.left)
const objects = getObjects()
const copyModules = []
const moduleSetupSurface = canvas
.getObjects()
.filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE && obj.id === activeModule.surfaceId)[0]
let width = -1
let isWarning = false
if (targetModules.length === 0) {
swalFire({
title: '마지막 모듈입니다.',
icon: 'error',
type: 'alert',
})
return
}
canvas.discardActiveObject()
targetModules.forEach((module) => {
if (width === -1)
width = type === MODULE_INSERT_TYPE.LEFT ? Number(activeModule.left) - Number(module.left) : Number(module.left) - Number(activeModule.left)
const { top, left } = getPosotion(module, type, module.width, false)
module.originPos = {
left: module.left,
top: module.top,
fill: module.fill,
}
module.set({ left, top })
canvas.renderAll()
if (isOverlapObjects(module, objects) || isOutsideSurface(module, moduleSetupSurface)) {
isWarning = true
module.set({ fill: 'red' })
}
module.setCoords()
})
canvas.renderAll()
otherModules = getOtherModules(columnModules)
columnModules.forEach((module) => {
const { top, left } = getPosotion(module, type, module.width, false)
let copyModule = null
module.clone((obj) => {
obj.set({
parentId: module.parentId,
initOptions: module.initOptions,
direction: module.direction,
arrow: module.arrow,
name: module.name,
type: module.type,
length: module.length,
points: module.points,
surfaceId: module.surfaceId,
left,
top,
id: uuidv4(),
})
copyModule = obj
canvas.add(obj)
copyModules.push(obj)
obj.setCoords()
})
canvas.renderAll()
if (
isOverlapOtherModules(copyModule, otherModules) ||
isOverlapObjects(copyModule, objects) ||
isOutsideSurface(copyModule, moduleSetupSurface)
) {
isWarning = true
}
module.setCoords()
})
canvas.renderAll()
if (isWarning) {
swalFire({
title: getMessage('can.not.insert.module'),
icon: 'error',
type: 'alert',
confirmFn: () => {
targetModules.forEach((module) => {
module.set({ top: module.originPos.top, left: module.originPos.left, fill: module.originPos.fill })
module.setCoords()
})
canvas.remove(...copyModules)
canvas.renderAll()
},
})
}
}
const muduleRowInsert = (type) => {
const activeModule = canvas.getObjects().filter((obj) => canvas.getActiveObjects()[0].id === obj.id)[0]
const rowModules = getRowModules(activeModule)
let otherModules = getOtherModules(rowModules)
const targetModules =
type === MODULE_INSERT_TYPE.TOP
? otherModules.filter((module) => module.top < activeModule.top).sort((a, b) => a.top - b.top)
: otherModules.filter((module) => module.top > activeModule.top).sort((a, b) => a.top - b.top)
if (targetModules.length === 0) {
swalFire({
title: '마지막 모듈입니다.',
icon: 'error',
type: 'alert',
})
return
}
const objects = getObjects()
const copyModules = []
const moduleSetupSurface = canvas
.getObjects()
.filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE && obj.id === activeModule.surfaceId)[0]
let height = -1
let isWarning = false
canvas.discardActiveObject()
targetModules.forEach((module) => {
if (height === -1)
height = type === MODULE_INSERT_TYPE.TOP ? Number(activeModule.top) - Number(module.top) : Number(module.top) - Number(activeModule.top)
const { top, left } = getPosotion(module, type, activeModule.height, false)
module.originPos = {
left: module.left,
top: module.top,
fill: module.fill,
}
module.set({ left, top })
if (isOverlapObjects(module, objects) || isOutsideSurface(module, moduleSetupSurface)) {
isWarning = true
module.set({ fill: 'red' })
}
module.setCoords()
})
canvas.renderAll()
otherModules = getOtherModules(rowModules)
rowModules.forEach((module) => {
const { top, left } = getPosotion(module, type, activeModule.height, false)
let copyModule = null
module.clone((obj) => {
obj.set({
parentId: module.parentId,
initOptions: module.initOptions,
direction: module.direction,
arrow: module.arrow,
name: module.name,
type: module.type,
length: module.length,
points: module.points,
surfaceId: module.surfaceId,
fill: module.fill,
left,
top,
id: uuidv4(),
})
copyModule = obj
canvas.add(obj)
copyModules.push(obj)
obj.setCoords()
})
canvas.renderAll()
if (
isOverlapOtherModules(copyModule, otherModules) ||
isOverlapObjects(copyModule, objects) ||
isOutsideSurface(copyModule, moduleSetupSurface)
) {
isWarning = true
copyModule.set({ fill: 'red' })
}
module.setCoords()
})
canvas.renderAll()
if (isWarning) {
swalFire({
title: getMessage('can.not.insert.module'),
icon: 'error',
type: 'alert',
confirmFn: () => {
targetModules.forEach((module) => {
module.set({ top: module.originPos.top, left: module.originPos.left, fill: module.originPos.fill })
module.setCoords()
})
canvas.remove(...copyModules)
canvas.renderAll()
},
})
}
}
const alignModule = (type) => {
const moduleSetupSurface = canvas.getObjects().filter((obj) => canvas.getActiveObjects()[0].id === obj.id)[0]
const modules = canvas.getObjects().filter((obj) => obj.surfaceId === moduleSetupSurface.id && obj.name === POLYGON_TYPE.MODULE)
const objects = getObjects()
let [top, bottom, left, right] = [0, 0, 0, 0]
top = Math.min(...modules.map((module) => module.top))
bottom = Math.max(...modules.map((module) => module.top + module.height))
left = Math.min(...modules.map((module) => module.left))
right = Math.max(...modules.map((module) => module.left + module.width))
const moduleSurfacePos = {
top: Math.min(...moduleSetupSurface.points.map((point) => point.y)),
left: Math.min(...moduleSetupSurface.points.map((point) => point.x)),
}
const [height, width] = [bottom - top, right - left]
const verticalCenterLength = moduleSurfacePos.top + moduleSetupSurface.height / 2 - (top + height / 2)
const horizontalCenterLength = moduleSurfacePos.left + moduleSetupSurface.width / 2 - (left + width / 2)
let isWarning = false
canvas.discardActiveObject()
modules.forEach((module) => {
module.originPos = {
left: module.left,
top: module.top,
fill: module.fill,
}
if (type === MODULE_ALIGN_TYPE.VERTICAL) {
module.set({ top: module.top + verticalCenterLength })
} else if (type === MODULE_ALIGN_TYPE.HORIZONTAL) {
module.set({ left: module.left + horizontalCenterLength })
}
canvas.renderAll()
module.setCoords()
if (isOverlapObjects(module, objects) || isOutsideSurface(module, moduleSetupSurface)) {
isWarning = true
module.set({ fill: 'red' })
}
})
canvas.renderAll()
if (isWarning) {
swalFire({
title: getMessage('can.not.align.module'),
icon: 'error',
type: 'alert',
confirmFn: () => {
modules.forEach((module) => {
module.set({ top: module.originPos.top, left: module.originPos.left, fill: module.originPos.fill })
module.setCoords()
})
canvas.renderAll()
},
})
}
}
const modulesRemove = () => {
const activeModule = canvas.getObjects().filter((obj) => canvas.getActiveObjects()[0].id === obj.id)[0]
const modules = canvas.getObjects().filter((obj) => obj.surfaceId === activeModule.surfaceId && obj.name === POLYGON_TYPE.MODULE)
canvas.remove(...modules)
canvas.renderAll()
}
const isOverlapOtherModules = (module, otherModules) => {
return otherModules.some(
(otherModule) =>
turf.booleanOverlap(polygonToTurfPolygon(module, true), polygonToTurfPolygon(otherModule, true)) ||
turf.booleanWithin(polygonToTurfPolygon(module, true), polygonToTurfPolygon(otherModule, true)),
)
}
const isOverlapObjects = (module, objects) => {
return !checkModuleDisjointObjects(polygonToTurfPolygon(module, true), objects)
}
const isOutsideSurface = (module, moduleSetupSurface) => {
return (
!turf.booleanContains(polygonToTurfPolygon(moduleSetupSurface, true), polygonToTurfPolygon(module, true)) ||
!turf.booleanWithin(polygonToTurfPolygon(module, true), polygonToTurfPolygon(moduleSetupSurface, true))
)
}
const getRowModules = (target) => {
return canvas
.getObjects()
.filter((obj) => target.surfaceId === obj.surfaceId && obj.name === POLYGON_TYPE.MODULE && obj.top === target.top)
.sort((a, b) => a.left - b.left)
}
const getColumnModules = (target) => {
return canvas
.getObjects()
.filter((obj) => target.surfaceId === obj.surfaceId && obj.name === POLYGON_TYPE.MODULE && obj.left === target.left)
.sort((a, b) => a.top - b.top)
}
const getPosotion = (target, direction, length, hasMargin = false) => {
let top = target.top
let left = target.left
if (direction === 'up') {
top = Number(target.top) - Number(length)
top = hasMargin ? top - Number(target.height) : top
} else if (direction === 'down') {
top = Number(target.top) + Number(length)
top = hasMargin ? top + Number(target.height) : top
} else if (direction === 'left') {
left = Number(target.left) - Number(length)
left = hasMargin ? left - Number(target.width) : left
} else if (direction === 'right') {
left = Number(target.left) + Number(length)
left = hasMargin ? left + Number(target.width) : left
}
return { top, left }
}
const getOtherModules = (modules) => {
const moduleIds = modules.map((module) => module.id)
return canvas
.getObjects()
.filter((obj) => obj.surfaceId === modules[0].surfaceId && obj.name === POLYGON_TYPE.MODULE && !moduleIds.includes(obj.id))
}
const getObjects = () => {
return canvas
?.getObjects()
.filter((obj) => [BATCH_TYPE.OPENING, BATCH_TYPE.TRIANGLE_DORMER, BATCH_TYPE.PENTAGON_DORMER, BATCH_TYPE.SHADOW].includes(obj.name))
}
return {
moduleMove,
moduleMultiMove,
moduleMoveAll,
moduleCopy,
moduleMultiCopy,
moduleCopyAll,
moduleColumnRemove,
moduleRowRemove,
moduleColumnInsert,
muduleRowInsert,
modulesRemove,
alignModule,
}
}

File diff suppressed because it is too large Load Diff

View File

@ -5,13 +5,14 @@ import { useRecoilValue } from 'recoil'
import { canvasState } from '@/store/canvasAtom' import { canvasState } from '@/store/canvasAtom'
import { BATCH_TYPE, INPUT_TYPE } from '@/common/common' import { BATCH_TYPE, INPUT_TYPE } from '@/common/common'
import { useEvent } from '@/hooks/useEvent' import { useEvent } from '@/hooks/useEvent'
import { pointsToTurfPolygon, polygonToTurfPolygon, rectToPolygon, setSurfaceShapePattern, triangleToPolygon } from '@/util/canvas-util' import { pointsToTurfPolygon, polygonToTurfPolygon, rectToPolygon, triangleToPolygon } 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 { usePolygon } from '@/hooks/usePolygon' import { usePolygon } from '@/hooks/usePolygon'
import { QPolygon } from '@/components/fabric/QPolygon' import { QPolygon } from '@/components/fabric/QPolygon'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
import { fontSelector } from '@/store/fontAtom' import { fontSelector } from '@/store/fontAtom'
import { useRoofFn } from '@/hooks/common/useRoofFn'
export function useObjectBatch({ isHidden, setIsHidden }) { export function useObjectBatch({ isHidden, setIsHidden }) {
const { getMessage } = useMessage() const { getMessage } = useMessage()
@ -20,6 +21,7 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
// const { addCanvasMouseEventListener, initEvent, addDocumentEventListener } = useContext(EventContext) // const { addCanvasMouseEventListener, initEvent, addDocumentEventListener } = useContext(EventContext)
const { swalFire } = useSwal() const { swalFire } = useSwal()
const { drawDirectionArrow } = usePolygon() const { drawDirectionArrow } = usePolygon()
const { setSurfaceShapePattern } = useRoofFn()
const lengthTextFont = useRecoilValue(fontSelector('lengthText')) const lengthTextFont = useRecoilValue(fontSelector('lengthText'))
useEffect(() => { useEffect(() => {

View File

@ -1,4 +1,4 @@
import { useCallback, useEffect, useState } from 'react' import { useEffect, useState, useRef } from 'react'
import { useRecoilState, useRecoilValue, useResetRecoilState, useSetRecoilState } from 'recoil' import { useRecoilState, useRecoilValue, useResetRecoilState, useSetRecoilState } from 'recoil'
import { import {
adsorptionPointModeState, adsorptionPointModeState,
@ -19,8 +19,9 @@ import {
settingModalSecondOptionsState, settingModalSecondOptionsState,
settingModalGridOptionsState, settingModalGridOptionsState,
basicSettingState, basicSettingState,
settingsState,
roofMaterialsAtom, roofMaterialsAtom,
selectedRoofMaterialSelector,
addedRoofsState,
} from '@/store/settingAtom' } from '@/store/settingAtom'
import { POLYGON_TYPE } from '@/common/common' import { POLYGON_TYPE } from '@/common/common'
import { globalFontAtom } from '@/store/fontAtom' import { globalFontAtom } from '@/store/fontAtom'
@ -28,6 +29,7 @@ import { dimensionLineSettingsState } from '@/store/commonUtilsAtom'
import { gridColorState } from '@/store/gridAtom' import { gridColorState } from '@/store/gridAtom'
import { useColor } from 'react-color-palette' import { useColor } from 'react-color-palette'
import { useMasterController } from '@/hooks/common/useMasterController' import { useMasterController } from '@/hooks/common/useMasterController'
import { ROOF_MATERIAL_LAYOUT } from '@/components/floor-plan/modal/placementShape/PlacementShapeSetting'
const defaultDotLineGridSetting = { const defaultDotLineGridSetting = {
INTERVAL: { INTERVAL: {
@ -41,6 +43,8 @@ const defaultDotLineGridSetting = {
LINE: false, LINE: false,
} }
let previousRoofMaterialsYn = 'N'; // 지붕재 select 정보 비교 후 변경된 것이 없으면 1회만 실행
export function useCanvasSetting() { export function useCanvasSetting() {
const canvas = useRecoilValue(canvasState) const canvas = useRecoilValue(canvasState)
// canvas가 null이 아닐 때에만 getObjects 호출 // canvas가 null이 아닐 때에만 getObjects 호출
@ -92,8 +96,9 @@ export function useCanvasSetting() {
const [canvasSetting, setCanvasSetting] = useRecoilState(canvasSettingState) const [canvasSetting, setCanvasSetting] = useRecoilState(canvasSettingState)
const [basicSetting, setBasicSettings] = useRecoilState(basicSettingState) const [basicSetting, setBasicSettings] = useRecoilState(basicSettingState)
const { getRoofMaterialList } = useMasterController() const { getRoofMaterialList, getModuleTypeItemList } = useMasterController()
const [roofMaterials, setRoofMaterials] = useRecoilState(roofMaterialsAtom) const [roofMaterials, setRoofMaterials] = useRecoilState(roofMaterialsAtom)
const [addedRoofs, setAddedRoofs] = useRecoilState(addedRoofsState)
const SelectOptions = [ const SelectOptions = [
{ id: 1, name: getMessage('modal.canvas.setting.grid.dot.line.setting.line.origin'), value: 1 }, { id: 1, name: getMessage('modal.canvas.setting.grid.dot.line.setting.line.origin'), value: 1 },
@ -102,15 +107,62 @@ export function useCanvasSetting() {
{ id: 4, name: '1/10', value: 1 / 10 }, { id: 4, name: '1/10', value: 1 / 10 },
] ]
const selectedRoofMaterial = useRecoilValue(selectedRoofMaterialSelector)
useEffect(() => { useEffect(() => {
addRoofMaterials() if (roofMaterials.length !== 0) {
return
}
addRoofMaterials()
}, []) }, [])
//지붕재 초기세팅
const addRoofMaterials = async () => { const addRoofMaterials = async () => {
if (roofMaterials.length !== 0) {
return
}
const { data } = await getRoofMaterialList() const { data } = await getRoofMaterialList()
setRoofMaterials(data)
const roofLists = data.map((item, idx) => ({
...item,
id: item.roofMatlCd,
name: item.roofMatlNm,
selected: idx === 0,
index: idx,
nameJp: item.roofMatlNmJp,
length: item.lenBase && parseInt(item.lenBase),
width: item.widBase && parseInt(item.widBase),
raft: item.raftBase && parseInt(item.raftBase),
layout: ['ROOF_ID_SLATE', 'ROOF_ID_SINGLE'].includes(item.roofMatlCd) ? ROOF_MATERIAL_LAYOUT.STAIRS : ROOF_MATERIAL_LAYOUT.PARALLEL,
hajebichi: item.roofPchBase && parseInt(item.roofPchBase),
}))
setRoofMaterials(roofLists)
const selectedRoofMaterial = roofLists[0]
if (addedRoofs.length === 0) {
const newAddedRoofs = []
newAddedRoofs.push({ ...selectedRoofMaterial, selected: true, index: 0 })
setAddedRoofs(newAddedRoofs)
}
setBasicSettings({ ...basicSetting, selectedRoofMaterial: selectedRoofMaterial })
} }
const previousRoofMaterialsRef = useRef(null);
useEffect(() => {
// 지붕재 select 정보가 존재해야 배치면초기설정 DB 정보 비교 후 지붕재 정보를 가져올 수 있음
if (roofMaterials.length !== 0 && JSON.stringify(previousRoofMaterialsRef.current) !== JSON.stringify(roofMaterials)) {
// 지붕재 select 정보 비교 후 변경된 것이 없으면 1회만 실행
if (roofMaterials && previousRoofMaterialsYn === 'N') {
fetchBasicSettings();
previousRoofMaterialsYn = 'Y';
}
// 이전 값을 업데이트
previousRoofMaterialsRef.current = roofMaterials;
}
}, [roofMaterials]);
useEffect(() => { useEffect(() => {
if (!canvas) { if (!canvas) {
return return
@ -141,14 +193,6 @@ export function useCanvasSetting() {
canvas?.renderAll() canvas?.renderAll()
}, [corridorDimension]) }, [corridorDimension])
// 배치면 초기설정 변경 시
useEffect(() => {
//console.log('useCanvasSetting canvasSetting 실행', canvasSetting)
if (canvasSetting.flag) {
basicSettingSave()
}
}, [canvasSetting])
useEffect(() => { useEffect(() => {
console.log('🚀 ~ useEffect ~ settingsDataSave:', settingsDataSave) console.log('🚀 ~ useEffect ~ settingsDataSave:', settingsDataSave)
if (settingsDataSave !== undefined) onClickOption2() if (settingsDataSave !== undefined) onClickOption2()
@ -228,59 +272,82 @@ export function useCanvasSetting() {
// 기본설정(PlacementShapeSetting) 조회 및 초기화 // 기본설정(PlacementShapeSetting) 조회 및 초기화
const fetchBasicSettings = async () => { const fetchBasicSettings = async () => {
try { try {
await get({ url: `/api/canvas-management/canvas-basic-settings/by-object/${correntObjectNo}` }).then((res) => { await get({ url: `/api/canvas-management/canvas-basic-settings/by-object/0/${correntObjectNo}` }).then((res) => {
console.log('fetchBasicSettings res ', res)
if (res.length == 0) return let roofsRow = {}
let roofsArray = {}
// 'roofs' 배열을 생성하여 각 항목을 추가 if (res.length > 0) {
const roofsRow = res.map((item) => { roofsRow = res.map((item) => {
return { return {
roofSizeSet: item.roofSizeSet, roofSizeSet: item.roofSizeSet,
roofAngleSet: item.roofAngleSet, roofAngleSet: item.roofAngleSet,
} }
}) })
roofsArray = res.map((item) => {
const roofsArray = res.some((item) => !item.roofSeq) return {
? //최초 지붕재 추가 정보의 경우 roofsArray를 초기화 설정
res.map(() => ({
flag: false,
roofApply: true, roofApply: true,
roofSeq: 1, roofSeq: 1,
roofType: 1, roofMatlCd: item.roofMatlCd,
roofWidth: 265,
roofHeight: 235,
roofHajebichi: 0,
roofGap: 455,
// roofType: 1,
// roofWidth: 200,
// roofHeight: 200,
// roofHajebichi: 200,
// roofGap: 0,
roofLayout: 'parallel',
}))
: res.map((item) => ({
flag: false,
roofApply: item.roofApply === '' || item.roofApply === false ? false : true,
roofSeq: item.roofSeq,
roofType: item.roofType,
roofWidth: item.roofWidth, roofWidth: item.roofWidth,
roofHeight: item.roofHeight, roofHeight: item.roofHeight,
roofHajebichi: item.roofHajebichi, roofHajebichi: item.roofHajebichi,
roofGap: item.roofGap, roofGap: item.roofGap,
roofLayout: item.roofLayout, roofLayout: item.roofLayout,
})) }
console.log('roofsArray ', roofsArray) })
} else {
roofsRow = [
{
roofSizeSet: 1,
roofAngleSet: 'slope',
},
]
roofsArray = [
{
roofApply: true,
roofSeq: 1,
roofMatlCd: 'ROOF_ID_WA_53A',
roofWidth: 265,
roofHeight: 235,
roofHajebichi: 0,
roofGap: 'HEI_455',
roofLayout: 'P',
},
]
}
// 나머지 데이터와 함께 'roofs' 배열을 patternData에 넣음 // 나머지 데이터와 함께 'roofs' 배열을 patternData에 넣음
const patternData = { const patternData = {
roofSizeSet: roofsRow[0].roofSizeSet, // 첫 번째 항목의 값을 사용 roofSizeSet: roofsRow[0].roofSizeSet,
roofAngleSet: roofsRow[0].roofAngleSet, // 첫 번째 항목의 값을 사용 roofAngleSet: roofsRow[0].roofAngleSet,
roofs: roofsArray, // 만들어진 roofs 배열 roofs: roofsArray, // 만들어진 roofs 배열
} }
//console.error('patternData', patternData) //console.log('fetchBasicSettings patternData', patternData)
// 데이터 설정 // 데이터 설정
setBasicSettings({ ...patternData }) const addRoofs = []
roofMaterials?.map((material) => {
if (material.roofMatlCd === roofsArray[0].roofMatlCd) {
addRoofs.push({ ...material, selected: true
, index: 0
, width: roofsArray[0].roofWidth
, length: roofsArray[0].roofHeight
, hajebichi: roofsArray[0].roofHajebichi
, raft: roofsArray[0].roofGap
, layout: roofsArray[0].roofLayout
})
setAddedRoofs(addRoofs)
setBasicSettings({ ...basicSetting, roofMaterials: addRoofs[0]
, roofSizeSet: roofsRow[0].roofSizeSet
, roofAngleSet: roofsRow[0].roofAngleSet
, roofsData: roofsArray
, selectedRoofMaterial: addRoofs[0] })
}
})
}) })
} catch (error) { } catch (error) {
console.error('Data fetching error:', error) console.error('Data fetching error:', error)
@ -299,15 +366,29 @@ export function useCanvasSetting() {
objectNo: correntObjectNo, objectNo: correntObjectNo,
roofSizeSet: basicSetting.roofSizeSet, roofSizeSet: basicSetting.roofSizeSet,
roofAngleSet: basicSetting.roofAngleSet, roofAngleSet: basicSetting.roofAngleSet,
roofMaterialsAddList: basicSetting.roofs, // TODO : 선택된 roof로 변경해야함 roofMaterialsAddList: [
{
roofApply: true,
roofSeq: 0,
roofMatlCd: basicSetting.roofsData.roofMatlCd === null || basicSetting.roofsData.roofMatlCd === undefined ? 'ROOF_ID_WA_53A' : basicSetting.roofsData.roofMatlCd,
roofWidth: basicSetting.roofsData.roofWidth === null || basicSetting.roofsData.roofWidth === undefined ? 0 : basicSetting.roofsData.roofWidth,
roofHeight: basicSetting.roofsData.roofHeight === null || basicSetting.roofsData.roofHeight === undefined ? 0 : basicSetting.roofsData.roofHeight,
roofHajebichi: basicSetting.roofsData.roofHajebichi === null || basicSetting.roofsData.roofHajebichi === undefined ? 0 : basicSetting.roofsData.roofHajebichi,
roofGap: basicSetting.roofsData.roofGap === null || basicSetting.roofsData.roofGap === undefined ? 'HEI_455' : basicSetting.roofsData.roofGap,
roofLayout: basicSetting.roofsData.roofLayout === null || basicSetting.roofsData.roofLayout === undefined ? 'P' : basicSetting.roofsData.roofLayout,
},
],
} }
console.log('basicSettingSave patternData ', patternData)
await post({ url: `/api/canvas-management/canvas-basic-settings`, data: patternData }).then((res) => { await post({ url: `/api/canvas-management/canvas-basic-settings`, data: patternData }).then((res) => {
swalFire({ text: getMessage(res.returnMessage) }) swalFire({ text: getMessage(res.returnMessage) })
}) })
//Recoil 설정 //Recoil 설정
setCanvasSetting({ ...basicSetting, flag: false }) setCanvasSetting({ ...basicSetting })
fetchBasicSettings()
} catch (error) { } catch (error) {
swalFire({ text: getMessage(res.returnMessage), icon: 'error' }) swalFire({ text: getMessage(res.returnMessage), icon: 'error' })
} }
@ -681,11 +762,12 @@ export function useCanvasSetting() {
setCanvasSetting, setCanvasSetting,
basicSetting, basicSetting,
setBasicSettings, setBasicSettings,
fetchBasicSettings,
basicSettingSave, basicSettingSave,
settingsData, settingsData,
setSettingsData, setSettingsData,
settingsDataSave, settingsDataSave,
setSettingsDataSave, setSettingsDataSave,
addedRoofs,
setAddedRoofs,
} }
} }

View File

@ -1,10 +1,9 @@
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil' import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
import { canvasState, currentMenuState, currentObjectState } from '@/store/canvasAtom' import { canvasState, currentMenuState, currentObjectState } from '@/store/canvasAtom'
import { useEffect, useState } from 'react' import { useEffect, useRef, useState } from 'react'
import { setSurfaceShapePattern } from '@/util/canvas-util'
import { useSwal } from '@/hooks/useSwal' import { useSwal } from '@/hooks/useSwal'
import { usePolygon } from '@/hooks/usePolygon' import { usePolygon } from '@/hooks/usePolygon'
import { roofDisplaySelector } from '@/store/settingAtom' import { addedRoofsState, basicSettingState, roofDisplaySelector, roofMaterialsSelector, selectedRoofMaterialSelector } from '@/store/settingAtom'
import { usePopup } from '@/hooks/usePopup' import { usePopup } from '@/hooks/usePopup'
import { POLYGON_TYPE } from '@/common/common' import { POLYGON_TYPE } from '@/common/common'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
@ -13,6 +12,8 @@ import { useMessage } from '@/hooks/useMessage'
import useMenu from '@/hooks/common/useMenu' import useMenu from '@/hooks/common/useMenu'
import { useCanvasMenu } from '@/hooks/common/useCanvasMenu' import { useCanvasMenu } from '@/hooks/common/useCanvasMenu'
import { menuTypeState } from '@/store/menuAtom' import { menuTypeState } from '@/store/menuAtom'
import { useRoofFn } from '@/hooks/common/useRoofFn'
import { ROOF_MATERIAL_LAYOUT } from '@/components/floor-plan/modal/placementShape/PlacementShapeSetting'
// 지붕면 할당 // 지붕면 할당
export function useRoofAllocationSetting(id) { export function useRoofAllocationSetting(id) {
@ -26,69 +27,15 @@ export function useRoofAllocationSetting(id) {
const { swalFire } = useSwal() const { swalFire } = useSwal()
const { setMenuNumber } = useCanvasMenu() const { setMenuNumber } = useCanvasMenu()
const setMenuType = useSetRecoilState(menuTypeState) const setMenuType = useSetRecoilState(menuTypeState)
const roofMaterials = [ const roofMaterials = useRecoilValue(roofMaterialsSelector)
{ const selectedRoofMaterial = useRecoilValue(selectedRoofMaterialSelector)
id: 'A', const [basicSetting, setBasicSetting] = useRecoilState(basicSettingState)
name: '기와1', const [currentRoofMaterial, setCurrentRoofMaterial] = useState(roofMaterials[0]) // 팝업 내 기준 지붕재
type: 'A', const [roofList, setRoofList] = useRecoilState(addedRoofsState) // 배치면 초기설정에서 선택한 지붕재 배열
width: '200',
length: '200',
alignType: 'parallel',
},
{
id: 'B',
name: '기와2',
type: 'B',
rafter: '200',
alignType: 'parallel',
},
{
id: 'C',
name: '기와3',
type: 'C',
hajebichi: '200',
alignType: 'stairs',
},
{
id: 'D',
name: '기와4',
type: 'D',
length: '200',
alignType: 'stairs',
},
]
const widths = [
{ name: '200', id: 'q' },
{ name: '250', id: 'q1' },
{ name: '300', id: 'q2' },
]
const lengths = [
{ name: '200', id: 'w' },
{ name: '250', id: 'w1' },
{ name: '300', id: 'w2' },
]
const rafters = [
{ name: '200', id: 'e' },
{ name: '250', id: 'e1' },
{ name: '300', id: 'e2' },
]
const [values, setValues] = useState([
{
id: 'A',
type: 'A',
roofMaterial: { name: '기와1' },
width: { name: '200' },
length: { name: '250' },
rafter: { name: '300' },
alignType: 'stairs',
},
])
const [radioValue, setRadioValue] = useState('A')
const [editingLines, setEditingLines] = useState([]) const [editingLines, setEditingLines] = useState([])
const [currentRoofList, setCurrentRoofList] = useState(roofList)
const [selectedRoofMaterial, setSelectedRoofMaterial] = useState(roofMaterials[0]) const { setSurfaceShapePattern } = useRoofFn()
useEffect(() => { useEffect(() => {
const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF) // roofPolygon.innerLines const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF) // roofPolygon.innerLines
@ -130,16 +77,31 @@ export function useRoofAllocationSetting(id) {
}, []) }, [])
const onAddRoofMaterial = () => { const onAddRoofMaterial = () => {
setValues([...values, selectedRoofMaterial]) if (currentRoofList.length >= 4) {
swalFire({ type: 'alert', icon: 'error', text: getMessage('지붕재는 4개까지 선택 가능합니다.') })
return
}
setCurrentRoofList([
...currentRoofList,
{
...currentRoofMaterial,
selected: false,
id: currentRoofMaterial.roofMatlCd,
name: currentRoofMaterial.roofMatlNm,
index: currentRoofList.length,
},
])
} }
const onDeleteRoofMaterial = (id) => { const onDeleteRoofMaterial = (idx) => {
setValues(values.filter((value) => value.id !== id)) const isSelected = currentRoofList[idx].selected
const newRoofList = [...currentRoofList].filter((_, index) => index !== idx)
if (isSelected) {
newRoofList[0].selected = true
}
setCurrentRoofList(newRoofList)
} }
const { handleMenu } = useMenu()
const [currentMenu, setCurrentMenu] = useRecoilState(currentMenuState)
// 선택한 지붕재로 할당 // 선택한 지붕재로 할당
const handleSave = () => { const handleSave = () => {
// 모두 actualSize 있으면 바로 적용 없으면 actualSize 설정 // 모두 actualSize 있으면 바로 적용 없으면 actualSize 설정
@ -150,6 +112,24 @@ export function useRoofAllocationSetting(id) {
} }
} }
// 지붕재 오른쪽 마우스 클릭 후 단일로 지붕재 변경 필요한 경우
const handleSaveContext = () => {
const newRoofList = currentRoofList.map((roof, idx) => {
return { ...roof, index: idx }
})
setBasicSetting((prev) => {
return {
...prev,
selectedRoofMaterial: newRoofList.find((roof) => roof.selected),
}
})
setRoofList(newRoofList)
const selectedRoofMaterial = newRoofList.find((roof) => roof.selected)
setSurfaceShapePattern(currentObject, roofDisplay.column, false, selectedRoofMaterial)
closeAll()
}
const handleAlloc = () => { const handleAlloc = () => {
if (!checkInnerLines()) { if (!checkInnerLines()) {
apply() apply()
@ -167,16 +147,18 @@ export function useRoofAllocationSetting(id) {
let result = false let result = false
roofBases.forEach((roof) => { roofBases.forEach((roof) => {
roof.innerLines.forEach((line) => { if (roof.separatePolygon.length === 0) {
if (!line.attributes.actualSize || line.attributes?.actualSize === 0) { roof.innerLines.forEach((line) => {
line.set({ if (!line.attributes.actualSize || line.attributes?.actualSize === 0) {
strokeWidth: 4, line.set({
stroke: 'black', strokeWidth: 4,
selectable: true, stroke: 'black',
}) selectable: true,
result = true })
} result = true
}) }
})
}
}) })
if (result) canvas?.renderAll() if (result) canvas?.renderAll()
@ -184,7 +166,7 @@ export function useRoofAllocationSetting(id) {
} }
const apply = () => { const apply = () => {
const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF && !obj.isFixed) const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
const wallLines = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.WALL) const wallLines = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.WALL)
roofBases.forEach((roofBase) => { roofBases.forEach((roofBase) => {
try { try {
@ -207,6 +189,19 @@ export function useRoofAllocationSetting(id) {
canvas.remove(wallLine) canvas.remove(wallLine)
}) })
const newRoofList = currentRoofList.map((roof, idx) => {
return { ...roof, index: idx }
})
setBasicSetting((prev) => {
return {
...prev,
selectedRoofMaterial: newRoofList.find((roof) => roof.selected),
}
})
setRoofList(newRoofList)
const roofs = canvas.getObjects().filter((obj) => obj.name === 'roof') const roofs = canvas.getObjects().filter((obj) => obj.name === 'roof')
roofs.forEach((roof) => { roofs.forEach((roof) => {
@ -214,7 +209,12 @@ export function useRoofAllocationSetting(id) {
roof.set({ roof.set({
isFixed: true, isFixed: true,
}) })
setSurfaceShapePattern(roof, roofDisplay.column) setSurfaceShapePattern(
roof,
roofDisplay.column,
false,
currentRoofList.find((roof) => roof.selected),
)
drawDirectionArrow(roof) drawDirectionArrow(roof)
}) })
@ -246,25 +246,72 @@ export function useRoofAllocationSetting(id) {
canvas?.renderAll() canvas?.renderAll()
} }
const handleRadioOnChange = (e) => { // 지붕재 변경
setRadioValue(e.target) const handleChangeRoofMaterial = (value, index) => {
const selectedIndex = roofMaterials.findIndex((roof) => roof.selected)
const selectedRoofMaterial = roofMaterials.find((roof) => roof.roofMatlCd === value.id)
const newRoofList = currentRoofList.map((roof, idx) => {
if (idx === index) {
return { ...selectedRoofMaterial }
}
return roof
})
setCurrentRoofList(newRoofList)
}
// 기본 지붕재 radio값 변경
const handleDefaultRoofMaterial = (index) => {
const newRoofList = currentRoofList.map((roof, idx) => {
return { ...roof, selected: idx === index }
})
setCurrentRoofList(newRoofList)
}
// 서까래 변경
const handleChangeRaft = (e, index) => {
const raftValue = e.clCode
const newRoofList = currentRoofList.map((roof, idx) => {
if (idx === index) {
return { ...roof, raft: raftValue }
}
return roof
})
setCurrentRoofList(newRoofList)
}
const handleChangeLayout = (layoutValue, index) => {
const newRoofList = currentRoofList.map((roof, idx) => {
if (idx === index) {
return { ...roof, layout: layoutValue }
}
return roof
})
setCurrentRoofList(newRoofList)
} }
return { return {
handleSave, handleSave,
onAddRoofMaterial, onAddRoofMaterial,
onDeleteRoofMaterial, onDeleteRoofMaterial,
handleRadioOnChange,
handleAlloc, handleAlloc,
setLineSize, setLineSize,
widths,
lengths,
rafters,
values,
roofMaterials, roofMaterials,
selectedRoofMaterial, selectedRoofMaterial,
setSelectedRoofMaterial, basicSetting,
radioValue, setBasicSetting,
setRadioValue, currentRoofMaterial,
setCurrentRoofMaterial,
handleDefaultRoofMaterial,
handleChangeRoofMaterial,
handleChangeRaft,
handleChangeLayout,
handleSaveContext,
currentRoofList,
} }
} }

View File

@ -86,12 +86,24 @@ export function useRoofShapePassivitySetting(id) {
useEffect(() => { useEffect(() => {
const lines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') const lines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
let stroke, strokeWidth
lines.forEach((line) => { lines.forEach((line) => {
if (line.attributes?.type === LINE_TYPE.WALLLINE.EAVES || line.attributes?.type === LINE_TYPE.WALLLINE.HIPANDGABLE) {
stroke = '#45CD7D'
strokeWidth = 4
} else if (line.attributes?.type === LINE_TYPE.WALLLINE.GABLE || line.attributes?.type === LINE_TYPE.WALLLINE.JERKINHEAD) {
stroke = '#3FBAE6'
strokeWidth = 4
} else {
stroke = '#000000'
strokeWidth = 4
}
line.set({ line.set({
stroke: '#000000', stroke,
strokeWidth: 4, strokeWidth,
}) })
}) })
canvas.renderAll()
if (!currentObject) { if (!currentObject) {
return return
} }
@ -105,7 +117,6 @@ export function useRoofShapePassivitySetting(id) {
}) })
currentLineRef.current = currentObject currentLineRef.current = currentObject
canvas.renderAll() canvas.renderAll()
}, [currentObject]) }, [currentObject])
@ -125,6 +136,10 @@ export function useRoofShapePassivitySetting(id) {
const index = lines.findIndex((line) => line === selectedLine) const index = lines.findIndex((line) => line === selectedLine)
const nextLine = lines[index + 1] || lines[0] const nextLine = lines[index + 1] || lines[0]
if (nextLine.attributes?.isFixed) {
canvas.discardActiveObject()
return
}
canvas.setActiveObject(nextLine) canvas.setActiveObject(nextLine)
} }
@ -155,7 +170,7 @@ export function useRoofShapePassivitySetting(id) {
} }
currentLineRef.current.set({ currentLineRef.current.set({
attributes, attributes: { ...attributes, isFixed: true },
}) })
history.current.push(currentLineRef.current) history.current.push(currentLineRef.current)

View File

@ -185,9 +185,20 @@ export function useRoofShapeSetting(id) {
} }
case 4: { case 4: {
outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
outerLines.forEach((line) => { const pitch = outerLines.find((line) => line.attributes.type === LINE_TYPE.WALLLINE.SHED)?.attributes.pitch
// hideLine(line) // 변별로 설정중 한쪽흐름일 경우 한쪽흐름의 pitch로 설정
}) if (pitch) {
outerLines.forEach((line) => {
if (line.attributes.type === LINE_TYPE.WALLLINE.EAVES) {
line.attributes = {
...line.attributes,
pitch: pitch,
onlyOffset: true,
}
}
})
}
break break
} }
@ -205,6 +216,7 @@ export function useRoofShapeSetting(id) {
offset: eavesOffset / 10, offset: eavesOffset / 10,
pitch: pitchRef.current, pitch: pitchRef.current,
type: LINE_TYPE.WALLLINE.EAVES, type: LINE_TYPE.WALLLINE.EAVES,
onlyOffset: true,
} }
} }
@ -221,6 +233,7 @@ export function useRoofShapeSetting(id) {
offset: eavesOffset / 10, offset: eavesOffset / 10,
pitch: pitchRef.current, pitch: pitchRef.current,
type: LINE_TYPE.WALLLINE.EAVES, type: LINE_TYPE.WALLLINE.EAVES,
onlyOffset: true,
} }
} }
@ -251,6 +264,7 @@ export function useRoofShapeSetting(id) {
offset: eavesOffset / 10, offset: eavesOffset / 10,
pitch: pitchRef.current, pitch: pitchRef.current,
type: LINE_TYPE.WALLLINE.EAVES, type: LINE_TYPE.WALLLINE.EAVES,
onlyOffset: true,
} }
} }
@ -267,6 +281,7 @@ export function useRoofShapeSetting(id) {
offset: eavesOffset / 10, offset: eavesOffset / 10,
pitch: pitchRef.current, pitch: pitchRef.current,
type: LINE_TYPE.WALLLINE.EAVES, type: LINE_TYPE.WALLLINE.EAVES,
onlyOffset: true,
} }
} }
@ -296,6 +311,7 @@ export function useRoofShapeSetting(id) {
offset: eavesOffset / 10, offset: eavesOffset / 10,
pitch: pitchRef.current, pitch: pitchRef.current,
type: LINE_TYPE.WALLLINE.EAVES, type: LINE_TYPE.WALLLINE.EAVES,
onlyOffset: true,
} }
} }
@ -312,6 +328,7 @@ export function useRoofShapeSetting(id) {
offset: eavesOffset / 10, offset: eavesOffset / 10,
pitch: pitchRef.current, pitch: pitchRef.current,
type: LINE_TYPE.WALLLINE.EAVES, type: LINE_TYPE.WALLLINE.EAVES,
onlyOffset: true,
} }
} }
@ -342,6 +359,7 @@ export function useRoofShapeSetting(id) {
offset: eavesOffset / 10, offset: eavesOffset / 10,
pitch: pitchRef.current, pitch: pitchRef.current,
type: LINE_TYPE.WALLLINE.EAVES, type: LINE_TYPE.WALLLINE.EAVES,
onlyOffset: true,
} }
} }
@ -358,6 +376,7 @@ export function useRoofShapeSetting(id) {
offset: eavesOffset / 10, offset: eavesOffset / 10,
pitch: pitchRef.current, pitch: pitchRef.current,
type: LINE_TYPE.WALLLINE.EAVES, type: LINE_TYPE.WALLLINE.EAVES,
onlyOffset: true,
} }
} }
@ -595,17 +614,23 @@ export function useRoofShapeSetting(id) {
break break
} }
} }
selectedLine.attributes = attributes selectedLine.attributes = { ...attributes, isFixed: true }
history.current.push(selectedLine)
canvas.renderAll() canvas.renderAll()
nextLineFocus(selectedLine) nextLineFocus(selectedLine)
} }
const nextLineFocus = (selectedLine) => { const nextLineFocus = (selectedLine) => {
const lines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') const lines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
const index = lines.findIndex((line) => line.idx === selectedLine.idx) const index = lines.findIndex((line) => line.idx === selectedLine.idx)
const nextLine = lines[index + 1] || lines[0] const nextLine = lines[index + 1] || lines[0]
if (nextLine.attributes.isFixed) {
canvas.discardActiveObject()
return
}
history.current.push(selectedLine)
canvas.setActiveObject(nextLine) canvas.setActiveObject(nextLine)
} }

View File

@ -13,7 +13,7 @@ import { useMouse } from '@/hooks/useMouse'
import { useLine } from '@/hooks/useLine' import { useLine } from '@/hooks/useLine'
import { useTempGrid } from '@/hooks/useTempGrid' import { useTempGrid } from '@/hooks/useTempGrid'
import { useEffect, useRef } from 'react' import { useEffect, useRef } from 'react'
import { distanceBetweenPoints, setSurfaceShapePattern } from '@/util/canvas-util' import { distanceBetweenPoints } from '@/util/canvas-util'
import { fabric } from 'fabric' import { fabric } from 'fabric'
import { calculateAngle } from '@/util/qpolygon-utils' import { calculateAngle } from '@/util/qpolygon-utils'
import { import {
@ -33,6 +33,7 @@ import { POLYGON_TYPE } from '@/common/common'
import { usePopup } from '@/hooks/usePopup' import { usePopup } from '@/hooks/usePopup'
import { roofDisplaySelector } from '@/store/settingAtom' import { roofDisplaySelector } from '@/store/settingAtom'
import { useRoofFn } from '@/hooks/common/useRoofFn'
// 면형상 배치 // 면형상 배치
export function usePlacementShapeDrawing(id) { export function usePlacementShapeDrawing(id) {
@ -46,6 +47,7 @@ export function usePlacementShapeDrawing(id) {
const { addLine, removeLine } = useLine() const { addLine, removeLine } = useLine()
const { addPolygonByLines, drawDirectionArrow } = usePolygon() const { addPolygonByLines, drawDirectionArrow } = usePolygon()
const { tempGridMode } = useTempGrid() const { tempGridMode } = useTempGrid()
const { setSurfaceShapePattern } = useRoofFn()
const verticalHorizontalMode = useRecoilValue(verticalHorizontalModeState) const verticalHorizontalMode = useRecoilValue(verticalHorizontalModeState)
const adsorptionPointAddMode = useRecoilValue(adsorptionPointAddModeState) const adsorptionPointAddMode = useRecoilValue(adsorptionPointAddModeState)

View File

@ -3,7 +3,7 @@
import { useRecoilValue } from 'recoil' import { useRecoilValue } from 'recoil'
import { canvasState, globalPitchState } from '@/store/canvasAtom' import { canvasState, globalPitchState } from '@/store/canvasAtom'
import { MENU, POLYGON_TYPE } from '@/common/common' import { MENU, POLYGON_TYPE } from '@/common/common'
import { getIntersectionPoint, setSurfaceShapePattern } from '@/util/canvas-util' import { getIntersectionPoint } from '@/util/canvas-util'
import { degreesToRadians } from '@turf/turf' import { degreesToRadians } from '@turf/turf'
import { QPolygon } from '@/components/fabric/QPolygon' import { QPolygon } from '@/components/fabric/QPolygon'
import { useSwal } from '@/hooks/useSwal' import { useSwal } from '@/hooks/useSwal'
@ -15,6 +15,7 @@ import { usePolygon } from '@/hooks/usePolygon'
import { fontSelector } from '@/store/fontAtom' import { fontSelector } from '@/store/fontAtom'
import { slopeSelector } from '@/store/commonAtom' import { slopeSelector } from '@/store/commonAtom'
import { QLine } from '@/components/fabric/QLine' import { QLine } from '@/components/fabric/QLine'
import { useRoofFn } from '@/hooks/common/useRoofFn'
export function useSurfaceShapeBatch() { export function useSurfaceShapeBatch() {
const { getMessage } = useMessage() const { getMessage } = useMessage()
@ -29,6 +30,7 @@ export function useSurfaceShapeBatch() {
const { addCanvasMouseEventListener, initEvent } = useEvent() const { addCanvasMouseEventListener, initEvent } = useEvent()
// const { addCanvasMouseEventListener, initEvent } = useContext(EventContext) // const { addCanvasMouseEventListener, initEvent } = useContext(EventContext)
const { closePopup } = usePopup() const { closePopup } = usePopup()
const { setSurfaceShapePattern } = useRoofFn()
const applySurfaceShape = (surfaceRefs, selectedType, id) => { const applySurfaceShape = (surfaceRefs, selectedType, id) => {
let length1, length2, length3, length4, length5 let length1, length2, length3, length4, length5

View File

@ -36,11 +36,20 @@ export function useAdsorptionPoint() {
canvas.renderAll() canvas.renderAll()
} }
const removeAdsorptionPoint = () => {
const adsorptionPoints = getAdsorptionPoints()
adsorptionPoints.forEach((adsorptionPoint) => {
canvas.remove(adsorptionPoint)
})
canvas.renderAll()
}
return { return {
adsorptionPointAddMode, adsorptionPointAddMode,
adsorptionPointMode, adsorptionPointMode,
adsorptionRange, adsorptionRange,
getAdsorptionPoints, getAdsorptionPoints,
adsorptionPointAddModeStateEvent, adsorptionPointAddModeStateEvent,
removeAdsorptionPoint,
} }
} }

View File

@ -3,7 +3,6 @@ import { useRecoilState, useRecoilValue } from 'recoil'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
import { canvasSizeState, canvasState, canvasZoomState, currentObjectState } from '@/store/canvasAtom' import { canvasSizeState, canvasState, canvasZoomState, currentObjectState } from '@/store/canvasAtom'
import { QPolygon } from '@/components/fabric/QPolygon' import { QPolygon } from '@/components/fabric/QPolygon'
import { usePlan } from '@/hooks/usePlan'
import { fontSelector } from '@/store/fontAtom' import { fontSelector } from '@/store/fontAtom'
// 캔버스에 필요한 이벤트 // 캔버스에 필요한 이벤트
@ -14,7 +13,6 @@ export function useCanvasEvent() {
const canvasSize = useRecoilValue(canvasSizeState) const canvasSize = useRecoilValue(canvasSizeState)
const [canvasZoom, setCanvasZoom] = useRecoilState(canvasZoomState) const [canvasZoom, setCanvasZoom] = useRecoilState(canvasZoomState)
const lengthTextOption = useRecoilValue(fontSelector('lengthText')) const lengthTextOption = useRecoilValue(fontSelector('lengthText'))
const { modifiedPlanFlag, setModifiedPlanFlag } = usePlan()
useEffect(() => { useEffect(() => {
canvas?.setZoom(canvasZoom / 100) canvas?.setZoom(canvasZoom / 100)
@ -40,10 +38,6 @@ export function useCanvasEvent() {
onChange: (e) => { onChange: (e) => {
const target = e.target const target = e.target
if (target.name !== 'mouseLine' && !modifiedPlanFlag) {
setModifiedPlanFlag((prev) => !prev)
}
if (target) { if (target) {
// settleDown(target) // settleDown(target)
} }
@ -58,10 +52,6 @@ export function useCanvasEvent() {
target.uuid = uuidv4() target.uuid = uuidv4()
} }
if (target.name !== 'mouseLine' && !modifiedPlanFlag) {
setModifiedPlanFlag((prev) => !prev)
}
if (target.type === 'QPolygon' || target.type === 'QLine') { if (target.type === 'QPolygon' || target.type === 'QLine') {
const textObjs = canvas?.getObjects().filter((obj) => obj.name === 'lengthText') const textObjs = canvas?.getObjects().filter((obj) => obj.name === 'lengthText')
textObjs.forEach((obj) => { textObjs.forEach((obj) => {
@ -164,9 +154,6 @@ export function useCanvasEvent() {
target.on('moving', (e) => { target.on('moving', (e) => {
target.uuid = uuidv4() target.uuid = uuidv4()
if (!modifiedPlanFlag) {
setModifiedPlanFlag((prev) => !prev)
}
if (target.parentDirection === 'left' || target.parentDirection === 'right') { if (target.parentDirection === 'left' || target.parentDirection === 'right') {
const minX = target.minX const minX = target.minX

View File

@ -1,7 +1,7 @@
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil' import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
import { canvasState, currentMenuState, currentObjectState } from '@/store/canvasAtom' import { canvasState, currentMenuState, currentObjectState } from '@/store/canvasAtom'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { MENU } from '@/common/common' import { MENU, POLYGON_TYPE } from '@/common/common'
import AuxiliarySize from '@/components/floor-plan/modal/auxiliary/AuxiliarySize' import AuxiliarySize from '@/components/floor-plan/modal/auxiliary/AuxiliarySize'
import { usePopup } from '@/hooks/usePopup' import { usePopup } from '@/hooks/usePopup'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
@ -23,7 +23,7 @@ import { useCommonUtils } from './common/useCommonUtils'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { useCanvasEvent } from '@/hooks/useCanvasEvent' import { useCanvasEvent } from '@/hooks/useCanvasEvent'
import { contextMenuListState, contextMenuState } from '@/store/contextMenu' import { contextMenuListState, contextMenuState } from '@/store/contextMenu'
import PanelEdit from '@/components/floor-plan/modal/module/PanelEdit' import PanelEdit, { PANEL_EDIT_TYPE } from '@/components/floor-plan/modal/module/PanelEdit'
import DimensionLineSetting from '@/components/floor-plan/modal/dimensionLine/DimensionLineSetting' import DimensionLineSetting from '@/components/floor-plan/modal/dimensionLine/DimensionLineSetting'
import ColumnRemove from '@/components/floor-plan/modal/module/column/ColumnRemove' import ColumnRemove from '@/components/floor-plan/modal/module/column/ColumnRemove'
import ColumnInsert from '@/components/floor-plan/modal/module/column/ColumnInsert' import ColumnInsert from '@/components/floor-plan/modal/module/column/ColumnInsert'
@ -35,6 +35,12 @@ import { useSurfaceShapeBatch } from '@/hooks/surface/useSurfaceShapeBatch'
import { fontSelector, globalFontAtom } from '@/store/fontAtom' import { fontSelector, globalFontAtom } from '@/store/fontAtom'
import { useLine } from '@/hooks/useLine' import { useLine } from '@/hooks/useLine'
import { useSwal } from '@/hooks/useSwal' import { useSwal } from '@/hooks/useSwal'
import ContextRoofAllocationSetting from '@/components/floor-plan/modal/roofAllocation/ContextRoofAllocationSetting'
import { useCanvasSetting } from './option/useCanvasSetting'
import { useGrid } from './common/useGrid'
import { useAdsorptionPoint } from './useAdsorptionPoint'
import { useRoofFn } from '@/hooks/common/useRoofFn'
import { MODULE_ALIGN_TYPE, useModule } from './module/useModule'
export function useContextMenu() { export function useContextMenu() {
const canvas = useRecoilValue(canvasState) const canvas = useRecoilValue(canvasState)
@ -56,8 +62,13 @@ export function useContextMenu() {
const { moveSurfaceShapeBatch } = useSurfaceShapeBatch() const { moveSurfaceShapeBatch } = useSurfaceShapeBatch()
const [globalFont, setGlobalFont] = useRecoilState(globalFontAtom) const [globalFont, setGlobalFont] = useRecoilState(globalFontAtom)
const { addLine, removeLine } = useLine() const { addLine, removeLine } = useLine()
const { removeGrid } = useGrid()
const { removeAdsorptionPoint } = useAdsorptionPoint()
const commonTextFont = useRecoilValue(fontSelector('commonText')) const commonTextFont = useRecoilValue(fontSelector('commonText'))
const { settingsData, setSettingsDataSave } = useCanvasSetting()
const { swalFire } = useSwal() const { swalFire } = useSwal()
const { alignModule } = useModule()
const { removeRoofMaterial, removeAllRoofMaterial } = useRoofFn()
const currentMenuSetting = () => { const currentMenuSetting = () => {
switch (currentMenu) { switch (currentMenu) {
@ -77,7 +88,15 @@ export function useContextMenu() {
{ {
id: 'gridColorEdit', id: 'gridColorEdit',
name: getMessage('modal.grid.color.edit'), name: getMessage('modal.grid.color.edit'),
component: <ColorPickerModal id={popupId} color={gridColor} setColor={setGridColor} />, component: (
<ColorPickerModal
id={popupId}
color={gridColor}
setColor={setGridColor}
settingsData={settingsData}
setSettingsDataSave={setSettingsDataSave}
/>
),
}, },
{ {
id: 'remove', id: 'remove',
@ -86,6 +105,10 @@ export function useContextMenu() {
{ {
id: 'removeAll', id: 'removeAll',
name: getMessage('delete.all'), name: getMessage('delete.all'),
fn: () => {
removeGrid()
removeAdsorptionPoint()
},
}, },
], ],
]) ])
@ -105,14 +128,17 @@ export function useContextMenu() {
{ {
id: 'roofMaterialPlacement', id: 'roofMaterialPlacement',
name: getMessage('contextmenu.roof.material.placement'), name: getMessage('contextmenu.roof.material.placement'),
component: <RoofAllocationSetting id={popupId} />,
}, },
{ {
id: 'roofMaterialRemove', id: 'roofMaterialRemove',
name: getMessage('contextmenu.roof.material.remove'), name: getMessage('contextmenu.roof.material.remove'),
fn: () => removeRoofMaterial(),
}, },
{ {
id: 'roofMaterialRemoveAll', id: 'roofMaterialRemoveAll',
name: getMessage('contextmenu.roof.material.remove.all'), name: getMessage('contextmenu.roof.material.remove.all'),
fn: () => removeAllRoofMaterial(),
}, },
{ {
id: 'selectMove', id: 'selectMove',
@ -377,7 +403,7 @@ export function useContextMenu() {
{ {
id: 'roofMaterialEdit', id: 'roofMaterialEdit',
name: getMessage('contextmenu.roof.material.edit'), name: getMessage('contextmenu.roof.material.edit'),
component: <RoofAllocationSetting id={popupId} />, component: <ContextRoofAllocationSetting id={popupId} />,
}, },
{ {
id: 'linePropertyEdit', id: 'linePropertyEdit',
@ -516,7 +542,15 @@ export function useContextMenu() {
{ {
id: 'gridColorEdit', id: 'gridColorEdit',
name: getMessage('contextmenu.grid.color.edit'), name: getMessage('contextmenu.grid.color.edit'),
component: <ColorPickerModal id={popupId} color={gridColor} setColor={setGridColor} />, component: (
<ColorPickerModal
id={popupId}
color={gridColor}
setColor={setGridColor}
settingsData={settingsData}
setSettingsDataSave={setSettingsDataSave}
/>
),
}, },
{ {
id: 'remove', id: 'remove',
@ -530,12 +564,8 @@ export function useContextMenu() {
id: 'removeAll', id: 'removeAll',
name: getMessage('contextmenu.remove.all'), name: getMessage('contextmenu.remove.all'),
fn: () => { fn: () => {
canvas removeGrid()
.getObjects() removeAdsorptionPoint()
.filter((obj) => ['tempGrid', 'lineGrid', 'dotGrid'].includes(obj.name))
.forEach((grid) => {
canvas.remove(grid)
})
canvas.discardActiveObject() canvas.discardActiveObject()
}, },
}, },
@ -597,34 +627,35 @@ export function useContextMenu() {
], ],
]) ])
break break
case 'panel': case 'module':
setContextMenu([ setContextMenu([
[ [
{ {
id: 'remove', id: 'remove',
name: getMessage('contextmenu.remove'), name: getMessage('contextmenu.remove'),
fn: () => deleteObject(),
}, },
{ {
id: 'move', id: 'move',
name: getMessage('contextmenu.move'), name: getMessage('contextmenu.move'),
component: <PanelEdit id={popupId} type={'move'} />, component: <PanelEdit id={popupId} type={PANEL_EDIT_TYPE.MOVE} />,
}, },
{ {
id: 'copy', id: 'copy',
name: getMessage('contextmenu.copy'), name: getMessage('contextmenu.copy'),
component: <PanelEdit id={popupId} type={'copy'} />, component: <PanelEdit id={popupId} type={PANEL_EDIT_TYPE.COPY} />,
}, },
], ],
[ [
{ {
id: 'columnMove', id: 'columnMove',
name: getMessage('contextmenu.column.move'), name: getMessage('contextmenu.column.move'),
component: <PanelEdit id={popupId} type={'move'} />, component: <PanelEdit id={popupId} type={PANEL_EDIT_TYPE.COLUMN_MOVE} />,
}, },
{ {
id: 'columnCopy', id: 'columnCopy',
name: getMessage('contextmenu.column.copy'), name: getMessage('contextmenu.column.copy'),
component: <PanelEdit id={popupId} type={'copy'} />, component: <PanelEdit id={popupId} type={PANEL_EDIT_TYPE.COLUMN_COPY} />,
}, },
{ {
id: 'columnRemove', id: 'columnRemove',
@ -641,12 +672,12 @@ export function useContextMenu() {
{ {
id: 'rowMove', id: 'rowMove',
name: getMessage('contextmenu.row.move'), name: getMessage('contextmenu.row.move'),
component: <PanelEdit id={popupId} type={'move'} />, component: <PanelEdit id={popupId} type={PANEL_EDIT_TYPE.ROW_MOVE} />,
}, },
{ {
id: 'rowCopy', id: 'rowCopy',
name: getMessage('contextmenu.row.copy'), name: getMessage('contextmenu.row.copy'),
component: <PanelEdit id={popupId} type={'copy'} />, component: <PanelEdit id={popupId} type={PANEL_EDIT_TYPE.ROW_COPY} />,
}, },
{ {
id: 'rowRemove', id: 'rowRemove',
@ -661,37 +692,39 @@ export function useContextMenu() {
], ],
]) ])
break break
case 'module': case 'moduleSetupSurface':
case 'dimensionLineText': case 'roof':
setContextMenu([ setContextMenu([
[ [
{ {
id: 'moduleVerticalCenterAlign', id: 'moduleVerticalCenterAlign',
name: getMessage('contextmenu.module.vertical.align'), name: getMessage('contextmenu.module.vertical.align'),
fn: () => alignModule(MODULE_ALIGN_TYPE.VERTICAL),
}, },
{ {
id: 'moduleHorizonCenterAlign', id: 'moduleHorizonCenterAlign',
name: getMessage('contextmenu.module.horizon.align'), name: getMessage('contextmenu.module.horizon.align'),
}, fn: () => alignModule(MODULE_ALIGN_TYPE.HORIZONTAL),
{
id: 'moduleLeftAlign',
name: getMessage('contextmenu.module.left.align'),
},
{
id: 'moduleRightAlign',
name: getMessage('contextmenu.module.right.align'),
},
{
id: 'moduleUpAlign',
name: getMessage('contextmenu.module.up.align'),
},
{
id: 'moduleDownAlign',
name: getMessage('contextmenu.module.down.align'),
}, },
{ {
id: 'moduleRemove', id: 'moduleRemove',
name: getMessage('contextmenu.module.remove'), name: getMessage('contextmenu.module.remove'),
fn: () => {
const moduleSetupSurface = canvas.getObjects().filter((obj) => canvas.getActiveObjects()[0].id === obj.id)[0]
const modules = canvas.getObjects().filter((obj) => obj.surfaceId === moduleSetupSurface.id && obj.name === POLYGON_TYPE.MODULE)
canvas.remove(...modules)
canvas.renderAll()
},
},
{
id: 'moduleMove',
name: getMessage('contextmenu.module.move'),
component: <PanelEdit id={popupId} type={PANEL_EDIT_TYPE.MOVE_ALL} />,
},
{
id: 'moduleCopy',
name: getMessage('contextmenu.module.copy'),
component: <PanelEdit id={popupId} type={PANEL_EDIT_TYPE.COPY_ALL} />,
}, },
{ {
id: 'moduleCircuitNumberEdit', id: 'moduleCircuitNumberEdit',

View File

@ -86,13 +86,14 @@ export const useLine = () => {
const addPitchText = (line) => { const addPitchText = (line) => {
removePitchText(line) removePitchText(line)
const { startPoint, endPoint, direction, attributes } = line const { startPoint, endPoint, direction, attributes } = line
const { offset, onlyOffset = false } = attributes
let left, top let left, top
const textStr = const textStr =
currentAngleType === ANGLE_TYPE.SLOPE currentAngleType === ANGLE_TYPE.SLOPE
? `${attributes.offset ? attributes.offset * 10 : attributes.width * 10}${attributes.pitch ? '-∠' + attributes.pitch + angleUnit : ''}` ? `${attributes.offset ? attributes.offset * 10 : attributes.width * 10}${onlyOffset && attributes.pitch ? '-∠' + attributes.pitch + angleUnit : ''}`
: `${attributes.offset ? attributes.offset * 10 : attributes.width * 10}${attributes.pitch ? '-∠' + getDegreeByChon(attributes.pitch) + angleUnit : ''}` : `${attributes.offset ? attributes.offset * 10 : attributes.width * 10}${onlyOffset && attributes.pitch ? '-∠' + getDegreeByChon(attributes.pitch) + angleUnit : ''}`
if (direction === 'top') { if (direction === 'top') {
left = (startPoint.x + endPoint.x) / 2 left = (startPoint.x + endPoint.x) / 2

View File

@ -1,7 +1,7 @@
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { useRecoilState } from 'recoil' import { useRecoilState } from 'recoil'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
import { canvasState, currentCanvasPlanState, plansState, modifiedPlansState, modifiedPlanFlagState } from '@/store/canvasAtom' import { canvasState, currentCanvasPlanState, plansState } from '@/store/canvasAtom'
import { useAxios } from '@/hooks/useAxios' import { useAxios } from '@/hooks/useAxios'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { useSwal } from '@/hooks/useSwal' import { useSwal } from '@/hooks/useSwal'
@ -12,14 +12,11 @@ import { useCanvas } from '@/hooks/useCanvas'
export function usePlan() { export function usePlan() {
const [planNum, setPlanNum] = useState(0) const [planNum, setPlanNum] = useState(0)
const [selectedPlan, setSelectedPlan] = useState(null) const [selectedPlan, setSelectedPlan] = useState(null)
const [currentCanvasStatus, setCurrentCanvasStatus] = useState(null)
const [canvas, setCanvas] = useRecoilState(canvasState) const [canvas, setCanvas] = useRecoilState(canvasState)
const [currentCanvasPlan, setCurrentCanvasPlan] = useRecoilState(currentCanvasPlanState) const [currentCanvasPlan, setCurrentCanvasPlan] = useRecoilState(currentCanvasPlanState)
const [plans, setPlans] = useRecoilState(plansState) // 전체 plan const [plans, setPlans] = useRecoilState(plansState) // 전체 plan
const [modifiedPlans, setModifiedPlans] = useRecoilState(modifiedPlansState) // 변경된 canvas plan
const [modifiedPlanFlag, setModifiedPlanFlag] = useRecoilState(modifiedPlanFlagState) // 캔버스 실시간 오브젝트 이벤트 감지 flag
const { swalFire } = useSwal() const { swalFire } = useSwal()
const { getMessage } = useMessage() const { getMessage } = useMessage()
@ -95,64 +92,6 @@ export function usePlan() {
return addCanvas() return addCanvas()
} }
/**
* 캔버스에서 발생하는 실시간 오브젝트 이벤트를 감지하여 수정 여부를 확인 관리
*/
const checkCanvasObjectEvent = (planId) => {
setCurrentCanvasStatus(currentCanvasData())
if (!modifiedPlans.some((modifiedPlan) => modifiedPlan === planId) && checkModifiedCanvasPlan(planId)) {
setModifiedPlans((prev) => [...prev, planId])
setModifiedPlanFlag(false)
}
}
useEffect(() => {
if (currentCanvasStatus) {
setCurrentCanvasPlan((prev) => ({ ...prev, canvasStatus: currentCanvasStatus }))
}
}, [currentCanvasStatus])
/**
* 현재 캔버스 상태와 DB에 저장된 캔버스 상태를 비교하여 수정 여부를 판단
*/
const checkModifiedCanvasPlan = (planId) => {
const planData = plans.find((plan) => plan.id === planId)
if (planData.canvasStatus === '') {
// 빈 상태로 저장된 캔버스
return true
}
// 각각 object들의 uuid 목록을 추출하여 비교
const canvasStatus = currentCanvasData()
const canvasObjsUuids = getObjectUuids(JSON.parse(canvasStatus).objects)
const dbObjsUuids = getObjectUuids(JSON.parse(planData.canvasStatus).objects)
return canvasObjsUuids.length !== dbObjsUuids.length || !canvasObjsUuids.every((uuid, index) => uuid === dbObjsUuids[index])
}
const getObjectUuids = (objects) => {
return objects
.filter((obj) => obj.hasOwnProperty('uuid'))
.map((obj) => obj.uuid)
.sort()
}
const resetModifiedPlans = () => {
setModifiedPlans([])
setModifiedPlanFlag(false)
}
/**
* 캔버스에 저장되지 않은 변경사항이 있을때 저장 여부를 확인 저장
*/
const checkUnsavedCanvasPlan = async () => {
swalFire({
text: `Plan ${currentCanvasPlan.ordering} ` + getMessage('plan.message.confirm.save.modified'),
type: 'confirm',
confirmFn: async () => {
await putCanvasStatus(currentCanvasPlan.canvasStatus)
},
})
resetModifiedPlans()
}
/** /**
* DB에 저장된 데이터를 canvas에서 사용할 있도록 포맷화 * DB에 저장된 데이터를 canvas에서 사용할 있도록 포맷화
*/ */
@ -206,8 +145,8 @@ export function usePlan() {
} }
await promisePost({ url: '/api/canvas-management/canvas-statuses', data: planData }) await promisePost({ url: '/api/canvas-management/canvas-statuses', data: planData })
.then((res) => { .then((res) => {
setPlans([...plans, { id: res.data, objectNo: objectNo, userId: userId, canvasStatus: canvasStatus, ordering: planNum + 1 }]) setPlans((plans) => [...plans, { id: res.data, objectNo: objectNo, userId: userId, canvasStatus: canvasStatus, ordering: planNum + 1 }])
handleCurrentPlan(res.data) updateCurrentPlan(res.data)
setPlanNum(planNum + 1) setPlanNum(planNum + 1)
}) })
.catch((error) => { .catch((error) => {
@ -228,7 +167,6 @@ export function usePlan() {
await promisePut({ url: '/api/canvas-management/canvas-statuses', data: planData }) await promisePut({ url: '/api/canvas-management/canvas-statuses', data: planData })
.then((res) => { .then((res) => {
setPlans((plans) => plans.map((plan) => (plan.id === currentCanvasPlan.id ? { ...plan, canvasStatus: canvasStatus } : plan))) setPlans((plans) => plans.map((plan) => (plan.id === currentCanvasPlan.id ? { ...plan, canvasStatus: canvasStatus } : plan)))
setModifiedPlans((modifiedPlans) => modifiedPlans.filter((planId) => planId !== currentCanvasPlan.id))
}) })
.catch((error) => { .catch((error) => {
swalFire({ text: error.message, icon: 'error' }) swalFire({ text: error.message, icon: 'error' })
@ -251,13 +189,11 @@ export function usePlan() {
/** /**
* plan 이동 * plan 이동
* 현재 plan의 작업상태를 확인, 저장 이동 * 현재 plan의 작업상태를 저장 이동
*/ */
const handleCurrentPlan = async (newCurrentId) => { const handleCurrentPlan = async (newCurrentId) => {
if (!currentCanvasPlan || currentCanvasPlan.id !== newCurrentId) { if (!currentCanvasPlan || currentCanvasPlan.id !== newCurrentId) {
if (currentCanvasPlan?.id && modifiedPlans.some((modifiedPlan) => modifiedPlan === currentCanvasPlan.id)) { await saveCanvas()
await saveCanvas()
}
updateCurrentPlan(newCurrentId) updateCurrentPlan(newCurrentId)
} }
} }
@ -281,9 +217,12 @@ export function usePlan() {
/** /**
* 새로운 plan 생성 * 새로운 plan 생성
* 현재 plan의 데이터가 있을 경우 복제 여부를 확인 * 현재 plan의 데이터가 있을 경우 현재 plan 저장 복제 여부를 확인
*/ */
const handleAddPlan = async (userId, objectNo) => { const handleAddPlan = async (userId, objectNo) => {
if (currentCanvasPlan?.id) {
await saveCanvas()
}
JSON.parse(currentCanvasData()).objects.length > 0 JSON.parse(currentCanvasData()).objects.length > 0
? swalFire({ ? swalFire({
text: `Plan ${currentCanvasPlan.ordering} ` + getMessage('plan.message.confirm.copy'), text: `Plan ${currentCanvasPlan.ordering} ` + getMessage('plan.message.confirm.copy'),
@ -324,7 +263,6 @@ export function usePlan() {
await delCanvasById(id) await delCanvasById(id)
.then((res) => { .then((res) => {
setPlans((plans) => plans.filter((plan) => plan.id !== id)) setPlans((plans) => plans.filter((plan) => plan.id !== id))
setModifiedPlans((modifiedPlans) => modifiedPlans.filter((planId) => planId !== currentCanvasPlan.id))
removeImage(currentCanvasPlan.id) removeImage(currentCanvasPlan.id)
swalFire({ text: getMessage('plan.message.delete') }) swalFire({ text: getMessage('plan.message.delete') })
}) })
@ -362,14 +300,6 @@ export function usePlan() {
canvas, canvas,
plans, plans,
selectedPlan, selectedPlan,
currentCanvasPlan,
setCurrentCanvasPlan,
modifiedPlans,
modifiedPlanFlag,
setModifiedPlanFlag,
checkCanvasObjectEvent,
checkUnsavedCanvasPlan,
resetModifiedPlans,
saveCanvas, saveCanvas,
handleCurrentPlan, handleCurrentPlan,
handleAddPlan, handleAddPlan,

View File

@ -1,10 +1,21 @@
import { useRecoilState } from 'recoil' import { useRecoilState } from 'recoil'
import { contextPopupState, popupState } from '@/store/popupAtom' import { contextPopupState, popupState } from '@/store/popupAtom'
/**
* 팝업 관리
* @returns
*/
export function usePopup() { export function usePopup() {
const [popup, setPopup] = useRecoilState(popupState) const [popup, setPopup] = useRecoilState(popupState)
const [contextMenuPopup, setContextMenuPopup] = useRecoilState(contextPopupState) const [contextMenuPopup, setContextMenuPopup] = useRecoilState(contextPopupState)
/**
* 팝업 추가
* @param {*} id 팝업 아이디
* @param {*} depth 팝업 깊이
* @param {*} component 팝업 컴포넌트
* @param {*} isConfig 팝업 타입
*/
const addPopup = (id, depth, component, isConfig = false) => { const addPopup = (id, depth, component, isConfig = false) => {
setPopup({ setPopup({
config: isConfig ? [...filterDepth(depth, isConfig), { id, depth, component, isConfig }] : [...popup.config], config: isConfig ? [...filterDepth(depth, isConfig), { id, depth, component, isConfig }] : [...popup.config],
@ -12,6 +23,11 @@ export function usePopup() {
}) })
} }
/**
* 팝업 닫기
* @param {*} id 팝업 아이디
* @param {*} isConfig 팝업 타입
*/
const closePopup = (id, isConfig = false) => { const closePopup = (id, isConfig = false) => {
if (contextMenuPopup) setContextMenuPopup(null) if (contextMenuPopup) setContextMenuPopup(null)
if (isConfig) { if (isConfig) {
@ -27,6 +43,10 @@ export function usePopup() {
} }
} }
/**
* 팝업 필터
* @param {*} depth 팝업 깊이
*/
const filterPopup = (depth) => { const filterPopup = (depth) => {
setPopup({ setPopup({
config: [...filterDepth(depth)], config: [...filterDepth(depth)],
@ -34,6 +54,12 @@ export function usePopup() {
}) })
} }
/**
* 팝업 자식 필터
* @param {*} id 팝업 아이디
* @param {*} isConfig 팝업 타입
* @returns
*/
const filterChildrenPopup = (id, isConfig) => { const filterChildrenPopup = (id, isConfig) => {
let target = [] let target = []
if (isConfig) { if (isConfig) {
@ -57,6 +83,10 @@ export function usePopup() {
} }
} }
/**
* 팝업 여러개 닫기
* @param {*} ids 팝업 아이디 배열
*/
const closePopups = (ids) => { const closePopups = (ids) => {
setPopup({ setPopup({
config: [...popup?.config.filter((child) => !ids.includes(child.id))], config: [...popup?.config.filter((child) => !ids.includes(child.id))],
@ -64,6 +94,9 @@ export function usePopup() {
}) })
} }
/**
* 팝업 전체 닫기
*/
const closeAll = () => { const closeAll = () => {
setPopup({ setPopup({
other: [], other: [],
@ -71,6 +104,9 @@ export function usePopup() {
}) })
} }
/**
* 이전 팝업 닫기
*/
const closePrevPopup = () => { const closePrevPopup = () => {
setPopup({ setPopup({
config: [...popup?.slice(popup?.length - 1)], config: [...popup?.slice(popup?.length - 1)],
@ -78,6 +114,12 @@ export function usePopup() {
}) })
} }
/**
* 팝업 깊이 필터
* @param {*} depth 팝업 깊이
* @param {*} isConfig 팝업 타입
* @returns
*/
const filterDepth = (depth, isConfig) => { const filterDepth = (depth, isConfig) => {
if (isConfig) { if (isConfig) {
return [...popup?.config.filter((child) => child.depth < depth)] return [...popup?.config.filter((child) => child.depth < depth)]

View File

@ -21,6 +21,8 @@ export const useSwal = () => {
text, text,
icon: icon === '' ? 'success' : icon, icon: icon === '' ? 'success' : icon,
confirmButtonText: '확인', confirmButtonText: '확인',
}).then(() => {
confirmFn()
}) })
} else if (type === 'confirm') { } else if (type === 'confirm') {
MySwal.fire({ MySwal.fire({

View File

@ -59,33 +59,36 @@ export async function setSession(data) {
await session.save() await session.save()
} }
export async function login(formData) { export async function login() {
const session = await getSession() const session = await getSession()
if (session) {
const userId = formData.get('id') redirect('/')
const password = formData.get('password')
console.log('id:', userId)
console.log('password:', password)
// const loginUser = await getUserByIdAndPassword({ userId, password })
const loginUser = {
id: 1,
userId: 'test123',
name: 'jinsoo Kim',
email: 'jinsoo.kim@example.com',
} }
if (!loginUser) { // const userId = formData.get('id')
throw Error('Wrong Credentials!') // const password = formData.get('password')
}
session.name = loginUser.name // console.log('id:', userId)
session.userId = loginUser.userId // console.log('password:', password)
session.email = loginUser.email
session.isLoggedIn = true
console.log('session:', session)
await session.save() // // const loginUser = await getUserByIdAndPassword({ userId, password })
redirect('/') // const loginUser = {
// id: 1,
// userId: 'test123',
// name: 'jinsoo Kim',
// email: 'jinsoo.kim@example.com',
// }
// if (!loginUser) {
// throw Error('Wrong Credentials!')
// }
// session.name = loginUser.name
// session.userId = loginUser.userId
// session.email = loginUser.email
// session.isLoggedIn = true
// console.log('session:', session)
// await session.save()
// redirect('/')
} }

View File

@ -3,8 +3,8 @@ export const defaultSession = {}
export const sessionOptions = { export const sessionOptions = {
password: process.env.SESSION_SECRET, password: process.env.SESSION_SECRET,
cookieName: 'lama-session', cookieName: 'lama-session',
// cookieOptions: { cookieOptions: {
// httpOnly: true, httpOnly: true,
// secure: process.env.NODE_ENV === 'production', secure: process.env.NODE_ENV === 'production',
// }, },
} }

View File

@ -157,6 +157,7 @@
"plan.menu.estimate": "見積", "plan.menu.estimate": "見積",
"plan.menu.estimate.roof.alloc": "屋根面の割り当て", "plan.menu.estimate.roof.alloc": "屋根面の割り当て",
"modal.roof.alloc.info": "※配置面初期設定で保存した[基本屋根材]を変更したり、屋根材を追加して割り当てることができます。", "modal.roof.alloc.info": "※配置面初期設定で保存した[基本屋根材]を変更したり、屋根材を追加して割り当てることができます。",
"modal.roof.alloc.default.roof.material": "基本屋根材",
"modal.roof.alloc.select.roof.material": "屋根材の選択", "modal.roof.alloc.select.roof.material": "屋根材の選択",
"modal.roof.alloc.select.parallel": "並列式", "modal.roof.alloc.select.parallel": "並列式",
"modal.roof.alloc.select.stairs": "カスケード", "modal.roof.alloc.select.stairs": "カスケード",
@ -296,7 +297,6 @@
"modal.actual.size.setting.plane.size.length": "廊下の寸法の長さ", "modal.actual.size.setting.plane.size.length": "廊下の寸法の長さ",
"modal.actual.size.setting.actual.size.length": "実寸長", "modal.actual.size.setting.actual.size.length": "実寸長",
"plan.message.confirm.save": "PLAN을 저장하시겠습니까?", "plan.message.confirm.save": "PLAN을 저장하시겠습니까?",
"plan.message.confirm.save.modified": "PLAN의 변경사항을 저장하시겠습니까?",
"plan.message.confirm.copy": "PLAN을 복사하시겠습니까?", "plan.message.confirm.copy": "PLAN을 복사하시겠습니까?",
"plan.message.confirm.delete": "PLAN을 삭제하시겠습니까?", "plan.message.confirm.delete": "PLAN을 삭제하시겠습니까?",
"plan.message.save": "저장되었습니다.", "plan.message.save": "저장되었습니다.",
@ -514,6 +514,8 @@
"color.darkblue": "남색(JA)", "color.darkblue": "남색(JA)",
"site.name": "Q.CAST III", "site.name": "Q.CAST III",
"site.sub_name": "太陽光発電システム図面管理サイト", "site.sub_name": "太陽光発電システム図面管理サイト",
"site.header.link1": "選択してください。",
"site.header.link2": "オンライン保証システム",
"board.notice.title": "お知らせ", "board.notice.title": "お知らせ",
"board.notice.sub.title": "お知らせ一覧", "board.notice.sub.title": "お知らせ一覧",
"board.faq.title": "FAQ", "board.faq.title": "FAQ",
@ -630,7 +632,7 @@
"stuff.detail.header.successCopy": "商品番号がコピーされました。", "stuff.detail.header.successCopy": "商品番号がコピーされました。",
"stuff.detail.header.failCopy": "存在しないものです。", "stuff.detail.header.failCopy": "存在しないものです。",
"stuff.detail.header.objectNo": "商品番号のコピーに失敗しました。", "stuff.detail.header.objectNo": "商品番号のコピーに失敗しました。",
"stuff.detail.header.specificationConfirmDate": "仕様拡張日", "stuff.detail.header.specificationConfirmDate": "仕様確認日",
"stuff.detail.header.lastEditDatetime": "更新日時", "stuff.detail.header.lastEditDatetime": "更新日時",
"stuff.detail.header.createDatetime": "登録日", "stuff.detail.header.createDatetime": "登録日",
"stuff.detail.required": "必須入力項目", "stuff.detail.required": "必須入力項目",
@ -664,6 +666,7 @@
"stuff.detail.tooltip.saleStoreId": "販売代理店または販売代理店IDを1文字以上入力してください", "stuff.detail.tooltip.saleStoreId": "販売代理店または販売代理店IDを1文字以上入力してください",
"stuff.detail.tooltip.surfaceType": "塩害地域の定義は各メーカーの設置マニュアルをご確認ください", "stuff.detail.tooltip.surfaceType": "塩害地域の定義は各メーカーの設置マニュアルをご確認ください",
"stuff.detail.tempSave.message1": "一時保存されました。商品番号を取得するには、必須項目をすべて入力してください。", "stuff.detail.tempSave.message1": "一時保存されました。商品番号を取得するには、必須項目をすべて入力してください。",
"stuff.detail.tempSave.message2": "担当者は10桁以下で入力してください.",
"stuff.detail.confirm.message1": "販売店情報を変更すると、設計依頼文書番号が削除されます。変更しますか?", "stuff.detail.confirm.message1": "販売店情報を変更すると、設計依頼文書番号が削除されます。変更しますか?",
"stuff.detail.delete.message1": "仕様が確定したものは削除できません。", "stuff.detail.delete.message1": "仕様が確定したものは削除できません。",
"stuff.detail.planList.title": "プランリスト", "stuff.detail.planList.title": "プランリスト",
@ -725,7 +728,7 @@
"stuff.search.grid.all": "全体", "stuff.search.grid.all": "全体",
"stuff.search.grid.selected": "選択", "stuff.search.grid.selected": "選択",
"stuff.search.grid.schSortTypeR": "最近の登録日", "stuff.search.grid.schSortTypeR": "最近の登録日",
"stuff.search.grid.schSortTypeU": "最近の更新日", "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": "風速",
@ -799,11 +802,14 @@
"main.storeName": "販売店名", "main.storeName": "販売店名",
"main.objectNo": "物件番号", "main.objectNo": "物件番号",
"main.faq": "FAQ", "main.faq": "FAQ",
"main.content.objectList.noData1": "登録された商品情報はありません.",
"main.content.objectList.noData2": "下のボタンをクリックして商品情報を登録してください.",
"main.content.objectList": "最近の更新物件一覧", "main.content.objectList": "最近の更新物件一覧",
"main.content.notice": "お知らせ", "main.content.notice": "お知らせ",
"main.content.download1": "操作マニュアル", "main.content.download1": "操作マニュアル",
"main.content.download2": "屋根の説明書", "main.content.download2": "屋根の説明書",
"main.content.noBusiness": "Hanwha Japanにお問い合わせください", "main.content.noBusiness": "Hanwha Japanにお問い合わせください",
"main.content.alert.noFile": "資料が準備中です",
"main.popup.login.popupTitle": "パスワード変更", "main.popup.login.popupTitle": "パスワード変更",
"main.popup.login.newPassword1": "新しいパスワードを入力", "main.popup.login.newPassword1": "新しいパスワードを入力",
"main.popup.login.newPassword2": "新規パスワード再入力", "main.popup.login.newPassword2": "新規パスワード再入力",
@ -934,5 +940,10 @@
"simulator.table.sub9": "予測発電量 (kWh)", "simulator.table.sub9": "予測発電量 (kWh)",
"simulator.notice.sub1": "Hanwha Japan 年間発電量", "simulator.notice.sub1": "Hanwha Japan 年間発電量",
"simulator.notice.sub2": "シミュレーション案内事項", "simulator.notice.sub2": "シミュレーション案内事項",
"master.moduletypeitem.message.error": "지붕재 코드를 입력하세요." "master.moduletypeitem.message.error": "지붕재 코드를 입력하세요.",
"can.not.move.module": "모듈을 이동할 수 없습니다.(JA)",
"can.not.copy.module": "모듈을 복사할 수 없습니다.(JA)",
"can.not.remove.module": "모듈을 삭제할 수 없습니다.(JA)",
"can.not.insert.module": "모듈을 삽입할 수 없습니다.(JA)",
"can.not.align.module": "모듈을 정렬할 수 없습니다.(JA)"
} }

View File

@ -161,6 +161,7 @@
"plan.menu.estimate": "견적서", "plan.menu.estimate": "견적서",
"plan.menu.estimate.roof.alloc": "지붕면 할당", "plan.menu.estimate.roof.alloc": "지붕면 할당",
"modal.roof.alloc.info": "※ 배치면 초기설정에서 저장한 [기본 지붕재]를 변경하거나, 지붕재를 추가하여 할당할 수 있습니다.", "modal.roof.alloc.info": "※ 배치면 초기설정에서 저장한 [기본 지붕재]를 변경하거나, 지붕재를 추가하여 할당할 수 있습니다.",
"modal.roof.alloc.default.roof.material": "기본지붕재",
"modal.roof.alloc.select.roof.material": "지붕재 선택", "modal.roof.alloc.select.roof.material": "지붕재 선택",
"modal.roof.alloc.select.parallel": "병렬식", "modal.roof.alloc.select.parallel": "병렬식",
"modal.roof.alloc.select.stairs": "계단식", "modal.roof.alloc.select.stairs": "계단식",
@ -301,7 +302,6 @@
"modal.actual.size.setting.plane.size.length": "복도치수 길이", "modal.actual.size.setting.plane.size.length": "복도치수 길이",
"modal.actual.size.setting.actual.size.length": "실제치수 길이", "modal.actual.size.setting.actual.size.length": "실제치수 길이",
"plan.message.confirm.save": "PLAN을 저장하시겠습니까?", "plan.message.confirm.save": "PLAN을 저장하시겠습니까?",
"plan.message.confirm.save.modified": "PLAN의 변경사항을 저장하시겠습니까?",
"plan.message.confirm.copy": "PLAN을 복사하시겠습니까?", "plan.message.confirm.copy": "PLAN을 복사하시겠습니까?",
"plan.message.confirm.delete": "PLAN을 삭제하시겠습니까?", "plan.message.confirm.delete": "PLAN을 삭제하시겠습니까?",
"plan.message.save": "저장되었습니다.", "plan.message.save": "저장되었습니다.",
@ -524,6 +524,8 @@
"color.darkblue": "남색", "color.darkblue": "남색",
"site.name": "Q.CAST III", "site.name": "Q.CAST III",
"site.sub_name": "태양광 발전 시스템 도면관리 사이트", "site.sub_name": "태양광 발전 시스템 도면관리 사이트",
"site.header.link1": "선택하세요.",
"site.header.link2": "온라인보증시스템",
"board.notice.title": "공지사항", "board.notice.title": "공지사항",
"board.notice.sub.title": "공지사항 목록", "board.notice.sub.title": "공지사항 목록",
"board.faq.title": "FAQ", "board.faq.title": "FAQ",
@ -640,7 +642,7 @@
"stuff.detail.header.successCopy": "물건번호가 복사되었습니다.", "stuff.detail.header.successCopy": "물건번호가 복사되었습니다.",
"stuff.detail.header.failCopy": "물건번호 복사에 실패했습니다.", "stuff.detail.header.failCopy": "물건번호 복사에 실패했습니다.",
"stuff.detail.header.objectNo": "물건번호", "stuff.detail.header.objectNo": "물건번호",
"stuff.detail.header.specificationConfirmDate": "사양확일", "stuff.detail.header.specificationConfirmDate": "사양확일",
"stuff.detail.header.lastEditDatetime": "갱신일시", "stuff.detail.header.lastEditDatetime": "갱신일시",
"stuff.detail.header.createDatetime": "등록일", "stuff.detail.header.createDatetime": "등록일",
"stuff.detail.required": "필수 입력항목", "stuff.detail.required": "필수 입력항목",
@ -674,6 +676,7 @@
"stuff.detail.tooltip.saleStoreId": "판매대리점 또는 판매대리점ID를 1자 이상 입력하세요", "stuff.detail.tooltip.saleStoreId": "판매대리점 또는 판매대리점ID를 1자 이상 입력하세요",
"stuff.detail.tooltip.surfaceType": "염해지역 정의는 각 메이커의 설치 메뉴얼을 확인해주십시오", "stuff.detail.tooltip.surfaceType": "염해지역 정의는 각 메이커의 설치 메뉴얼을 확인해주십시오",
"stuff.detail.tempSave.message1": "임시저장 되었습니다. 물건번호를 획득하려면 필수 항목을 모두 입력해 주십시오.", "stuff.detail.tempSave.message1": "임시저장 되었습니다. 물건번호를 획득하려면 필수 항목을 모두 입력해 주십시오.",
"stuff.detail.tempSave.message2": "담당자는 10자리 이하로 입력해 주십시오.",
"stuff.detail.confirm.message1": "판매점 정보를 변경하면, 설계의뢰 문서번호가 삭제됩니다. 변경하시겠습니까?", "stuff.detail.confirm.message1": "판매점 정보를 변경하면, 설계의뢰 문서번호가 삭제됩니다. 변경하시겠습니까?",
"stuff.detail.delete.message1": "사양이 확정된 물건은 삭제할 수 없습니다.", "stuff.detail.delete.message1": "사양이 확정된 물건은 삭제할 수 없습니다.",
"stuff.detail.planList.title": "플랜리스트", "stuff.detail.planList.title": "플랜리스트",
@ -735,7 +738,7 @@
"stuff.search.grid.all": "전체", "stuff.search.grid.all": "전체",
"stuff.search.grid.selected": "선택", "stuff.search.grid.selected": "선택",
"stuff.search.grid.schSortTypeR": "최근 등록일", "stuff.search.grid.schSortTypeR": "최근 등록일",
"stuff.search.grid.schSortTypeU": "최근 갱신일", "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": "풍속",
@ -809,11 +812,14 @@
"main.storeName": "판매점명", "main.storeName": "판매점명",
"main.objectNo": "물건번호", "main.objectNo": "물건번호",
"main.faq": "FAQ", "main.faq": "FAQ",
"main.content.objectList.noData1": "등록된 물건정보가 없습니다.",
"main.content.objectList.noData2": "아래 버튼을 클릭하여 물건정보를 등록하십시오.",
"main.content.objectList": "최근 갱신 물건목록", "main.content.objectList": "최근 갱신 물건목록",
"main.content.notice": "공지사항", "main.content.notice": "공지사항",
"main.content.download1": "조작메뉴얼", "main.content.download1": "조작메뉴얼",
"main.content.download2": "지붕설명서", "main.content.download2": "지붕설명서",
"main.content.noBusiness": "Hanwha Japan에 문의하세요", "main.content.noBusiness": "Hanwha Japan에 문의하세요",
"main.content.alert.noFile": "자료가 준비중입니다",
"main.popup.login.popupTitle": "비밀번호변경", "main.popup.login.popupTitle": "비밀번호변경",
"main.popup.login.newPassword1": "새 비밀번호 입력", "main.popup.login.newPassword1": "새 비밀번호 입력",
"main.popup.login.newPassword2": "새 비밀번호 재입력", "main.popup.login.newPassword2": "새 비밀번호 재입력",
@ -944,5 +950,10 @@
"simulator.table.sub9": "예측발전량 (kWh)", "simulator.table.sub9": "예측발전량 (kWh)",
"simulator.notice.sub1": "Hanwha Japan 연간 발전량", "simulator.notice.sub1": "Hanwha Japan 연간 발전량",
"simulator.notice.sub2": "시뮬레이션 안내사항", "simulator.notice.sub2": "시뮬레이션 안내사항",
"master.moduletypeitem.message.error": "지붕재 코드를 입력하세요." "master.moduletypeitem.message.error": "지붕재 코드를 입력하세요.",
"can.not.move.module": "모듈을 이동할 수 없습니다.",
"can.not.copy.module": "모듈을 복사할 수 없습니다.",
"can.not.remove.module": "모듈을 삭제할 수 없습니다.",
"can.not.insert.module": "모듈을 삽입할 수 없습니다.",
"can.not.align.module": "모듈을 정렬할 수 없습니다."
} }

42
src/models/apiModels.js Normal file
View File

@ -0,0 +1,42 @@
// 가대 목록 Request Models
export const trestleRequestModels = {
moduleTpCd: '',
roofMatlCd: '',
raftBaseCd: '',
trestleMkrCd: '',
constMthdCd: '',
roofBaseCd: '',
}
// 시공법 목록 Request Models
export const constructionRequestModels = {
moduleTpCd: '',
roofMatlCd: '',
trestleMkrCd: '',
constMthdCd: '',
roofBaseCd: '',
illuminationTp: '',
instHt: '',
stdWindSpeed: '',
stdSnowLd: '',
inclCd: '',
raftBaseCd: '',
roofPitch: 0,
}
// 가대 상세 Request Models
export const trestleDetailRequestModels = {
moduleTpCd: '',
roofMatlCd: '',
trestleMkrCd: '',
constMthdCd: '',
roofBaseCd: '',
illuminationTp: '',
instHt: '',
stdWindSpeed: '',
stdSnowLd: '',
inclCd: '',
constTp: '',
mixMatlNo: 0,
roofPitch: 0,
}

View File

@ -272,17 +272,6 @@ export const plansState = atom({
default: [], default: [],
}) })
// 변경된 canvas plan 목록
export const modifiedPlansState = atom({
key: 'modifiedPlansState',
default: [],
})
// 변경감지 flag
export const modifiedPlanFlagState = atom({
key: 'modifiedPlanFlagState',
default: false,
})
export const tempGridModeState = atom({ export const tempGridModeState = atom({
key: 'tempGridModeState', key: 'tempGridModeState',
default: false, default: false,

View File

@ -203,8 +203,15 @@ export const basicSettingState = atom({
default: { default: {
roofSizeSet: 1, roofSizeSet: 1,
roofAngleSet: 'slope', roofAngleSet: 'slope',
selectedRoofMaterial: {}, selectedRoofMaterial: {}, // 선택된 지붕재
roofs: [], // 지붕면 할당에서 추가된 지붕재 목록
}, },
dangerouslyAllowMutability: true,
})
export const addedRoofsState = atom({
key: 'addedRoofsState',
default: [],
}) })
// db에 등록된 지붕재 목록 // db에 등록된 지붕재 목록
@ -213,6 +220,23 @@ export const roofMaterialsAtom = atom({
default: [], default: [],
}) })
//현재 선택된 지붕재
export const selectedRoofMaterialSelector = selector({
key: 'selectedRoofMaterialSelector',
get: ({ get }) => {
return get(basicSettingState).selectedRoofMaterial
},
})
// QSelectBox에서 사용할 지붕재 목록
export const roofMaterialsSelector = selector({
key: 'roofMaterialsSelector',
get: ({ get }) => {
const roofMaterials = get(roofMaterialsAtom)
return roofMaterials.map((material) => ({ ...material, id: material.roofMatlCd, name: material.roofMatlNm }))
},
})
/** /**
* 현재 선택된 물건 번호 * 현재 선택된 물건 번호
*/ */

View File

@ -17,7 +17,7 @@ export const stuffSearchState = atom({
schOtherSelSaleStoreId: '', //1차 이외 판매대리점 선택 schOtherSelSaleStoreId: '', //1차 이외 판매대리점 선택
startRow: 1, startRow: 1,
endRow: 100, endRow: 100,
schSortType: 'R', //정렬조건 (R:최근등록일 U:최근수정일) schSortType: 'U', //정렬조건 (R:최근등록일 U:최근수정일)
pageNo: 1, pageNo: 1,
pageSize: 100, pageSize: 100,
}, },

File diff suppressed because it is too large Load Diff

View File

@ -207,6 +207,7 @@ header{
.select-box{ .select-box{
min-width: 165px; min-width: 165px;
margin-right: 8px; margin-right: 8px;
height: 30px;
>div{ >div{
width: 100%; width: 100%;
} }

View File

@ -155,6 +155,7 @@
.product-item-content{ .product-item-content{
margin-top: 30px; margin-top: 30px;
overflow: hidden; overflow: hidden;
height: 100%;
.recently-list{ .recently-list{
.recently-item{ .recently-item{
border: 1px solid #F2F2F2; border: 1px solid #F2F2F2;
@ -208,6 +209,25 @@
} }
} }
} }
.recently-no-data{
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
h3{
font-size: 16px;
color: #101010;
font-weight: 600;
margin-bottom: 5px;
}
p{
font-size: 12px;
color: #666;
font-weight: 400;
margin-bottom: 10px;
}
}
.notice-box{ .notice-box{
height: 100%; height: 100%;
overflow-y: auto; overflow-y: auto;

File diff suppressed because it is too large Load Diff

View File

@ -751,16 +751,8 @@ export const pointsToTurfPolygon = (points) => {
return turf.polygon([coordinates]) return turf.polygon([coordinates])
} }
export const polygonToTurfPolygon = (polygon) => { export function isOverlap(polygon1, polygon2) {
const coordinates = polygon.points.map((point) => [point.x, point.y]) return turf.booleanOverlap(polygon1, polygon2)
coordinates.push(coordinates[0])
return turf.polygon(
[coordinates],
{},
{
parentId: polygon.parentId,
},
)
} }
export const triangleToPolygon = (triangle) => { export const triangleToPolygon = (triangle) => {
@ -792,7 +784,7 @@ export const rectToPolygon = (rect) => {
} }
//면형상 선택 클릭시 지붕 패턴 입히기 //면형상 선택 클릭시 지붕 패턴 입히기
export function setSurfaceShapePattern(polygon, mode = 'onlyBorder', trestleMode = false) { function setSurfaceShapePattern(polygon, mode = 'onlyBorder', trestleMode = false) {
const ratio = window.devicePixelRatio || 1 const ratio = window.devicePixelRatio || 1
let width = 265 / 10 let width = 265 / 10
@ -1003,3 +995,17 @@ export function findAndRemoveClosestPoint(targetPoint, points) {
return closestPoint return closestPoint
} }
export function polygonToTurfPolygon(object, current = false) {
let coordinates
coordinates = object.points.map((point) => [point.x, point.y])
if (current) coordinates = object.getCurrentPoints().map((point) => [point.x, point.y])
coordinates.push(coordinates[0])
return turf.polygon(
[coordinates],
{},
{
parentId: object.parentId,
},
)
}

View File

@ -94,6 +94,11 @@ export const inputNumberCheck = (e) => {
} }
} }
// 값이 숫자인지 확인
export const numberCheck = (value) => {
return !isNaN(value)
}
/** /**
* 파이프함수 정의 * 파이프함수 정의
* @param {...any} fns 순수함수들 * @param {...any} fns 순수함수들