Merge branch 'dev' into feature/test-jy

This commit is contained in:
Jaeyoung Lee 2024-11-25 15:30:32 +09:00
commit 7bc2d9acd6
64 changed files with 4973 additions and 3560 deletions

View File

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

View File

@ -1,23 +1,40 @@
'use client' 'use client'
import { useEffect, useState } from 'react' import { createContext, useEffect, useState } from 'react'
import { ErrorBoundary } from 'next/dist/client/components/error-boundary' import { ErrorBoundary } from 'next/dist/client/components/error-boundary'
import { useCommonCode } from '@/hooks/common/useCommonCode' import { useCommonCode } from '@/hooks/common/useCommonCode'
import { usePlan } from '@/hooks/usePlan' import { usePlan } from '@/hooks/usePlan'
import ServerError from './error' import ServerError from './error'
import '@/styles/common.scss' import '@/styles/common.scss'
import GlobalSpinner from '@/components/common/spinner/GlobalSpinner'
export const QcastContext = createContext({
qcastState: {},
setQcastState: () => {},
isGlobalLoading: false,
setIsGlobalLoading: () => {},
})
export const QcastProvider = ({ children }) => { export const QcastProvider = ({ children }) => {
const [planSave, setPlanSave] = useState(false) const [planSave, setPlanSave] = useState(false)
const [isGlobalLoading, setIsGlobalLoading] = useState(false)
const { currentCanvasPlan, modifiedPlans, checkUnsavedCanvasPlan } = usePlan() const { currentCanvasPlan, modifiedPlans, checkUnsavedCanvasPlan } = usePlan()
const { commonCode, findCommonCode } = useCommonCode() const { commonCode, findCommonCode } = useCommonCode()
const [qcastState, setQcastState] = useState({
saleStoreId: '',
saleStoreName: '',
objectList: [],
businessCharger: null,
businessChargerMail: null,
})
useEffect(() => { useEffect(() => {
const targetElement = document.getElementById('canvas') const targetElement = document.getElementById('canvas')
if (!targetElement && currentCanvasPlan?.id && planSave) { if (!targetElement && currentCanvasPlan?.id && planSave) {
setPlanSave((prev) => !prev) setPlanSave((prev) => !prev)
checkUnsavedCanvasPlan(currentCanvasPlan.userId) checkUnsavedCanvasPlan(currentCanvasPlan.userId)
} else if (targetElement && currentCanvasPlan?.id) { } else if (targetElement && currentCanvasPlan?.id) {
setPlanSave(true) setPlanSave(true)
} }
@ -30,7 +47,14 @@ export const QcastProvider = ({ children }) => {
return ( return (
<> <>
<ErrorBoundary fallback={<ServerError />}>{children}</ErrorBoundary> {isGlobalLoading && (
<div className="fixed inset-0 bg-white z-50 flex items-center justify-center">
<GlobalSpinner />
</div>
)}
<QcastContext.Provider value={{ qcastState, setQcastState, isGlobalLoading, setIsGlobalLoading }}>
<ErrorBoundary fallback={<ServerError />}>{children}</ErrorBoundary>
</QcastContext.Provider>
</> </>
) )
} }

View File

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

View File

@ -0,0 +1,25 @@
'use server'
import fs from 'fs/promises'
import { NextResponse } from 'next/server'
export async function POST(req) {
const path = 'public/plan-bg-images'
const formData = await req.formData()
const file = formData.get('file')
const arrayBuffer = await file.arrayBuffer()
const buffer = Buffer.from(arrayBuffer)
// const buffer = new Uint8Array(arrayBuffer)
try {
await fs.readdir(path)
} catch {
await fs.mkdir(path)
} finally {
await fs.writeFile(`${path}/${file.name}`, buffer)
}
return NextResponse.json({ fileNm: `${file.name}` })
}

View File

@ -1,9 +1,73 @@
'ues client' 'ues client'
import { ErrorBoundary } from 'next/dist/client/components/error-boundary' import { correntObjectNoState } from '@/store/settingAtom'
import ServerError from '../error' import { notFound, usePathname, useSearchParams } from 'next/navigation'
// import { ErrorBoundary } from 'next/dist/client/components/error-boundary'
// import ServerError from '../error'
import { createContext, useEffect, useReducer, useState } from 'react'
import { useSetRecoilState } from 'recoil'
export const FloorPlanProvider = ({ children }) => { const reducer = (prevState, nextState) => {
console.log('FloorPlanProvider') return { ...prevState, ...nextState }
return <ErrorBoundary fallback={<ServerError />}>{children}</ErrorBoundary>
} }
const defaultEstimateData = {
estimateDate: new Date(), //견적일
charger: '', //담당자
objectName: '', //안건명
objectNameOmit: '', //경칭코드
estimateType: '', //주문분류
remarks: '', //비고
estimateOption: '', //견적특이사항
itemList: [],
fileList: [],
fileFlg: '0', //후일 자료 제출 (체크 1 노체크 0)
priceCd: '',
}
export const FloorPlanContext = createContext({
floorPlanState: {},
setFloorPlanState: () => {},
estimateContextState: {},
setEstimateContextState: () => {},
})
const FloorPlanProvider = ({ children }) => {
const pathname = usePathname()
const setCurrentObjectNo = useSetRecoilState(correntObjectNoState)
const searchParams = useSearchParams()
const objectNo = searchParams.get('objectNo')
const pid = searchParams.get('pid')
if (pathname === '/floor-plan') {
if (pid === undefined || pid === '' || objectNo === undefined || objectNo === '') {
notFound()
}
setCurrentObjectNo(objectNo)
}
const [floorPlanState, setFloorPlanState] = useState({
// 플랜 파일 업로드 모달 오픈 제어
refFileModalOpen: false,
// 플랜 회전 모드 제어
toggleRotate: false,
// 물건 번호
objectNo,
// 플랜 번호
pid,
})
useEffect(() => {
console.log('🚀 ~ FloorPlanProvider ~ floorPlanState:', floorPlanState)
}, [floorPlanState])
const [estimateContextState, setEstimateContextState] = useReducer(reducer, defaultEstimateData)
return (
<FloorPlanContext.Provider value={{ floorPlanState, setFloorPlanState, estimateContextState, setEstimateContextState }}>
{children}
</FloorPlanContext.Provider>
)
}
export default FloorPlanProvider

View File

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

View File

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

View File

@ -12,6 +12,7 @@ import '@/styles/contents.scss'
import ChangePasswordPop from './main/ChangePasswordPop' import ChangePasswordPop from './main/ChangePasswordPop'
import { searchState } from '@/store/boardAtom' import { searchState } from '@/store/boardAtom'
import { SessionContext } from '@/app/SessionProvider' import { SessionContext } from '@/app/SessionProvider'
import { QcastContext } from '@/app/QcastProvider'
export default function MainPage() { export default function MainPage() {
const { session } = useContext(SessionContext) const { session } = useContext(SessionContext)
@ -26,18 +27,20 @@ export default function MainPage() {
const [searchRadioType, setSearchRadioType] = useState('object') const [searchRadioType, setSearchRadioType] = useState('object')
const [saleStoreId, setSaleStoreId] = useState('') // const [saleStoreId, setSaleStoreId] = useState('')
const [saleStoreName, setSaleStoreName] = useState('') // const [saleStoreName, setSaleStoreName] = useState('')
const [stuffSearch, setStuffSearch] = useRecoilState(stuffSearchState) const [stuffSearch, setStuffSearch] = useRecoilState(stuffSearchState)
const [searchForm, setSearchForm] = useRecoilState(searchState) const [searchForm, setSearchForm] = useRecoilState(searchState)
useEffect(() => { const { qcastState } = useContext(QcastContext)
if (session.pwdInitYn === 'Y') {
fetchObjectList() // useEffect(() => {
} // if (session.pwdInitYn === 'Y') {
}, [session]) // fetchObjectList()
// }
// }, [session])
const fetchObjectList = async () => { const fetchObjectList = async () => {
try { try {
@ -108,7 +111,7 @@ export default function MainPage() {
</div> </div>
<span className="store-arr"></span> <span className="store-arr"></span>
<div className="store-id-name"> <div className="store-id-name">
{saleStoreId} / {saleStoreName} {qcastState?.saleStoreId} / {qcastState?.saleStoreName}
</div> </div>
</div> </div>
<div className="main-search-wrap"> <div className="main-search-wrap">

View File

@ -80,79 +80,32 @@ export default function Login() {
e.preventDefault() e.preventDefault()
const formData = new FormData(e.target) const formData = new FormData(e.target)
/////////////////////////////////////////////////////////// //
// const param = {
setSession({ loginId: formData.get('id'),
userId: 'NEW0166102', pwd: formData.get('password'),
saleStoreId: null,
name: null,
mail: null,
tel: null,
storeId: 'TEMP02',
userNm: 'ㅇㅇ6610',
userNmKana: '신규사용자 16610',
category: '인상6610',
telNo: '336610',
fax: null,
email: 't10t@naver.com',
pwdInitYn: 'Y',
storeLvl: '1',
groupId: '60000',
custCd: '100000',
})
setSessionState({
userId: 'NEW0166102',
saleStoreId: null,
name: null,
mail: null,
tel: null,
storeId: 'TEMP02',
userNm: 'ㅇㅇ6610',
userNmKana: '신규사용자 16610',
category: '인상6610',
telNo: '336610',
fax: null,
email: 't10t@naver.com',
pwdInitYn: 'Y',
storeLvl: '1',
groupId: '60000',
custCd: '100000',
})
if (chkLoginId) {
Cookies.set('chkLoginId', formData.get('id'), { expires: 7 })
} else {
Cookies.remove('chkLoginId')
} }
router.push('/') await promisePost({ url: '/api/login/v1.0/login', data: param })
// .then((res) => {
/////////////////////////////////////////////////////////// if (res) {
if (res.data.result.resultCode === 'S') {
// - ** ** setSession(res.data.data)
// const param = { setSessionState(res.data.data)
// loginId: formData.get('id'), // ID SAVE ,
// pwd: formData.get('password'), if (chkLoginId) {
// } Cookies.set('chkLoginId', formData.get('id'), { expires: 7 })
// await promisePost({ url: '/api/login/v1.0/login', data: param }) } else {
// .then((res) => { Cookies.remove('chkLoginId')
// if (res) { }
// if (res.data.result.resultCode === 'S') { router.push('/')
// setSession(res.data.data) } else {
// setSessionState(res.data.data) alert(res.data.result.resultMsg)
// // ID SAVE , }
// if (chkLoginId) { }
// Cookies.set('chkLoginId', formData.get('id'), { expires: 7 }) })
// } else { .catch((error) => {
// Cookies.remove('chkLoginId') alert(error.response.data.message)
// } })
// router.push('/')
// } else {
// alert(res.data.result.resultMsg)
// }
// }
// })
// .catch((error) => {
// alert(error.response.data.message)
// })
} }
// //

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -13,13 +13,12 @@ export default function DocDownOptionPop({ planNo, setEstimatePopupOpen }) {
const [schDownload, setSchDownload] = useState('EXCEL') const [schDownload, setSchDownload] = useState('EXCEL')
// EXCEL // EXCEL
const [schUnitPriceFlg, setSchUnitPriceFlg] = useState('0') const [schUnitPriceFlg, setSchUnitPriceFlg] = useState('0')
// //
const [schDisplayFlg, setSchSchDisplayFlg] = useState('0') const [schDisplayFlg, setSchSchDisplayFlg] = useState('0')
// // (:1 : 0)
const [schWeightFlg, setSchWeightFlg] = useState('0') const [schWeightFlg, setSchWeightFlg] = useState('1')
/// ///
const [schDrawingFlg, setSchDrawingFlg] = useState('0') const [schDrawingFlg, setSchDrawingFlg] = useState('1')
// recoil // recoil
const objectRecoil = useRecoilValue(floorPlanObjectState) const objectRecoil = useRecoilValue(floorPlanObjectState)
@ -38,6 +37,18 @@ export default function DocDownOptionPop({ planNo, setEstimatePopupOpen }) {
sendUnitPriceFlg = '1' sendUnitPriceFlg = '1'
} }
//schDrawingFlg
// , / schDrawingFlg |
//SchDrawingFlg (1 : ,2 : , 3 : , 4 : )
// ex) 1|2|3|4
let defaultSchDrawingFlg = '1'
if (schWeightFlg === '1') {
defaultSchDrawingFlg = defaultSchDrawingFlg.concat('|', '4')
}
if (schDrawingFlg === '1') {
defaultSchDrawingFlg = defaultSchDrawingFlg.concat('|', '2', '|', '3')
}
const params = { const params = {
objectNo: objectRecoil.floorPlanObjectNo, objectNo: objectRecoil.floorPlanObjectNo,
planNo: planNo, planNo: planNo,
@ -45,25 +56,27 @@ export default function DocDownOptionPop({ planNo, setEstimatePopupOpen }) {
schUnitPriceFlg: sendUnitPriceFlg, schUnitPriceFlg: sendUnitPriceFlg,
schDisplayFlg: schDisplayFlg, schDisplayFlg: schDisplayFlg,
schWeightFlg: schWeightFlg, schWeightFlg: schWeightFlg,
schDrawingFlg: schDrawingFlg, schDrawingFlg: defaultSchDrawingFlg,
pwrGnrSimType: 'D', //default
} }
const options = { responseType: 'blob' } const options = { responseType: 'blob' }
let fileName
console.log('다운로드 PARAM:::', params)
await promisePost({ url: url, data: params, option: options }) await promisePost({ url: url, data: params, option: options })
.then((resultData) => { .then((resultData) => {
if (resultData) { if (resultData) {
console.log('결과::::::', resultData) let fileName = 'unknow'
const blob = new Blob([resultData.data], { type: resultData.headers['content-type'] || 'application/octet-stream' }) const blob = new Blob([resultData.data], { type: resultData.headers['content-type'] || 'application/octet-stream' })
const fileUrl = window.URL.createObjectURL(blob) const fileUrl = window.URL.createObjectURL(blob)
const link = document.createElement('a') const link = document.createElement('a')
link.href = fileUrl link.href = fileUrl
if (schDownload === 'EXCEL') { //
fileName = 'EXCEL_DOWN.xlsx' const contentDisposition = resultData.headers['content-disposition']
} else { if (contentDisposition) {
fileName = 'PDF_DOWN.pdf' fileName = contentDisposition.split('filename=')[1].replace(/['"]/g, '')
} }
link.download = fileName link.download = fileName
document.body.appendChild(link) document.body.appendChild(link)
link.click() link.click()
@ -211,19 +224,6 @@ export default function DocDownOptionPop({ planNo, setEstimatePopupOpen }) {
<td> <td>
<div className="form-flex-wrap"> <div className="form-flex-wrap">
<div className="d-check-radio light mr10"> <div className="d-check-radio light mr10">
<input
type="radio"
name="schWeightFlg"
id="schWeightFlg0"
value={'0'}
checked={schWeightFlg === '0'}
onChange={(e) => {
setSchWeightFlg(e.target.value)
}}
/>
<label htmlFor="schWeightFlg0">{getMessage('estimate.detail.docPopup.schWeightFlg.schWeightFlg0')}</label>
</div>
<div className="d-check-radio light">
<input <input
type="radio" type="radio"
name="schWeightFlg" name="schWeightFlg"
@ -236,6 +236,19 @@ export default function DocDownOptionPop({ planNo, setEstimatePopupOpen }) {
/> />
<label htmlFor="schWeightFlg1">{getMessage('estimate.detail.docPopup.schWeightFlg.schWeightFlg1')}</label> <label htmlFor="schWeightFlg1">{getMessage('estimate.detail.docPopup.schWeightFlg.schWeightFlg1')}</label>
</div> </div>
<div className="d-check-radio light">
<input
type="radio"
name="schWeightFlg"
id="schWeightFlg0"
value={'0'}
checked={schWeightFlg === '0'}
onChange={(e) => {
setSchWeightFlg(e.target.value)
}}
/>
<label htmlFor="schWeightFlg0">{getMessage('estimate.detail.docPopup.schWeightFlg.schWeightFlg0')}</label>
</div>
</div> </div>
</td> </td>
</tr> </tr>
@ -244,6 +257,19 @@ export default function DocDownOptionPop({ planNo, setEstimatePopupOpen }) {
<td> <td>
<div className="form-flex-wrap"> <div className="form-flex-wrap">
<div className="d-check-radio light mr10"> <div className="d-check-radio light mr10">
<input
type="radio"
name="schDrawingFlg"
id="schDrawingFlg1"
value={'1'}
checked={schDrawingFlg === '1'}
onChange={(e) => {
setSchDrawingFlg(e.target.value)
}}
/>
<label htmlFor="schDrawingFlg1">{getMessage('estimate.detail.docPopup.schDrawingFlg.schDrawingFlg1')}</label>
</div>
<div className="d-check-radio light">
<input <input
type="radio" type="radio"
name="schDrawingFlg" name="schDrawingFlg"
@ -256,19 +282,6 @@ export default function DocDownOptionPop({ planNo, setEstimatePopupOpen }) {
/> />
<label htmlFor="schDrawingFlg0">{getMessage('estimate.detail.docPopup.schDrawingFlg.schDrawingFlg0')}</label> <label htmlFor="schDrawingFlg0">{getMessage('estimate.detail.docPopup.schDrawingFlg.schDrawingFlg0')}</label>
</div> </div>
<div className="d-check-radio light">
<input
type="radio"
name="schDrawingFlg"
id="schDrawingFlg01"
value={'1'}
checked={schDrawingFlg === '1'}
onChange={(e) => {
setSchDrawingFlg(e.target.value)
}}
/>
<label htmlFor="schDrawingFlg01">{getMessage('estimate.detail.docPopup.schDrawingFlg.schDrawingFlg1')}</label>
</div>
</div> </div>
</td> </td>
</tr> </tr>

View File

@ -0,0 +1,313 @@
'use client'
import { useEffect, useState, useContext, useRef } from 'react'
import { useMessage } from '@/hooks/useMessage'
import { useAxios } from '@/hooks/useAxios'
import Select, { components } from 'react-select'
import { SessionContext } from '@/app/SessionProvider'
import { isEmptyArray, isObjectNotEmpty } from '@/util/common-utils'
import { useEstimateController } from '@/hooks/floorPlan/estimate/useEstimateController'
export default function EstimateCopyPop({ planNo, setEstimateCopyPopupOpen }) {
const { getMessage } = useMessage()
const { get } = useAxios()
const { handleEstimateCopy, state } = useEstimateController(planNo)
const { session } = useContext(SessionContext)
const [saleStoreList, setSaleStoreList] = useState([]) //
const [favoriteStoreList, setFavoriteStoreList] = useState([]) //
const [showSaleStoreList, setShowSaleStoreList] = useState([]) //
const [otherSaleStoreList, setOtherSaleStoreList] = useState([])
const [originOtherSaleStoreList, setOriginOtherSaleStoreList] = useState([])
const [saleStoreId, setSaleStoreId] = useState('') // 1
const [otherSaleStoreId, setOtherSaleStoreId] = useState('') // 1
const [sendPlanNo, setSendPlanNo] = useState('1')
const [copyReceiveUser, setCopyReceiveUser] = useState('')
const ref = useRef() //2
useEffect(() => {
let url
let firstList
let favList
let otherList
if (session.storeId === 'T01') {
url = `/api/object/saleStore/${session?.storeId}/firstList?userId=${session?.userId}`
} else {
if (session.storeLvl === '1') {
url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=1&userId=${session?.userId}`
} else {
//T01 or 1
// url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=1&userId=${session?.userId}`
}
}
get({ url: url }).then((res) => {
if (!isEmptyArray(res)) {
res.map((row) => {
//
row.value = row.saleStoreId
row.label = row.saleStoreName
})
if (session.storeId === 'T01') {
firstList = res
//T01
firstList.sort((a, b) => (a.saleStoreId !== 'T01') - (b.saleStoreId !== 'T01') || a.saleStoreId - b.saleStoreId)
favList = firstList.filter((row) => row.saleStoreId === 'T01' || row.priority !== 'B')
setSaleStoreList(firstList)
setFavoriteStoreList(favList)
setShowSaleStoreList(favList)
setSaleStoreId(session?.storeId)
// T01 T01 2 (onSelectionChange..) 2
url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=0&userId=${session?.userId}`
get({ url: url }).then((res) => {
if (!isEmptyArray(res)) {
res.map((row) => {
row.value == row.saleStoreId
row.label = row.saleStoreName
})
otherList = res
setOtherSaleStoreList(otherList)
setOriginOtherSaleStoreList(otherList)
} else {
setOtherSaleStoreList([])
}
})
} else {
if (session.storeLvl === '1') {
firstList = res
favList = res.filter((row) => row.priority !== 'B')
otherList = res.filter((row) => row.firstAgentYn === 'N')
setSaleStoreList(firstList)
setFavoriteStoreList(firstList)
setShowSaleStoreList(firstList)
setSaleStoreId(firstList[0].saleStoreId)
setOtherSaleStoreList(otherList)
} else {
//T01 or 1
}
}
}
})
}, [])
useEffect(() => {
if (planNo) {
setSendPlanNo(planNo)
}
}, [planNo])
useEffect(() => {
if (state?.charger) {
setCopyReceiveUser(state.charger)
}
}, [state.charger])
//T01 1
const onInputChange = (key) => {
if (key !== '') {
setShowSaleStoreList(saleStoreList)
} else {
setShowSaleStoreList(favoriteStoreList)
}
}
// 1
const onSelectionChange = (key) => {
if (isObjectNotEmpty(key)) {
if (key.saleStoreId === saleStoreId) {
return
}
}
if (isObjectNotEmpty(key)) {
setSaleStoreId(key.saleStoreId)
const url = `/api/object/saleStore/${key.saleStoreId}/list?firstFlg=0&userId=${session?.userId}`
let otherList
get({ url: url }).then((res) => {
if (!isEmptyArray(res)) {
res.map((row) => {
row.value = row.saleStoreId
row.label = row.saleStoreName
})
otherList = res
setOtherSaleStoreList(otherList)
setOtherSaleStoreId('')
} else {
setOtherSaleStoreId('')
setOtherSaleStoreList([])
}
})
} else {
setSaleStoreId('')
//otherSaleStoreId onSelectionChange2
setOtherSaleStoreList(originOtherSaleStoreList)
handleClear()
}
}
// 2
const onSelectionChange2 = (key) => {
if (isObjectNotEmpty(key)) {
if (key.saleStoreId === otherSaleStoreId) {
return
}
}
if (isObjectNotEmpty(key)) {
setOtherSaleStoreId(key.saleStoreId)
} else {
setOtherSaleStoreId('')
}
}
//2
const handleClear = () => {
if (ref.current) {
ref.current.clearValue()
}
}
return (
<div className="modal-popup">
<div className="modal-dialog">
<div className="modal-content">
<div className="modal-header">
<h1 className="title">{getMessage('estimate.detail.estimateCopyPopup.title')}</h1>
<button
type="button"
className="modal-close"
onClick={() => {
setEstimateCopyPopupOpen(false)
}}
>
{getMessage('estimate.detail.estimateCopyPopup.close')}
</button>
</div>
<div className="modal-body">
<div className="modal-body-inner">
<div className="explane">{getMessage('estimate.detail.estimateCopyPopup.explane')}</div>
<div className="estimate-copy-info-wrap">
<div className="estimate-copy-info-item">
<div className="estimate-copy-info-tit">
{getMessage('estimate.detail.estimateCopyPopup.label.saleStoreId')} <span className="red">*</span>
</div>
{session.storeId === 'T01' && (
<div className="estimate-copy-info-box">
<div className="estimate-copy-sel">
<Select
id="long-value-select1"
instanceId="long-value-select1"
className="react-select-custom"
classNamePrefix="custom"
placeholder="Select"
options={showSaleStoreList}
onInputChange={onInputChange}
onChange={onSelectionChange}
getOptionLabel={(x) => x.saleStoreName}
getOptionValue={(x) => x.saleStoreId}
isClearable={true}
isDisabled={false}
value={saleStoreList.filter(function (option) {
return option.saleStoreId === saleStoreId
})}
/>
</div>
<div className="estimate-copy-id">{saleStoreId}</div>
</div>
)}
{session.storeId !== 'T01' && session.storeLvl === '1' && (
<div className="estimate-copy-info-box">
<div className="estimate-copy-sel">
<Select
id="long-value-select1"
instanceId="long-value-select1"
className="react-select-custom"
classNamePrefix="custom"
placeholder="Select"
options={showSaleStoreList[0]}
onChange={onSelectionChange}
getOptionLabel={(x) => x.saleStoreName}
getOptionValue={(x) => x.saleStoreId}
isClearable={false}
isDisabled={session?.storeLvl !== '1' ? true : session?.storeId !== 'T01' ? true : false}
value={showSaleStoreList.filter(function (option) {
return option.saleStoreId === saleStoreId
})}
/>
</div>
<div className="estimate-copy-id">{saleStoreId}</div>
</div>
)}
</div>
<div className="estimate-copy-info-item">
<div className="estimate-copy-info-tit">{getMessage('estimate.detail.estimateCopyPopup.label.otherSaleStoreId')}</div>
<div className="estimate-copy-info-box">
<div className="estimate-copy-sel">
<Select
id="long-value-select2"
instanceId="long-value-select2"
className="react-select-custom"
classNamePrefix="custom"
placeholder="Select"
ref={ref}
options={otherSaleStoreList}
onChange={onSelectionChange2}
getOptionLabel={(x) => x.saleStoreName}
getOptionValue={(x) => x.saleStoreId}
isClearable={true}
isDisabled={otherSaleStoreList.length > 0 ? false : true}
value={otherSaleStoreList.filter(function (option) {
return option.saleStoreId === otherSaleStoreId
})}
/>
</div>
<div className="estimate-copy-id">{otherSaleStoreId}</div>
</div>
</div>
<div className="estimate-copy-info-item">
<div className="estimate-copy-info-tit">
{getMessage('estimate.detail.estimateCopyPopup.label.receiveUser')} <span className="red">*</span>
</div>
<div className="input-wrap">
<input
type="text"
className="input-light"
required
defaultValue={state?.charger}
onChange={(e) => {
setCopyReceiveUser(e.target.value)
}}
/>
</div>
</div>
</div>
</div>
<div className="footer-btn-wrap">
<button type="button" className="btn-origin grey mr5" onClick={() => setEstimateCopyPopupOpen(false)}>
{getMessage('estimate.detail.estimateCopyPopup.close')}
</button>
<button
type="button"
className="btn-origin navy"
onClick={() => {
handleEstimateCopy(sendPlanNo, copyReceiveUser, saleStoreId, otherSaleStoreId)
}}
>
{getMessage('estimate.detail.estimateCopyPopup.copyBtn')}
</button>
</div>
</div>
</div>
</div>
</div>
)
}

View File

@ -42,7 +42,8 @@ export default function ProductFeaturesPop({ specialNoteList, showProductFeature
return ( return (
<dl> <dl>
<dt>{row.codeNm}</dt> <dt>{row.codeNm}</dt>
<dd dangerouslySetInnerHTML={{ __html: row.remarks }}></dd> {/* <dd dangerouslySetInnerHTML={{ __html: row.remarks }}></dd> */}
<dd dangerouslySetInnerHTML={{ __html: row.remarks }} style={{ whiteSpace: 'pre-wrap' }}></dd>
</dl> </dl>
) )
})} })}

View File

@ -14,6 +14,7 @@ import { useCanvasConfigInitialize } from '@/hooks/common/useCanvasConfigInitial
import { MENU } from '@/common/common' import { MENU } from '@/common/common'
import PanelBatchStatistics from '@/components/floor-plan/modal/panelBatch/PanelBatchStatistics' import PanelBatchStatistics from '@/components/floor-plan/modal/panelBatch/PanelBatchStatistics'
import { totalDisplaySelector } from '@/store/settingAtom' import { totalDisplaySelector } from '@/store/settingAtom'
import ImgLoad from '@/components/floor-plan/modal/ImgLoad'
export default function CanvasFrame() { export default function CanvasFrame() {
const canvasRef = useRef(null) const canvasRef = useRef(null)
@ -76,6 +77,8 @@ export default function CanvasFrame() {
MENU.MODULE_CIRCUIT_SETTING.PLAN_ORIENTATION, MENU.MODULE_CIRCUIT_SETTING.PLAN_ORIENTATION,
].includes(currentMenu) && ].includes(currentMenu) &&
totalDisplay && <PanelBatchStatistics />} totalDisplay && <PanelBatchStatistics />}
{/* 이미지 로드 팝업 */}
<ImgLoad />
</div> </div>
) )
} }

View File

@ -2,7 +2,7 @@
import { useContext, useEffect, useState } from 'react' import { useContext, useEffect, useState } from 'react'
import { useRecoilValue } from 'recoil' import { useRecoilValue } from 'recoil'
import CanvasFrame from './CanvasFrame' import { FloorPlanContext } from '@/app/floor-plan/FloorPlanProvider'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { useSwal } from '@/hooks/useSwal' import { useSwal } from '@/hooks/useSwal'
import { usePlan } from '@/hooks/usePlan' import { usePlan } from '@/hooks/usePlan'
@ -14,7 +14,8 @@ export default function CanvasLayout({ children }) {
// const { menuNumber } = props // const { menuNumber } = props
const { menuNumber } = useCanvasMenu() const { menuNumber } = useCanvasMenu()
const { session } = useContext(SessionContext) const { session } = useContext(SessionContext)
const [objectNo, setObjectNo] = useState('test123240822001') // const { floorPlanState } = useContext(FloorPlanContext)
const { objectNo, pid } = floorPlanState
const globalLocaleState = useRecoilValue(globalLocaleStore) const globalLocaleState = useRecoilValue(globalLocaleStore)
const { getMessage } = useMessage() const { getMessage } = useMessage()
@ -22,7 +23,7 @@ export default function CanvasLayout({ children }) {
const { plans, modifiedPlans, loadCanvasPlanData, handleCurrentPlan, handleAddPlan, handleDeletePlan } = usePlan() const { plans, modifiedPlans, loadCanvasPlanData, handleCurrentPlan, handleAddPlan, handleDeletePlan } = usePlan()
useEffect(() => { useEffect(() => {
loadCanvasPlanData(session.userId, objectNo) loadCanvasPlanData(session.userId, objectNo, pid)
}, []) }, [])
return ( return (

View File

@ -1,6 +1,6 @@
'use client' 'use client'
import { useEffect, useState } from 'react' import { useContext, useEffect, useState } from 'react'
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil' import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
@ -35,6 +35,12 @@ import { MENU } from '@/common/common'
import { useEstimateController } from '@/hooks/floorPlan/estimate/useEstimateController' import { useEstimateController } from '@/hooks/floorPlan/estimate/useEstimateController'
import { estimateState } from '@/store/floorPlanObjectAtom' import { estimateState } from '@/store/floorPlanObjectAtom'
import DocDownOptionPop from '../estimate/popup/DocDownOptionPop' import DocDownOptionPop from '../estimate/popup/DocDownOptionPop'
import { FloorPlanContext } from '@/app/floor-plan/FloorPlanProvider'
import EstimateCopyPop from '../estimate/popup/EstimateCopyPop'
import { floorPlanObjectState } from '@/store/floorPlanObjectAtom'
import { pwrGnrSimTypeState } from '@/store/simulatorAtom'
import { useAxios } from '@/hooks/useAxios'
export default function CanvasMenu(props) { export default function CanvasMenu(props) {
const { menuNumber, setMenuNumber } = props const { menuNumber, setMenuNumber } = props
@ -59,6 +65,7 @@ export default function CanvasMenu(props) {
const { handleEstimateSubmit } = useEstimateController() const { handleEstimateSubmit } = useEstimateController()
const estimateRecoilState = useRecoilValue(estimateState) const estimateRecoilState = useRecoilValue(estimateState)
const [estimatePopupOpen, setEstimatePopupOpen] = useState(false) const [estimatePopupOpen, setEstimatePopupOpen] = useState(false)
const [estimateCopyPopupOpen, setEstimateCopyPopupOpen] = useState(false)
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { currentCanvasPlan, saveCanvas } = usePlan() const { currentCanvasPlan, saveCanvas } = usePlan()
@ -67,6 +74,7 @@ export default function CanvasMenu(props) {
const commonUtils = useRecoilValue(commonUtilsState) const commonUtils = useRecoilValue(commonUtilsState)
const { commonFunctions } = useCommonUtils() const { commonFunctions } = useCommonUtils()
const SelectOption = [{ name: '瓦53A' }, { name: '瓦53A' }] const SelectOption = [{ name: '瓦53A' }, { name: '瓦53A' }]
const { floorPlanState, setFloorPlanState } = useContext(FloorPlanContext)
const onClickNav = (menu) => { const onClickNav = (menu) => {
setMenuNumber(menu.index) setMenuNumber(menu.index)
@ -161,20 +169,6 @@ export default function CanvasMenu(props) {
}) })
} }
/**
* 견적서 복사버튼
* (견적서 번호(estimateRecoilState.docNo) 생성된 이후 버튼 활성화 )
* T01관리자 계정 1차판매점에게만 제공
*/
const handleEstimateCopy = () => {
// console.log('estimateRecoilState::', estimateRecoilState)
//objectNo, planNo
console.log('복사')
console.log('물건정보+도면+견적서를 모두 복사')
console.log('견적서 가격은 정가를 표시')
}
useEffect(() => { useEffect(() => {
if (globalLocale === 'ko') { if (globalLocale === 'ko') {
setAppMessageState(KO) setAppMessageState(KO)
@ -184,7 +178,7 @@ export default function CanvasMenu(props) {
}, [type, globalLocale]) }, [type, globalLocale])
useEffect(() => { useEffect(() => {
if (['2', '3'].includes(canvasSetting?.roofSizeSet?.toString())) { if ([2, 3].some((num) => num === canvasSetting?.roofSizeSet)) {
setMenuNumber(3) setMenuNumber(3)
setType('surface') setType('surface')
setCurrentMenu(MENU.BATCH_CANVAS.BATCH_DRAWING) setCurrentMenu(MENU.BATCH_CANVAS.BATCH_DRAWING)
@ -195,8 +189,60 @@ export default function CanvasMenu(props) {
} }
}, [canvasSetting]) }, [canvasSetting])
const checkMenuState = (menu) => {
return ([2, 3].some((num) => num === canvasSetting?.roofSizeSet) && menu.index === 2) || (menuNumber === 4 && menu.index === 2)
}
// Excel/PDF
const { promisePost } = useAxios(globalLocale)
const objectRecoil = useRecoilValue(floorPlanObjectState)
const pwrGnrSimTypeRecoil = useRecoilValue(pwrGnrSimTypeState)
const { plans } = usePlan()
const plan = plans.find((plan) => plan.isCurrent === true)
const handleExcelPdfFileDown = async (donwloadType, drawingFlg) => {
const url = '/api/estimate/excel-download'
const params = {
objectNo: objectRecoil.floorPlanObjectNo,
planNo: plan?.id,
schDownload: donwloadType,
schDrawingFlg: drawingFlg,
pwrGnrSimType: pwrGnrSimTypeRecoil.type,
}
const options = { responseType: 'blob' }
await promisePost({ url: url, data: params, option: options })
.then((resultData) => {
if (resultData) {
let fileName = 'unknow'
const blob = new Blob([resultData.data], { type: resultData.headers['content-type'] || 'application/octet-stream' })
const fileUrl = window.URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = fileUrl
//
const contentDisposition = resultData.headers['content-disposition']
if (contentDisposition) {
fileName = contentDisposition.split('filename=')[1].replace(/['"]/g, '')
}
link.download = fileName
document.body.appendChild(link)
link.click()
link.remove()
window.URL.revokeObjectURL(fileUrl)
}
})
.catch((error) => {
alert('File does not exist.')
})
}
return ( return (
<div className={`canvas-menu-wrap ${menuNumber === 2 || menuNumber === 3 || menuNumber === 4 ? 'active' : ''}`}> <div className={`canvas-menu-wrap ${[2, 3, 4].some((num) => num === menuNumber) ? 'active' : ''}`}>
<div className="canvas-menu-inner"> <div className="canvas-menu-inner">
<ul className="canvas-menu-list"> <ul className="canvas-menu-list">
{canvasMenus.map((menu) => { {canvasMenus.map((menu) => {
@ -205,11 +251,12 @@ export default function CanvasMenu(props) {
key={`canvas-menu-${menu.index}`} key={`canvas-menu-${menu.index}`}
className={`canvas-menu-item ${menuNumber === menu.index ? 'active' : ''}`} className={`canvas-menu-item ${menuNumber === menu.index ? 'active' : ''}`}
onClick={() => { onClick={() => {
if (['2', '3'].includes(canvasSetting?.roofSizeSet?.toString()) && menu.index === 2) return if ([2, 3].some((num) => num === canvasSetting?.roofSizeSet) && menu.index === 2) return
if (menuNumber === 4 && menu.index === 2) return
onClickNav(menu) onClickNav(menu)
}} }}
> >
<button className={['2', '3'].includes(canvasSetting?.roofSizeSet?.toString()) && menu.index === 2 ? 'no-click' : ''}> <button className={checkMenuState(menu) ? 'no-click' : ''}>
<span className={`menu-icon ${menu.icon}`}></span> <span className={`menu-icon ${menu.icon}`}></span>
{getMessage(menu.name)} {getMessage(menu.name)}
</button> </button>
@ -218,7 +265,7 @@ export default function CanvasMenu(props) {
})} })}
</ul> </ul>
<div className="canvas-side-btn-wrap"> <div className="canvas-side-btn-wrap">
{menuNumber !== 6 && menuNumber !== 5 && ( {![5, 6].some((num) => num === menuNumber) && (
<> <>
{ {
<div className={`vertical-horizontal ${verticalHorizontalMode ? 'on' : ''}`}> <div className={`vertical-horizontal ${verticalHorizontalMode ? 'on' : ''}`}>
@ -235,7 +282,10 @@ export default function CanvasMenu(props) {
<QSelectBox title={'瓦53A'} option={SelectOption} /> <QSelectBox title={'瓦53A'} option={SelectOption} />
</div> </div>
<div className="btn-from"> <div className="btn-from">
<button className="btn10"></button> <button
className={`btn10 ${floorPlanState.refFileModalOpen && 'active'}`}
onClick={() => setFloorPlanState({ ...floorPlanState, refFileModalOpen: true })}
></button>
{/*<button className="btn04" onClick={() => setShowCanvasSettingModal(true)}></button>*/} {/*<button className="btn04" onClick={() => setShowCanvasSettingModal(true)}></button>*/}
<button className="btn04" onClick={handlePopup}></button> <button className="btn04" onClick={handlePopup}></button>
<button className="btn05"></button> <button className="btn05"></button>
@ -248,7 +298,7 @@ export default function CanvasMenu(props) {
handleZoom(false) handleZoom(false)
}} }}
></button> ></button>
<span>{canvasZoom}%</span> <span onClick={handleZoomClear}>{canvasZoom}%</span>
<button <button
className="control-btn plus" className="control-btn plus"
onClick={() => { onClick={() => {
@ -274,7 +324,6 @@ export default function CanvasMenu(props) {
<span className="ico ico02"></span> <span className="ico ico02"></span>
<span>{getMessage('plan.menu.estimate.save')}</span> <span>{getMessage('plan.menu.estimate.save')}</span>
</button> </button>
{/* {estimateRecoilState?.docNo != null && ( */}
<button <button
className="btn-frame gray ico-flx" className="btn-frame gray ico-flx"
onClick={() => { onClick={() => {
@ -284,28 +333,29 @@ export default function CanvasMenu(props) {
<span className="ico ico03"></span> <span className="ico ico03"></span>
<span>{getMessage('plan.menu.estimate.reset')}</span> <span>{getMessage('plan.menu.estimate.reset')}</span>
</button> </button>
{/* )} */}
<button {estimateRecoilState?.docNo !== null && (sessionState.storeId === 'T01' || sessionState.storeLvl === '1') && (
className="btn-frame gray ico-flx" <button
onClick={() => { className="btn-frame gray ico-flx"
handleEstimateCopy() onClick={() => {
}} setEstimateCopyPopupOpen(true)
> }}
<span className="ico ico04"></span> >
<span>{getMessage('plan.menu.estimate.copy')}</span> <span className="ico ico04"></span>
</button> <span>{getMessage('plan.menu.estimate.copy')}</span>
</button>
)}
</div> </div>
</> </>
)} )}
{menuNumber === 6 && ( {menuNumber === 6 && (
<> <>
<div className="ico-btn-from"> <div className="ico-btn-from">
<button className="btn-frame gray ico-flx"> <button type="button" className="btn-frame gray ico-flx" onClick={() => handleExcelPdfFileDown('EXCEL', '2')}>
<span className="ico ico01"></span> <span className="ico ico01"></span>
<span>{getMessage('plan.menu.simulation.excel')}</span> <span>{getMessage('plan.menu.simulation.excel')}</span>
</button> </button>
<button className="btn-frame gray ico-flx"> <button type="button" className="btn-frame gray ico-flx" onClick={() => handleExcelPdfFileDown('PDF', '2')}>
<span className="ico ico01"></span> <span className="ico ico01"></span>
<span>{getMessage('plan.menu.simulation.pdf')}</span> <span>{getMessage('plan.menu.simulation.pdf')}</span>
</button> </button>
@ -314,11 +364,13 @@ export default function CanvasMenu(props) {
)} )}
</div> </div>
</div> </div>
<div className={`canvas-depth2-wrap ${menuNumber === 2 || menuNumber === 3 || menuNumber === 4 ? 'active' : ''}`}> <div className={`canvas-depth2-wrap ${[2, 3, 4].some((num) => num === menuNumber) ? 'active' : ''}`}>
{(menuNumber === 2 || menuNumber === 3 || menuNumber === 4) && <MenuDepth01 />} {[2, 3, 4].some((num) => num === menuNumber) && <MenuDepth01 />}
</div> </div>
{/* 견적서(menuNumber=== 5) 상세화면인경우 문서다운로드 팝업 */} {/* 견적서(menuNumber=== 5) 상세화면인경우 문서다운로드 팝업 */}
{estimatePopupOpen && <DocDownOptionPop planNo={estimateRecoilState?.planNo} setEstimatePopupOpen={setEstimatePopupOpen} />} {estimatePopupOpen && <DocDownOptionPop planNo={estimateRecoilState?.planNo} setEstimatePopupOpen={setEstimatePopupOpen} />}
{/* 견적서(menuNumber ===5)복사 팝업 */}
{estimateCopyPopupOpen && <EstimateCopyPop planNo={estimateRecoilState?.planNo} setEstimateCopyPopupOpen={setEstimateCopyPopupOpen} />}
</div> </div>
) )
} }

View File

@ -6,12 +6,14 @@ import { settingModalFirstOptionsState, settingModalSecondOptionsState } from '@
import CanvasMenu from '@/components/floor-plan/CanvasMenu' import CanvasMenu from '@/components/floor-plan/CanvasMenu'
import { useCanvasMenu } from '@/hooks/common/useCanvasMenu' import { useCanvasMenu } from '@/hooks/common/useCanvasMenu'
import { useCanvasSetting } from '@/hooks/option/useCanvasSetting' import { useCanvasSetting } from '@/hooks/option/useCanvasSetting'
import { usePopup } from '@/hooks/usePopup'
import '@/styles/contents.scss' import '@/styles/contents.scss'
export default function FloorPlan({ children }) { export default function FloorPlan({ children }) {
const [settingModalFirstOptions, setSettingModalFirstOptions] = useRecoilState(settingModalFirstOptionsState) const [settingModalFirstOptions, setSettingModalFirstOptions] = useRecoilState(settingModalFirstOptionsState)
const [settingModalSecondOptions, setSettingModalSecondOptions] = useRecoilState(settingModalSecondOptionsState) const [settingModalSecondOptions, setSettingModalSecondOptions] = useRecoilState(settingModalSecondOptionsState)
const [objectNo, setObjectNo] = useState('test123240912001') // const [objectNo, setObjectNo] = useState('test123240912001') //
const { closeAll } = usePopup()
const { menuNumber, setMenuNumber } = useCanvasMenu() const { menuNumber, setMenuNumber } = useCanvasMenu()
@ -24,6 +26,9 @@ export default function FloorPlan({ children }) {
useEffect(() => { useEffect(() => {
fetchSettings() fetchSettings()
return () => {
closeAll()
}
}, [objectNo]) }, [objectNo])
return ( return (

View File

@ -1,3 +1,6 @@
import { useContext, useEffect } from 'react'
import { FloorPlanContext } from '@/app/floor-plan/FloorPlanProvider'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { useRefFiles } from '@/hooks/common/useRefFiles' import { useRefFiles } from '@/hooks/common/useRefFiles'
import { usePlan } from '@/hooks/usePlan' import { usePlan } from '@/hooks/usePlan'
@ -20,9 +23,24 @@ export default function ImgLoad() {
} = useRefFiles() } = useRefFiles()
const { currentCanvasPlan } = usePlan() const { currentCanvasPlan } = usePlan()
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { floorPlanState, setFloorPlanState } = useContext(FloorPlanContext)
const handleModal = () => {
setFloorPlanState({ ...floorPlanState, refFileModalOpen: false })
}
useEffect(() => {
console.log('🚀 ~ ImgLoad ~ floorPlanState.refFileModalOpen:', floorPlanState.refFileModalOpen)
console.log('🚀 ~ ImgLoad ~ currentCanvasPlan:', currentCanvasPlan)
}, [floorPlanState.refFileModalOpen])
useEffect(() => {
const refFileMethod = currentCanvasPlan?.mapPositionAddress === null ? '1' : '2'
setRefFileMethod(refFileMethod)
}, [currentCanvasPlan])
return ( return (
<WithDraggable isShow={true}> <WithDraggable isShow={floorPlanState.refFileModalOpen} pos={{ x: 1000, y: 200 }} handle=".modal">
<div className={`modal-pop-wrap r`}> <div className={`modal-pop-wrap r`}>
<div className="modal-head"> <div className="modal-head">
<h1 className="title">{getMessage('common.input.file')}</h1> <h1 className="title">{getMessage('common.input.file')}</h1>
@ -32,7 +50,11 @@ export default function ImgLoad() {
<div className="img-flex-box"> <div className="img-flex-box">
<span className="normal-font mr10">サイズ調整と回転</span> <span className="normal-font mr10">サイズ調整と回転</span>
<label className="toggle-btn"> <label className="toggle-btn">
<input type="checkbox" /> <input
type="checkbox"
checked={floorPlanState.toggleRotate}
onChange={(e) => setFloorPlanState({ ...floorPlanState, toggleRotate: e.target.checked })}
/>
<span className="slider"></span> <span className="slider"></span>
</label> </label>
</div> </div>
@ -48,13 +70,18 @@ export default function ImgLoad() {
<span className="img-edit"></span> <span className="img-edit"></span>
ファイルの追加 ファイルの追加
</label> </label>
<input type="file" id="img_file" style={{ display: 'none' }} onChange={(e) => handleRefFile(e.target.files[0])} /> <input
type="file"
id="img_file"
style={{ display: 'none' }}
onChange={refFileMethod === '1' ? (e) => handleRefFile(e.target.files[0]) : () => {}}
/>
</div> </div>
<div className="img-name-wrap"> <div className="img-name-wrap">
{/* <input type="text" className="input-origin al-l" defaultValue={'IMG_Name.PNG'} readOnly /> {/* <input type="text" className="input-origin al-l" defaultValue={'IMG_Name.PNG'} readOnly />
<button className="img-check"></button> */} <button className="img-check"></button> */}
{currentCanvasPlan?.bgImageName === null ? ( {currentCanvasPlan?.bgImageName === null ? (
<input type="text" className="input-origin al-l" defaultValue={''} value={refImage ? refImage.name : ''} readOnly /> <input type="text" className="input-origin al-l" value={refImage ? refImage.name : ''} readOnly />
) : ( ) : (
<input type="text" className="input-origin al-l" value={currentCanvasPlan?.bgImageName} readOnly /> <input type="text" className="input-origin al-l" value={currentCanvasPlan?.bgImageName} readOnly />
)} )}
@ -64,13 +91,20 @@ export default function ImgLoad() {
</div> </div>
<div className="img-load-item"> <div className="img-load-item">
<div className="d-check-radio pop"> <div className="d-check-radio pop">
<input type="radio" name="radio03" id="ra06" value={'2'} onChange={(e) => handleRefFileMethod(e)} checked={refFileMethod === '2'} /> <input type="radio" name="radio03" id="ra07" value={'2'} onChange={(e) => handleRefFileMethod(e)} checked={refFileMethod === '2'} />
<label htmlFor="ra06">アドレスを読み込む</label> <label htmlFor="ra07">アドレスを読み込む</label>
</div> </div>
<div className="img-flex-box for-address"> <div className="img-flex-box for-address">
<input type="text" className="input-origin al-l mr10" placeholder={'住所入力'} /> <input
type="text"
ref={queryRef}
className="input-origin al-l mr10"
placeholder={'住所入力'}
value={mapPositionAddress}
onChange={(e) => setMapPositionAddress(e.target.value)}
/>
<div className="img-edit-wrap"> <div className="img-edit-wrap">
<button className="img-edit-btn" onClick={handleMapImageDown}> <button className="img-edit-btn" onClick={refFileMethod === '2' ? handleMapImageDown : () => {}}>
完了 完了
</button> </button>
</div> </div>
@ -80,7 +114,9 @@ export default function ImgLoad() {
</div> </div>
</div> </div>
<div className="grid-btn-wrap"> <div className="grid-btn-wrap">
<button className="btn-frame modal act">完了</button> <button className="btn-frame modal act" onClick={handleModal}>
完了
</button>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,84 +0,0 @@
import { useMessage } from '@/hooks/useMessage'
import WithDraggable from '@/components/common/draggable/WithDraggable'
import { usePopup } from '@/hooks/usePopup'
import { useRecoilValue } from 'recoil'
import { contextPopupPositionState } from '@/store/popupAtom'
import { useState } from 'react'
export default function AuxiliaryCopy(props) {
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
const { id, pos = contextPopupPosition } = props
const { getMessage } = useMessage()
const { closePopup } = usePopup()
const [arrow1, setArrow1] = useState(null)
const [arrow2, setArrow2] = useState(null)
return (
<WithDraggable isShow={true} pos={pos}>
<div className={`modal-pop-wrap xm mount`}>
<div className="modal-head">
<h1 className="title">{getMessage('modal.auxiliary.copy')} </h1>
<button className="modal-close" onClick={() => closePopup(id)}>
닫기
</button>
</div>
<div className="modal-body">
<div className="grid-option-tit">{getMessage('modal.auxiliary.copy.info')}</div>
<div className="grid-option-wrap">
<div className="grid-option-box">
<div className="move-form">
<p className="mb5">{getMessage('length')}</p>
<div className="input-move-wrap mb5">
<div className="input-move">
<input type="text" className="input-origin" defaultValue={910} />
</div>
<span>mm</span>
<div className="direction-move-wrap">
<button
className={`direction up ${arrow1 === '↑' ? 'act' : ''}`}
onClick={() => {
setArrow1('↑')
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' }))
}}
></button>
<button
className={`direction down ${arrow1 === '↓' ? 'act' : ''}`}
onClick={() => {
setArrow1('↓')
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' }))
}}
></button>
</div>
</div>
<div className="input-move-wrap">
<div className="input-move">
<input type="text" className="input-origin" defaultValue={910} />
</div>
<span>mm</span>
<div className="direction-move-wrap">
<button
className={`direction left ${arrow2 === '←' ? 'act' : ''}`}
onClick={() => {
setArrow2('←')
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowLeft' }))
}}
></button>
<button
className={`direction right ${arrow2 === '→' ? 'act' : ''}`}
onClick={() => {
setArrow2('→')
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowRight' }))
}}
></button>
</div>
</div>
</div>
</div>
</div>
<div className="grid-btn-wrap">
<button className="btn-frame modal act">{getMessage('modal.common.save')}</button>
</div>
</div>
</div>
</WithDraggable>
)
}

View File

@ -1,37 +1,62 @@
'use client'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import WithDraggable from '@/components/common/draggable/WithDraggable' import WithDraggable from '@/components/common/draggable/WithDraggable'
import { usePopup } from '@/hooks/usePopup'
import { useRecoilValue } from 'recoil' import { useRecoilValue } from 'recoil'
import { contextPopupPositionState } from '@/store/popupAtom' import { contextPopupPositionState } from '@/store/popupAtom'
import { usePopup } from '@/hooks/usePopup'
import { useState } from 'react' import { useState } from 'react'
import { currentObjectState } from '@/store/canvasAtom'
import { useLine } from '@/hooks/useLine'
import { useAuxiliaryDrawing } from '@/hooks/roofcover/useAuxiliaryDrawing'
export default function AuxiliaryMove(props) { export default function AuxiliaryEdit(props) {
const contextPopupPosition = useRecoilValue(contextPopupPositionState) const contextPopupPosition = useRecoilValue(contextPopupPositionState)
const { id, pos = contextPopupPosition } = props const { id, pos = contextPopupPosition, type } = props
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { closePopup } = usePopup() const { closePopup } = usePopup()
const { move, copy } = useAuxiliaryDrawing()
const [verticalSize, setVerticalSize] = useState('0')
const [horizonSize, setHorizonSize] = useState('0')
const [arrow1, setArrow1] = useState(null) const [arrow1, setArrow1] = useState(null)
const [arrow2, setArrow2] = useState(null) const [arrow2, setArrow2] = useState(null)
const { addLine, removeLine } = useLine()
const currentObject = useRecoilValue(currentObjectState)
const handleSave = () => {
if (type === 'copy') {
if (currentObject) {
copy(
currentObject,
arrow2 === '←' ? Number(horizonSize) * -1 : Number(horizonSize),
arrow1 === '↑' ? Number(verticalSize) * -1 : Number(verticalSize),
)
}
} else {
move(
currentObject,
arrow2 === '←' ? Number(horizonSize) * -1 : Number(horizonSize),
arrow1 === '↑' ? Number(verticalSize) * -1 : Number(verticalSize),
)
}
closePopup(id)
}
return ( return (
<WithDraggable isShow={true} pos={pos}> <WithDraggable isShow={true} pos={pos}>
<div className={`modal-pop-wrap xm mount`}> <div className={`modal-pop-wrap xm mount`}>
<div className="modal-head"> <div className="modal-head">
<h1 className="title">{getMessage('modal.auxiliary.move')}</h1> <h1 className="title">{getMessage(type === 'copy' ? 'modal.auxiliary.copy' : 'modal.auxiliary.move')} </h1>
<button className="modal-close" onClick={() => closePopup(id)}> <button className="modal-close" onClick={() => closePopup(id)}>
닫기 닫기
</button> </button>
</div> </div>
<div className="modal-body"> <div className="modal-body">
<div className="grid-option-tit">{getMessage('modal.auxiliary.move.info')}</div> <div className="grid-option-tit">{getMessage(type === 'copy' ? 'modal.auxiliary.copy.info' : 'modal.auxiliary.move.info')}</div>
<div className="grid-option-wrap"> <div className="grid-option-wrap">
<div className="grid-option-box"> <div className="grid-option-box">
<div className="move-form"> <div className="move-form">
<p className="mb5">{getMessage('length')}</p> <p className="mb5">{getMessage('length')}</p>
<div className="input-move-wrap mb5"> <div className="input-move-wrap mb5">
<div className="input-move"> <div className="input-move">
<input type="text" className="input-origin" defaultValue={910} /> <input type="text" className="input-origin" value={verticalSize} onChange={(e) => setVerticalSize(e.target.value)} />
</div> </div>
<span>mm</span> <span>mm</span>
<div className="direction-move-wrap"> <div className="direction-move-wrap">
@ -53,7 +78,7 @@ export default function AuxiliaryMove(props) {
</div> </div>
<div className="input-move-wrap"> <div className="input-move-wrap">
<div className="input-move"> <div className="input-move">
<input type="text" className="input-origin" defaultValue={910} /> <input type="text" className="input-origin" value={horizonSize} onChange={(e) => setHorizonSize(e.target.value)} />
</div> </div>
<span>mm</span> <span>mm</span>
<div className="direction-move-wrap"> <div className="direction-move-wrap">
@ -77,7 +102,9 @@ export default function AuxiliaryMove(props) {
</div> </div>
</div> </div>
<div className="grid-btn-wrap"> <div className="grid-btn-wrap">
<button className="btn-frame modal act">{getMessage('modal.common.save')}</button> <button className="btn-frame modal act" onClick={handleSave}>
{getMessage('modal.common.save')}
</button>
</div> </div>
</div> </div>
</div> </div>

View File

@ -35,6 +35,12 @@ export default function BasicSetting({ id, pos = { x: 50, y: 230 } }) {
} }
}, []) }, [])
const placementRef = {
isChidori: useRef('false'),
setupLocation: useRef(null),
isMaxSetup: useRef('false'),
}
return ( return (
<WithDraggable isShow={true} pos={pos}> <WithDraggable isShow={true} pos={pos}>
<div className={`modal-pop-wrap lx-2`}> <div className={`modal-pop-wrap lx-2`}>
@ -55,7 +61,7 @@ export default function BasicSetting({ id, pos = { x: 50, y: 230 } }) {
{tabNum === 1 && <Orientation ref={orientationRef} tabNum={tabNum} setTabNum={setTabNum} />} {tabNum === 1 && <Orientation ref={orientationRef} tabNum={tabNum} setTabNum={setTabNum} />}
{/*배치면 초기설정 - 입력방법: 복시도 입력 || 실측값 입력*/} {/*배치면 초기설정 - 입력방법: 복시도 입력 || 실측값 입력*/}
{canvasSetting.roofSizeSet && canvasSetting.roofSizeSet != 3 && tabNum === 2 && <Module setTabNum={setTabNum} />} {canvasSetting.roofSizeSet && canvasSetting.roofSizeSet != 3 && tabNum === 2 && <Module setTabNum={setTabNum} />}
{canvasSetting.roofSizeSet && canvasSetting.roofSizeSet != 3 && tabNum === 3 && <Placement setTabNum={setTabNum} />} {canvasSetting.roofSizeSet && canvasSetting.roofSizeSet != 3 && tabNum === 3 && <Placement setTabNum={setTabNum} ref={placementRef} />}
{/*배치면 초기설정 - 입력방법: 육지붕*/} {/*배치면 초기설정 - 입력방법: 육지붕*/}
{canvasSetting.roofSizeSet && canvasSetting.roofSizeSet == 3 && tabNum === 2 && <PitchModule setTabNum={setTabNum} />} {canvasSetting.roofSizeSet && canvasSetting.roofSizeSet == 3 && tabNum === 2 && <PitchModule setTabNum={setTabNum} />}
@ -78,7 +84,7 @@ export default function BasicSetting({ id, pos = { x: 50, y: 230 } }) {
<button className="btn-frame modal mr5" onClick={manualModuleSetup}> <button className="btn-frame modal mr5" onClick={manualModuleSetup}>
{getMessage('modal.module.basic.setting.passivity.placement')} {getMessage('modal.module.basic.setting.passivity.placement')}
</button> </button>
<button className="btn-frame modal act" onClick={autoModuleSetup}> <button className="btn-frame modal act" onClick={() => autoModuleSetup(placementRef)}>
{getMessage('modal.module.basic.setting.auto.placement')} {getMessage('modal.module.basic.setting.auto.placement')}
</button> </button>
</> </>

View File

@ -1,6 +1,6 @@
import { forwardRef, useImperativeHandle, useState } from 'react' import { forwardRef, useImperativeHandle, useState } from 'react'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { useOrientation } from '@/hooks/popup/useOrientation' import { useOrientation } from '@/hooks/module/useOrientation'
import { getDegreeInOrientation } from '@/util/canvas-util' import { getDegreeInOrientation } from '@/util/canvas-util'
export const Orientation = forwardRef(({ tabNum }, ref) => { export const Orientation = forwardRef(({ tabNum }, ref) => {

View File

@ -1,7 +1,10 @@
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { forwardRef, useState } from 'react'
export default function Placement() { const Placement = forwardRef((props, refs) => {
const { getMessage } = useMessage() const { getMessage } = useMessage()
const [isChidori, setIsChidori] = useState('false')
const moduleData = { const moduleData = {
header: [ header: [
{ type: 'check', name: '', prop: 'check', width: 70 }, { type: 'check', name: '', prop: 'check', width: 70 },
@ -24,6 +27,12 @@ export default function Placement() {
}, },
], ],
} }
const handleChangeChidori = (e) => {
setIsChidori(e.target.value)
refs.isChidori.current = e.target.value
}
return ( return (
<> <>
<div className="module-table-flex-wrap mb10"> <div className="module-table-flex-wrap mb10">
@ -96,12 +105,20 @@ export default function Placement() {
<div className="self-item-td"> <div className="self-item-td">
<div className="pop-form-radio"> <div className="pop-form-radio">
<div className="d-check-radio pop"> <div className="d-check-radio pop">
<input type="radio" name="radio01" id="ra01" /> <input type="radio" name="radio01" id="ra01" checked={isChidori === 'true'} value={'true'} onChange={handleChangeChidori} />
<label htmlFor="ra01">{getMessage('modal.module.basic.setting.module.placement.do')}</label> <label htmlFor="ra01">{getMessage('modal.module.basic.setting.module.placement.do')}</label>
</div> </div>
<div className="d-check-radio pop"> <div className="d-check-radio pop">
<input type="radio" name="radio01" id="ra02" /> <input
<label htmlFor="ra01">{getMessage('modal.module.basic.setting.module.placement.do.not')}</label> type="radio"
name="radio02"
id="ra02"
value={'false'}
defaultChecked
checked={isChidori === 'false'}
onChange={handleChangeChidori}
/>
<label htmlFor="ra02">{getMessage('modal.module.basic.setting.module.placement.do.not')}</label>
</div> </div>
</div> </div>
</div> </div>
@ -137,4 +154,6 @@ export default function Placement() {
</div> </div>
</> </>
) )
} })
export default Placement

View File

@ -6,7 +6,7 @@ import { contextPopupPositionState } from '@/store/popupAtom'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { usePopup } from '@/hooks/usePopup' import { usePopup } from '@/hooks/usePopup'
import { useSurfaceShapeBatch } from '@/hooks/surface/useSurfaceShapeBatch' import { useSurfaceShapeBatch } from '@/hooks/surface/useSurfaceShapeBatch'
import { FLOW_DIRECTION_TYPE, useFlowDirectionSetting } from '@/hooks/popup/useFlowDirectionSetting' import { FLOW_DIRECTION_TYPE, useFlowDirectionSetting } from '@/hooks/contextpopup/useFlowDirectionSetting'
import { canvasState } from '@/store/canvasAtom' import { canvasState } from '@/store/canvasAtom'
export default function FlowDirectionSetting(props) { export default function FlowDirectionSetting(props) {
const contextPopupPosition = useRecoilValue(contextPopupPositionState) const contextPopupPosition = useRecoilValue(contextPopupPositionState)

View File

@ -1,42 +0,0 @@
import WithDraggable from '@/components/common/draggable/WithDraggable'
import { useState } from 'react'
import { usePopup } from '@/hooks/usePopup'
import { useRecoilValue } from 'recoil'
import { contextPopupPositionState } from '@/store/popupAtom'
import { useMessage } from '@/hooks/useMessage'
export default function ImageSizeSetting(props) {
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
const { id, pos = contextPopupPosition, size, setSize } = props
const [sizeValue, setSizeValue] = useState(100)
const { getMessage } = useMessage()
const { closePopup } = usePopup()
return (
<WithDraggable isShow={true} pos={pos}>
<div className={`modal-pop-wrap xxxm mount`}>
<div className="modal-head">
<h1 className="title">{getMessage('modal.image.size.setting')} </h1>
<button className="modal-close" onClick={() => closePopup(id)}>
닫기
</button>
</div>
<div className="modal-body">
<div className="range-wrap">
<input
type="range"
id="size"
name="volume"
min="20"
max="200"
step={10}
value={sizeValue}
onChange={(e) => setSizeValue(e.target.value)}
/>
<label htmlFor="size">{sizeValue}%</label>
</div>
</div>
</div>
</WithDraggable>
)
}

View File

@ -12,7 +12,6 @@ import { usePopup } from '@/hooks/usePopup'
import SizeGuide from '@/components/floor-plan/modal/placementShape/SizeGuide' import SizeGuide from '@/components/floor-plan/modal/placementShape/SizeGuide'
import MaterialGuide from '@/components/floor-plan/modal/placementShape/MaterialGuide' import MaterialGuide from '@/components/floor-plan/modal/placementShape/MaterialGuide'
import WithDraggable from '@/components/common/draggable/WithDraggable' import WithDraggable from '@/components/common/draggable/WithDraggable'
import { SessionContext } from '@/app/SessionProvider'
export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, setShowPlaceShapeModal }) { export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, setShowPlaceShapeModal }) {
const [objectNo, setObjectNo] = useState('test123241008001') // const [objectNo, setObjectNo] = useState('test123241008001') //

View File

@ -1,10 +1,10 @@
'use client' 'use client'
import { Fragment, useCallback, useEffect, useState } from 'react' import { Fragment, useCallback, useContext, useEffect, useState } from 'react'
import Link from 'next/link' import Link from 'next/link'
import { usePathname } from 'next/navigation' import { usePathname } from 'next/navigation'
import { useRecoilState, useRecoilValue } from 'recoil' import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil'
import { dimmedStore, sessionStore } from '@/store/commonAtom' import { dimmedStore, sessionStore } from '@/store/commonAtom'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
@ -16,6 +16,9 @@ import UserInfoModal from '@/components/myInfo/UserInfoModal'
import { useAxios } from '@/hooks/useAxios' import { useAxios } from '@/hooks/useAxios'
import { globalLocaleStore } from '@/store/localeAtom' import { globalLocaleStore } from '@/store/localeAtom'
import { stuffSearchState } from '@/store/stuffAtom'
import { QcastContext } from '@/app/QcastProvider'
export const ToggleonMouse = (e, act, target) => { export const ToggleonMouse = (e, act, target) => {
const listWrap = e.target.closest(target) const listWrap = e.target.closest(target)
const ListItem = Array.from(listWrap.childNodes) const ListItem = Array.from(listWrap.childNodes)
@ -34,6 +37,8 @@ export const ToggleonMouse = (e, act, target) => {
export default function Header(props) { export default function Header(props) {
const [userInfoModal, setUserInfoModal] = useState(false) const [userInfoModal, setUserInfoModal] = useState(false)
const [stuffSearch, setStuffSearch] = useRecoilState(stuffSearchState)
const { userSession } = props const { userSession } = props
const [sessionState, setSessionState] = useRecoilState(sessionStore) const [sessionState, setSessionState] = useRecoilState(sessionStore)
const { getMessage } = useMessage() const { getMessage } = useMessage()
@ -43,6 +48,8 @@ export default function Header(props) {
// } // }
const [selected, setSelected] = useState('') const [selected, setSelected] = useState('')
const { isGlobalLoading } = useContext(QcastContext)
const dimmedState = useRecoilValue(dimmedStore) const dimmedState = useRecoilValue(dimmedStore)
const isDimmed = dimmedState ? 'opacity-50 bg-black' : '' const isDimmed = dimmedState ? 'opacity-50 bg-black' : ''
@ -160,45 +167,56 @@ export default function Header(props) {
} }
return ( return (
!(pathName.includes('login') || pathName.includes('join') || sessionState.pwdInitYn === 'N') && ( <>
<header className={isDimmed}> {!isGlobalLoading && !(pathName.includes('login') || pathName.includes('join') || sessionState.pwdInitYn === 'N') && (
<div className="header-inner"> <header className={isDimmed}>
<div className="header-right"> <div className="header-inner">
<h1 className="logo"> <div className="header-right">
<Link href={'/'}></Link> <h1 className="logo">
</h1> <Link href={'/'}></Link>
<nav> </h1>
<ul className="nav-list ">{getMenuTemplate(menus)}</ul> <nav>
</nav> <ul className="nav-list ">{getMenuTemplate(menus)}</ul>
</div> </nav>
<div className="header-left">
<div className="profile-box">
<Link
href="#"
onClick={() => {
setUserInfoModal(true)
}}
>
<button className="profile">{userSession.userNm}</button>
</Link>
{userInfoModal && <UserInfoModal userId={sessionState.userId} userInfoModal={userInfoModal} setUserInfoModal={setUserInfoModal} />}
</div> </div>
<div className="sign-out-box"> <div className="header-left">
<button className="sign-out" onClick={() => logout()}> <div className="profile-box">
{getMessage('header.logout')} <Link
</button> href="#"
</div> onClick={() => {
<div className="select-box"> setUserInfoModal(true)
<QSelectBox options={SelectOptions} onChange={onChangeSelect} /> }}
</div> >
<div className="btn-wrap"> <button className="profile">{userSession.userNm}</button>
<button className="btn-frame small dark" onClick={() => navPage()}> </Link>
{getMessage('header.go')} {userInfoModal && <UserInfoModal userId={sessionState.userId} userInfoModal={userInfoModal} setUserInfoModal={setUserInfoModal} />}
</button> </div>
<div className="sign-out-box">
<button
className="sign-out"
onClick={() => {
setStuffSearch({
...stuffSearch,
code: 'DELETE',
})
logout()
}}
>
{getMessage('header.logout')}
</button>
</div>
<div className="select-box">
<QSelectBox options={SelectOptions} onChange={onChangeSelect} />
</div>
<div className="btn-wrap">
<button className="btn-frame small dark" onClick={() => navPage()}>
{getMessage('header.go')}
</button>
</div>
</div> </div>
</div> </div>
</div> </header>
</header> )}
) </>
) )
} }

View File

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

View File

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

View File

@ -10,7 +10,7 @@ import { globalLocaleStore } from '@/store/localeAtom'
import { isEmptyArray, isNotEmptyArray, isObjectNotEmpty } from '@/util/common-utils' import { isEmptyArray, isNotEmptyArray, isObjectNotEmpty } from '@/util/common-utils'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { useForm } from 'react-hook-form' import { useForm } from 'react-hook-form'
import { useRecoilValue, useSetRecoilState } from 'recoil' import { useRecoilValue, useSetRecoilState, useResetRecoilState, useRecoilState } from 'recoil'
import { SessionContext } from '@/app/SessionProvider' import { SessionContext } from '@/app/SessionProvider'
import FindAddressPop from './popup/FindAddressPop' import FindAddressPop from './popup/FindAddressPop'
import PlanRequestPop from './popup/PlanRequestPop' import PlanRequestPop from './popup/PlanRequestPop'
@ -20,8 +20,13 @@ import StuffPlanQGrid from './StuffPlanQGrid'
import { floorPlanObjectState } from '@/store/floorPlanObjectAtom' import { floorPlanObjectState } from '@/store/floorPlanObjectAtom'
import { ManagementContext } from '@/app/management/ManagementProvider' import { ManagementContext } from '@/app/management/ManagementProvider'
import DocDownOptionPop from '../estimate/popup/DocDownOptionPop' import DocDownOptionPop from '../estimate/popup/DocDownOptionPop'
import { stuffSearchState } from '@/store/stuffAtom'
import { QcastContext } from '@/app/QcastProvider'
export default function StuffDetail() { export default function StuffDetail() {
const { setIsGlobalLoading } = useContext(QcastContext)
const resetStuffRecoil = useResetRecoilState(stuffSearchState)
const stuffSearchParams = useRecoilValue(stuffSearchState)
const setFloorPlanObjectNo = useSetRecoilState(floorPlanObjectState) // const setFloorPlanObjectNo = useSetRecoilState(floorPlanObjectState) //
const [estimatePopupOpen, setEstimatePopupOpen] = useState(false) const [estimatePopupOpen, setEstimatePopupOpen] = useState(false)
@ -301,10 +306,12 @@ export default function StuffDetail() {
} }
promiseGet({ url: `/api/object/${objectNo}/detail` }).then((res) => { promiseGet({ url: `/api/object/${objectNo}/detail` }).then((res) => {
if (res.status === 200) { if (res.status === 200) {
if (res.data != null) { if (isObjectNotEmpty(res.data)) {
setManagementState(res.data) setManagementState(res.data)
} else { } else {
setManagementState({}) setManagementState({})
alert(getMessage('stuff.detail.header.notExistObjectNo'))
router.push('/management/stuff')
} }
if (isNotEmptyArray(res.data.planList)) { if (isNotEmptyArray(res.data.planList)) {
setPlanGridProps({ ...planGridProps, planGridData: res.data.planList }) setPlanGridProps({ ...planGridProps, planGridData: res.data.planList })
@ -314,6 +321,9 @@ export default function StuffDetail() {
} else { } else {
setManagementState({}) setManagementState({})
setPlanGridProps({ ...planGridProps, planGridData: [] }) setPlanGridProps({ ...planGridProps, planGridData: [] })
alert(getMessage('stuff.detail.header.notExistObjectNo'))
router.push('/management/stuff')
} }
}) })
} else { } else {
@ -409,6 +419,8 @@ export default function StuffDetail() {
} }
} }
}) })
setIsGlobalLoading(false)
} }
}, [objectNo, session]) }, [objectNo, session])
@ -428,158 +440,163 @@ export default function StuffDetail() {
}, [commonCode]) }, [commonCode])
useEffect(() => { useEffect(() => {
if (isObjectNotEmpty(managementState)) { if (objectNo) {
// API if (isObjectNotEmpty(managementState)) {
get({ url: '/api/object/prefecture/list' }).then((res) => { // API
if (!isEmptyArray(res)) { get({ url: '/api/object/prefecture/list' }).then((res) => {
setPrefCodeList(res) if (!isEmptyArray(res)) {
} setPrefCodeList(res)
}) }
})
//1 : X167 T01 //1 : X167 T01
//2 : 10X22, 201X112 //2 : 10X22, 201X112
let url let url
let firstList let firstList
let otherList let otherList
let favList let favList
if (session?.storeId === 'T01') { if (session?.storeId === 'T01') {
url = `/api/object/saleStore/${session?.storeId}/firstList?userId=${session?.userId}` url = `/api/object/saleStore/${session?.storeId}/firstList?userId=${session?.userId}`
} else {
if (session.storeLvl === '1') {
url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=1&userId=${session?.userId}`
} else { } else {
url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=1&userId=${session?.userId}` if (session.storeLvl === '1') {
} url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=1&userId=${session?.userId}`
}
get({ url: url }).then((res) => {
if (!isEmptyArray(res)) {
if (session?.storeId === 'T01') {
firstList = res.filter((row) => row.saleStoreLevel === '1')
firstList.sort((a, b) => (a.saleStoreId !== 'T01') - (b.saleStoreId !== 'T01') || a.saleStoreId - b.saleStoreId)
favList = firstList.filter((row) => row.saleStoreId === 'T01' || row.priority !== 'B')
setSaleStoreList(firstList)
setFavoriteStoreList(favList)
setShowSaleStoreList(favList)
if (managementState.firstAgentId != null) {
form.setValue('saleStoreId', managementState.firstAgentId)
setSelOptions(managementState.firstAgentId)
} else {
form.setValue('saleStoreId', managementState.saleStoreId)
setSelOptions(managementState.saleStoreId)
}
// 1 2
let data = managementState?.firstAgentId ? managementState.firstAgentId : managementState.saleStoreId
url = `/api/object/saleStore/${data}/list?firstFlg=0&userId=${session?.userId}`
get({ url: url }).then((res) => {
if (!isEmptyArray(res)) {
res.map((row) => {
row.value = row.saleStoreId
row.label = row.saleStoreName
})
otherList = res
setOriginOtherSaleStoreList(otherList)
setOtherSaleStoreList(otherList)
}
})
} else { } else {
//1 url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=1&userId=${session?.userId}`
if (session?.storeLvl === '1') {
firstList = res
favList = res.filter((row) => row.priority !== 'B')
otherList = res.filter((row) => row.firstAgentYn === 'N')
setSaleStoreList(firstList)
setFavoriteStoreList(firstList)
setShowSaleStoreList(firstList)
setOtherSaleStoreList(otherList)
} else {
setSelOptions(res[0].saleStoreId)
form.setValue('saleStoreId', res[0].saleStoreId)
form.setValue('saleStoreLevel', res[0].storeLvl)
setSaleStoreList(res)
setFavoriteStoreList(res)
setShowSaleStoreList(res)
otherList = res.filter((row) => row.firstAgentYn === 'N')
setOtherSaleStoreList(otherList)
}
} }
} }
get({ url: url }).then((res) => {
if (!isEmptyArray(res)) {
if (session?.storeId === 'T01') {
firstList = res.filter((row) => row.saleStoreLevel === '1')
firstList.sort((a, b) => (a.saleStoreId !== 'T01') - (b.saleStoreId !== 'T01') || a.saleStoreId - b.saleStoreId)
favList = firstList.filter((row) => row.saleStoreId === 'T01' || row.priority !== 'B')
setSaleStoreList(firstList)
setFavoriteStoreList(favList)
setShowSaleStoreList(favList)
// 1 1 if (managementState.firstAgentId != null) {
// 2 2 1 form.setValue('saleStoreId', managementState.firstAgentId)
if (managementState.saleStoreLevel === '1') { setSelOptions(managementState.firstAgentId)
setSelOptions(managementState.saleStoreId) } else {
form.setValue('saleStoreId', managementState.saleStoreId) form.setValue('saleStoreId', managementState.saleStoreId)
form.setValue('saleStoreLevel', managementState.saleStoreLevel) setSelOptions(managementState.saleStoreId)
} else { }
setOtherSelOptions(managementState.saleStoreId)
form.setValue('otherSaleStoreId', managementState.saleStoreId)
form.setValue('otherSaleStoreLevel', managementState.saleStoreLevel)
form.setValue('saleStoreLevel', '1') // 1 2
}
//No. let data = managementState?.firstAgentId ? managementState.firstAgentId : managementState.saleStoreId
form.setValue('planReqNo', managementState.planReqNo) url = `/api/object/saleStore/${data}/list?firstFlg=0&userId=${session?.userId}`
//
form.setValue('receiveUser', managementState.receiveUser)
//objectStatusId get({ url: url }).then((res) => {
setSelectObjectStatusId(managementState.objectStatusId) if (!isEmptyArray(res)) {
form.setValue('objectStatusId', managementState.objectStatusId) res.map((row) => {
row.value = row.saleStoreId
row.label = row.saleStoreName
})
// otherList = res
form.setValue('objectName', managementState.objectName) setOriginOtherSaleStoreList(otherList)
setOtherSaleStoreList(otherList)
}
})
} else {
//1
if (session?.storeLvl === '1') {
firstList = res
favList = res.filter((row) => row.priority !== 'B')
otherList = res.filter((row) => row.firstAgentYn === 'N')
// setSaleStoreList(firstList)
setSelHonorificCode(managementState.objectNameOmit) setFavoriteStoreList(firstList)
form.setValue('objectNameOmit', managementState.objectNameOmit) setShowSaleStoreList(firstList)
// setOtherSaleStoreList(otherList)
form.setValue('objectNameKana', managementState.objectNameKana) } else {
setSelOptions(res[0].saleStoreId)
form.setValue('saleStoreId', res[0].saleStoreId)
form.setValue('saleStoreLevel', res[0].storeLvl)
setSaleStoreList(res)
setFavoriteStoreList(res)
setShowSaleStoreList(res)
otherList = res.filter((row) => row.firstAgentYn === 'N')
setOtherSaleStoreList(otherList)
}
}
}
// // 1 1
form.setValue('zipNo', managementState.zipNo) // 2 2 1
if (managementState.saleStoreLevel === '1') {
setSelOptions(managementState.saleStoreId)
form.setValue('saleStoreId', managementState.saleStoreId)
form.setValue('saleStoreLevel', managementState.saleStoreLevel)
} else {
setOtherSelOptions(managementState.saleStoreId)
form.setValue('otherSaleStoreId', managementState.saleStoreId)
form.setValue('otherSaleStoreLevel', managementState.saleStoreLevel)
// / form.setValue('saleStoreLevel', '1')
setPrefValue(managementState.prefId) }
form.setValue('prefId', managementState.prefId)
form.setValue('prefName', managementState.prefName)
form.setValue('address', managementState.address)
//
form.setValue('areaId', managementState.areaId)
// //No.
form.setValue('standardWindSpeedId', managementState.standardWindSpeedId) form.setValue('planReqNo', managementState.planReqNo)
// //
form.setValue('verticalSnowCover', managementState.verticalSnowCover) form.setValue('receiveUser', managementState.receiveUser)
// coldRegionFlg 1 true
form.setValue('coldRegionFlg', managementState.coldRegionFlg === '1' ? true : false)
// surfaceType null //objectStatusId
// form.setValue('surfaceType', '') setSelectObjectStatusId(managementState.objectStatusId)
// form.setValue('surfaceType', '') form.setValue('objectStatusId', managementState.objectStatusId)
form.setValue('surfaceType', managementState.surfaceType)
// saltAreaFlg 1 true //
form.setValue('saltAreaFlg', managementState.saltAreaFlg === '1' ? true : false) form.setValue('objectName', managementState.objectName)
//
form.setValue('installHeight', managementState.installHeight) //
// null 0 setSelHonorificCode(managementState.objectNameOmit)
if (managementState.conType === null) { form.setValue('objectNameOmit', managementState.objectNameOmit)
form.setValue('conType', '0')
} else { //
form.setValue('conType', managementState.conType) form.setValue('objectNameKana', managementState.objectNameKana)
}
// //
form.setValue('remarks', managementState.remarks) form.setValue('zipNo', managementState.zipNo)
})
// /
setPrefValue(managementState.prefId)
form.setValue('prefId', managementState.prefId)
form.setValue('prefName', managementState.prefName)
form.setValue('address', managementState.address)
//
form.setValue('areaId', managementState.areaId)
//
form.setValue('standardWindSpeedId', managementState.standardWindSpeedId)
//
form.setValue('verticalSnowCover', managementState.verticalSnowCover)
// coldRegionFlg 1 true
form.setValue('coldRegionFlg', managementState.coldRegionFlg === '1' ? true : false)
// surfaceType null
// form.setValue('surfaceType', '')
// form.setValue('surfaceType', '')
form.setValue('surfaceType', managementState.surfaceType)
// saltAreaFlg 1 true
form.setValue('saltAreaFlg', managementState.saltAreaFlg === '1' ? true : false)
//
form.setValue('installHeight', managementState.installHeight)
// null 0
if (managementState.conType === null) {
form.setValue('conType', '0')
} else {
form.setValue('conType', managementState.conType)
}
//
form.setValue('remarks', managementState.remarks)
})
//
setIsGlobalLoading(false)
}
} }
}, [managementState]) }, [managementState])
@ -1262,7 +1279,7 @@ export default function StuffDetail() {
if (res.status === 201) { if (res.status === 201) {
alert(getMessage('stuff.detail.save')) alert(getMessage('stuff.detail.save'))
setFloorPlanObjectNo({ floorPlanObjectNo: objectNo }) setFloorPlanObjectNo({ floorPlanObjectNo: objectNo })
router.push(`/management/stuff/detail?objectNo=${res.data.objectNo.toString()}`) router.push(`/management/stuff/detail?objectNo=${res.data.objectNo.toString()}`, { scroll: false })
} }
}) })
} else { } else {
@ -1271,8 +1288,7 @@ export default function StuffDetail() {
if (res.status === 201) { if (res.status === 201) {
setFloorPlanObjectNo({ floorPlanObjectNo: res.data.objectNo }) setFloorPlanObjectNo({ floorPlanObjectNo: res.data.objectNo })
alert(getMessage('stuff.detail.save')) alert(getMessage('stuff.detail.save'))
// router.refresh() router.push(`/management/stuff/detail?objectNo=${res.data.objectNo.toString()}`, { scroll: false })
router.push(`/management/stuff/detail?objectNo=${res.data.objectNo.toString()}`)
} }
}) })
} }
@ -1307,6 +1323,7 @@ export default function StuffDetail() {
tempFlg: '1', tempFlg: '1',
workNo: null, workNo: null,
workName: null, workName: null,
objectNo: objectNo ? objectNo : '',
} }
//1 or 2 //1 or 2
@ -1315,12 +1332,22 @@ export default function StuffDetail() {
params.saleStoreLevel = session.storeLvl params.saleStoreLevel = session.storeLvl
} }
await promisePost({ url: '/api/object/save-object', data: params }).then((res) => { const apiUrl = '/api/object/save-object'
if (res.status === 201) { if (objectNo) {
alert(getMessage('stuff.detail.tempSave.message1')) await promisePut({ url: apiUrl, data: params }).then((res) => {
router.push(`${pathname}?objectNo=${res.data.objectNo.toString()}`) if (res.status === 201) {
} alert(getMessage('stuff.detail.tempSave.message1'))
}) router.push(`/management/stuff/tempdetail?objectNo=${res.data.objectNo.toString()}`, { scroll: false })
}
})
} else {
await promisePost({ url: apiUrl, data: params }).then((res) => {
if (res.status === 201) {
alert(getMessage('stuff.detail.tempSave.message1'))
router.push(`/management/stuff/tempdetail?objectNo=${res.data.objectNo.toString()}`, { scroll: false })
}
})
}
} }
// //
@ -1332,6 +1359,11 @@ export default function StuffDetail() {
if (confirm(getMessage('common.message.data.delete'))) { if (confirm(getMessage('common.message.data.delete'))) {
del({ url: `/api/object/${objectNo}` }).then(() => { del({ url: `/api/object/${objectNo}` }).then(() => {
setFloorPlanObjectNo({ floorPlanObjectNo: '' }) setFloorPlanObjectNo({ floorPlanObjectNo: '' })
if (session.storeId === 'T01') {
stuffSearchParams.code = 'DELETE'
} else {
resetStuffRecoil()
}
router.push('/management/stuff') router.push('/management/stuff')
}) })
} }

View File

@ -17,6 +17,8 @@ import { isObjectNotEmpty } from '@/util/common-utils'
import { SessionContext } from '@/app/SessionProvider' import { SessionContext } from '@/app/SessionProvider'
import { QcastContext } from '@/app/QcastProvider'
export default function StuffSearchCondition() { export default function StuffSearchCondition() {
const { session } = useContext(SessionContext) const { session } = useContext(SessionContext)
const setAppMessageState = useSetRecoilState(appMessageStore) const setAppMessageState = useSetRecoilState(appMessageStore)
@ -66,31 +68,64 @@ export default function StuffSearchCondition() {
const [otherSaleStoreList, setOtherSaleStoreList] = useState([]) //1 const [otherSaleStoreList, setOtherSaleStoreList] = useState([]) //1
const [otherSaleStoreId, setOtherSaleStoreId] = useState('') const [otherSaleStoreId, setOtherSaleStoreId] = useState('')
const { setIsGlobalLoading } = useContext(QcastContext)
// //
const onSubmit = () => { const onSubmit = () => {
let diff = dayjs(endDate).diff(startDate, 'day') let diff = dayjs(endDate).diff(startDate, 'day')
if (diff > 366) { if (diff > 366) {
return alert(getMessage('stuff.message.periodError')) return alert(getMessage('stuff.message.periodError'))
} }
if (isNaN(diff)) {
return alert(getMessage('stuff.message.periodError'))
}
setIsGlobalLoading(true)
if (stuffSearch.code === 'S') { if (stuffSearch.code === 'S') {
setStuffSearch({ if (stuffSearch.pageNo !== 1) {
schObjectNo: objectNo ? objectNo : stuffSearch.schObjectNo, setStuffSearch({
schSaleStoreName: saleStoreName ? saleStoreName : '', schObjectNo: objectNo ? objectNo : stuffSearch.schObjectNo,
schAddress: address ? address : '', schSaleStoreName: saleStoreName ? saleStoreName : '',
schObjectName: objectName ? objectName : '', schAddress: address ? address : '',
schDispCompanyName: dispCompanyName ? dispCompanyName : '', schObjectName: objectName ? objectName : '',
schSelSaleStoreId: stuffSearch?.schSelSaleStoreId ? stuffSearch.schSelSaleStoreId : '', schDispCompanyName: dispCompanyName ? dispCompanyName : '',
schOtherSelSaleStoreId: stuffSearch?.schOtherSelSaleStoreId ? stuffSearch.schOtherSelSaleStoreId : '', schSelSaleStoreId: stuffSearch?.schSelSaleStoreId ? stuffSearch.schSelSaleStoreId : '',
schReceiveUser: receiveUser ? receiveUser : '', schOtherSelSaleStoreId: stuffSearch?.schOtherSelSaleStoreId ? stuffSearch.schOtherSelSaleStoreId : '',
schDateType: dateType, schReceiveUser: receiveUser ? receiveUser : '',
schFromDt: dayjs(startDate).format('YYYY-MM-DD'), schDateType: dateType,
schToDt: dayjs(endDate).format('YYYY-MM-DD'), schFromDt: startDate ? dayjs(startDate).format('YYYY-MM-DD') : '',
code: 'E', schToDt: endDate ? dayjs(endDate).format('YYYY-MM-DD') : '',
startRow: stuffSearch?.startRow ? stuffSearch.startRow : 1, code: 'E',
endRow: stuffSearch?.endRow ? stuffSearch.endRow : 100, startRow: 1,
schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'R', endRow: 1 * stuffSearch?.pageSize,
}) schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'R',
pageNo: 1,
pageSize: stuffSearch?.pageSize,
})
} else {
setStuffSearch({
schObjectNo: objectNo ? objectNo : stuffSearch.schObjectNo,
schSaleStoreName: saleStoreName ? saleStoreName : '',
schAddress: address ? address : '',
schObjectName: objectName ? objectName : '',
schDispCompanyName: dispCompanyName ? dispCompanyName : '',
schSelSaleStoreId: stuffSearch?.schSelSaleStoreId ? stuffSearch.schSelSaleStoreId : '',
schOtherSelSaleStoreId: stuffSearch?.schOtherSelSaleStoreId ? stuffSearch.schOtherSelSaleStoreId : '',
schReceiveUser: receiveUser ? receiveUser : '',
schDateType: dateType,
schFromDt: startDate ? dayjs(startDate).format('YYYY-MM-DD') : '',
schToDt: endDate ? dayjs(endDate).format('YYYY-MM-DD') : '',
code: 'E',
startRow: stuffSearch?.startRow ? stuffSearch.startRow : 1,
endRow: stuffSearch?.endRow ? stuffSearch.endRow : 100,
schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'R',
pageNo: stuffSearch?.pageNo,
pageSize: stuffSearch?.pageSize,
})
}
} else if (stuffSearch.code === 'FINISH') { } else if (stuffSearch.code === 'FINISH') {
setStuffSearch({ setStuffSearch({
schObjectNo: objectNo, schObjectNo: objectNo,
@ -102,13 +137,98 @@ export default function StuffSearchCondition() {
schOtherSelSaleStoreId: otherSaleStoreId, schOtherSelSaleStoreId: otherSaleStoreId,
schReceiveUser: receiveUser, schReceiveUser: receiveUser,
schDateType: dateType, schDateType: dateType,
schFromDt: dayjs(startDate).format('YYYY-MM-DD'), schFromDt: startDate ? dayjs(startDate).format('YYYY-MM-DD') : '',
schToDt: dayjs(endDate).format('YYYY-MM-DD'), schToDt: endDate ? dayjs(endDate).format('YYYY-MM-DD') : '',
code: 'E', code: 'E',
startRow: 1, startRow: 1,
endRow: 100, endRow: 100,
schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'R', schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'R',
}) })
} else if (stuffSearch.code === 'E') {
if (session.storeId !== 'T01' && session.storeLvl === '1') {
setStuffSearch({
schObjectNo: stuffSearch?.schObjectNo ? stuffSearch.schObjectNo : objectNo,
schSaleStoreName: stuffSearch?.schSaleStoreName ? stuffSearch.schSaleStoreName : saleStoreName,
schAddress: stuffSearch?.schAddress ? stuffSearch.schAddress : address,
schObjectName: stuffSearch?.schObjectName ? stuffSearch.schObjectName : objectName,
schDispCompanyName: stuffSearch?.schDispCompanyName ? stuffSearch.schDispCompanyName : dispCompanyName,
schSelSaleStoreId: otherSaleStoreId ? schSelSaleStoreId : '',
schOtherSelSaleStoreId: otherSaleStoreId,
schReceiveUser: stuffSearch?.schReceiveUser ? stuffSearch.schReceiveUser : receiveUser,
schDateType: dateType,
schFromDt: startDate ? dayjs(startDate).format('YYYY-MM-DD') : '',
schToDt: endDate ? dayjs(endDate).format('YYYY-MM-DD') : '',
code: 'E',
startRow: stuffSearch?.startRow ? stuffSearch.startRow : 1,
endRow: stuffSearch?.endRow ? stuffSearch.endRow : 100,
schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'R',
pageNo: stuffSearch?.pageNo,
pageSize: stuffSearch?.pageSize,
})
} else if (session.storeId === 'T01') {
if (stuffSearch.pageNo !== 1) {
setStuffSearch({
schObjectNo: objectNo ? objectNo : stuffSearch.schObjectNo,
schSaleStoreName: saleStoreName ? saleStoreName : '',
schAddress: address ? address : '',
schObjectName: objectName ? objectName : '',
schDispCompanyName: dispCompanyName ? dispCompanyName : '',
schSelSaleStoreId: stuffSearch?.schSelSaleStoreId ? stuffSearch.schSelSaleStoreId : '',
schOtherSelSaleStoreId: stuffSearch?.schOtherSelSaleStoreId ? stuffSearch.schOtherSelSaleStoreId : '',
schReceiveUser: receiveUser ? receiveUser : '',
schDateType: dateType,
schFromDt: startDate ? dayjs(startDate).format('YYYY-MM-DD') : '',
schToDt: endDate ? dayjs(endDate).format('YYYY-MM-DD') : '',
code: 'E',
startRow: 1,
endRow: 1 * stuffSearch?.pageSize,
// schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'R',
schSortType: 'R',
pageNo: 1,
pageSize: stuffSearch?.pageSize,
})
} else {
setStuffSearch({
schObjectNo: stuffSearch?.schObjectNo ? stuffSearch.schObjectNo : objectNo,
schSaleStoreName: stuffSearch?.schSaleStoreName ? stuffSearch.schSaleStoreName : saleStoreName,
schAddress: stuffSearch?.schAddress ? stuffSearch.schAddress : address,
schObjectName: stuffSearch?.schObjectName ? stuffSearch.schObjectName : objectName,
schDispCompanyName: stuffSearch?.schDispCompanyName ? stuffSearch.schDispCompanyName : dispCompanyName,
schSelSaleStoreId: schSelSaleStoreId,
schOtherSelSaleStoreId: otherSaleStoreId,
schReceiveUser: stuffSearch?.schReceiveUser ? stuffSearch.schReceiveUser : receiveUser,
schDateType: dateType,
schFromDt: startDate ? dayjs(startDate).format('YYYY-MM-DD') : '',
schToDt: endDate ? dayjs(endDate).format('YYYY-MM-DD') : '',
code: 'E',
startRow: stuffSearch?.startRow ? stuffSearch.startRow : 1,
endRow: stuffSearch?.endRow ? stuffSearch.endRow : 100,
schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'R',
pageNo: stuffSearch?.pageNo,
pageSize: stuffSearch?.pageSize,
})
}
} else {
setStuffSearch({
schObjectNo: stuffSearch?.schObjectNo ? stuffSearch.schObjectNo : objectNo,
schSaleStoreName: stuffSearch?.schSaleStoreName ? stuffSearch.schSaleStoreName : saleStoreName,
schAddress: stuffSearch?.schAddress ? stuffSearch.schAddress : address,
schObjectName: stuffSearch?.schObjectName ? stuffSearch.schObjectName : objectName,
schDispCompanyName: stuffSearch?.schDispCompanyName ? stuffSearch.schDispCompanyName : dispCompanyName,
schSelSaleStoreId: schSelSaleStoreId,
schOtherSelSaleStoreId: otherSaleStoreId,
schReceiveUser: stuffSearch?.schReceiveUser ? stuffSearch.schReceiveUser : receiveUser,
schDateType: dateType,
schFromDt: startDate ? dayjs(startDate).format('YYYY-MM-DD') : '',
schToDt: endDate ? dayjs(endDate).format('YYYY-MM-DD') : '',
code: 'E',
startRow: stuffSearch?.startRow ? stuffSearch.startRow : 1,
endRow: stuffSearch?.endRow ? stuffSearch.endRow : 100,
schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'R',
pageNo: stuffSearch?.pageNo,
pageSize: stuffSearch?.pageSize,
})
}
} else { } else {
setStuffSearch({ setStuffSearch({
schObjectNo: objectNo, schObjectNo: objectNo,
@ -116,16 +236,18 @@ export default function StuffSearchCondition() {
schAddress: address, schAddress: address,
schObjectName: objectName, schObjectName: objectName,
schDispCompanyName: dispCompanyName, schDispCompanyName: dispCompanyName,
schSelSaleStoreId: stuffSearch?.schSelSaleStoreId, schSelSaleStoreId: schSelSaleStoreId,
schOtherSelSaleStoreId: stuffSearch?.schOtherSelSaleStoreId, schOtherSelSaleStoreId: otherSaleStoreId,
schReceiveUser: receiveUser, schReceiveUser: receiveUser,
schDateType: dateType, schDateType: dateType,
schFromDt: dayjs(startDate).format('YYYY-MM-DD'), schFromDt: startDate ? dayjs(startDate).format('YYYY-MM-DD') : '',
schToDt: dayjs(endDate).format('YYYY-MM-DD'), schToDt: endDate ? dayjs(endDate).format('YYYY-MM-DD') : '',
code: 'E', code: 'E',
startRow: 1, startRow: stuffSearch?.startRow ? stuffSearch.startRow : 1,
endRow: 100, endRow: stuffSearch?.endRow ? stuffSearch.endRow : 100,
schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'R', schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'R',
pageNo: stuffSearch?.pageNo,
pageSize: stuffSearch?.pageSize,
}) })
} }
} }
@ -155,8 +277,21 @@ export default function StuffSearchCondition() {
handleClear1() // handleClear1() //
resetStuffRecoil() resetStuffRecoil()
setStuffSearch({ setStuffSearch({
schObjectNo: '',
schAddress: '',
schObjectName: '',
schSaleStoreName: '',
schReceiveUser: '',
schDispCompanyName: '',
schSelSaleStoreId: '', schSelSaleStoreId: '',
schOtherSelSaleStoreId: '', schOtherSelSaleStoreId: '',
schDateType: 'U',
startRow: 1,
endRow: 100,
schSortType: 'R',
pageNo: 1,
pageSize: 100,
code: 'S',
}) })
} else { } else {
if (otherSaleStoreList.length > 1) { if (otherSaleStoreList.length > 1) {
@ -313,7 +448,11 @@ export default function StuffSearchCondition() {
//X //X
// //
setSchSelSaleStoreId('') setSchSelSaleStoreId('')
// stuffSearch.schSelSaleStoreId = '' setOtherSaleStoreId('')
if (stuffSearch.code === 'S') {
stuffSearch.schSelSaleStoreId = ''
stuffSearch.schOtherSelSaleStoreId = ''
}
//2 //2
setOtherSaleStoreList([]) setOtherSaleStoreList([])
@ -330,14 +469,17 @@ export default function StuffSearchCondition() {
stuffSearch.schSelSaleStoreId = schSelSaleStoreId stuffSearch.schSelSaleStoreId = schSelSaleStoreId
} else { } else {
//X 1 //X 1
if (session.storeLvl === '1') { if (session.storeLvl === '1') {
if (stuffSearch.schOtherSelSaleStoreId === '') { if (stuffSearch.schOtherSelSaleStoreId === '') {
// //
// stuffSearch.schSelSaleStoreId = ''
setSchSelSaleStoreId(session.storeId) setSchSelSaleStoreId(session.storeId)
} else { } else {
// //
setOtherSaleStoreId('') setOtherSaleStoreId('')
if (stuffSearch.code === 'S') {
stuffSearch.schOtherSelSaleStoreId = ''
}
} }
} else { } else {
setOtherSaleStoreId('') setOtherSaleStoreId('')
@ -349,14 +491,52 @@ export default function StuffSearchCondition() {
} }
useEffect(() => { useEffect(() => {
setStartDate(stuffSearch?.schFromDt ? stuffSearch.schFromDt : dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD')) //X42
setEndDate(stuffSearch?.schToDt ? stuffSearch.schToDt : dayjs(new Date()).format('YYYY-MM-DD')) if (session?.storeId === 'T01') {
setObjectNo(stuffSearch.schObjectNo ? stuffSearch.schObjectNo : objectNo) if (stuffSearch.code === 'DELETE') {
setSaleStoreName(stuffSearch.schSaleStoreName ? stuffSearch.schSaleStoreName : saleStoreName) setObjectNo('')
setAddress(stuffSearch.schAddress ? stuffSearch.schAddress : address) setSaleStoreName('')
setobjectName(stuffSearch.schObjectName ? stuffSearch.schObjectName : objectName) setAddress('')
setDispCompanyName(stuffSearch.schDispCompanyName ? stuffSearch.schDispCompanyName : dispCompanyName) setobjectName('')
setReceiveUser(stuffSearch.schReceiveUser ? stuffSearch.schReceiveUser : receiveUser) setDispCompanyName('')
setReceiveUser('')
objectNoRef.current.value = ''
saleStoreNameRef.current.value = ''
addressRef.current.value = ''
objectNameRef.current.value = ''
dispCompanyNameRef.current.value = ''
receiveUserRef.current.value = ''
stuffSearch.schObjectNo = ''
stuffSearch.schAddress = ''
stuffSearch.schObjectName = ''
stuffSearch.schSaleStoreName = ''
stuffSearch.schReceiveUser = ''
stuffSearch.schDispCompanyName = ''
stuffSearch.schDateType = 'U'
stuffSearch.schFromDt = dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD')
stuffSearch.schToDt = dayjs(new Date()).format('YYYY-MM-DD')
stuffSearch.startRow = 1
stuffSearch.endRow = 100
stuffSearch.schSelSaleStoreId = ''
stuffSearch.schOtherSelSaleStoreId = ''
stuffSearch.schSortType = 'R'
stuffSearch.pageNo = 1
stuffSearch.pageSize = 100
setSchSelSaleStoreId('')
setOtherSaleStoreId('')
}
} else {
setStartDate(stuffSearch?.schFromDt ? stuffSearch.schFromDt : dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD'))
setEndDate(stuffSearch?.schToDt ? stuffSearch.schToDt : dayjs(new Date()).format('YYYY-MM-DD'))
setObjectNo(stuffSearch.schObjectNo ? stuffSearch.schObjectNo : objectNo)
setSaleStoreName(stuffSearch.schSaleStoreName ? stuffSearch.schSaleStoreName : saleStoreName)
setAddress(stuffSearch.schAddress ? stuffSearch.schAddress : address)
setobjectName(stuffSearch.schObjectName ? stuffSearch.schObjectName : objectName)
setDispCompanyName(stuffSearch.schDispCompanyName ? stuffSearch.schDispCompanyName : dispCompanyName)
setReceiveUser(stuffSearch.schReceiveUser ? stuffSearch.schReceiveUser : receiveUser)
setDateType(stuffSearch.schDateType ? stuffSearch.schDateType : dateType)
}
}, [stuffSearch]) }, [stuffSearch])
useEffect(() => { useEffect(() => {
@ -386,14 +566,14 @@ export default function StuffSearchCondition() {
<div className="left-unit-box"> <div className="left-unit-box">
<Link href="/management/stuff/tempdetail" scroll={false}> <Link href="/management/stuff/tempdetail" scroll={false}>
<button type="button" className="btn-origin navy mr5"> <button type="button" className="btn-origin navy mr5">
{getMessage('stuff.search.btn1')} {getMessage('stuff.search.btn.register')}
</button> </button>
</Link> </Link>
<button type="button" className="btn-origin navy mr5" onClick={onSubmit}> <button type="button" className="btn-origin navy mr5" onClick={onSubmit}>
{getMessage('stuff.search.btn2')} {getMessage('stuff.search.btn.search')}
</button> </button>
<button type="button" className="btn-origin grey" onClick={resetRecoil}> <button type="button" className="btn-origin grey" onClick={resetRecoil}>
{getMessage('stuff.search.btn3')} {getMessage('stuff.search.btn.reset')}
</button> </button>
</div> </div>
</div> </div>
@ -420,6 +600,7 @@ export default function StuffSearchCondition() {
className="input-light" className="input-light"
defaultValue={stuffSearch?.schObjectNo ? stuffSearch.schObjectNo : objectNo} defaultValue={stuffSearch?.schObjectNo ? stuffSearch.schObjectNo : objectNo}
onChange={() => { onChange={() => {
stuffSearch.schObjectNo = objectNoRef.current.value
setObjectNo(objectNoRef.current.value) setObjectNo(objectNoRef.current.value)
}} }}
onKeyUp={handleByOnKeyUp} onKeyUp={handleByOnKeyUp}
@ -435,6 +616,7 @@ export default function StuffSearchCondition() {
className="input-light" className="input-light"
defaultValue={stuffSearch?.schSaleStoreName ? stuffSearch.schSaleStoreName : saleStoreName} defaultValue={stuffSearch?.schSaleStoreName ? stuffSearch.schSaleStoreName : saleStoreName}
onChange={() => { onChange={() => {
stuffSearch.schSaleStoreName = saleStoreNameRef.current.value
setSaleStoreName(saleStoreNameRef.current.value) setSaleStoreName(saleStoreNameRef.current.value)
}} }}
onKeyUp={handleByOnKeyUp} onKeyUp={handleByOnKeyUp}
@ -450,6 +632,7 @@ export default function StuffSearchCondition() {
className="input-light" className="input-light"
defaultValue={stuffSearch?.schAddress ? stuffSearch.schAddress : address} defaultValue={stuffSearch?.schAddress ? stuffSearch.schAddress : address}
onChange={() => { onChange={() => {
stuffSearch.schAddress = addressRef.current.value
setAddress(addressRef.current.value) setAddress(addressRef.current.value)
}} }}
onKeyUp={handleByOnKeyUp} onKeyUp={handleByOnKeyUp}
@ -465,6 +648,7 @@ export default function StuffSearchCondition() {
className="input-light" className="input-light"
defaultValue={stuffSearch?.schDispCompanyName ? stuffSearch.schDispCompanyName : dispCompanyName} defaultValue={stuffSearch?.schDispCompanyName ? stuffSearch.schDispCompanyName : dispCompanyName}
onChange={() => { onChange={() => {
stuffSearch.schDispCompanyName = dispCompanyNameRef.current.value
setDispCompanyName(dispCompanyNameRef.current.value) setDispCompanyName(dispCompanyNameRef.current.value)
}} }}
onKeyUp={handleByOnKeyUp} onKeyUp={handleByOnKeyUp}
@ -482,6 +666,7 @@ export default function StuffSearchCondition() {
className="input-light" className="input-light"
defaultValue={stuffSearch?.schObjectName ? stuffSearch.schObjectName : objectName} defaultValue={stuffSearch?.schObjectName ? stuffSearch.schObjectName : objectName}
onChange={() => { onChange={() => {
stuffSearch.schObjectName = objectNameRef.current.value
setobjectName(objectNameRef.current.value) setobjectName(objectNameRef.current.value)
}} }}
onKeyUp={handleByOnKeyUp} onKeyUp={handleByOnKeyUp}
@ -497,6 +682,7 @@ export default function StuffSearchCondition() {
ref={receiveUserRef} ref={receiveUserRef}
defaultValue={stuffSearch?.schReceiveUser ? stuffSearch.schReceiveUser : receiveUser} defaultValue={stuffSearch?.schReceiveUser ? stuffSearch.schReceiveUser : receiveUser}
onChange={() => { onChange={() => {
stuffSearch.schReceiveUser = receiveUserRef.current.value
setReceiveUser(receiveUserRef.current.value) setReceiveUser(receiveUserRef.current.value)
}} }}
onKeyUp={handleByOnKeyUp} onKeyUp={handleByOnKeyUp}
@ -638,7 +824,7 @@ export default function StuffSearchCondition() {
type="radio" type="radio"
name="radio_ptype" name="radio_ptype"
id="radio_u" id="radio_u"
checked={stuffSearch.schDateType === 'U' ? true : false} defaultChecked={stuffSearch.schDateType === 'U' ? true : false}
value={'U'} value={'U'}
onChange={(e) => { onChange={(e) => {
setDateType(e.target.value) setDateType(e.target.value)
@ -652,7 +838,7 @@ export default function StuffSearchCondition() {
type="radio" type="radio"
name="radio_ptype" name="radio_ptype"
id="radio_r" id="radio_r"
checked={stuffSearch.schDateType === 'R' ? true : false} defaultChecked={stuffSearch.schDateType === 'R' ? true : false}
value={'R'} value={'R'}
onChange={(e) => { onChange={(e) => {
setDateType(e.target.value) setDateType(e.target.value)

View File

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

View File

@ -5,8 +5,9 @@ import { Bar } from 'react-chartjs-2'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { useEffect, useState, useRef } from 'react' import { useEffect, useState, useRef } from 'react'
import { useRecoilValue } from 'recoil' import { useRecoilValue, useRecoilState } from 'recoil'
import { floorPlanObjectState } from '@/store/floorPlanObjectAtom' import { floorPlanObjectState } from '@/store/floorPlanObjectAtom'
import { pwrGnrSimTypeState } from '@/store/simulatorAtom'
import { useAxios } from '@/hooks/useAxios' import { useAxios } from '@/hooks/useAxios'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
@ -41,6 +42,7 @@ export default function Simulator() {
// //
const [chartData, setChartData] = useState([]) const [chartData, setChartData] = useState([])
const data = { const data = {
labels: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'], labels: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
datasets: [ datasets: [
@ -105,8 +107,10 @@ export default function Simulator() {
useEffect(() => { useEffect(() => {
if (objectNo) { if (objectNo) {
fetchObjectDetail(objectNo) fetchObjectDetail(objectNo)
fetchSimulatorNotice()
setPwrGnrSimType('D')
setPwrRecoil({ ...pwrRecoil, type: 'D' })
} }
fetchSimulatorNotice()
}, [objectNo, plan]) }, [objectNo, plan])
// //
@ -118,13 +122,30 @@ export default function Simulator() {
// //
const [pcsInfoList, setPcsInfoList] = useState([]) const [pcsInfoList, setPcsInfoList] = useState([])
// list
const [hatsudenryouAll, setHatsudenryouAll] = useState([])
const [hatsudenryouAllSnow, setHatsudenryouAllSnow] = useState([])
const [hatsudenryouPeakcutAll, setHatsudenryouPeakcutAll] = useState([])
const [hatsudenryouPeakcutAllSnow, setHatsudenryouPeakcutAllSnow] = useState([])
const fetchObjectDetail = async (objectNo) => { const fetchObjectDetail = async (objectNo) => {
const apiUrl = `/api/pwrGnrSimulation/calculations?objectNo=${objectNo}&planNo=${plan?.id}` const apiUrl = `/api/pwrGnrSimulation/calculations?objectNo=${objectNo}&planNo=${plan?.id}`
const resultData = await get({ url: apiUrl }) const resultData = await get({ url: apiUrl })
if (resultData) { if (resultData) {
setObjectDetail(resultData) setObjectDetail(resultData)
if (resultData.frcPwrGnrList) { if (resultData.hatsudenryouAll) {
setChartData(resultData.frcPwrGnrList) setHatsudenryouAll(resultData.hatsudenryouAll)
}
if (resultData.hatsudenryouAllSnow) {
setHatsudenryouAllSnow(resultData.hatsudenryouAllSnow)
}
if (resultData.hatsudenryouPeakcutAll) {
setHatsudenryouPeakcutAll(resultData.hatsudenryouPeakcutAll)
}
if (resultData.hatsudenryouPeakcutAllSnow) {
setHatsudenryouPeakcutAllSnow(resultData.hatsudenryouPeakcutAllSnow)
setChartData(resultData.hatsudenryouPeakcutAllSnow)
} }
if (resultData.pcsList) { if (resultData.pcsList) {
setPcsInfoList(resultData.pcsList) setPcsInfoList(resultData.pcsList)
@ -148,6 +169,28 @@ export default function Simulator() {
}) })
} }
// , list type
const [pwrGnrSimType, setPwrGnrSimType] = useState('D')
const [pwrRecoil, setPwrRecoil] = useRecoilState(pwrGnrSimTypeState)
const handleChartChangeData = (type) => {
setPwrRecoil({ ...pwrRecoil, type: type })
switch (type) {
case 'A':
setChartData(hatsudenryouAll)
break
case 'B':
setChartData(hatsudenryouAllSnow)
break
case 'C':
setChartData(hatsudenryouPeakcutAll)
break
case 'D':
setChartData(hatsudenryouPeakcutAllSnow)
break
}
}
return ( return (
<div className="sub-content estimate"> <div className="sub-content estimate">
<div className="sub-content-inner"> <div className="sub-content-inner">
@ -158,23 +201,25 @@ export default function Simulator() {
<div className="estimate-box"> <div className="estimate-box">
<div className="estimate-tit">{getMessage('simulator.title.sub1')}</div> <div className="estimate-tit">{getMessage('simulator.title.sub1')}</div>
<div className="estimate-name"> <div className="estimate-name">
{objectDetail.objectNo} (Plan No: {objectDetail.planNo}) {objectDetail.objectNo} (Plan No: {plan?.id})
</div> </div>
</div> </div>
{/* 작성일 */} {/* 작성일 */}
<div className="estimate-box"> <div className="estimate-box">
<div className="estimate-tit">{getMessage('simulator.title.sub2')}</div> <div className="estimate-tit">{getMessage('simulator.title.sub2')}</div>
<div className="estimate-name">{`${dayjs(objectDetail.drawingEstimateCreateDate).format('YYYY.MM.DD')}`}</div> <div className="estimate-name">
{objectDetail.drawingEstimateCreateDate ? `${dayjs(objectDetail.drawingEstimateCreateDate).format('YYYY.MM.DD')}` : ''}
</div>
</div> </div>
{/* 시스템용량 */} {/* 시스템용량 */}
<div className="estimate-box"> <div className="estimate-box">
<div className="estimate-tit">{getMessage('simulator.title.sub3')}</div> <div className="estimate-tit">{getMessage('simulator.title.sub3')}</div>
<div className="estimate-name">{convertNumberToPriceDecimal(objectDetail.capacity)}kW</div> <div className="estimate-name">{objectDetail.capacity ? `${convertNumberToPriceDecimal(objectDetail.capacity)}kW` : ''}</div>
</div> </div>
{/* 연간예측발전량 */} {/* 연간예측발전량 */}
<div className="estimate-box"> <div className="estimate-box">
<div className="estimate-tit">{getMessage('simulator.title.sub4')}</div> <div className="estimate-tit">{getMessage('simulator.title.sub4')}</div>
<div className="estimate-name">{convertNumberToPriceDecimal(objectDetail.anlFrcsGnrt)}</div> <div className="estimate-name">{chartData[chartData.length - 1]}</div>
</div> </div>
</div> </div>
<div className="estimate-list-wrap"> <div className="estimate-list-wrap">
@ -206,6 +251,24 @@ export default function Simulator() {
<div className="chart-inner"> <div className="chart-inner">
<div className="sub-table-box"> <div className="sub-table-box">
<div className="chart-box"> <div className="chart-box">
{/* chart */}
<div className="select-wrap">
<select
style={{ width: '30%' }}
className="select-light"
value={pwrGnrSimType}
defaultValue={`D`}
onChange={(e) => {
handleChartChangeData(e.target.value)
setPwrGnrSimType(e.target.value)
}}
>
<option value={`A`}>積雪考慮なし(ピークカットなし発電量)</option>
<option value={`B`}>積雪考慮なし(ピークカットあり発電量)</option>
<option value={`C`}>積雪考慮あり(ピークカットなし発電量)</option>
<option value={`D`}>積雪考慮あり(ピークカットあり発電量)</option>
</select>
</div>
<Bar ref={chartRef} data={data} options={options} /> <Bar ref={chartRef} data={data} options={options} />
</div> </div>
<div className="table-box-title-wrap"> <div className="table-box-title-wrap">
@ -237,7 +300,7 @@ export default function Simulator() {
{chartData.length > 0 ? ( {chartData.length > 0 ? (
<tr> <tr>
{chartData.map((data) => ( {chartData.map((data) => (
<td key={data}>{convertNumberToPriceDecimal(data)}</td> <td key={data}>{data}</td>
))} ))}
</tr> </tr>
) : ( ) : (

View File

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

View File

@ -1,8 +1,30 @@
import { menuNumberState } from '@/store/menuAtom' import { menuNumberState } from '@/store/menuAtom'
import { useRecoilState } from 'recoil' import { useRecoilState, useRecoilValue } from 'recoil'
import { useEffect } from 'react'
import { canvasState } from '@/store/canvasAtom'
import { usePolygon } from '@/hooks/usePolygon'
import { POLYGON_TYPE } from '@/common/common'
export const useCanvasMenu = () => { export const useCanvasMenu = () => {
const [menuNumber, setMenuNumber] = useRecoilState(menuNumberState) const [menuNumber, setMenuNumber] = useRecoilState(menuNumberState)
const canvas = useRecoilValue(canvasState)
const { drawDirectionArrow } = usePolygon()
useEffect(() => {
/*
* 모듈,회로 구성을 벗어나면 방향 표시 초기화 필요
* */
if (!canvas) return
if (![4, 5].includes(menuNumber)) {
canvas
.getObjects()
.filter((obj) => obj.name === POLYGON_TYPE.ROOF)
.forEach((obj) => {
obj.set('moduleCompass', null)
drawDirectionArrow(obj)
})
}
}, [menuNumber])
return { return {
menuNumber, menuNumber,

View File

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

View File

@ -6,27 +6,11 @@ import { estimateState, floorPlanObjectState } from '@/store/floorPlanObjectAtom
import { isObjectNotEmpty } from '@/util/common-utils' import { isObjectNotEmpty } from '@/util/common-utils'
import { SessionContext } from '@/app/SessionProvider' import { SessionContext } from '@/app/SessionProvider'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { useRouter } from 'next/navigation'
const reducer = (prevState, nextState) => { import { FloorPlanContext } from '@/app/floor-plan/FloorPlanProvider'
return { ...prevState, ...nextState }
}
// Constants // Constants
const ESTIMATE_API_ENDPOINT = '/api/estimates' // API 엔드포인트 정의 const ESTIMATE_API_ENDPOINT = '/api/estimate' // API 엔드포인트 정의
const defaultEstimateData = {
estimateDate: new Date(), //견적일
charger: '', //담당자
objectName: '', //안건명
objectNameOmit: '', //경칭코드
estimateType: '', //주문분류
remarks: '', //비고
estimateOption: '', //견적특이사항
itemList: [],
fileList: [],
fileFlg: '0', //후일 자료 제출 (체크 1 노체크 0)
priceCd: '',
}
// Helper functions // Helper functions
const updateItemInList = (itemList, dispOrder, updates) => { const updateItemInList = (itemList, dispOrder, updates) => {
@ -34,6 +18,7 @@ const updateItemInList = (itemList, dispOrder, updates) => {
} }
export const useEstimateController = (planNo) => { export const useEstimateController = (planNo) => {
const router = useRouter()
const { session } = useContext(SessionContext) const { session } = useContext(SessionContext)
const globalLocaleState = useRecoilValue(globalLocaleStore) const globalLocaleState = useRecoilValue(globalLocaleStore)
const objectRecoil = useRecoilValue(floorPlanObjectState) const objectRecoil = useRecoilValue(floorPlanObjectState)
@ -41,25 +26,32 @@ export const useEstimateController = (planNo) => {
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { get, post, promisePost } = useAxios(globalLocaleState) const { promiseGet, post, promisePost } = useAxios(globalLocaleState)
const [isLoading, setIsLoading] = useState(false) const [isLoading, setIsLoading] = useState(false)
const [state, setState] = useReducer(reducer, defaultEstimateData) const { estimateContextState, setEstimateContextState } = useContext(FloorPlanContext)
useEffect(() => { useEffect(() => {
if (!isLoading) { if (planNo && !isLoading) {
if (objectRecoil.floorPlanObjectNo && planNo) { if (objectRecoil.floorPlanObjectNo && planNo) {
fetchSetting() fetchSetting(objectRecoil.floorPlanObjectNo, planNo)
} }
} }
}, []) }, [])
// 상세 조회 // 상세 조회
const fetchSetting = async () => { const fetchSetting = async (objectNo, planNo) => {
try { try {
await get({ url: `/api/estimate/${objectRecoil.floorPlanObjectNo}/${planNo}/detail` }).then((res) => { await promiseGet({ url: `/api/estimate/${objectNo}/${planNo}/detail` }).then((res) => {
if (isObjectNotEmpty(res)) { if (res.status === 200) {
setState(res) if (isObjectNotEmpty(res.data)) {
if (res.data.itemList.length > 0) {
res.data.itemList.map((item) => {
item.delFlg = '0'
})
}
setEstimateContextState(res.data)
}
} }
}) })
setIsLoading(true) setIsLoading(true)
@ -70,16 +62,17 @@ export const useEstimateController = (planNo) => {
} }
const updateItem = (dispOrder, updates) => { const updateItem = (dispOrder, updates) => {
setState({ setEstimateContextState({
itemList: updateItemInList(state.itemList, dispOrder, updates), itemList: updateItemInList(estimateContextState.itemList, dispOrder, updates),
}) })
} }
const addItem = () => { const addItem = () => {
const newItemDispOrder = Math.max(...state.itemList.map((item) => item.dispOrder)) + 1 let newItemDispOrder = Math.max(...estimateContextState.itemList.map((item) => item.dispOrder))
setState({ newItemDispOrder = (Math.floor(newItemDispOrder / 100) + 1) * 100
setEstimateContextState({
itemList: [ itemList: [
...state.itemList, ...estimateContextState.itemList,
{ {
objectNo: objectRecoil.floorPlanObjectNo, objectNo: objectRecoil.floorPlanObjectNo,
planNo: planNo, planNo: planNo,
@ -90,8 +83,8 @@ export const useEstimateController = (planNo) => {
amount: '', //수량 amount: '', //수량
unitPrice: '0', unitPrice: '0',
unit: '', //단위 unit: '', //단위
salePrice: '0', //단가 salePrice: '', //단가
saleTotPrice: '0', //금액(부가세별도) saleTotPrice: '', //금액(부가세별도)
itemChangeFlg: '1', //추가시 체인지플래그 1로 itemChangeFlg: '1', //추가시 체인지플래그 1로
partAdd: '1', //NEW 체인지 플래그 partAdd: '1', //NEW 체인지 플래그
delFlg: '0', //삭제 플래그 0 삭제하면 1 delFlg: '0', //삭제 플래그 0 삭제하면 1
@ -101,8 +94,8 @@ export const useEstimateController = (planNo) => {
} }
useEffect(() => { useEffect(() => {
setEstimateData({ ...state, userId: session.userId, sapSalesStoreCd: session.custCd }) setEstimateData({ ...estimateContextState, userId: session.userId, sapSalesStoreCd: session.custCd })
}, [state]) }, [estimateContextState])
// 첨부파일 다운로드 // 첨부파일 다운로드
const handleEstimateFileDownload = async (originFile) => { const handleEstimateFileDownload = async (originFile) => {
@ -133,27 +126,43 @@ export const useEstimateController = (planNo) => {
const handleEstimateSubmit = async () => { const handleEstimateSubmit = async () => {
//0. 필수체크 //0. 필수체크
let flag = true let flag = true
console.log('::담긴 estimateData:::', estimateData) if (estimateData.charger.trim().length === 0) {
flag = false
return alert(getMessage('estimate.detail.save.requiredCharger'))
}
if (estimateData.objectName.trim().length === 0) {
flag = false
return alert(getMessage('estimate.detail.save.requiredObjectName'))
}
if (isNaN(Date.parse(estimateData.estimateDate))) {
flag = false
return alert(getMessage('estimate.detail.save.requiredEstimateDate'))
}
// console.log('첨부파일:::::', estimateData.fileList) // console.log('첨부파일:::::', estimateData.fileList)
//첨부파일을 첨부안했는데 //첨부파일을 첨부안했는데
//아이템 fileUploadFlg가1(첨부파일 필수)이 1개라도 있는데 후일 자료 제출(fileFlg) 체크안했으면(0) alert 저장안돼 //아이템 fileUploadFlg가1(첨부파일 필수)이 1개라도 있는데 후일 자료 제출(fileFlg) 체크안했으면(0) alert 저장안돼
let fileFlg = true
if (estimateData.fileList.length < 1) { if (estimateData.fileList.length < 1) {
if (estimateData.itemList.length > 1) { if (estimateData.itemList.length > 1) {
estimateData.itemList.map((row) => { estimateData.itemList.map((row) => {
if (row.fileUploadFlg === '1') { if (row.fileUploadFlg === '1') {
if (estimateData.fileFlg === '0') { if (fileFlg) {
alert(getMessage('estimate.detail.save.requiredMsg')) if (estimateData.fileFlg === '0') {
flag = false fileFlg = false
return alert(getMessage('estimate.detail.save.requiredFileUpload'))
}
} }
} }
}) })
} }
} }
if (flag) { if (flag && fileFlg) {
//1. 첨부파일 저장 //1. 첨부파일 저장시작
const formData = new FormData() const formData = new FormData()
console.log('첨부파일:!!!', estimateData.fileList)
formData.append('file', estimateData.fileList) formData.append('file', estimateData.fileList)
formData.append('objectNo', estimateData.objectNo) formData.append('objectNo', estimateData.objectNo)
formData.append('planNo', estimateData.planNo) formData.append('planNo', estimateData.planNo)
@ -161,36 +170,87 @@ export const useEstimateController = (planNo) => {
formData.append('userId', estimateData.userId) formData.append('userId', estimateData.userId)
await post({ url: '/api/file/fileUpload', data: formData }) await post({ url: '/api/file/fileUpload', data: formData })
//첨부파일저장끝
//2. 상세데이터 저장 //제품라인 추가했는데 아이템 안고르고 저장하면itemId=''은 날리고 나머지 저장하기
return estimateData.itemList = estimateData.itemList.filter((item) => item.itemId !== '')
await promisePost({ url: `${ESTIMATE_API_ENDPOINT}/save-estimate`, data: estimateData }).then((res) => {
if (res) { let delCnt = 0
alert(getMessage('estimate.detail.save.alertMsg')) estimateData.itemList.map((item) => {
if (item.delFlg === '1') {
delCnt++
} }
}) })
if (delCnt === estimateData.itemList.length) {
return alert(getMessage('estimate.detail.save.requiredItem'))
}
// try { estimateData.itemList.map((item) => {
// const result = await promisePost({ item.amount = item.amount.replaceAll(',', '')
// url: ESTIMATE_API_ENDPOINT, item.salePrice = parseFloat(item.salePrice.replaceAll(',', '')).toFixed(2)
// data: estimateData, item.saleTotPrice = parseFloat(item.saleTotPrice.replaceAll(',', '')).toFixed(2)
// }) })
// alert(getMessage('estimate.detail.save.alertMsg')) console.log('최종 정보::;', estimateData)
// return result
// } catch (error) { //2. 상세데이터 저장
// console.error('Failed to submit estimate:', error) // return
// throw error try {
// } await promisePost({ url: `${ESTIMATE_API_ENDPOINT}/save-estimate`, data: estimateData }).then((res) => {
if (res.status === 201) {
alert(getMessage('estimate.detail.save.alertMsg'))
//어디로 보낼지
fetchSetting(objectRecoil.floorPlanObjectNo, estimateData.planNo)
}
})
} catch (e) {
console.log('error::::::::::::', e.response.data.message)
}
} }
} }
/**
* 견적서 복사버튼
* (견적서 번호(estimateData.docNo) 생성된 이후 버튼 활성화 )
* T01관리자 계정 1차판매점에게만 제공
*/
const handleEstimateCopy = async (sendPlanNo, copyReceiveUser, saleStoreId, otherSaleStoreId) => {
if (saleStoreId === '') {
return alert(getMessage('estimate.detail.productFeaturesPopup.requiredStoreId'))
}
if (copyReceiveUser.trim().length === 0) {
return alert(getMessage('estimate.detail.productFeaturesPopup.requiredReceiveUser'))
}
const params = {
saleStoreId: session.storeId,
sapSalesStoreCd: session.custCd,
objectNo: objectRecoil.floorPlanObjectNo,
planNo: sendPlanNo,
copySaleStoreId: otherSaleStoreId ? otherSaleStoreId : saleStoreId,
copyReceiveUser: copyReceiveUser,
userId: session.userId,
}
// return
await promisePost({ url: '/api/estimate/save-estimate-copy', data: params }).then((res) => {
if (res.status === 201) {
if (isObjectNotEmpty(res.data)) {
let newObjectNo = res.data.objectNo
alert(getMessage('estimate.detail.estimateCopyPopup.copy.alertMessage'))
router.push(`/management/stuff/detail?objectNo=${newObjectNo.toString()}`, { scroll: false })
}
}
})
}
return { return {
state, estimateContextState,
setState, setEstimateContextState,
updateItem, updateItem,
addItem, addItem,
handleEstimateSubmit, handleEstimateSubmit,
fetchSetting, fetchSetting,
handleEstimateFileDownload, handleEstimateFileDownload,
handleEstimateCopy,
} }
} }

View File

@ -0,0 +1,94 @@
'use client'
import { useContext, useEffect } from 'react'
import { useAxios } from '../useAxios'
import { SessionContext } from '@/app/SessionProvider'
import { QcastContext } from '@/app/QcastProvider'
export const useMainContentsController = () => {
const { session } = useContext(SessionContext)
const { promiseGet } = useAxios()
const { setQcastState, setIsGlobalLoading } = useContext(QcastContext)
useEffect(() => {
setIsGlobalLoading(true)
}, [])
/**
* main search area
*/
// const [saleStoreId, setSaleStoreId] = useState('')
// const [saleStoreName, setSaleStoreName] = useState('')
/**
* main contents area
*/
// const [objectList, setObjectList] = useState([])
// const [businessCharger, setBusinessCharger] = useState(null)
// const [businessChargerMail, setBusinessChargerMail] = useState(null)
/**
* 최근 물건 목록 조회
*/
const fetchObjectList = async () => {
try {
const apiUrl = `/api/main-page/object/${session?.storeId}/list`
await promiseGet({
url: apiUrl,
}).then((res) => {
if (res.status === 200) {
// setSaleStoreId(res.data.saleStoreId)
// setSaleStoreName(res.data.saleStoreName)
// setObjectList(res.data.objectList)
// setBusinessCharger(res.data.businessCharger)
// setBusinessChargerMail(res.data.businessChargerMail)
setQcastState({
saleStoreId: res.data.saleStoreId,
saleStoreName: res.data.saleStoreName,
objectList: res.data.objectList,
businessCharger: res.data.businessCharger,
businessChargerMail: res.data.businessChargerMail,
})
setIsGlobalLoading(false)
} else {
// setSaleStoreId('')
// setSaleStoreName('')
// setObjectList([])
// setBusinessCharger(null)
// setBusinessChargerMail(null)
setQcastState({
saleStoreId: '',
saleStoreName: '',
objectList: [],
businessCharger: null,
businessChargerMail: null,
})
}
})
} catch (error) {
console.error('MAIN API fetching error:', error)
}
}
const initObjectList = () => {
setQcastState({
saleStoreId: '',
saleStoreName: '',
objectList: [],
businessCharger: null,
businessChargerMail: null,
})
}
return {
// saleStoreId,
// saleStoreName,
// objectList,
// businessCharger,
// businessChargerMail,
fetchObjectList,
initObjectList,
}
}

View File

@ -10,6 +10,7 @@ import { useEvent } from '@/hooks/useEvent'
import { POLYGON_TYPE, BATCH_TYPE } from '@/common/common' import { POLYGON_TYPE, BATCH_TYPE } from '@/common/common'
import * as turf from '@turf/turf' import * as turf from '@turf/turf'
import next from 'next'
export function useModuleBasicSetting() { export function useModuleBasicSetting() {
const canvas = useRecoilValue(canvasState) const canvas = useRecoilValue(canvasState)
@ -31,7 +32,7 @@ export function useModuleBasicSetting() {
setSurfaceShapePattern(roof, roofDisplay.column, true) //패턴 변경 setSurfaceShapePattern(roof, roofDisplay.column, true) //패턴 변경
const offsetPoints = offsetPolygon(roof.points, -20) //안쪽 offset const offsetPoints = offsetPolygon(roof.points, -20) //안쪽 offset
//모듈설치영역?? 생성 //모듈설치영역?? 생성
const setupSurface = new QPolygon(offsetPoints, { let setupSurface = new QPolygon(offsetPoints, {
stroke: 'red', stroke: 'red',
fill: 'transparent', fill: 'transparent',
strokeDashArray: [10, 4], strokeDashArray: [10, 4],
@ -45,8 +46,12 @@ export function useModuleBasicSetting() {
parentId: roof.id, //가대 폴리곤의 임시 인덱스를 넣어줌 parentId: roof.id, //가대 폴리곤의 임시 인덱스를 넣어줌
name: POLYGON_TYPE.MODULE_SETUP_SURFACE, name: POLYGON_TYPE.MODULE_SETUP_SURFACE,
flowDirection: roof.direction, flowDirection: roof.direction,
flipX: roof.flipX,
flipY: roof.flipY,
}) })
setupSurface.setViewLengthText(false)
canvas.add(setupSurface) canvas.add(setupSurface)
//지붕면 선택 금지 //지붕면 선택 금지
roof.set({ roof.set({
@ -62,6 +67,7 @@ export function useModuleBasicSetting() {
//설치 범위 지정 클릭 이벤트 //설치 범위 지정 클릭 이벤트
const toggleSelection = (setupSurface) => { const toggleSelection = (setupSurface) => {
console.log('setupSurface', setupSurface)
const isExist = selectedModuleInstSurfaceArray.some((obj) => obj.parentId === setupSurface.parentId) const isExist = selectedModuleInstSurfaceArray.some((obj) => obj.parentId === setupSurface.parentId)
//최초 선택일때 //최초 선택일때
if (!isExist) { if (!isExist) {
@ -376,9 +382,12 @@ export function useModuleBasicSetting() {
} }
//자동 모듈 설치(그리드 방식) //자동 모듈 설치(그리드 방식)
const autoModuleSetup = () => { const autoModuleSetup = (placementRef) => {
const isChidori = placementRef.isChidori.current
initEvent() initEvent()
const moduleSetupSurfaces = moduleSetupSurface //선택 설치면 const moduleSetupSurfaces = moduleSetupSurface //선택 설치면
const notSelectedTrestlePolygons = canvas const notSelectedTrestlePolygons = canvas
?.getObjects() ?.getObjects()
.filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE && !moduleSetupSurfaces.includes(obj)) //설치면이 아닌것 .filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE && !moduleSetupSurfaces.includes(obj)) //설치면이 아닌것
@ -400,6 +409,9 @@ export function useModuleBasicSetting() {
if (moduleIsSetup.length > 0) { if (moduleIsSetup.length > 0) {
alert('기존 모듈은 제거됩니다.') alert('기존 모듈은 제거됩니다.')
moduleIsSetup.forEach((module) => {
canvas?.remove(module)
})
} }
notSelectedTrestlePolygons.forEach((obj) => { notSelectedTrestlePolygons.forEach((obj) => {
@ -472,8 +484,11 @@ export function useModuleBasicSetting() {
width = moduleSetupSurface.flowDirection === 'south' || moduleSetupSurface.flowDirection === 'north' ? 172.2 : 113.4 width = moduleSetupSurface.flowDirection === 'south' || moduleSetupSurface.flowDirection === 'north' ? 172.2 : 113.4
height = moduleSetupSurface.flowDirection === 'south' || moduleSetupSurface.flowDirection === 'north' ? 113.4 : 172.2 height = moduleSetupSurface.flowDirection === 'south' || moduleSetupSurface.flowDirection === 'north' ? 113.4 : 172.2
} }
const cols = Math.floor((bbox[2] - bbox[0]) / width)
const rows = Math.floor((bbox[3] - bbox[1]) / height) let cols = Math.floor((bbox[2] - bbox[0]) / width)
let rows = Math.floor((bbox[3] - bbox[1]) / height)
// cols = cols * 2
for (let col = 0; col <= cols; col++) { for (let col = 0; col <= cols; col++) {
for (let row = 0; row <= rows; row++) { for (let row = 0; row <= rows; row++) {
@ -481,10 +496,8 @@ export function useModuleBasicSetting() {
y = 0, y = 0,
square = [], square = [],
margin = 0 margin = 0
if (moduleSetupSurface.flowDirection !== undefined) { if (moduleSetupSurface.flowDirection !== undefined) {
//배치면 처림 방향이 정해져있는 경우 //배치면 처림 방향이 정해져있는 경우
if (moduleSetupSurface.flowDirection === 'south' || moduleSetupSurface.flowDirection === 'north') { if (moduleSetupSurface.flowDirection === 'south' || moduleSetupSurface.flowDirection === 'north') {
//남,북 //남,북
margin = (bbox[2] - bbox[0] - cols * width) / 2 //박스 끝에서 박스 시작값을 빼고 width와 계산된 cols를 곱한값을 뺀뒤 나누기 2 하면 가운데 배치됨 margin = (bbox[2] - bbox[0] - cols * width) / 2 //박스 끝에서 박스 시작값을 빼고 width와 계산된 cols를 곱한값을 뺀뒤 나누기 2 하면 가운데 배치됨
@ -514,25 +527,50 @@ export function useModuleBasicSetting() {
y = bbox[1] + row * height y = bbox[1] + row * height
} }
square = [ if (isChidori === 'true') {
[x, y], if (row % 2 !== 0) {
[x + width, y], square = [
[x + width, y + height], [x, y],
[x, y + height], [x + width, y],
[x, y], [x + width, y + height],
] [x, y + height],
[x, y],
]
} else {
square = [
[x - width / 2, y],
[x - width / 2 + width, y],
[x - width / 2 + width, y + height],
[x - width / 2, y + height],
[x - width / 2, y],
]
}
} else {
square = [
[x, y],
[x + width, y],
[x + width, y + height],
[x, y + height],
[x, y],
]
}
// square = [
// [x - width / 2, y],
// [x - width / 2 + width, y],
// [x - width / 2 + width, y + height],
// [x - width / 2, y + height],
// [x - width / 2, y],
// ]
const squarePolygon = turf.polygon([square]) const squarePolygon = turf.polygon([square])
const disjointFromTrestle = const disjointFromTrestle =
turf.booleanContains(turfModuleSetupSurface, squarePolygon) || turf.booleanWithin(squarePolygon, turfModuleSetupSurface) turf.booleanContains(turfModuleSetupSurface, squarePolygon) || turf.booleanWithin(squarePolygon, turfModuleSetupSurface)
if (disjointFromTrestle) { if (disjointFromTrestle) {
let turfCoordnates = squarePolygon.geometry.coordinates[0].slice(0, -1) let turfCoordnates = squarePolygon.geometry.coordinates[0].slice(0, -1)
const points = turfCoordnates.map((coord) => ({ x: coord[0], y: coord[1] })) const points = turfCoordnates.map((coord) => ({ x: coord[0], y: coord[1] }))
if (containsBatchObjects.length > 0) { if (containsBatchObjects.length > 0) {
let convertBatchObject let convertBatchObject
//도머가 있으면 적용되는 로직 //도머가 있으면 적용되는 로직
const isDisjoint = containsBatchObjects.every((batchObject) => { const isDisjoint = containsBatchObjects.every((batchObject) => {
if (batchObject.type === 'group') { if (batchObject.type === 'group') {
@ -540,12 +578,8 @@ export function useModuleBasicSetting() {
} else { } else {
convertBatchObject = polygonToTurfPolygon(batchObject) convertBatchObject = polygonToTurfPolygon(batchObject)
} }
return turf.booleanDisjoint(squarePolygon, convertBatchObject) //도머가 여러개일수있으므로 겹치는게 있다면... return turf.booleanDisjoint(squarePolygon, convertBatchObject) //도머가 여러개일수있으므로 겹치는게 있다면...
}) })
console.log('isDisjoint', isDisjoint)
if (isDisjoint) { if (isDisjoint) {
const tempModule = new QPolygon(points, { const tempModule = new QPolygon(points, {
fill: '#BFFD9F', fill: '#BFFD9F',
@ -558,7 +592,11 @@ export function useModuleBasicSetting() {
lockScalingY: false, // Y 축 크기 조정 잠금 lockScalingY: false, // Y 축 크기 조정 잠금
opacity: 0.8, opacity: 0.8,
parentId: moduleSetupSurface.parentId, parentId: moduleSetupSurface.parentId,
lineCol: col,
lineRow: row,
name: 'module',
}) })
tempModule.setViewLengthText(false)
canvas?.add(tempModule) canvas?.add(tempModule)
moduleSetupArray.push(tempModule) moduleSetupArray.push(tempModule)
} }
@ -577,6 +615,7 @@ export function useModuleBasicSetting() {
parentId: moduleSetupSurface.parentId, parentId: moduleSetupSurface.parentId,
lineCol: col, lineCol: col,
lineRow: row, lineRow: row,
name: 'module',
}) })
canvas?.add(tempModule) canvas?.add(tempModule)
moduleSetupArray.push(tempModule) moduleSetupArray.push(tempModule)
@ -584,47 +623,115 @@ export function useModuleBasicSetting() {
} }
} }
} }
// let drawRoofCells
// if (maxLengthLine.flowDirection === 'right' || maxLengthLine.flowDirection === 'left') {
// drawRoofCells = trestle.fillCell({ width: 113.4, height: 172.2, padding: 0 })
// trestle.flowDirection = 'south'
// } else {
// drawRoofCells = trestle.fillCell({ width: 172.2, height: 113.4, padding: 0 })
// trestle.flowDirection = 'east'
// }
// drawRoofCells.forEach((cell) => {
// moduleSetupArray.push(cell)
// })
/**
* 추후에 가대까지 완료하면 그룹시켜버림
*/
// const groupCellObj = canvas
// ?.getObjects()
// .filter(
// (obj) =>
// obj?.parentId === trestle.parentId ||
// obj?.id === trestle.parentId ||
// (obj?.name === 'arrow' && obj?.parent.id === trestle.parentId) ||
// (obj?.name === 'flowDirectionText' && obj?.parent.parent.id === trestle.parentId),
// )
// console.log('groupCellObj', groupCellObj)
// canvas?.add(
// new fabric.Group(groupCellObj, {
// name: 'cellGroup',
// originX: 'center',
// originY: 'center',
// }),
// )
moduleSetupSurface.set({ modules: moduleSetupArray }) moduleSetupSurface.set({ modules: moduleSetupArray })
}) })
setModuleIsSetup(moduleSetupArray) setModuleIsSetup(moduleSetupArray)
console.log(calculateForApi(moduleSetupArray))
}
const calculateForApi = (moduleSetupArray) => {
const centerPoints = []
moduleSetupArray.forEach((module, index) => {
module.tempIndex = index
const { x, y } = module.getCenterPoint()
const { width, height } = module
centerPoints.push({ x, y, width, height, index })
const circle = new fabric.Circle({
radius: 5,
fill: 'red',
name: 'redCircle',
left: x - 5,
top: y - 5,
index: index,
selectable: false,
})
canvas.add(circle)
})
//완전 노출 하면
let exposedBottom = 0
// 반 노출 하면
let exposedHalfBottom = 0
// 완전 노출 상면
let exposedTop = 0
//반 노출 상면
let exposedHalfTop = 0
// 완전 접면
let touchDimension = 0
//반접면
let halfTouchDimension = 0
// 노출하면 체크
centerPoints.forEach((centerPoint, index) => {
const { x, y, width, height } = centerPoint
// centerPoints중에 현재 centerPoint와 x값이 같고, y값이 y-height값과 같은 centerPoint가 있는지 확인
const bottomCell = centerPoints.filter((centerPoint) => centerPoint.x === x && Math.abs(centerPoint.y - (y + height)) < 2)
if (bottomCell.length === 1) {
touchDimension++
return
}
const bottomLeftPoint = { x: x - width / 2, y: y + height }
const bottomRightPoint = { x: x + width / 2, y: y + height }
// 바로 아래에 셀이 없는 경우 물떼세 배치가 왼쪽 되어있는 셀을 찾는다.
const leftBottomCnt = centerPoints.filter(
(centerPoint) => Math.abs(centerPoint.x - bottomLeftPoint.x) < 2 && Math.abs(centerPoint.y - bottomLeftPoint.y) < 2,
).length
const rightBottomCnt = centerPoints.filter(
(centerPoint) => Math.abs(centerPoint.x - bottomRightPoint.x) < 2 && Math.abs(centerPoint.y - bottomRightPoint.y) < 2,
).length
if (leftBottomCnt + rightBottomCnt === 2) {
touchDimension++
return
}
if (leftBottomCnt + rightBottomCnt === 1) {
halfTouchDimension++
exposedHalfBottom++
return
}
if (leftBottomCnt + rightBottomCnt === 0) {
exposedBottom++
return
}
})
// 노출상면 체크
centerPoints.forEach((centerPoint, index) => {
const { x, y, width, height } = centerPoint
const topCell = centerPoints.filter((centerPoint) => centerPoint.x === x && Math.abs(centerPoint.y - (y - height)) < 2)
if (topCell.length === 1) {
return
}
const topLeftPoint = { x: x - width / 2, y: y - height }
const topRightPoint = { x: x + width / 2, y: y - height }
const leftTopCnt = centerPoints.filter(
(centerPoint) => Math.abs(centerPoint.x - topLeftPoint.x) < 2 && Math.abs(centerPoint.y - topRightPoint.y) < 2,
).length
const rightTopCnt = centerPoints.filter(
(centerPoint) => Math.abs(centerPoint.x - topRightPoint.x) < 2 && Math.abs(centerPoint.y - topRightPoint.y) < 2,
).length
if (leftTopCnt + rightTopCnt === 1) {
exposedHalfTop++
return
}
if (leftTopCnt + rightTopCnt === 0) {
exposedTop++
return
}
})
return {
exposedBottom,
exposedHalfBottom,
exposedTop,
exposedHalfTop,
touchDimension,
halfTouchDimension,
}
} }
const coordToTurfPolygon = (points) => { const coordToTurfPolygon = (points) => {
@ -646,7 +753,7 @@ export function useModuleBasicSetting() {
) )
} }
function batchObjectGroupToTurfPolygon(group) { const batchObjectGroupToTurfPolygon = (group) => {
const polygons = group.getObjects().filter((obj) => obj.type === 'QPolygon') const polygons = group.getObjects().filter((obj) => obj.type === 'QPolygon')
let allPoints = [] let allPoints = []
@ -658,6 +765,43 @@ export function useModuleBasicSetting() {
return hull return hull
} }
const calcMinXByHeightDistance = (nowSurface, index, reverse) => {
function calculateSlopeIntercept(x1, y1, x2, y2) {
console.log('Intercept', x1, y1, x2, y2)
const slope = (y2 - y1) / (x2 - x1)
const intercept = y1 - slope * x1
return { slope, intercept }
}
let prevLines = nowSurface.lines[(index - 1 + nowSurface.lines.length) % nowSurface.lines.length]
let nextLines = nowSurface.lines[index]
// 선분 정보
const l1 = prevLines
const l2 = nextLines
const lineLength = 172.2
// l1과 l2의 기울기 및 절편
let { slope: m1, intercept: b1 } = calculateSlopeIntercept(l1.x1, l1.y1, l1.x2, l1.y2)
let { slope: m2, intercept: b2 } = calculateSlopeIntercept(l2.x1, l2.y1, l2.x2, l2.y2)
console.log(m1, b1, m2, b2)
// 가로선 x1 계산
const x1 = (m2 * lineLength + b2 - b1) / (m1 - m2)
const x2 = x1 + lineLength // 끝점 x2
// 가로선 y값 계산
const y0 = m1 * x1 + b1
// 결과 출력
console.log({ x1: x1, y1: y0, x2: x2, y2: y0 })
return { x1: x1, y1: y0, x2: x2, y2: y0 }
}
return { return {
makeModuleInstArea, makeModuleInstArea,
manualModuleSetup, manualModuleSetup,

View File

@ -3,6 +3,7 @@ import { canvasState } from '@/store/canvasAtom'
import { usePolygon } from '@/hooks/usePolygon' import { usePolygon } from '@/hooks/usePolygon'
import { POLYGON_TYPE } from '@/common/common' import { POLYGON_TYPE } from '@/common/common'
import { compasDegAtom } from '@/store/orientationAtom' import { compasDegAtom } from '@/store/orientationAtom'
import { useEffect } from 'react'
// 모듈,회로 구성 탭 기본설정 > 방위설정 탭 // 모듈,회로 구성 탭 기본설정 > 방위설정 탭
export function useOrientation() { export function useOrientation() {
@ -11,6 +12,16 @@ export function useOrientation() {
const { drawDirectionArrow } = usePolygon() const { drawDirectionArrow } = usePolygon()
useEffect(() => {
const roofs = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
roofs.forEach((roof) => {
roof.set({
moduleCompass: null,
})
drawDirectionArrow(roof)
})
}, [])
const nextStep = () => { const nextStep = () => {
const roofs = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF) const roofs = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
roofs.forEach((roof) => { roofs.forEach((roof) => {

View File

@ -277,7 +277,7 @@ export function useCanvasSetting() {
optionName = ['outerLine', POLYGON_TYPE.WALL] optionName = ['outerLine', POLYGON_TYPE.WALL]
break break
case 'gridDisplay': //그리드 표시 case 'gridDisplay': //그리드 표시
optionName = ['lindGrid', 'dotGrid'] optionName = ['lindGrid', 'dotGrid', 'tempGrid']
break break
case 'lineDisplay': //지붕선 표시 case 'lineDisplay': //지붕선 표시
optionName = ['roof', POLYGON_TYPE.ROOF] optionName = ['roof', POLYGON_TYPE.ROOF]

View File

@ -1,81 +0,0 @@
import { useRecoilState, useRecoilValue } from 'recoil'
import { canvasState } from '@/store/canvasAtom'
import { useEffect } from 'react'
import { settingModalFirstOptionsState } from '@/store/settingAtom'
import { POLYGON_TYPE } from '@/common/common'
export function useFirstOption() {
const canvas = useRecoilValue(canvasState)
const [settingModalFirstOptions, setSettingModalFirstOptions] = useRecoilState(settingModalFirstOptionsState)
useEffect(() => {
const option1 = settingModalFirstOptions.option1
// 'allocDisplay' 할당 표시
// 'outlineDisplay' 외벽선 표시 'outerLine', POLYGON_TYPE.WALL
// 'gridDisplay' 그리드 표시 'lindGrid', 'dotGrid'
// 'lineDisplay' 지붕선 표시 'roof', POLYGON_TYPE.ROOF
// 'wordDisplay' 문자 표시
// 'circuitNumDisplay' 회로번호 표시
// 'flowDisplay' 흐름방향 표시 'arrow'
// 'trestleDisplay' 가대 표시
// 'totalDisplay' 집계표 표시
let optionName //옵션명
let optionSelected //옵션상태
for (let i = 0; i < option1.length; i++) {
switch (option1[i].column) {
case 'allocDisplay': //할당 표시
optionName = ['1']
break
case 'outlineDisplay': //외벽선 표시
optionName = ['outerLine', POLYGON_TYPE.WALL]
break
case 'gridDisplay': //그리드 표시
optionName = ['lineGrid', 'dotGrid', 'adsorptionPoint', 'tempGrid']
break
case 'lineDisplay': //지붕선 표시
optionName = ['roof', POLYGON_TYPE.ROOF]
break
case 'wordDisplay': //문자 표시
optionName = ['commonText']
break
case 'circuitNumDisplay': //회로번호 표시
optionName = ['7']
break
case 'flowDisplay': //흐름방향 표시
optionName = ['arrow', 'flowText']
break
case 'trestleDisplay': //가대 표시
optionName = ['8']
break
case 'totalDisplay': //집계표 표시
optionName = ['9']
break
}
// 표시 선택 상태(true/false)
optionSelected = option1[i].selected
canvas
.getObjects()
.filter((obj) => optionName.includes(obj.name))
//.filter((obj) => obj.name === optionName)
.forEach((obj) => {
obj.set({ visible: optionSelected })
//obj.set({ visible: !obj.visible })
})
canvas.renderAll()
// console.log(
// 'optionName',
// optionName,
// canvas.getObjects().filter((obj) => optionName.includes(obj.name)),
// )
}
}, [settingModalFirstOptions])
return { settingModalFirstOptions, setSettingModalFirstOptions }
}

View File

@ -15,14 +15,12 @@ import {
outerLineLength2State, outerLineLength2State,
outerLineTypeState, outerLineTypeState,
} from '@/store/outerLineAtom' } from '@/store/outerLineAtom'
import { calculateIntersection, distanceBetweenPoints, findClosestPoint, isPointOnLine, polygonToTurfPolygon } from '@/util/canvas-util' import { calculateIntersection, distanceBetweenPoints, findClosestPoint, isPointOnLine } from '@/util/canvas-util'
import { fabric } from 'fabric' import { fabric } from 'fabric'
import { useAdsorptionPoint } from '@/hooks/useAdsorptionPoint' import { useAdsorptionPoint } from '@/hooks/useAdsorptionPoint'
import { useSwal } from '@/hooks/useSwal' import { useSwal } from '@/hooks/useSwal'
import { booleanPointInPolygon } from '@turf/turf'
import { usePopup } from '@/hooks/usePopup' import { usePopup } from '@/hooks/usePopup'
import { calculateAngle, isSamePoint } from '@/util/qpolygon-utils' import { calculateAngle, isSamePoint } from '@/util/qpolygon-utils'
import { QPolygon } from '@/components/fabric/QPolygon'
import { POLYGON_TYPE } from '@/common/common' import { POLYGON_TYPE } from '@/common/common'
// 보조선 작성 // 보조선 작성
@ -122,6 +120,38 @@ export function useAuxiliaryDrawing(id) {
setOuterLineDiagonalLength(0) setOuterLineDiagonalLength(0)
} }
const move = (object, x, y) => {
const line = copy(object, x, y)
canvas.remove(object)
canvas.setActiveObject(line)
}
const copy = (object, x, y) => {
return addLine([object.x1 + x, object.y1 + y, object.x2 + x, object.y2 + y], {
stroke: 'red',
strokeWidth: 1,
selectable: true,
name: 'auxiliaryLine',
})
}
const addBisectorLine = (target) => {
const slope = (target.y2 - target.y1) / (target.x2 - target.x1)
const bisectorSlope = -1 / slope
const length = target.length
const dx = length / Math.sqrt(1 + bisectorSlope * bisectorSlope)
const dy = bisectorSlope * dx
const endX = (target.x1 + target.x2) / 2
const endY = (target.y1 + target.y2) / 2
addLine([dx, dy, endX, endY], {
stroke: 'red',
strokeWidth: 1,
selectable: true,
name: 'auxiliaryLine',
})
}
const keydown = { const keydown = {
outerLine: (e) => { outerLine: (e) => {
if (mousePointerArr.current.length === 0) { if (mousePointerArr.current.length === 0) {
@ -130,7 +160,7 @@ export function useAuxiliaryDrawing(id) {
// 포커스가 length1에 있지 않으면 length1에 포커스를 줌 // 포커스가 length1에 있지 않으면 length1에 포커스를 줌
const activeElem = document.activeElement const activeElem = document.activeElement
if (activeElem !== length1Ref.current) { if (activeElem !== length1Ref.current) {
length1Ref.current.focus() length1Ref?.current?.focus()
} }
const key = e.key const key = e.key
@ -180,7 +210,7 @@ export function useAuxiliaryDrawing(id) {
const activeElem = document.activeElement const activeElem = document.activeElement
if (activeElem !== length1Ref.current && activeElem !== length2Ref.current) { if (activeElem !== length1Ref.current && activeElem !== length2Ref.current) {
length1Ref.current.focus() length1Ref?.current?.focus()
} }
switch (key) { switch (key) {
@ -455,9 +485,24 @@ export function useAuxiliaryDrawing(id) {
name: 'auxiliaryLine', name: 'auxiliaryLine',
}) })
lineHistory.current.push(line) const historyLines = [...lineHistory.current]
const hasSameLine = historyLines.some((history) => {
return (
(isSamePoint(history.startPoint, line.startPoint) && isSamePoint(history.endPoint, line.endPoint)) ||
(isSamePoint(history.startPoint, line.endPoint) && isSamePoint(history.endPoint, line.startPoint))
)
})
mousePointerArr.current = [] mousePointerArr.current = []
clear() clear()
if (hasSameLine) {
canvas.remove(line)
return
}
lineHistory.current.push(line)
} }
const mouseDown = (e) => { const mouseDown = (e) => {
@ -856,5 +901,8 @@ export function useAuxiliaryDrawing(id) {
handleRollback, handleRollback,
buttonAct, buttonAct,
setButtonAct, setButtonAct,
move,
copy,
addBisectorLine,
} }
} }

View File

@ -125,6 +125,12 @@ export function usePropertiesSetting(id) {
} }
const lines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') const lines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
const notSetAttributes = lines.filter((line) => !line.attributes?.type)
if (notSetAttributes.length > 0) {
alert('설정되지 않은 외벽선이 있습니다.')
return
}
lines.forEach((line) => { lines.forEach((line) => {
line.set({ line.set({
attributes: line.attributes ? line.attributes : { offset: 0, type: LINE_TYPE.WALLLINE.WALL }, attributes: line.attributes ? line.attributes : { offset: 0, type: LINE_TYPE.WALLLINE.WALL },

View File

@ -1,5 +1,5 @@
import { useRecoilValue } from 'recoil' import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
import { canvasState, currentObjectState } from '@/store/canvasAtom' import { canvasState, currentMenuState, currentObjectState } from '@/store/canvasAtom'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { setSurfaceShapePattern } from '@/util/canvas-util' import { setSurfaceShapePattern } from '@/util/canvas-util'
import { useSwal } from '@/hooks/useSwal' import { useSwal } from '@/hooks/useSwal'
@ -10,6 +10,9 @@ import { POLYGON_TYPE } from '@/common/common'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
import ActualSizeSetting from '@/components/floor-plan/modal/roofAllocation/ActualSizeSetting' import ActualSizeSetting from '@/components/floor-plan/modal/roofAllocation/ActualSizeSetting'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import useMenu from '@/hooks/common/useMenu'
import { useCanvasMenu } from '@/hooks/common/useCanvasMenu'
import { menuTypeState } from '@/store/menuAtom'
// 지붕면 할당 // 지붕면 할당
export function useRoofAllocationSetting(id) { export function useRoofAllocationSetting(id) {
@ -21,6 +24,8 @@ export function useRoofAllocationSetting(id) {
const { getMessage } = useMessage() const { getMessage } = useMessage()
const currentObject = useRecoilValue(currentObjectState) const currentObject = useRecoilValue(currentObjectState)
const { swalFire } = useSwal() const { swalFire } = useSwal()
const { setMenuNumber } = useCanvasMenu()
const setMenuType = useSetRecoilState(menuTypeState)
const roofMaterials = [ const roofMaterials = [
{ {
id: 'A', id: 'A',
@ -132,6 +137,9 @@ export function useRoofAllocationSetting(id) {
setValues(values.filter((value) => value.id !== id)) setValues(values.filter((value) => value.id !== id))
} }
const { handleMenu } = useMenu()
const [currentMenu, setCurrentMenu] = useRecoilState(currentMenuState)
// 선택한 지붕재로 할당 // 선택한 지붕재로 할당
const handleSave = () => { const handleSave = () => {
// 모두 actualSize 있으면 바로 적용 없으면 actualSize 설정 // 모두 actualSize 있으면 바로 적용 없으면 actualSize 설정
@ -213,6 +221,8 @@ export function useRoofAllocationSetting(id) {
}) })
setEditingLines([]) setEditingLines([])
closeAll() closeAll()
setMenuNumber(3)
setMenuType('surface')
} }
const setLineSize = (id, size) => { const setLineSize = (id, size) => {

View File

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

View File

@ -102,6 +102,15 @@ export function useSurfaceShapeBatch() {
canvas?.add(obj) canvas?.add(obj)
canvas?.renderAll()
closePopup(id)
})
addCanvasMouseEventListener('mouse:down', (e) => {
isDrawing = false
canvas?.remove(obj)
//각도 추가 //각도 추가
let originAngle = 0 //기본 남쪽 let originAngle = 0 //기본 남쪽
let direction = 'south' let direction = 'south'
@ -119,21 +128,31 @@ export function useSurfaceShapeBatch() {
direction = 'north' direction = 'north'
} }
obj.set({ direction: direction }) //회전, flip등이 먹은 기준으로 새로생성
obj.set({ originAngle: originAngle }) const batchSurface = new QPolygon(obj.getCurrentPoints(), {
fill: 'transparent',
canvas?.renderAll() stroke: 'red',
strokeWidth: 1,
strokeDasharray: [10, 4],
fontSize: 12,
selectable: true,
lockMovementX: true, // X 축 이동 잠금
lockMovementY: true, // Y 축 이동 잠금
lockRotation: true, // 회전 잠금
lockScalingX: true, // X 축 크기 조정 잠금
lockScalingY: true, // Y 축 크기 조정 잠금
name: POLYGON_TYPE.ROOF,
originX: 'center',
originY: 'center',
pitch: globalPitch,
surfaceId: surfaceId,
direction: direction,
})
canvas?.add(batchSurface)
setSurfaceShapePattern(batchSurface, roofDisplay.column)
drawDirectionArrow(batchSurface)
closePopup(id) closePopup(id)
})
addCanvasMouseEventListener('mouse:down', (e) => {
isDrawing = false
obj.set('name', POLYGON_TYPE.ROOF)
obj.set('surfaceId', surfaceId)
initEvent() initEvent()
setSurfaceShapePattern(obj, roofDisplay.column)
closePopup(id)
drawDirectionArrow(obj)
}) })
} }
} }
@ -419,8 +438,8 @@ export function useSurfaceShapeBatch() {
{ {
fill: 'transparent', fill: 'transparent',
stroke: 'black', //black stroke: 'black', //black
strokeWidth: 2, strokeWidth: 1,
selectable: true, selectable: false,
fontSize: 0, fontSize: 0,
}, },
) )
@ -429,6 +448,7 @@ export function useSurfaceShapeBatch() {
const scale = (length1 - length2) / coord.x const scale = (length1 - length2) / coord.x
tmpPolygon.set({ scaleX: scale }) tmpPolygon.set({ scaleX: scale })
tmpPolygon.setViewLengthText(false)
pointsArray[0].x = 0 pointsArray[0].x = 0
pointsArray[0].y = length3 //바닥면부터 시작하게 pointsArray[0].y = length3 //바닥면부터 시작하게
@ -584,18 +604,6 @@ export function useSurfaceShapeBatch() {
text: '배치면 내용을 전부 삭제하시겠습니까?', text: '배치면 내용을 전부 삭제하시겠습니까?',
type: 'confirm', type: 'confirm',
confirmFn: () => { confirmFn: () => {
// canvas?.getObjects().forEach((obj) => {
// if (
// obj.name === POLYGON_TYPE.ROOF ||
// obj.name === BATCH_TYPE.OPENING ||
// obj.name === BATCH_TYPE.SHADOW ||
// obj.name === BATCH_TYPE.TRIANGLE_DORMER ||
// obj.name === BATCH_TYPE.PENTAGON_DORMER ||
// obj.name === 'lengthText'
// ) {
// canvas?.remove(obj)
// }
// })
canvas.clear() canvas.clear()
swalFire({ text: '삭제 완료 되었습니다.' }) swalFire({ text: '삭제 완료 되었습니다.' })
}, },
@ -661,34 +669,6 @@ export function useSurfaceShapeBatch() {
return groupObjectsArray return groupObjectsArray
} }
function getAllRelatedObjects(id) {
const ult = []
const map = new Map()
// Create a map of objects by their id
canvas.getObjects().forEach((obj) => {
map.set(obj.id, obj)
})
// Helper function to recursively find all related objects
function findRelatedObjects(id) {
const obj = map.get(id)
if (obj) {
result.push(obj)
canvas.getObjects().forEach((o) => {
if (o.parentId === id) {
findRelatedObjects(o.id)
}
})
}
}
// Start the search with the given parentId
findRelatedObjects(id)
return result
}
const moveSurfaceShapeBatch = () => { const moveSurfaceShapeBatch = () => {
const roof = canvas.getActiveObject() const roof = canvas.getActiveObject()
@ -919,12 +899,45 @@ export function useSurfaceShapeBatch() {
canvas?.renderAll() canvas?.renderAll()
} }
const changeSurfaceFlowDirection = (roof, direction, orientation) => { const updateFlippedPoints = (polygon) => {
roof.set({ if (!(polygon instanceof fabric.Polygon)) {
direction: direction, console.error('The object is not a Polygon.')
return
}
const { flipX, flipY, width, height, points, left, top, scaleX, scaleY } = polygon
// 현재 points의 사본 가져오기
const newPoints = points.map((point) => {
let x = point.x
let y = point.y
// flipX 적용
if (flipX) {
x = width - x
}
// flipY 적용
if (flipY) {
y = height - y
}
// 스케일 및 전역 좌표 고려
x = (x - width / 2) * scaleX + width / 2
y = (y - height / 2) * scaleY + height / 2
return { x, y }
}) })
drawDirectionArrow(roof)
canvas?.renderAll() // flipX, flipY를 초기화
polygon.flipX = false
polygon.flipY = false
// points 업데이트
polygon.set({ points: newPoints })
polygon.setCoords()
return polygon
} }
return { return {

View File

@ -1,8 +1,7 @@
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil' import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
import { currentMenuState, currentObjectState } from '@/store/canvasAtom' import { canvasState, currentMenuState, currentObjectState } from '@/store/canvasAtom'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { MENU } from '@/common/common' import { MENU } from '@/common/common'
import AuxiliaryMove from '@/components/floor-plan/modal/auxiliary/AuxiliaryMove'
import AuxiliarySize from '@/components/floor-plan/modal/auxiliary/AuxiliarySize' import AuxiliarySize from '@/components/floor-plan/modal/auxiliary/AuxiliarySize'
import { usePopup } from '@/hooks/usePopup' import { usePopup } from '@/hooks/usePopup'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
@ -11,7 +10,7 @@ import GridCopy from '@/components/floor-plan/modal/grid/GridCopy'
import ColorPickerModal from '@/components/common/color-picker/ColorPickerModal' import ColorPickerModal from '@/components/common/color-picker/ColorPickerModal'
import { gridColorState } from '@/store/gridAtom' import { gridColorState } from '@/store/gridAtom'
import { contextPopupPositionState, contextPopupState } from '@/store/popupAtom' import { contextPopupPositionState, contextPopupState } from '@/store/popupAtom'
import AuxiliaryCopy from '@/components/floor-plan/modal/auxiliary/AuxiliaryCopy' import AuxiliaryEdit from '@/components/floor-plan/modal/auxiliary/AuxiliaryEdit'
import SizeSetting from '@/components/floor-plan/modal/object/SizeSetting' import SizeSetting from '@/components/floor-plan/modal/object/SizeSetting'
import RoofMaterialSetting from '@/components/floor-plan/modal/object/RoofMaterialSetting' import RoofMaterialSetting from '@/components/floor-plan/modal/object/RoofMaterialSetting'
import DormerOffset from '@/components/floor-plan/modal/object/DormerOffset' import DormerOffset from '@/components/floor-plan/modal/object/DormerOffset'
@ -34,8 +33,10 @@ import CircuitNumberEdit from '@/components/floor-plan/modal/module/CircuitNumbe
import { useObjectBatch } from '@/hooks/object/useObjectBatch' import { useObjectBatch } from '@/hooks/object/useObjectBatch'
import { useSurfaceShapeBatch } from '@/hooks/surface/useSurfaceShapeBatch' import { useSurfaceShapeBatch } from '@/hooks/surface/useSurfaceShapeBatch'
import { fontSelector, globalFontAtom } from '@/store/fontAtom' import { fontSelector, globalFontAtom } from '@/store/fontAtom'
import { useLine } from '@/hooks/useLine'
export function useContextMenu() { export function useContextMenu() {
const canvas = useRecoilValue(canvasState)
const currentMenu = useRecoilValue(currentMenuState) // 현재 메뉴 const currentMenu = useRecoilValue(currentMenuState) // 현재 메뉴
const setContextPopupPosition = useSetRecoilState(contextPopupPositionState) // 현재 메뉴 const setContextPopupPosition = useSetRecoilState(contextPopupPositionState) // 현재 메뉴
const [contextMenu, setContextMenu] = useRecoilState(contextMenuListState) // 메뉴.object 별 context menu const [contextMenu, setContextMenu] = useRecoilState(contextMenuListState) // 메뉴.object 별 context menu
@ -53,6 +54,7 @@ export function useContextMenu() {
const { moveObjectBatch } = useObjectBatch({}) const { moveObjectBatch } = useObjectBatch({})
const { moveSurfaceShapeBatch } = useSurfaceShapeBatch() const { moveSurfaceShapeBatch } = useSurfaceShapeBatch()
const [globalFont, setGlobalFont] = useRecoilState(globalFontAtom) const [globalFont, setGlobalFont] = useRecoilState(globalFontAtom)
const { addLine, removeLine } = useLine()
const commonTextFont = useRecoilValue(fontSelector('commonText')) const commonTextFont = useRecoilValue(fontSelector('commonText'))
const currentMenuSetting = () => { const currentMenuSetting = () => {
@ -130,22 +132,57 @@ export function useContextMenu() {
id: 'auxiliaryMove', id: 'auxiliaryMove',
name: `${getMessage('contextmenu.auxiliary.move')}(M)`, name: `${getMessage('contextmenu.auxiliary.move')}(M)`,
shortcut: ['m', 'M'], shortcut: ['m', 'M'],
component: <AuxiliaryMove id={popupId} />, component: <AuxiliaryEdit id={popupId} type={'move'} />,
}, },
{ {
id: 'auxiliaryCopy', id: 'auxiliaryCopy',
name: `${getMessage('contextmenu.auxiliary.copy')}(C)`, name: `${getMessage('contextmenu.auxiliary.copy')}(C)`,
shortcut: ['c', 'C'], shortcut: ['c', 'C'],
component: <AuxiliaryCopy id={popupId} />, component: <AuxiliaryEdit id={popupId} type={'copy'} />,
}, },
{ {
id: 'auxiliaryRemove', id: 'auxiliaryRemove',
shortcut: ['d', 'D'], shortcut: ['d', 'D'],
name: `${getMessage('contextmenu.auxiliary.remove')}(D)`, name: `${getMessage('contextmenu.auxiliary.remove')}(D)`,
fn: () => {
canvas.remove(currentObject)
canvas.discardActiveObject()
},
}, },
{ {
id: 'auxiliaryVerticalBisector', id: 'auxiliaryVerticalBisector',
name: getMessage('contextmenu.auxiliary.vertical.bisector'), name: getMessage('contextmenu.auxiliary.vertical.bisector'),
fn: () => {
const slope = (currentObject.y2 - currentObject.y1) / (currentObject.x2 - currentObject.x1)
const length = currentObject.length
let startX, startY, endX, endY
if (slope === 0) {
startX = endX = (currentObject.x1 + currentObject.x2) / 2
startY = currentObject.y2 - length / 2
endY = currentObject.y2 + length / 2
} else if (slope === Infinity) {
startX = currentObject.x1 - length / 2
startY = endY = (currentObject.y1 + currentObject.y2) / 2
endX = currentObject.x1 + length / 2
} else {
const bisectorSlope = -1 / slope
const dx = length / 2 / Math.sqrt(1 + bisectorSlope * bisectorSlope)
const dy = bisectorSlope * dx
startX = (currentObject.x1 + currentObject.x2) / 2 + dx
startY = (currentObject.y1 + currentObject.y2) / 2 + dy
endX = (currentObject.x1 + currentObject.x2) / 2 - dx
endY = (currentObject.y1 + currentObject.y2) / 2 - dy
}
addLine([startX, startY, endX, endY], {
stroke: 'red',
strokeWidth: 1,
selectable: true,
name: 'auxiliaryLine',
})
},
}, },
{ {
id: 'auxiliaryCut', id: 'auxiliaryCut',
@ -248,8 +285,6 @@ export function useContextMenu() {
}, [currentContextMenu]) }, [currentContextMenu])
useEffect(() => { useEffect(() => {
console.log(currentObject)
if (currentObject?.name) { if (currentObject?.name) {
console.log('object', currentObject) console.log('object', currentObject)
switch (currentObject.name) { switch (currentObject.name) {
@ -446,19 +481,24 @@ export function useContextMenu() {
]) ])
break break
case 'lineGrid': case 'lineGrid':
case 'dotGrid':
case 'tempGrid':
setContextMenu([ setContextMenu([
[ [
{ {
id: 'gridMove', id: 'gridMove',
name: getMessage('modal.grid.move'), name: getMessage('modal.grid.move'),
component: <GridMove id={popupId} />,
}, },
{ {
id: 'gridCopy', id: 'gridCopy',
name: getMessage('modal.grid.copy'), name: getMessage('modal.grid.copy'),
component: <GridCopy id={popupId} />,
}, },
{ {
id: 'gridColorEdit', id: 'gridColorEdit',
name: getMessage('contextmenu.grid.color.edit'), name: getMessage('contextmenu.grid.color.edit'),
component: <ColorPickerModal id={popupId} color={gridColor} setColor={setGridColor} />,
}, },
{ {
id: 'remove', id: 'remove',
@ -467,6 +507,7 @@ export function useContextMenu() {
{ {
id: 'removeAll', id: 'removeAll',
name: getMessage('contextmenu.remove.all'), name: getMessage('contextmenu.remove.all'),
fn: () => {},
}, },
], ],
]) ])

View File

@ -23,6 +23,9 @@ export function usePlan() {
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { get, promisePost, promisePut, promiseDel } = useAxios() const { get, promisePost, promisePut, promiseDel } = useAxios()
const planLabel = 'Plan '
const newPlanLabel = 'New Plan '
/** /**
* 마우스 포인터의 가이드라인을 제거합니다. * 마우스 포인터의 가이드라인을 제거합니다.
*/ */
@ -184,7 +187,7 @@ export function usePlan() {
return get({ url: `/api/canvas-management/canvas-statuses/by-object/${objectNo}/${userId}` }).then((res) => return get({ url: `/api/canvas-management/canvas-statuses/by-object/${objectNo}/${userId}` }).then((res) =>
res.map((item) => ({ res.map((item) => ({
id: item.id, id: item.id,
name: item.objectNo + '-' + item.id, // tab button에 표출될 이름 (임시) name: planLabel + item.id,
userId: item.userId, userId: item.userId,
canvasStatus: dbToCanvasFormat(item.canvasStatus), canvasStatus: dbToCanvasFormat(item.canvasStatus),
isCurrent: false, isCurrent: false,
@ -213,7 +216,7 @@ export function usePlan() {
? { ? {
...plan, ...plan,
id: res.data, id: res.data,
name: currentCanvasPlan.objectNo + '-' + res.data, name: planLabel + res.data,
canvasStatus: canvasStatus, canvasStatus: canvasStatus,
} }
: plan, : plan,
@ -318,7 +321,7 @@ export function usePlan() {
const id = uuidv4() const id = uuidv4()
const newPlan = { const newPlan = {
id: id, id: id,
name: `Plan ${planNum + 1}`, name: newPlanLabel + `${planNum + 1}`,
objectNo: objectNo, objectNo: objectNo,
userId: userId, userId: userId,
canvasStatus: canvasStatus, canvasStatus: canvasStatus,
@ -364,14 +367,13 @@ export function usePlan() {
/** /**
* plan 조회 * plan 조회
*/ */
const loadCanvasPlanData = (userId, objectNo) => { const loadCanvasPlanData = (userId, objectNo, pid) => {
getCanvasByObjectNo(userId, objectNo).then((res) => { getCanvasByObjectNo(userId, objectNo).then((res) => {
// console.log('canvas 목록 ', res) // console.log('canvas 목록 ', res)
if (res.length > 0) { if (res.length > 0) {
setInitCanvasPlans(res) setInitCanvasPlans(res)
setPlans(res) setPlans(res)
updateCurrentPlan(res.at(-1).id) // last 데이터에 포커싱 updateCurrentPlan(Number(pid)) // last 데이터에 포커싱
setPlanNum(res.length)
} else { } else {
addPlan(userId, objectNo) addPlan(userId, objectNo)
} }

View File

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

View File

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

64
src/lib/fileAction.js Normal file
View File

@ -0,0 +1,64 @@
'use server'
import fs from 'fs/promises'
const CAD_FILE_PATH = 'public/cad-images'
const IMAGE_FILE_PATH = 'public/plan-bg-images'
/**
* 파일 변환 & 저장
* @param {*} fileName
* @param {*} data
* @returns
*/
const convertDwgToPng = async (fileName, data) => {
console.log('fileName', fileName)
try {
await fs.readdir(CAD_FILE_PATH)
} catch {
await fs.mkdir(CAD_FILE_PATH)
}
return await fs.writeFile(`${CAD_FILE_PATH}/${fileName}`, data, 'base64')
}
/**
* 이미지 저장
* base64 형식으로 저장
* @param {*} title
* @param {*} data
* @returns
*/
const writeImageBase64 = async (title, data) => {
// 해당 경로에 Directory 가 없다면 생성
try {
await fs.readdir(IMAGE_FILE_PATH)
} catch {
await fs.mkdir(IMAGE_FILE_PATH)
}
return fs.writeFile(`${IMAGE_FILE_PATH}/${title}.png`, data, 'base64')
}
/**
* 이미지 저장
* Buffer 형식으로 저장
* @param {*} title
* @param {*} data
* @returns
*/
const writeImageBuffer = async (file) => {
// 해당 경로에 Directory 가 없다면 생성
try {
await fs.readdir(IMAGE_FILE_PATH)
} catch {
await fs.mkdir(IMAGE_FILE_PATH)
}
const arrayBuffer = await fileURLToPath.arrayBuffer()
const buffer = new Uint8Array(arrayBuffer)
return fs.writeFile(`${IMAGE_FILE_PATH}/${file.fileName}`, buffer)
}
export { convertDwgToPng, writeImageBase64, writeImageBuffer }

View File

@ -4,6 +4,7 @@
"header.menus.home": "ホームへv", "header.menus.home": "ホームへv",
"header.menus.management": "物品及び図面管理", "header.menus.management": "物品及び図面管理",
"header.menus.management.newStuff": "新規 物件 登録", "header.menus.management.newStuff": "新規 物件 登録",
"header.menus.management.detail": "物件詳細",
"header.menus.management.stuffList": "物件の状況", "header.menus.management.stuffList": "物件の状況",
"header.menus.community": "コミュニティ", "header.menus.community": "コミュニティ",
"header.menus.community.notice": "お知らせ", "header.menus.community.notice": "お知らせ",
@ -160,7 +161,7 @@
"plan.menu.estimate.docDown": "文書のダウンロード", "plan.menu.estimate.docDown": "文書のダウンロード",
"plan.menu.estimate.save": "保存", "plan.menu.estimate.save": "保存",
"plan.menu.estimate.reset": "初期化", "plan.menu.estimate.reset": "初期化",
"plan.menu.estimate.copy": "コピー", "plan.menu.estimate.copy": "見積書のコピー",
"plan.menu.simulation": "発展シミュレーション", "plan.menu.simulation": "発展シミュレーション",
"plan.menu.simulation.excel": "Excel", "plan.menu.simulation.excel": "Excel",
"plan.menu.simulation.pdf": "PDF", "plan.menu.simulation.pdf": "PDF",
@ -284,7 +285,6 @@
"modal.panel.batch.statistic.total": "合計", "modal.panel.batch.statistic.total": "合計",
"modal.flow.direction.setting": "流れ方向の設定", "modal.flow.direction.setting": "流れ方向の設定",
"modal.flow.direction.setting.info": "流れ方向を選択してください。", "modal.flow.direction.setting.info": "流れ方向を選択してください。",
"modal.image.size.setting": "画像のサイズ変更",
"modal.actual.size.setting": "実測値設定", "modal.actual.size.setting": "実測値設定",
"modal.actual.size.setting.info": "※隅棟・谷・棟の実際の寸法を入力してください。", "modal.actual.size.setting.info": "※隅棟・谷・棟の実際の寸法を入力してください。",
"modal.actual.size.setting.not.exist.auxiliary.line": "실측치 입력할 보조선을 선택해 주세요(JA)", "modal.actual.size.setting.not.exist.auxiliary.line": "실측치 입력할 보조선을 선택해 주세요(JA)",
@ -667,6 +667,7 @@
"stuff.detail.btn.save": "保存", "stuff.detail.btn.save": "保存",
"stuff.detail.btn.tempSave": "一時保存", "stuff.detail.btn.tempSave": "一時保存",
"stuff.detail.save": "保存しました", "stuff.detail.save": "保存しました",
"stuff.detail.tempSave": "一時保存されました",
"stuff.detail.noChgData": "変更内容はありません", "stuff.detail.noChgData": "変更内容はありません",
"stuff.detail.save.valierror1": "垂直説説は0より大きい値を入力してください", "stuff.detail.save.valierror1": "垂直説説は0より大きい値を入力してください",
"stuff.detail.save.valierror2": "設置高さ0より大きい値を入力してください", "stuff.detail.save.valierror2": "設置高さ0より大きい値を入力してください",
@ -697,9 +698,9 @@
"stuff.planReqPopup.error.message1": "設計依頼を選択してください.", "stuff.planReqPopup.error.message1": "設計依頼を選択してください.",
"stuff.planReqPopup.error.message2": "販売店を選択してください.", "stuff.planReqPopup.error.message2": "販売店を選択してください.",
"stuff.search.title": "物件状況", "stuff.search.title": "物件状況",
"stuff.search.btn1": "新規 物件 登録", "stuff.search.btn.register": "新規 物件 登録",
"stuff.search.btn2": "照会", "stuff.search.btn.search": "照会",
"stuff.search.btn3": "初期化", "stuff.search.btn.reset": "初期化",
"stuff.search.schObjectNo": "品番", "stuff.search.schObjectNo": "品番",
"stuff.search.schSaleStoreName": "販売代理店名", "stuff.search.schSaleStoreName": "販売代理店名",
"stuff.search.schAddress": "商品アドレス", "stuff.search.schAddress": "商品アドレス",
@ -823,6 +824,8 @@
"estimate.detail.objectName": "案件名", "estimate.detail.objectName": "案件名",
"estimate.detail.objectRemarks": "メモ", "estimate.detail.objectRemarks": "メモ",
"estimate.detail.estimateType": "注文分類", "estimate.detail.estimateType": "注文分類",
"estimate.detail.estimateType.yjss": "住宅PKG",
"estimate.detail.estimateType.yjod": "積上げ( YJOD )",
"estimate.detail.roofCns": "屋根材・仕様施工", "estimate.detail.roofCns": "屋根材・仕様施工",
"estimate.detail.remarks": "備考", "estimate.detail.remarks": "備考",
"estimate.detail.fileFlg": "後日資料提出", "estimate.detail.fileFlg": "後日資料提出",
@ -839,11 +842,10 @@
"estimate.detail.sepcialEstimateProductInfo.pkgUnitPrice": "住宅PKG単価 (W)", "estimate.detail.sepcialEstimateProductInfo.pkgUnitPrice": "住宅PKG単価 (W)",
"estimate.detail.sepcialEstimateProductInfo.pkgWeight": "PKG容量 (Kw)", "estimate.detail.sepcialEstimateProductInfo.pkgWeight": "PKG容量 (Kw)",
"estimate.detail.sepcialEstimateProductInfo.pkgPrice": "PKG金額", "estimate.detail.sepcialEstimateProductInfo.pkgPrice": "PKG金額",
"estimate.detail.sepcialEstimateProductInfo.calcFormula1": "(モジュール容量 × 数量)÷1000",
"estimate.detail.sepcialEstimateProductInfo.calcFormula2": "PKG単価 (W)×PKG容量(W)",
"estimate.detail.header.showPrice": "価格表示", "estimate.detail.header.showPrice": "価格表示",
"estimate.detail.header.unitPrice": "定価", "estimate.detail.header.unitPrice": "定価",
"estimate.detail.showPrice.pricingBtn": "Pricing", "estimate.detail.showPrice.pricingBtn": "Pricing",
"estimate.detail.showPrice.pricingBtn.noItemId": "Pricingが欠落しているアイテムがあります。 Pricingを進めてください.",
"estimate.detail.showPrice.description1": "製品価格 OPEN", "estimate.detail.showPrice.description1": "製品価格 OPEN",
"estimate.detail.showPrice.description2": "追加, 変更資材", "estimate.detail.showPrice.description2": "追加, 変更資材",
"estimate.detail.showPrice.description3": "添付必須", "estimate.detail.showPrice.description3": "添付必須",
@ -868,17 +870,31 @@
"estimate.detail.docPopup.schDisplayFlg.schDisplayFlg0": "販売店名", "estimate.detail.docPopup.schDisplayFlg.schDisplayFlg0": "販売店名",
"estimate.detail.docPopup.schDisplayFlg.schDisplayFlg1": "案件名", "estimate.detail.docPopup.schDisplayFlg.schDisplayFlg1": "案件名",
"estimate.detail.docPopup.schWeightFlg": "架台重量表を含む", "estimate.detail.docPopup.schWeightFlg": "架台重量表を含む",
"estimate.detail.docPopup.schWeightFlg.schWeightFlg0": "含む", "estimate.detail.docPopup.schWeightFlg.schWeightFlg1": "含む",
"estimate.detail.docPopup.schWeightFlg.schWeightFlg1": "含まない", "estimate.detail.docPopup.schWeightFlg.schWeightFlg0": "含まない",
"estimate.detail.docPopup.schDrawingFlg": "図面/シミュレーションファイルを含む", "estimate.detail.docPopup.schDrawingFlg": "図面/シミュレーションファイルを含む",
"estimate.detail.docPopup.schDrawingFlg.schDrawingFlg0": "含む", "estimate.detail.docPopup.schDrawingFlg.schDrawingFlg1": "含む",
"estimate.detail.docPopup.schDrawingFlg.schDrawingFlg1": "含まない", "estimate.detail.docPopup.schDrawingFlg.schDrawingFlg0": "含まない",
"estimate.detail.docPopup.close": "閉じる", "estimate.detail.docPopup.close": "閉じる",
"estimate.detail.docPopup.docDownload": "文書のダウンロード", "estimate.detail.docPopup.docDownload": "文書のダウンロード",
"estimate.detail.estimateCopyPopup.title": "見積もり",
"estimate.detail.estimateCopyPopup.explane": "見積書をコピーする販売店を設定します。見積もりは定価にコピーされます.",
"estimate.detail.estimateCopyPopup.label.saleStoreId": "一次販売店名 / ID",
"estimate.detail.estimateCopyPopup.label.otherSaleStoreId": "二次販売店名 / ID",
"estimate.detail.estimateCopyPopup.label.receiveUser": "担当者",
"estimate.detail.estimateCopyPopup.close": "閉じる",
"estimate.detail.estimateCopyPopup.copyBtn": "見積もり",
"estimate.detail.estimateCopyPopup.copy.alertMessage": "見積書がコピーされました. コピーした商品情報に移動します.",
"estimate.detail.productFeaturesPopup.title": "製品特異事項", "estimate.detail.productFeaturesPopup.title": "製品特異事項",
"estimate.detail.productFeaturesPopup.close": "閉じる", "estimate.detail.productFeaturesPopup.close": "閉じる",
"estimate.detail.productFeaturesPopup.requiredStoreId": "一次販売店は必須です.",
"estimate.detail.productFeaturesPopup.requiredReceiveUser": "担当者は必須です.",
"estimate.detail.save.alertMsg": "保存されている見積書で製品を変更した場合、図面や回路には反映されません.", "estimate.detail.save.alertMsg": "保存されている見積書で製品を変更した場合、図面や回路には反映されません.",
"estimate.detail.save.requiredMsg": "ファイル添付が必須のアイテムがあります。ファイルを添付するか、後日添付をチェックしてください.", "estimate.detail.save.requiredFileUpload": "ファイル添付が必須のアイテムがあります。ファイルを添付するか、後日添付をチェックしてください.",
"estimate.detail.save.requiredItem": "製品は1つ以上登録する必要があります.",
"estimate.detail.save.requiredCharger": "担当者は必須です.",
"estimate.detail.save.requiredObjectName": "案件名は必須です.",
"estimate.detail.save.requiredEstimateDate": "見積日は必須です.",
"estimate.detail.reset.confirmMsg": "保存した見積書情報が初期化され、図面情報が反映されます。本当に初期化しますか?", "estimate.detail.reset.confirmMsg": "保存した見積書情報が初期化され、図面情報が反映されます。本当に初期化しますか?",
"simulator.title.sub1": "物件番号", "simulator.title.sub1": "物件番号",
"simulator.title.sub2": "作成日", "simulator.title.sub2": "作成日",

View File

@ -4,6 +4,7 @@
"header.menus.home": "Home", "header.menus.home": "Home",
"header.menus.management": "물건 및 도면 관리", "header.menus.management": "물건 및 도면 관리",
"header.menus.management.newStuff": "신규 물건 등록", "header.menus.management.newStuff": "신규 물건 등록",
"header.menus.management.detail": "물건 상세",
"header.menus.management.stuffList": "물건 현황", "header.menus.management.stuffList": "물건 현황",
"header.menus.community": "커뮤니티", "header.menus.community": "커뮤니티",
"header.menus.community.notice": "공지", "header.menus.community.notice": "공지",
@ -164,7 +165,7 @@
"plan.menu.estimate.docDown": "문서 다운로드", "plan.menu.estimate.docDown": "문서 다운로드",
"plan.menu.estimate.save": "저장", "plan.menu.estimate.save": "저장",
"plan.menu.estimate.reset": "초기화", "plan.menu.estimate.reset": "초기화",
"plan.menu.estimate.copy": "복사", "plan.menu.estimate.copy": "견적서 복사",
"plan.menu.simulation": "발전 시뮬레이션", "plan.menu.simulation": "발전 시뮬레이션",
"plan.menu.simulation.excel": "Excel", "plan.menu.simulation.excel": "Excel",
"plan.menu.simulation.pdf": "PDF", "plan.menu.simulation.pdf": "PDF",
@ -289,7 +290,6 @@
"modal.panel.batch.statistic.total": "합계", "modal.panel.batch.statistic.total": "합계",
"modal.flow.direction.setting": "흐름 방향 설정", "modal.flow.direction.setting": "흐름 방향 설정",
"modal.flow.direction.setting.info": "흐름방향을 선택하세요.", "modal.flow.direction.setting.info": "흐름방향을 선택하세요.",
"modal.image.size.setting": "이미지 크기 조절",
"modal.actual.size.setting": "실측치 설정", "modal.actual.size.setting": "실측치 설정",
"modal.actual.size.setting.info": "※隅棟・谷・棟의 실제 치수를 입력해주세요.", "modal.actual.size.setting.info": "※隅棟・谷・棟의 실제 치수를 입력해주세요.",
"modal.actual.size.setting.not.exist.auxiliary.line": "실측치 입력할 보조선을 선택해 주세요", "modal.actual.size.setting.not.exist.auxiliary.line": "실측치 입력할 보조선을 선택해 주세요",
@ -677,6 +677,7 @@
"stuff.detail.btn.save": "저장", "stuff.detail.btn.save": "저장",
"stuff.detail.btn.tempSave": "임시저장", "stuff.detail.btn.tempSave": "임시저장",
"stuff.detail.save": "저장되었습니다", "stuff.detail.save": "저장되었습니다",
"stuff.detail.tempSave": "임시저장 되었습니다",
"stuff.detail.noChgData": "변경된 내용이 없습니다", "stuff.detail.noChgData": "변경된 내용이 없습니다",
"stuff.detail.save.valierror1": "수직적설량은 0보다 큰 값을 입력하세요", "stuff.detail.save.valierror1": "수직적설량은 0보다 큰 값을 입력하세요",
"stuff.detail.save.valierror2": "설치높이는 0보다 큰 값을 입력하세요", "stuff.detail.save.valierror2": "설치높이는 0보다 큰 값을 입력하세요",
@ -707,9 +708,9 @@
"stuff.planReqPopup.error.message1": "설계의뢰를 선택해주세요.", "stuff.planReqPopup.error.message1": "설계의뢰를 선택해주세요.",
"stuff.planReqPopup.error.message2": "판매점을 선택해주세요.", "stuff.planReqPopup.error.message2": "판매점을 선택해주세요.",
"stuff.search.title": "물건현황", "stuff.search.title": "물건현황",
"stuff.search.btn1": "신규 물건 등록", "stuff.search.btn.register": "신규 물건 등록",
"stuff.search.btn2": "조회", "stuff.search.btn.search": "조회",
"stuff.search.btn3": "초기화", "stuff.search.btn.reset": "초기화",
"stuff.search.schObjectNo": "물건번호", "stuff.search.schObjectNo": "물건번호",
"stuff.search.schSaleStoreName": "판매대리점명", "stuff.search.schSaleStoreName": "판매대리점명",
"stuff.search.schAddress": "물건주소", "stuff.search.schAddress": "물건주소",
@ -833,6 +834,8 @@
"estimate.detail.objectName": "안건명", "estimate.detail.objectName": "안건명",
"estimate.detail.objectRemarks": "메모", "estimate.detail.objectRemarks": "메모",
"estimate.detail.estimateType": "주문분류", "estimate.detail.estimateType": "주문분류",
"estimate.detail.estimateType.yjss": "住宅PKG",
"estimate.detail.estimateType.yjod": "積上げ( YJOD )",
"estimate.detail.roofCns": "지붕재・사양시공", "estimate.detail.roofCns": "지붕재・사양시공",
"estimate.detail.remarks": "비고", "estimate.detail.remarks": "비고",
"estimate.detail.fileFlg": "후일자료제출", "estimate.detail.fileFlg": "후일자료제출",
@ -849,11 +852,10 @@
"estimate.detail.sepcialEstimateProductInfo.pkgUnitPrice": "주택PKG단가 (W)", "estimate.detail.sepcialEstimateProductInfo.pkgUnitPrice": "주택PKG단가 (W)",
"estimate.detail.sepcialEstimateProductInfo.pkgWeight": "PKG 용량 (Kw)", "estimate.detail.sepcialEstimateProductInfo.pkgWeight": "PKG 용량 (Kw)",
"estimate.detail.sepcialEstimateProductInfo.pkgPrice": "PKG 금액", "estimate.detail.sepcialEstimateProductInfo.pkgPrice": "PKG 금액",
"estimate.detail.sepcialEstimateProductInfo.calcFormula1": "(모듈수량 * 수량)÷100",
"estimate.detail.sepcialEstimateProductInfo.calcFormula2": "PKG단가(W) * PKG용량(W)",
"estimate.detail.header.showPrice": "가격표시", "estimate.detail.header.showPrice": "가격표시",
"estimate.detail.header.unitPrice": "정가", "estimate.detail.header.unitPrice": "정가",
"estimate.detail.showPrice.pricingBtn": "Pricing", "estimate.detail.showPrice.pricingBtn": "Pricing",
"estimate.detail.showPrice.pricingBtn.noItemId": "Pricing이 누락된 아이템이 있습니다. Pricing을 진행해주세요.",
"estimate.detail.showPrice.description1": "제품 가격 OPEN", "estimate.detail.showPrice.description1": "제품 가격 OPEN",
"estimate.detail.showPrice.description2": "추가, 변경 자재", "estimate.detail.showPrice.description2": "추가, 변경 자재",
"estimate.detail.showPrice.description3": "첨부필수", "estimate.detail.showPrice.description3": "첨부필수",
@ -878,17 +880,31 @@
"estimate.detail.docPopup.schDisplayFlg.schDisplayFlg0": "판매점명", "estimate.detail.docPopup.schDisplayFlg.schDisplayFlg0": "판매점명",
"estimate.detail.docPopup.schDisplayFlg.schDisplayFlg1": "안건명", "estimate.detail.docPopup.schDisplayFlg.schDisplayFlg1": "안건명",
"estimate.detail.docPopup.schWeightFlg": "가대 중량표 포함", "estimate.detail.docPopup.schWeightFlg": "가대 중량표 포함",
"estimate.detail.docPopup.schWeightFlg.schWeightFlg0": "포함", "estimate.detail.docPopup.schWeightFlg.schWeightFlg1": "포함",
"estimate.detail.docPopup.schWeightFlg.schWeightFlg1": "미포함", "estimate.detail.docPopup.schWeightFlg.schWeightFlg0": "미포함",
"estimate.detail.docPopup.schDrawingFlg": "도면/시뮬레이션 파일 포함", "estimate.detail.docPopup.schDrawingFlg": "도면/시뮬레이션 파일 포함",
"estimate.detail.docPopup.schDrawingFlg.schDrawingFlg0": "포함", "estimate.detail.docPopup.schDrawingFlg.schDrawingFlg1": "포함",
"estimate.detail.docPopup.schDrawingFlg.schDrawingFlg1": "미포함", "estimate.detail.docPopup.schDrawingFlg.schDrawingFlg0": "미포함",
"estimate.detail.docPopup.close": "닫기", "estimate.detail.docPopup.close": "닫기",
"estimate.detail.docPopup.docDownload": "문서 다운로드", "estimate.detail.docPopup.docDownload": "문서 다운로드",
"estimate.detail.estimateCopyPopup.title": "견적복사",
"estimate.detail.estimateCopyPopup.explane": "견적서를 복사할 판매점을 설정하십시오. 견적서는 정가로 복사됩니다.",
"estimate.detail.estimateCopyPopup.label.saleStoreId": "1차 판매점명 / ID",
"estimate.detail.estimateCopyPopup.label.otherSaleStoreId": "2차 판매점명 / ID",
"estimate.detail.estimateCopyPopup.label.receiveUser": "담당자",
"estimate.detail.estimateCopyPopup.close": "닫기",
"estimate.detail.estimateCopyPopup.copyBtn": "견적복사",
"estimate.detail.estimateCopyPopup.copy.alertMessage": "견적서가 복사되었습니다. 복사된 물건정보로 이동합니다.",
"estimate.detail.productFeaturesPopup.title": "제품특이사항", "estimate.detail.productFeaturesPopup.title": "제품특이사항",
"estimate.detail.productFeaturesPopup.close": "닫기", "estimate.detail.productFeaturesPopup.close": "닫기",
"estimate.detail.productFeaturesPopup.requiredStoreId": "1차 판매점은 필수값 입니다.",
"estimate.detail.productFeaturesPopup.requiredReceiveUser": "담당자는 필수값 입니다.",
"estimate.detail.save.alertMsg": "저장되었습니다. 견적서에서 제품을 변경할 경우, 도면 및 회로에 반영되지 않습니다.", "estimate.detail.save.alertMsg": "저장되었습니다. 견적서에서 제품을 변경할 경우, 도면 및 회로에 반영되지 않습니다.",
"estimate.detail.save.requiredMsg": "파일첨부가 필수인 아이템이 있습니다. 파일을 첨부하거나 후일첨부를 체크해주십시오.", "estimate.detail.save.requiredFileUpload": "파일첨부가 필수인 아이템이 있습니다. 파일을 첨부하거나 후일첨부를 체크해주십시오.",
"estimate.detail.save.requiredItem": "제품은 1개이상 등록해야 됩니다.",
"estimate.detail.save.requiredCharger": "담당자는 필수값 입니다.",
"estimate.detail.save.requiredObjectName": "안건명은 필수값 입니다.",
"estimate.detail.save.requiredEstimateDate": "견적일은 필수값 입니다.",
"estimate.detail.reset.confirmMsg": "저장된 견적서 정보가 초기화되고, 도면정보가 반영됩니다. 정말로 초기화 하시겠습니까?", "estimate.detail.reset.confirmMsg": "저장된 견적서 정보가 초기화되고, 도면정보가 반영됩니다. 정말로 초기화 하시겠습니까?",
"simulator.title.sub1": "물건번호", "simulator.title.sub1": "물건번호",
"simulator.title.sub2": "작성일", "simulator.title.sub2": "작성일",

View File

@ -1,7 +1,6 @@
import { atom } from 'recoil' import { atom } from 'recoil'
import { v1 } from 'uuid'
export const floorPlanObjectState = atom({ export const floorPlanObjectState = atom({
key: `floorPlanObjectState/${v1()}`, key: 'floorPlanObjectState',
default: { default: {
floorPlanObjectNo: '', //물건번호 floorPlanObjectNo: '', //물건번호
}, },
@ -9,7 +8,7 @@ export const floorPlanObjectState = atom({
}) })
export const estimateState = atom({ export const estimateState = atom({
key: `estimateState`, key: 'estimateState',
default: {}, default: {},
dangerouslyAllowMutability: true, dangerouslyAllowMutability: true,
}) })

View File

@ -217,3 +217,11 @@ export const basicSettingState = atom({
], ],
}, },
}) })
/**
* 현재 선택된 물건 번호
*/
export const correntObjectNoState = atom({
key: 'correntObjectNoState',
default: '',
})

View File

@ -0,0 +1,8 @@
import { atom } from 'recoil'
export const pwrGnrSimTypeState = atom({
key: 'pwrGnrSimType',
default: {
type: 'D',
},
})

View File

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1201,7 +1201,7 @@ export function removeDuplicatePolygons(polygons) {
} }
export const isSamePoint = (a, b) => { export const isSamePoint = (a, b) => {
return Math.abs(Math.round(a.x) - Math.round(b.x)) <= 1 && Math.abs(Math.round(a.y) - Math.round(b.y)) <= 1 return Math.abs(Math.round(a.x) - Math.round(b.x)) <= 2 && Math.abs(Math.round(a.y) - Math.round(b.y)) <= 2
} }
/** /**

View File

@ -529,7 +529,7 @@
"@kurkle/color@^0.3.0": "@kurkle/color@^0.3.0":
version "0.3.2" version "0.3.2"
resolved "https://registry.yarnpkg.com/@kurkle/color/-/color-0.3.2.tgz#5acd38242e8bde4f9986e7913c8fdf49d3aa199f" resolved "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz"
integrity sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw== integrity sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==
"@mapbox/node-pre-gyp@^1.0.0": "@mapbox/node-pre-gyp@^1.0.0":
@ -4344,7 +4344,7 @@ chalk@^2.4.2:
chart.js@^4.4.6: chart.js@^4.4.6:
version "4.4.6" version "4.4.6"
resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-4.4.6.tgz#da39b84ca752298270d4c0519675c7659936abec" resolved "https://registry.npmjs.org/chart.js/-/chart.js-4.4.6.tgz"
integrity sha512-8Y406zevUPbbIBA/HRk33khEmQPk5+cxeflWE/2rx1NJsjVWMPw/9mSP9rxHP5eqi6LNoPBVMfZHxbwLSgldYA== integrity sha512-8Y406zevUPbbIBA/HRk33khEmQPk5+cxeflWE/2rx1NJsjVWMPw/9mSP9rxHP5eqi6LNoPBVMfZHxbwLSgldYA==
dependencies: dependencies:
"@kurkle/color" "^0.3.0" "@kurkle/color" "^0.3.0"
@ -5846,7 +5846,7 @@ rbush@^3.0.1:
react-chartjs-2@^5.2.0: react-chartjs-2@^5.2.0:
version "5.2.0" version "5.2.0"
resolved "https://registry.yarnpkg.com/react-chartjs-2/-/react-chartjs-2-5.2.0.tgz#43c1e3549071c00a1a083ecbd26c1ad34d385f5d" resolved "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-5.2.0.tgz"
integrity sha512-98iN5aguJyVSxp5U3CblRLH67J8gkfyGNbiK3c+l1QI/G4irHMPQw44aEPmjVag+YKTyQ260NcF82GTQ3bdscA== integrity sha512-98iN5aguJyVSxp5U3CblRLH67J8gkfyGNbiK3c+l1QI/G4irHMPQw44aEPmjVag+YKTyQ260NcF82GTQ3bdscA==
react-color-palette@^7.2.2: react-color-palette@^7.2.2:
@ -5954,6 +5954,11 @@ react-select@^5.8.1:
react-transition-group "^4.3.0" react-transition-group "^4.3.0"
use-isomorphic-layout-effect "^1.1.2" use-isomorphic-layout-effect "^1.1.2"
react-spinners@^0.14.1:
version "0.14.1"
resolved "https://registry.npmjs.org/react-spinners/-/react-spinners-0.14.1.tgz"
integrity sha512-2Izq+qgQ08HTofCVEdcAQCXFEYfqTDdfeDQJeo/HHQiQJD4imOicNLhkfN2eh1NYEWVOX4D9ok2lhuDB0z3Aag==
react-style-singleton@^2.2.1: react-style-singleton@^2.2.1:
version "2.2.1" version "2.2.1"
resolved "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz" resolved "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz"
@ -6280,14 +6285,7 @@ string_decoder@^1.1.1, string_decoder@^1.3.0:
dependencies: dependencies:
safe-buffer "~5.2.0" safe-buffer "~5.2.0"
"strip-ansi-cjs@npm:strip-ansi@^6.0.1": "strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
dependencies:
ansi-regex "^5.0.1"
strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1" version "6.0.1"
resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==