Merge branch 'dev' into feature/jaeyoung

This commit is contained in:
Jaeyoung Lee 2024-12-09 16:13:31 +09:00
commit 453fb80fd4
65 changed files with 3328 additions and 2075 deletions

View File

@ -1,28 +0,0 @@
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "sqlserver"
url = env("DATABASE_URL")
}
model M_USER {
USER_ID String @id(map: "M_USER_pk") @db.VarChar(50)
SALE_STORE_ID String @db.VarChar(10)
PASSWORD String @db.VarChar(10)
CATEGORY String? @db.NVarChar(20)
NAME String @db.NVarChar(20)
NAME_KANA String @db.NVarChar(20)
TEL String? @db.VarChar(15)
FAX String? @db.VarChar(15)
MAIL String? @db.NVarChar(100)
GROUP_ID String @db.VarChar(5)
MODULE_SELECT_GROUP_ID String? @db.VarChar(5)
VERSION_MANAGEMENT_ID String? @db.VarChar(20)
DISP_COST_PRICE Boolean?
DISP_SELLING_PRICE Boolean?
REGIST_DATETIME DateTime? @db.DateTime
LAST_EDIT_DATETIME DateTime? @db.DateTime
LAST_EDIT_USER String? @db.VarChar(50)
}

View File

@ -0,0 +1,5 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.19444 12.9993C6.09354 12.9998 5.99172 13 5.88889 13C4.92046 13 4.04106 12.9796 3.17535 12.9406C2.14652 12.8941 1.30109 12.109 1.16366 11.1069C1.07397 10.4528 1 9.78258 1 9.10001C1 8.41744 1.07397 7.7472 1.16366 7.09317C1.30109 6.09102 2.14652 5.30591 3.17535 5.25947C4.04106 5.22039 4.92046 5.20001 5.88889 5.20001C6.85732 5.20001 7.73672 5.22039 8.60243 5.25947C9.4922 5.29963 10.1961 5.89229 10.4722 6.70001" stroke="white" stroke-linecap="round"/>
<path d="M3.13885 5.2V3.7C3.13885 2.20883 4.37007 1 5.88885 1C7.40764 1 8.63885 2.20883 8.63885 3.7V5.2" stroke="white" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.95286 10.4138C9.70279 10.2967 9.40511 10.4045 9.28798 10.6545C9.17085 10.9046 9.27862 11.2023 9.52869 11.3194L9.95286 10.4138ZM10.284 11.4L9.85526 11.6572C9.94961 11.8145 10.1224 11.9074 10.3056 11.8995C10.4888 11.8915 10.653 11.784 10.7334 11.6192L10.284 11.4ZM11.604 10.5087C11.8482 10.3796 11.9415 10.0771 11.8125 9.83297C11.6834 9.58883 11.3809 9.49553 11.1368 9.62457L11.604 10.5087ZM12.5 10.6C12.5 11.6407 11.6382 12.5 10.5556 12.5V13.5C12.1731 13.5 13.5 12.2102 13.5 10.6H12.5ZM10.5556 12.5C9.47301 12.5 8.61115 11.6407 8.61115 10.6H7.61115C7.61115 12.2102 8.93811 13.5 10.5556 13.5V12.5ZM8.61115 10.6C8.61115 9.55922 9.47301 8.69995 10.5556 8.69995V7.69995C8.93811 7.69995 7.61115 8.98972 7.61115 10.6H8.61115ZM10.5556 8.69995C11.6382 8.69995 12.5 9.55922 12.5 10.6H13.5C13.5 8.98972 12.1731 7.69995 10.5556 7.69995V8.69995ZM9.74077 10.8666C9.52869 11.3194 9.52853 11.3193 9.52837 11.3193C9.52832 11.3192 9.52816 11.3192 9.52805 11.3191C9.52785 11.319 9.52764 11.3189 9.52744 11.3188C9.52704 11.3186 9.52666 11.3185 9.5263 11.3183C9.52557 11.3179 9.52491 11.3176 9.52432 11.3173C9.52313 11.3168 9.52222 11.3163 9.52157 11.316C9.52026 11.3153 9.51999 11.3152 9.52066 11.3155C9.52202 11.3163 9.52708 11.319 9.53519 11.3239C9.55151 11.3337 9.57943 11.3518 9.6139 11.3792C9.68306 11.4342 9.77529 11.524 9.85526 11.6572L10.7127 11.1427C10.5599 10.888 10.3805 10.7111 10.2363 10.5964C10.164 10.539 10.0998 10.4965 10.0506 10.4669C10.026 10.4521 10.005 10.4405 9.98847 10.4317C9.9802 10.4273 9.97303 10.4237 9.96707 10.4207C9.96408 10.4192 9.9614 10.4179 9.95903 10.4168C9.95784 10.4162 9.95673 10.4157 9.9557 10.4152C9.95519 10.4149 9.95469 10.4147 9.95422 10.4145C9.95398 10.4144 9.95375 10.4142 9.95352 10.4141C9.95341 10.4141 9.95324 10.414 9.95319 10.414C9.95302 10.4139 9.95286 10.4138 9.74077 10.8666ZM10.284 11.4C10.7334 11.6192 10.7333 11.6192 10.7333 11.6193C10.7333 11.6193 10.7333 11.6194 10.7332 11.6194C10.7332 11.6195 10.7332 11.6196 10.7332 11.6196C10.7331 11.6197 10.7331 11.6197 10.7331 11.6196C10.7332 11.6196 10.7333 11.6192 10.7336 11.6187C10.7341 11.6176 10.7352 11.6156 10.7366 11.6127C10.7395 11.6069 10.7442 11.5976 10.7506 11.5852C10.7634 11.5605 10.7829 11.5237 10.8082 11.4781C10.8592 11.3863 10.9325 11.2614 11.0217 11.1288C11.215 10.8417 11.4284 10.6015 11.604 10.5087L11.1368 9.62457C10.725 9.84221 10.3952 10.2687 10.1921 10.5706C10.0831 10.7325 9.99497 10.8828 9.93403 10.9925C9.90342 11.0476 9.87932 11.0931 9.86253 11.1256C9.85413 11.1418 9.84754 11.1548 9.84286 11.1641C9.84051 11.1688 9.83865 11.1725 9.83727 11.1753C9.83658 11.1767 9.83601 11.1779 9.83557 11.1788C9.83534 11.1792 9.83515 11.1796 9.83499 11.1799C9.83491 11.1801 9.83484 11.1803 9.83477 11.1804C9.83474 11.1805 9.8347 11.1805 9.83468 11.1806C9.83464 11.1807 9.8346 11.1807 10.284 11.4Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@ -0,0 +1,3 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.8055 1V2.87932C11.8055 3.05563 11.5851 3.13545 11.4722 3C10.3736 1.7725 8.777 1 7 1C3.68629 1 1 3.68629 1 7C1 10.3137 3.68629 13 7 13C10.3137 13 13 10.3137 13 7" stroke="#B0BCCD" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 344 B

View File

@ -34,7 +34,7 @@ export const QcastProvider = ({ children }) => {
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()
} else if (targetElement && currentCanvasPlan?.id) { } else if (targetElement && currentCanvasPlan?.id) {
setPlanSave(true) setPlanSave(true)
} }

View File

@ -3,6 +3,7 @@
import fs from 'fs/promises' import fs from 'fs/promises'
import { NextResponse } from 'next/server' import { NextResponse } from 'next/server'
import { writeImage, writeImageBuffer } from '@/lib/fileAction'
export async function GET(req) { export async function GET(req) {
const path = 'public/plan-map-images' const path = 'public/plan-map-images'
@ -15,14 +16,7 @@ export async function GET(req) {
const response = await fetch(decodeUrl) const response = await fetch(decodeUrl)
const data = await response.arrayBuffer() const data = await response.arrayBuffer()
const buffer = Buffer.from(data) const buffer = Buffer.from(data)
await writeImage(fileNm, buffer)
try {
await fs.readdir(path)
} catch {
await fs.mkdir(path)
} finally {
await fs.writeFile(`${path}/${fileNm}.png`, buffer)
}
return NextResponse.json({ fileNm: `${fileNm}.png` }) return NextResponse.json({ fileNm: `${fileNm}.png` })
} }

View File

@ -1,25 +1,15 @@
'use server' 'use server'
import fs from 'fs/promises'
import { NextResponse } from 'next/server' import { NextResponse } from 'next/server'
import { writeImage } from '@/lib/fileAction'
export async function POST(req) { export async function POST(req) {
const path = 'public/plan-bg-images'
const formData = await req.formData() const formData = await req.formData()
const file = formData.get('file') const file = formData.get('file')
const fileName = formData.get('fileName')
const arrayBuffer = await file.arrayBuffer() const arrayBuffer = await file.arrayBuffer()
const buffer = Buffer.from(arrayBuffer) const buffer = Buffer.from(arrayBuffer)
// const buffer = new Uint8Array(arrayBuffer) await writeImage(fileName, buffer)
try { return NextResponse.json({ fileNm: `${fileName}.png` })
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,34 +0,0 @@
import { useEvent } from '@/hooks/useEvent'
import { createContext, useState } from 'react'
export const EventContext = createContext({})
const EventProvider = ({ children }) => {
const {
addDocumentEventListener,
addCanvasMouseEventListener,
addTargetMouseEventListener,
removeAllMouseEventListeners,
removeAllDocumentEventListeners,
removeDocumentEvent,
removeMouseEvent,
removeMouseLine,
initEvent,
} = useEvent()
const [value, setValue] = useState({
addDocumentEventListener,
addCanvasMouseEventListener,
addTargetMouseEventListener,
removeAllMouseEventListeners,
removeAllDocumentEventListeners,
removeDocumentEvent,
removeMouseEvent,
removeMouseLine,
initEvent,
})
return <EventContext.Provider value={value}>{children}</EventContext.Provider>
}
export default EventProvider

View File

@ -2,8 +2,6 @@
import { correntObjectNoState } from '@/store/settingAtom' import { correntObjectNoState } from '@/store/settingAtom'
import { notFound, usePathname, useSearchParams } from 'next/navigation' import { notFound, usePathname, useSearchParams } from 'next/navigation'
// import { ErrorBoundary } from 'next/dist/client/components/error-boundary'
// import ServerError from '../error'
import { createContext, useReducer, useState } from 'react' import { createContext, useReducer, useState } from 'react'
import { useSetRecoilState } from 'recoil' import { useSetRecoilState } from 'recoil'

View File

@ -3,20 +3,25 @@
import FloorPlanProvider from './FloorPlanProvider' import FloorPlanProvider from './FloorPlanProvider'
import FloorPlan from '@/components/floor-plan/FloorPlan' import FloorPlan from '@/components/floor-plan/FloorPlan'
import CanvasLayout from '@/components/floor-plan/CanvasLayout' import CanvasLayout from '@/components/floor-plan/CanvasLayout'
import EventProvider from './EventProvider' import { usePathname } from 'next/navigation'
export default function FloorPlanLayout({ children }) { export default function FloorPlanLayout({ children }) {
console.log('🚀 ~ FloorPlanLayout ~ FloorPlanLayout:') console.log('🚀 ~ FloorPlanLayout ~ FloorPlanLayout:')
const pathname = usePathname()
console.log('🚀 ~ FloorPlanLayout ~ pathname:', pathname)
return ( return (
<> <>
<EventProvider> <FloorPlanProvider>
<FloorPlanProvider> <FloorPlan>
<FloorPlan> {pathname.includes('estimate') || pathname.includes('simulator') ? (
<div className="canvas-layout">{children}</div>
) : (
<CanvasLayout>{children}</CanvasLayout> <CanvasLayout>{children}</CanvasLayout>
</FloorPlan> )}
</FloorPlanProvider> {/* <CanvasLayout>{children}</CanvasLayout> */}
</EventProvider> </FloorPlan>
</FloorPlanProvider>
</> </>
) )
} }

View File

@ -1,7 +1,9 @@
import StuffSearchCondition from '@/components/management/StuffSearchCondition'
import Stuff from '@/components/management/Stuff' import Stuff from '@/components/management/Stuff'
import StuffSearchCondition from '@/components/management/StuffSearchCondition'
import StuffSubHeader from '@/components/management/StuffSubHeader' import StuffSubHeader from '@/components/management/StuffSubHeader'
import '@/styles/grid.scss' import '@/styles/grid.scss'
export default async function ManagementStuffPage() { export default async function ManagementStuffPage() {
return ( return (
<> <>

View File

@ -0,0 +1,18 @@
import React from 'react'
import StuffSubHeader from '@/components/management/StuffSubHeader'
import '@/styles/contents.scss'
import StuffDetail from '@/components/management/StuffDetail'
export default function ManagementStuffRegPage() {
return (
<>
<StuffSubHeader type={'temp'} />
<div className="sub-content">
<div className="sub-content-inner">
<div className="sub-content-box">
<StuffDetail />
</div>
</div>
</div>
</>
)
}

View File

@ -161,6 +161,7 @@ export const SAVE_KEY = [
'surfaceCompass', 'surfaceCompass',
'moduleCompass', 'moduleCompass',
'isFixed', 'isFixed',
'modules',
] ]
export const OBJECT_PROTOTYPE = [fabric.Line.prototype, fabric.Polygon.prototype, fabric.Triangle.prototype] export const OBJECT_PROTOTYPE = [fabric.Line.prototype, fabric.Polygon.prototype, fabric.Triangle.prototype]

View File

@ -15,6 +15,7 @@ import { SessionContext } from '@/app/SessionProvider'
import { QcastContext } from '@/app/QcastProvider' import { QcastContext } from '@/app/QcastProvider'
export default function MainPage() { export default function MainPage() {
const [chagePasswordPopOpen, setChagePasswordPopOpen] = useState(false)
const { session } = useContext(SessionContext) const { session } = useContext(SessionContext)
const globalLocaleState = useRecoilValue(globalLocaleStore) const globalLocaleState = useRecoilValue(globalLocaleStore)
@ -27,37 +28,12 @@ export default function MainPage() {
const [searchRadioType, setSearchRadioType] = useState('object') const [searchRadioType, setSearchRadioType] = useState('object')
// const [saleStoreId, setSaleStoreId] = 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)
const { qcastState } = useContext(QcastContext) const { qcastState } = useContext(QcastContext)
// useEffect(() => {
// if (session.pwdInitYn === 'Y') {
// fetchObjectList()
// }
// }, [session])
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)
}
})
} catch (error) {
console.error('MAIN API fetching error:', error)
}
}
// //
const handleByOnKeyUp = (e) => { const handleByOnKeyUp = (e) => {
if (e.key === 'Enter') { if (e.key === 'Enter') {
@ -68,7 +44,7 @@ export default function MainPage() {
schObjectNo: searchTxt, schObjectNo: searchTxt,
code: 'M', code: 'M',
}) })
router.push('/management/stuff') router.push('/management/stuff', { scroll: false })
} else { } else {
setSearchForm({ ...searchForm, searchValue: searchTxt, mainFlag: 'Y' }) setSearchForm({ ...searchForm, searchValue: searchTxt, mainFlag: 'Y' })
router.push('/community/faq') router.push('/community/faq')
@ -90,16 +66,22 @@ export default function MainPage() {
code: 'M', code: 'M',
}) })
router.push('/management/stuff') router.push('/management/stuff', { scroll: false })
} else { } else {
setSearchForm({ ...searchForm, searchValue: searchTxt, mainFlag: 'Y' }) setSearchForm({ ...searchForm, searchValue: searchTxt, mainFlag: 'Y' })
router.push('/community/faq') router.push('/community/faq')
} }
} }
useEffect(() => {
if (session?.pwdInitYn !== 'Y') {
setChagePasswordPopOpen(true)
}
}, [session])
return ( return (
<> <>
{(session?.pwdInitYn !== 'N' && ( {(!chagePasswordPopOpen && (
<> <>
<div className="background-bord"></div> <div className="background-bord"></div>
<div className="main-contents"> <div className="main-contents">
@ -143,7 +125,7 @@ export default function MainPage() {
</> </>
)) || ( )) || (
<> <>
<ChangePasswordPop /> <ChangePasswordPop setChagePasswordPopOpen={setChagePasswordPopOpen} />
</> </>
)} )}
</> </>

View File

@ -8,13 +8,13 @@ import { contextPopupPositionState } from '@/store/popupAtom'
export default function ColorPickerModal(props) { export default function ColorPickerModal(props) {
const contextPopupPosition = useRecoilValue(contextPopupPositionState) // const contextPopupPosition = useRecoilValue(contextPopupPositionState) //
const { isShow, setIsShow, pos = contextPopupPosition, color = '#ff0000', setColor, id, isConfig = false } = props const { isShow, setIsShow, pos = contextPopupPosition, color, setColor, id, isConfig = false } = props //color = '#ff0000'
const { getMessage } = useMessage()
const [originColor, setOriginColor] = useState(color) const [originColor, setOriginColor] = useState(color)
const { getMessage } = useMessage()
const { closePopup } = usePopup() const { closePopup } = usePopup()
useEffect(() => { useEffect(() => {
setOriginColor(color) setOriginColor(originColor)
}, [isShow]) }, [isShow])
return ( return (

View File

@ -1,7 +1,7 @@
'use client' 'use client'
import { useEffect, useState, useContext } from 'react' import { useEffect, useState, useContext } from 'react'
import { useRecoilValue } from 'recoil' import { useRecoilValue, useSetRecoilState } from 'recoil'
import { floorPlanObjectState } from '@/store/floorPlanObjectAtom' import { floorPlanObjectState } from '@/store/floorPlanObjectAtom'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { useCanvasMenu } from '@/hooks/common/useCanvasMenu' import { useCanvasMenu } from '@/hooks/common/useCanvasMenu'
@ -9,7 +9,7 @@ import SingleDatePicker from '../common/datepicker/SingleDatePicker'
import EstimateFileUploader from './EstimateFileUploader' import EstimateFileUploader from './EstimateFileUploader'
import { useAxios } from '@/hooks/useAxios' import { useAxios } from '@/hooks/useAxios'
import { globalLocaleStore } from '@/store/localeAtom' import { globalLocaleStore } from '@/store/localeAtom'
import { isNotEmptyArray, isObjectNotEmpty, queryStringFormatter } from '@/util/common-utils' import { isEmptyArray, isNotEmptyArray, isObjectNotEmpty, queryStringFormatter } from '@/util/common-utils'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { useCommonCode } from '@/hooks/common/useCommonCode' import { useCommonCode } from '@/hooks/common/useCommonCode'
import { useEstimateController } from '@/hooks/floorPlan/estimate/useEstimateController' import { useEstimateController } from '@/hooks/floorPlan/estimate/useEstimateController'
@ -18,8 +18,11 @@ import Select, { components } from 'react-select'
import { convertNumberToPriceDecimal, convertNumberToPriceDecimalToFixed } from '@/util/common-utils' import { convertNumberToPriceDecimal, convertNumberToPriceDecimalToFixed } from '@/util/common-utils'
import ProductFeaturesPop from './popup/ProductFeaturesPop' import ProductFeaturesPop from './popup/ProductFeaturesPop'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
import { correntObjectNoState } from '@/store/settingAtom'
import { useSearchParams } from 'next/navigation'
export default function Estimate({ params }) { export default function Estimate({ params }) {
const [uniqueData, setUniqueData] = useState([])
const [handlePricingFlag, setHandlePricingFlag] = useState(false) const [handlePricingFlag, setHandlePricingFlag] = useState(false)
const [specialNoteFirstFlg, setSpecialNoteFirstFlg] = useState(false) const [specialNoteFirstFlg, setSpecialNoteFirstFlg] = useState(false)
const fixedKey = 'itemKey' const fixedKey = 'itemKey'
@ -73,6 +76,15 @@ export default function Estimate({ params }) {
const { setMenuNumber } = useCanvasMenu() const { setMenuNumber } = useCanvasMenu()
/**
* objectNo 셋팅
* url로 넘어온 objectNo을 리코일에 세팅
*/
const setCurrentObjectNo = useSetRecoilState(correntObjectNoState)
const searchParams = useSearchParams()
const currentObjectNo = searchParams.get('objectNo')
setCurrentObjectNo(currentObjectNo)
// props // props
const fileUploadProps = { const fileUploadProps = {
uploadFiles: files, uploadFiles: files,
@ -119,11 +131,9 @@ export default function Estimate({ params }) {
let url = `/api/estimate/special-note-title-list` let url = `/api/estimate/special-note-title-list`
get({ url: url }).then((res) => { get({ url: url }).then((res) => {
if (isNotEmptyArray(res)) { if (isNotEmptyArray(res)) {
// ATTR001ATTR002ATTR003ATTR007ATTR009ATTR010ATTR015ATTR019
if (estimateContextState?.estimateOption) { if (estimateContextState?.estimateOption) {
res.map((row) => { res.map((row) => {
let estimateOption = estimateContextState?.estimateOption?.split('、') let estimateOption = estimateContextState?.estimateOption?.split('、')
// console.log(':::', estimateOption)
row.check = false row.check = false
estimateOption.map((row2) => { estimateOption.map((row2) => {
if (row.pkgYn === '0') { if (row.pkgYn === '0') {
@ -140,10 +150,10 @@ export default function Estimate({ params }) {
//detail //detail
//ATTR003,ATTR007 //ATTR003,ATTR007
if (row.code === 'ATTR003') { if (row.code === 'ATTR003') {
row.check = true //row.check = true
} }
if (row.code === 'ATTR007') { if (row.code === 'ATTR007') {
row.check = true //row.check = true
} }
}) })
@ -167,14 +177,20 @@ export default function Estimate({ params }) {
// set // set
useEffect(() => { useEffect(() => {
let estimateDate = dayjs(startDate).format('YYYY-MM-DD') let estimateDate
if (begin === 1) { if (startDate) {
setEstimateContextState({ estimateDate: estimateDate }) estimateDate = dayjs(startDate).format('YYYY-MM-DD')
if (begin === 1) {
setEstimateContextState({ estimateDate: estimateDate })
}
} else {
setEstimateContextState({ estimateDate: '' })
} }
}, [startDate]) }, [startDate])
useEffect(() => { useEffect(() => {
// setEstimateContextState // setEstimateContextState
if (isNotEmptyArray(specialNoteList)) { if (isNotEmptyArray(specialNoteList)) {
const liveCheckedData = specialNoteList.filter((row) => row.check === true) const liveCheckedData = specialNoteList.filter((row) => row.check === true)
@ -184,14 +200,14 @@ export default function Estimate({ params }) {
} }
const newData = data.join('、') const newData = data.join('、')
setEstimateContextState({ estimateOption: newData }) //
setEstimateContextState({ estimateOption: newData, specialNoteList: specialNoteList, uniqueData: uniqueData })
} }
}, [specialNoteList]) }, [specialNoteList])
// remark // remark
const settingShowContent = (code, event) => { const settingShowContent = (code) => {
setShowContentCode(code) setShowContentCode(code)
event.stopPropagation()
} }
// estimateContextState // estimateContextState
@ -200,10 +216,11 @@ export default function Estimate({ params }) {
let fileList = [] let fileList = []
files.map((row) => { files.map((row) => {
fileList.push(row.data) fileList.push(row.data)
setEstimateContextState({ fileList: row.data, tempFileList: fileList }) setEstimateContextState({ fileList: row.data, newFileList: fileList })
// setEstimateContextState({ fileList: row.data })
}) })
} else { } else {
setEstimateContextState({ fileList: [] }) setEstimateContextState({ fileList: [], newFileList: [] })
} }
}, [files]) }, [files])
@ -213,26 +230,49 @@ export default function Estimate({ params }) {
// //
setFiles([]) setFiles([])
setOriginFiles(estimateContextState.fileList) setOriginFiles(estimateContextState.fileList)
} else {
if (originFiles.length > 0) {
if (isEmptyArray(files)) {
let file
file = originFiles.filter((item) => item.delFlg === '0')
setEstimateContextState({
originFiles: file,
})
setOriginFiles(file)
}
}
} }
}, [estimateContextState?.fileList]) }, [estimateContextState?.fileList])
// //
const deleteOriginFile = async (objectNo, no) => { const returnOriginFile = (no) => {
const delParams = { originFiles.map((file) => {
userId: session.userId, if (file.no === no) {
objectNo: objectNo, file.delFlg = '0'
no: no,
}
await promisePost({ url: 'api/file/fileDelete', data: delParams }).then((res) => {
if (res.status === 204) {
setOriginFiles(originFiles.filter((file) => file.objectNo === objectNo && file.no !== no))
setEstimateContextState({
fileList: originFiles.filter((file) => file.objectNo === objectNo && file.no !== no),
})
alert(getMessage('plan.message.delete'))
} }
}) })
setOriginFiles((prev) => {
return [...prev]
})
setEstimateContextState({
originFiles: originFiles,
})
}
// ( ?)
const deleteOriginFile = (no) => {
originFiles.map((file) => {
if (file.no === no) {
file.delFlg = '1'
}
})
setOriginFiles((prev) => {
return [...prev]
})
setEstimateContextState({
originFiles: originFiles,
})
alert(getMessage('estimate.detail.alert.delFile'))
} }
// option && // option &&
@ -291,22 +331,66 @@ export default function Estimate({ params }) {
// option // option
const onChangeStorePriceList = (priceCd) => { const onChangeStorePriceList = (priceCd) => {
const param = {
saleStoreId: session.storeId,
sapSalesStoreCd: session.custCd,
docTpCd: priceCd,
}
// priceCd setEstimateContextState // priceCd setEstimateContextState
// showPriceCd // showPriceCd
setShowPriceCd(priceCd) setShowPriceCd(priceCd)
// return
// const param = {
// saleStoreId: session.storeId,
// sapSalesStoreCd: session.custCd,
// docTpCd: priceCd,
// }
const apiUrl = `/api/estimate/price/store-price-list?${queryStringFormatter(param)}` // const apiUrl = `/api/estimate/price/store-price-list?${queryStringFormatter(param)}`
get({ url: apiUrl }).then((res) => { // get({ url: apiUrl }).then((res) => {
if (isNotEmptyArray(res?.data)) { // if (isNotEmptyArray(res?.data)) {
setStorePriceList(res.data) // setStorePriceList(res.data)
// }
// })
}
const makeUniqueSpecialNoteCd = (itemList) => {
let pushData = []
let uniquSet = new Set()
itemList.forEach((item) => {
if (item.delFlg === '0') {
if (item.specialNoteCd) {
let splitData = item.specialNoteCd.split('、')
splitData.forEach((note) => {
if (!uniquSet.has(note)) {
uniquSet.add(note)
pushData.push(note)
}
})
setSpecialNoteFirstFlg(false)
}
} }
}) })
setUniqueData(pushData)
specialNoteList.map((item) => {
if (item.pkgYn === '1') {
let flg = '0'
let codes = item.code
for (let i = 0; i < pushData.length; i++) {
if (codes.indexOf(pushData[i]) > -1) {
flg = '1'
}
}
if (flg === '1') {
item.check = true
} else {
item.check = false
}
}
})
setEstimateContextState({
specialNoteList: specialNoteList,
uniqueData: uniqueData,
})
} }
//Pricing //Pricing
@ -351,23 +435,26 @@ export default function Estimate({ params }) {
if (isNotEmptyArray(data.data2)) { if (isNotEmptyArray(data.data2)) {
estimateContextState.itemList.map((item) => { estimateContextState.itemList.map((item) => {
let checkYn = false let checkYn = false
data.data2.map((item2) => { for (let i = 0; i < data.data2.length; i++) {
if (item2) { if (data.data2[i]) {
if (item2.itemId === item.itemId) { if (data.data2[i].itemId === item.itemId) {
updateList.push({ updateList.push({
...item, ...item,
salePrice: item2.unitPrice === null ? '0' : item2.unitPrice, openFlg: data.data2[i].unitPrice === '0.0' ? '1' : '0',
saleTotPrice: (item.amount * item2.unitPrice).toString(), salePrice: data.data2[i].unitPrice === null ? '0' : data.data2[i].unitPrice,
saleTotPrice: (item.amount * data.data2[i].unitPrice).toString(),
}) })
checkYn = true checkYn = true
break
} }
} }
}) }
if (!checkYn) { if (!checkYn) {
updateList.push({ ...item, salePrice: '0', saleTotPrice: '0' }) updateList.push({ ...item, salePrice: '0', saleTotPrice: '0' })
} }
}) })
setEstimateContextState({ setEstimateContextState({
priceCd: showPriceCd, priceCd: showPriceCd,
itemList: updateList, itemList: updateList,
@ -411,23 +498,41 @@ export default function Estimate({ params }) {
setSelection(newSelection) setSelection(newSelection)
} }
function formatNumberWithComma(number) {
return number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')
}
//PKG input //PKG input
const onChangePkgAsp = (value) => { const onChangePkgAsp = (value) => {
if (estimateContextState.estimateType === 'YJSS') { if (estimateContextState.estimateType === 'YJSS') {
let pkgAsp = Number(value.replaceAll(',', '')) let newValue = value.replace(/[^0-9.]/g, '')
if (isNaN(pkgAsp)) { if (newValue.length > 1) {
pkgAsp = 0 newValue = newValue.replace(/(^0+)/, '')
} else { if (newValue.length === 0) {
pkgAsp = pkgAsp.toLocaleString() newValue = '0'
}
} }
// PKG
const parts = newValue.split('.')
if (parts.length > 2) {
newValue = parts[0] + '.' + parts.slice(1).join('')
}
if (parts[1] && parts[1].length > 2) {
newValue = parts[0] + '.' + parts[1].substring(0, 2)
}
let pkgAsp = newValue || '0'
// PKG
let totVolKw = estimateContextState.totVolKw * 1000 let totVolKw = estimateContextState.totVolKw * 1000
let pkgTotPrice = pkgAsp.replaceAll(',', '') * totVolKw * 1000 let pkgTotPrice = parseFloat(pkgAsp?.replaceAll(',', '')) * totVolKw * 1000
// pkgAsp = formatNumberWithComma(parseFloat(pkgAsp).toFixed(2))
setEstimateContextState({ setEstimateContextState({
pkgAsp: pkgAsp, pkgAsp: pkgAsp,
pkgTotPrice: pkgTotPrice.toFixed(3), pkgTotPrice: pkgTotPrice.toFixed(2),
}) })
// //
setItemChangeYn(true) setItemChangeYn(true)
@ -438,6 +543,7 @@ export default function Estimate({ params }) {
const onChangeAmount = (value, dispOrder, index) => { const onChangeAmount = (value, dispOrder, index) => {
//itemChangeFlg = 1, partAdd = 0 //itemChangeFlg = 1, partAdd = 0
let amount = Number(value.replace(/[^0-9]/g, '').replaceAll(',', '')) let amount = Number(value.replace(/[^0-9]/g, '').replaceAll(',', ''))
if (isNaN(amount)) { if (isNaN(amount)) {
amount = '0' amount = '0'
} else { } else {
@ -511,7 +617,7 @@ export default function Estimate({ params }) {
let updateList = [] let updateList = []
let updates = {} let updates = {}
get({ url: apiUrl }).then((res) => { get({ url: apiUrl }).then((res) => {
console.log('아이템디테일::::::::', res) // console.log('::::::::', res)
updates.objectNo = objectNo updates.objectNo = objectNo
updates.planNo = planNo updates.planNo = planNo
updates.itemId = res.itemId updates.itemId = res.itemId
@ -526,22 +632,27 @@ export default function Estimate({ params }) {
updates.pkgMaterialFlg = res.pkgMaterialFlg updates.pkgMaterialFlg = res.pkgMaterialFlg
updates.pnowW = res.pnowW updates.pnowW = res.pnowW
updates.salePrice = res.salePrice updates.salePrice = res.salePrice
// updates.salePrice = ''
updates.specification = res.specification updates.specification = res.specification
updates.unit = res.unit updates.unit = res.unit
updates.specialNoteCd = res.spnAttrCds updates.specialNoteCd = res.spnAttrCds
updates.itemGroup = res.itemGroup updates.itemGroup = res.itemGroup
updates.delFlg = '0' // 0 updates.delFlg = '0' // 0
updates.saleTotPrice = (res.salePrice * estimateContextState.itemList[index].amount).toString() updates.saleTotPrice = (res.salePrice * estimateContextState.itemList[index].amount).toString()
// updates.saleTotPrice = ''
updates.amount = '' updates.amount = ''
updates.openFlg = res.openFlg
if (estimateContextState.estimateType === 'YJSS') { if (estimateContextState.estimateType === 'YJSS') {
if (res.pkgMaterialFlg === '0') { if (res.pkgMaterialFlg === '0') {
updates.showSalePrice = '0' updates.showSalePrice = '0'
updates.showSaleTotPrice = '0' updates.showSaleTotPrice = '0'
} }
} else {
if (res.openFlg === '1') {
updates.showSalePrice = '0'
updates.showSaleTotPrice = '0'
}
} }
//104671 //104671
let bomList = res.itemBomList let bomList = res.itemBomList
@ -557,7 +668,6 @@ export default function Estimate({ params }) {
} }
} }
} else if (item.paDispOrder === dispOrder) { } else if (item.paDispOrder === dispOrder) {
//
return { ...item, delFlg: '1' } return { ...item, delFlg: '1' }
} else { } else {
return item return item
@ -574,19 +684,23 @@ export default function Estimate({ params }) {
bomItem.salePrice = '0' bomItem.salePrice = '0'
bomItem.saleTotPrice = '0' bomItem.saleTotPrice = '0'
bomItem.unitPrice = '0' bomItem.unitPrice = '0'
bomItem.showSalePrice = '0'
} else { } else {
bomItem.dispOrder = (index + 1 + Number(dispOrder)).toString() bomItem.dispOrder = (index + 1 + Number(dispOrder)).toString()
bomItem.paDispOrder = dispOrder bomItem.paDispOrder = dispOrder
bomItem.salePrice = '0' bomItem.salePrice = '0'
bomItem.saleTotPrice = '0' bomItem.saleTotPrice = '0'
bomItem.unitPrice = '0' bomItem.unitPrice = '0'
bomItem.showSalePrice = '0'
} }
bomItem.delFlg = '0' bomItem.delFlg = '0'
bomItem.objectNo = objectNo bomItem.objectNo = objectNo
bomItem.planNo = planNo bomItem.planNo = planNo
bomItem.addFlg = true // addFlg
}) })
updateList = updateList.filter((item) => item.delFlg === '0')
setEstimateContextState({ setEstimateContextState({
itemList: [...updateList, ...bomList], itemList: [...updateList, ...bomList],
}) })
@ -603,6 +717,9 @@ export default function Estimate({ params }) {
// //
const removeItem = () => { const removeItem = () => {
const array = [...selection] const array = [...selection]
if (isEmptyArray(array)) {
return alert(getMessage('estimate.detail.alert.selectDelItem'))
}
let delList = [] let delList = []
estimateContextState.itemList.filter((row) => { estimateContextState.itemList.filter((row) => {
array.map((row2) => { array.map((row2) => {
@ -644,135 +761,147 @@ export default function Estimate({ params }) {
useEffect(() => { useEffect(() => {
if (itemChangeYn) { if (itemChangeYn) {
let totAmount = 0 let totals = {
let totVolKw = 0 totAmount: 0,
let supplyPrice = 0 totVolKw: 0,
let vatPrice = 0 supplyPrice: 0,
let totPrice = 0 vatPrice: 0,
let addSupplyPrice = 0 totPrice: 0,
if (estimateContextState.estimateType === 'YJOD') { addSupplyPrice: 0,
estimateContextState.itemList.sort((a, b) => { pkgTotPrice: 0,
return a.dispOrder - b.dispOrder }
})
console.log('YJOD 토탈만들어주기::::::::::', estimateContextState.itemList)
let pushData = [] const calculateYJODTotals = (itemList) => {
let uniquSet = new Set() itemList.sort((a, b) => a.dispOrder - b.dispOrder)
makeUniqueSpecialNoteCd(itemList)
estimateContextState.itemList.forEach((item) => { itemList.forEach((item) => {
if (item.delFlg === '1') {
if (item.specialNoteCd) {
let splitData = item.specialNoteCd.split('、')
splitData.forEach((note) => {
if (!uniquSet.has(note)) {
uniquSet.add(note)
pushData.push(note)
}
})
setSpecialNoteFirstFlg(false)
}
}
})
estimateContextState.itemList.map((item) => {
delete item.showSalePrice delete item.showSalePrice
delete item.showSaleTotPrice delete item.showSaleTotPrice
if (item.delFlg === '0') { if (item.delFlg === '0') {
let amount = Number(item?.amount?.replace(/[^0-9]/g, '').replaceAll(',', '')) let amount = Number(item.amount?.replace(/[^0-9]/g, '').replaceAll(',', '')) || 0
if (isNaN(amount)) { let price
amount = '0' if (amount === 0) {
}
let price = Number(item?.saleTotPrice?.replaceAll(',', ''))
if (isNaN(price)) {
price = 0 price = 0
} else {
price = Number(item.saleTotPrice?.replaceAll(',', '')) || 0
} }
if (item.moduleFlg === '1') { if (item.moduleFlg === '1') {
//(Kw) 1
const volKw = (item.pnowW * amount) / 1000 const volKw = (item.pnowW * amount) / 1000
// const volKw = item.pnowW * amount totals.totVolKw += volKw
totVolKw += volKw }
totals.supplyPrice += price
totals.totAmount += amount
if (item.paDispOrder) {
item.showSalePrice = '0'
item.showSaleTotPrice = '0'
}
if (item.openFlg === '1') {
item.showSalePrice = '0'
item.showSaleTotPrice = '0'
} }
// const price
totAmount += amount
supplyPrice += price
} }
}) })
vatPrice = supplyPrice * 0.1 totals.vatPrice = totals.supplyPrice * 0.1
totPrice = supplyPrice + vatPrice totals.totPrice = totals.supplyPrice + totals.vatPrice
}
setEstimateContextState({ const calculateYJSSTotals = (itemList) => {
totAmount: totAmount, itemList.sort((a, b) => a.dispOrder - b.dispOrder)
totVolKw: totVolKw.toFixed(3), makeUniqueSpecialNoteCd(itemList)
supplyPrice: supplyPrice.toFixed(3), itemList.forEach((item) => {
vatPrice: vatPrice.toFixed(3),
totPrice: totPrice.toFixed(3),
})
} else {
//YJSS
console.log('YJSS 토탈만들어주기::::::::::', estimateContextState.itemList)
estimateContextState.itemList.sort((a, b) => {
return a.dispOrder - b.dispOrder
})
estimateContextState.itemList.map((item) => {
if (item.delFlg === '0') { if (item.delFlg === '0') {
let amount = Number(item.amount?.replace(/[^0-9]/g, '').replaceAll(',', '')) let amount = Number(item.amount?.replace(/[^0-9]/g, '').replaceAll(',', '')) || 0
let salePrice = Number(item.salePrice?.replaceAll(',', '')) let salePrice
let saleTotPrice = Number(item.saleTotPrice?.replaceAll(',', ''))
if (isNaN(amount)) {
amount = '0'
}
if (isNaN(saleTotPrice)) {
saleTotPrice = 0
}
if (isNaN(salePrice)) {
salePrice = 0
}
if (item.moduleFlg === '1') { if (item.moduleFlg === '1') {
//(Kw) 1
const volKw = (item.pnowW * amount) / 1000 const volKw = (item.pnowW * amount) / 1000
// const volKw = item.pnowW * amount totals.totVolKw += volKw
totVolKw += volKw
} }
setEstimateContextState({ if (amount === 0) {
pkgTotPrice: estimateContextState.pkgAsp.replaceAll(',', '') * totVolKw * 1000, salePrice = 0
}) } else {
//pkgTotPrice salePrice = Number(item.salePrice?.replaceAll(',', '')) || 0
// const saleTotPrice }
totAmount += amount
totals.totAmount += amount
if (item.pkgMaterialFlg === '1') { if (item.pkgMaterialFlg === '1') {
const pkgPrice = amount * salePrice const saleTotPrice = amount * salePrice
// totals.addSupplyPrice += saleTotPrice
//YJSS PKG(1) * (supplyPrice)
addSupplyPrice += pkgPrice
} }
if (!item.paDispOrder) { if (!item.paDispOrder) {
//paDispOrder
if (item.pkgMaterialFlg === '0') { if (item.pkgMaterialFlg === '0') {
item.showSalePrice = '0' item.showSalePrice = '0'
item.showSaleTotPrice = '0' item.showSaleTotPrice = '0'
} }
} else {
item.showSalePrice = '0'
item.showSaleTotPrice = '0'
}
if (item.openFlg === '1') {
item.showSalePrice = '0'
item.showSaleTotPrice = '0'
} }
} }
}) })
supplyPrice = addSupplyPrice + Number(estimateContextState.pkgTotPrice / 1000) let pkgAsp = estimateContextState.pkgAsp ? Number(estimateContextState.pkgAsp.replaceAll(',', '')) : 0
vatPrice = supplyPrice * 0.1
totPrice = supplyPrice + vatPrice totals.pkgTotPrice = pkgAsp * totals.totVolKw * 1000
totals.supplyPrice = totals.addSupplyPrice + totals.pkgTotPrice
totals.vatPrice = totals.supplyPrice * 0.1
totals.totPrice = totals.supplyPrice + totals.vatPrice
}
if (estimateContextState.estimateType === 'YJOD') {
calculateYJODTotals(estimateContextState.itemList)
setEstimateContextState({ setEstimateContextState({
totAmount: totAmount, totAmount: totals.totAmount,
totVolKw: totVolKw.toFixed(3), totVolKw: totals.totVolKw.toFixed(2),
supplyPrice: supplyPrice.toFixed(3), supplyPrice: totals.supplyPrice.toFixed(0), //
vatPrice: vatPrice.toFixed(3), vatPrice: totals.vatPrice.toFixed(0), //
totPrice: totPrice.toFixed(3), totPrice: totals.totPrice.toFixed(0), //
})
} else if (estimateContextState.estimateType === 'YJSS') {
calculateYJSSTotals(estimateContextState.itemList)
setEstimateContextState({
pkgTotPrice: totals.pkgTotPrice,
totAmount: totals.totAmount,
totVolKw: totals.totVolKw.toFixed(2),
supplyPrice: totals.supplyPrice.toFixed(0), //
vatPrice: totals.vatPrice.toFixed(0), //
totPrice: totals.totPrice.toFixed(0), //
}) })
} }
setItemChangeYn(false) setItemChangeYn(false)
} else {
estimateContextState.itemList.forEach((item) => {
if (estimateContextState.estimateType === 'YJSS' && !item.paDispOrder && item.pkgMaterialFlg === '0') {
item.showSalePrice = '0'
item.showSaleTotPrice = '0'
}
if (estimateContextState.estimateType === 'YJSS' && item.openFlg === '1') {
item.showSalePrice = '0'
item.showSaleTotPrice = '0'
}
if (estimateContextState.estimateType === 'YJSS' && item.paDispOrder) {
item.showSalePrice = '0'
item.showSaleTotPrice = '0'
}
})
estimateContextState.itemList.forEach((item) => {
if (estimateContextState.estimateType === 'YJOD' && item.openFlg === '1') {
item.showSalePrice = '0'
item.showSaleTotPrice = '0'
}
if (estimateContextState.estimateType === 'YJOD' && item.paDispOrder) {
item.showSalePrice = '0'
item.showSaleTotPrice = '0'
}
})
} }
}, [itemChangeYn, estimateContextState.itemList]) }, [itemChangeYn, estimateContextState.itemList])
@ -966,18 +1095,15 @@ export default function Estimate({ params }) {
} }
// //
let constructSpecificationMulti = estimateContextState?.constructSpecificationMulti?.split('、') let constructSpecificationMulti = estimateContextState?.constructSpecificationMulti?.split('、')
return ( return (
<> <div className={`form-flex-wrap ${style}`} key={`roof_${row}`}>
<div className={`form-flex-wrap ${style}`} key={fixedKey}> <div className="input-wrap mr5" style={{ width: '610px' }}>
<div className="input-wrap mr5" style={{ width: '610px' }} key={`roof${index}`}> <input type="text" className="input-light" value={roofList} readOnly />
<input type="text" className="input-light" value={roofList} readOnly />
</div>
<div className="input-wrap" style={{ width: '200px' }}>
<input type="text" className="input-light" value={constructSpecificationMulti[index]} readOnly />
</div>
</div> </div>
</> <div className="input-wrap" style={{ width: '200px' }}>
<input type="text" className="input-light" value={constructSpecificationMulti[index]} readOnly />
</div>
</div>
) )
})} })}
</td> </td>
@ -987,7 +1113,7 @@ export default function Estimate({ params }) {
<th>{getMessage('estimate.detail.remarks')}</th> <th>{getMessage('estimate.detail.remarks')}</th>
<td colSpan={3}> <td colSpan={3}>
<div className="input-wrap"> <div className="input-wrap">
<input type="text" className="input-light" defaultValue={estimateContextState?.remarks} onBlur={handleBlurRemarks} /> <input type="text" className="input-light" defaultValue={estimateContextState?.remarks || ''} onBlur={handleBlurRemarks} />
</div> </div>
</td> </td>
</tr> </tr>
@ -1064,18 +1190,38 @@ export default function Estimate({ params }) {
<ul className="file-list"> <ul className="file-list">
{originFiles.map((originFile) => { {originFiles.map((originFile) => {
return ( return (
<li className="file-item" key={uuidv4()}> <li className="file-item" key={originFile.no}>
<span onClick={() => handleEstimateFileDownload(originFile)}> {/* <li className="file-item" key={uuidv4()}> */}
{originFile.faileName} <div className="file-item-wrap">
<button <span
type="button" style={{ display: originFile.delFlg === '0' ? '' : 'none' }}
className="delete" onClick={() => handleEstimateFileDownload(originFile)}
onClick={(e) => { >
deleteOriginFile(originFile.objectNo, originFile.no) {originFile.faileName}
e.stopPropagation() <button
}} type="button"
></button> className="delete"
</span> onClick={(e) => {
deleteOriginFile(originFile.no)
e.stopPropagation()
}}
></button>
</span>
<div className="return-wrap" style={{ display: originFile.delFlg !== '0' ? '' : 'none' }}>
<span className="return">{originFile.faileName}</span>
<button
type="button"
className="return-btn"
onClick={(e) => {
returnOriginFile(originFile.no)
e.stopPropagation()
}}
>
<i className="return-ico"></i>
{getMessage('estimate.detail.fileList2.btn.return')}
</button>
</div>
</div>
</li> </li>
) )
})} })}
@ -1108,29 +1254,31 @@ export default function Estimate({ params }) {
{specialNoteList.length > 0 && {specialNoteList.length > 0 &&
specialNoteList.map((row) => { specialNoteList.map((row) => {
return ( return (
<div // <div key={uuidv4()} className="special-note-check-item">
key={uuidv4()} <div key={row.code} className="special-note-check-item">
className="special-note-check-item" <div className="special-note-check-box">
onClick={(event) => { <div className="d-check-box light">
// settingShowContent(row.code, event) <input
}} type="checkbox"
> id={row.code}
<div className="d-check-box light"> checked={!!row.check}
<input disabled={row.code === 'ATTR001' || row.pkgYn === '1' ? true : false}
type="checkbox" onChange={() => {
id={row.code} setSpecialNoteList((specialNote) =>
checked={!!row.check} specialNote.map((temp) => (temp.code === row.code ? { ...temp, check: !temp.check } : temp)),
disabled={row.code === 'ATTR001' ? true : false} )
onChange={(event) => { }}
setSpecialNoteList((specialNote) => />
specialNote.map((temp) => (temp.code === row.code ? { ...temp, check: !temp.check } : temp)), <label htmlFor={row.code}></label>
) </div>
settingShowContent(row.code, event) <span
className="check-name"
onClick={() => {
settingShowContent(row.code)
}} }}
/> >
<label htmlFor={row.code}> {row.codeNm}
{row.codeNm} / {row.code} </span>
</label>
</div> </div>
</div> </div>
) )
@ -1146,7 +1294,7 @@ export default function Estimate({ params }) {
if (isObjectNotEmpty(showcontent)) { if (isObjectNotEmpty(showcontent)) {
return ( return (
<dl key={uuidv4()}> <dl key={row.code}>
<dt>{showcontent.codeNm}</dt> <dt>{showcontent.codeNm}</dt>
<dd dangerouslySetInnerHTML={{ __html: showcontent.remarks }} style={{ whiteSpace: 'pre-wrap' }}></dd> <dd dangerouslySetInnerHTML={{ __html: showcontent.remarks }} style={{ whiteSpace: 'pre-wrap' }}></dd>
</dl> </dl>
@ -1161,12 +1309,23 @@ export default function Estimate({ params }) {
} }
}) })
}) })
return pushData.map((item) => ( //
<dl key={uuidv4()}> let filterData = pushData.filter((item) => uniqueData.includes(item.code))
<dt>{item.codeNm}</dt> if (filterData.length > 0) {
<dd dangerouslySetInnerHTML={{ __html: item.remarks }} style={{ whiteSpace: 'pre-wrap' }}></dd> return filterData.map((item) => (
</dl> <dl key={item.code}>
)) <dt>{item.codeNm}</dt>
<dd dangerouslySetInnerHTML={{ __html: item.remarks }} style={{ whiteSpace: 'pre-wrap' }}></dd>
</dl>
))
} else {
return pushData.map((item) => (
<dl key={item.code}>
<dt>{item.codeNm}</dt>
<dd dangerouslySetInnerHTML={{ __html: item.remarks }} style={{ whiteSpace: 'pre-wrap' }}></dd>
</dl>
))
}
} }
} }
})} })}
@ -1192,19 +1351,19 @@ export default function Estimate({ params }) {
</div> </div>
<div className="estimate-box"> <div className="estimate-box">
<div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.totVolKw')}</div> <div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.totVolKw')}</div>
<div className="estimate-name blue">{convertNumberToPriceDecimalToFixed(estimateContextState?.totVolKw, 3)}</div> <div className="estimate-name blue">{convertNumberToPriceDecimalToFixed(estimateContextState?.totVolKw, 2)}</div>
</div> </div>
<div className="estimate-box"> <div className="estimate-box">
<div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.supplyPrice')}</div> <div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.supplyPrice')}</div>
<div className="estimate-name blue">{convertNumberToPriceDecimal(estimateContextState?.supplyPrice)}</div> <div className="estimate-name blue">{convertNumberToPriceDecimalToFixed(estimateContextState?.supplyPrice, 0)}</div>
</div> </div>
<div className="estimate-box"> <div className="estimate-box">
<div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.vatPrice')}</div> <div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.vatPrice')}</div>
<div className="estimate-name blue">{convertNumberToPriceDecimal(estimateContextState?.vatPrice)}</div> <div className="estimate-name blue">{convertNumberToPriceDecimalToFixed(estimateContextState?.vatPrice, 0)}</div>
</div> </div>
<div className="estimate-box"> <div className="estimate-box">
<div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.totPrice')}</div> <div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.totPrice')}</div>
<div className="estimate-name red">{convertNumberToPriceDecimal(estimateContextState?.totPrice)}</div> <div className="estimate-name red">{convertNumberToPriceDecimalToFixed(estimateContextState?.totPrice, 0)}</div>
</div> </div>
</div> </div>
</div> </div>
@ -1238,7 +1397,7 @@ export default function Estimate({ params }) {
</div> </div>
</td> </td>
<th>{getMessage('estimate.detail.sepcialEstimateProductInfo.pkgWeight')}</th> <th>{getMessage('estimate.detail.sepcialEstimateProductInfo.pkgWeight')}</th>
<td>{convertNumberToPriceDecimalToFixed(estimateContextState?.totVolKw, 3)}</td> <td>{convertNumberToPriceDecimalToFixed(estimateContextState?.totVolKw, 2)}</td>
<th>{getMessage('estimate.detail.sepcialEstimateProductInfo.pkgPrice')}</th> <th>{getMessage('estimate.detail.sepcialEstimateProductInfo.pkgPrice')}</th>
<td>{convertNumberToPriceDecimal(estimateContextState?.pkgTotPrice)}</td> <td>{convertNumberToPriceDecimal(estimateContextState?.pkgTotPrice)}</td>
</tr> </tr>
@ -1253,14 +1412,18 @@ export default function Estimate({ params }) {
<div className="select-wrap"> <div className="select-wrap">
{session?.storeLvl === '1' ? ( {session?.storeLvl === '1' ? (
<select <select
key={uuidv4()}
className="select-light" className="select-light"
onChange={(e) => { onChange={(e) => {
onChangeStorePriceList(e.target.value) onChangeStorePriceList(e.target.value)
}} }}
value={showPriceCd} value={showPriceCd}
> >
{storePriceList.length > 0 && storePriceList.map((row) => <option value={row.priceCd}>{row.priceNm}</option>)} {storePriceList.length > 0 &&
storePriceList.map((row) => (
<option key={row.priceCd} value={row.priceCd}>
{row.priceNm}
</option>
))}
</select> </select>
) : ( ) : (
<select key={uuidv4()} className="select-light"> <select key={uuidv4()} className="select-light">
@ -1322,9 +1485,9 @@ export default function Estimate({ params }) {
<table> <table>
<colgroup> <colgroup>
<col width={50} /> <col width={50} />
<col width={50} /> <col width={60} />
<col /> <col />
<col width={250} /> <col width={300} />
<col width={90} /> <col width={90} />
<col width={80} /> <col width={80} />
<col width={170} /> <col width={170} />
@ -1333,7 +1496,6 @@ export default function Estimate({ params }) {
<thead> <thead>
<tr> <tr>
<th> <th>
{/* <div className="d-check-box pop no-text" style={{ display: 'none' }}> */}
<div className="d-check-box pop no-text"> <div className="d-check-box pop no-text">
<input type="checkbox" id="ch97" checked={isSelectedAll()} onChange={onChangeSelectAll} /> <input type="checkbox" id="ch97" checked={isSelectedAll()} onChange={onChangeSelectAll} />
<label htmlFor="ch97"></label> <label htmlFor="ch97"></label>
@ -1371,7 +1533,7 @@ export default function Estimate({ params }) {
<div className="form-flex-wrap"> <div className="form-flex-wrap">
<div className="select-wrap mr5"> <div className="select-wrap mr5">
<Select <Select
id="long-value-select1" name="long-value-select1"
instanceId="long-value-select1" instanceId="long-value-select1"
className="react-select-custom" className="react-select-custom"
classNamePrefix="custom" classNamePrefix="custom"
@ -1439,11 +1601,17 @@ export default function Estimate({ params }) {
className="input-light al-r" className="input-light al-r"
value={convertNumberToPriceDecimal(item?.showSalePrice === '0' ? null : item?.salePrice?.replaceAll(',', ''))} value={convertNumberToPriceDecimal(item?.showSalePrice === '0' ? null : item?.salePrice?.replaceAll(',', ''))}
disabled={ disabled={
estimateContextState?.estimateType === 'YJSS' item.openFlg === '1'
? item?.paDispOrder ? true
? true : estimateContextState?.estimateType === 'YJSS'
: item.pkgMaterialFlg !== '1' ? item?.paDispOrder
: item.itemId === '' || !!item?.paDispOrder ? true
: item.pkgMaterialFlg !== '1'
: item.itemId === '' || !!item?.paDispOrder
? true
: item.openFlg === '1'
? true
: false
} }
onChange={(e) => { onChange={(e) => {
onChangeSalePrice(e.target.value, item.dispOrder, index) onChangeSalePrice(e.target.value, item.dispOrder, index)
@ -1451,13 +1619,23 @@ export default function Estimate({ params }) {
maxLength={12} maxLength={12}
/> />
</div> </div>
{/* <div className="btn-area"> {item.openFlg === '1' && (
<span className="tb_ico open_check"></span> <div className="btn-area">
</div> */} <span className="tb_ico open_check"></span>
</div>
)}
</div> </div>
</td> </td>
<td className="al-r"> <td className="al-r">
{convertNumberToPriceDecimal(item?.showSaleTotPrice === '0' ? null : item?.saleTotPrice?.replaceAll(',', ''))} {convertNumberToPriceDecimal(
item?.showSaleTotPrice === '0'
? null
: item?.amount === ''
? null
: item?.saleTotPrice === '0'
? null
: item?.saleTotPrice?.replaceAll(',', ''),
)}
</td> </td>
</tr> </tr>
) )

View File

@ -11,7 +11,7 @@ export default function EstimateCopyPop({ planNo, setEstimateCopyPopupOpen }) {
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { get } = useAxios() const { get } = useAxios()
const { handleEstimateCopy, state } = useEstimateController(planNo) const { handleEstimateCopy, estimateContextState } = useEstimateController(planNo)
const { session } = useContext(SessionContext) const { session } = useContext(SessionContext)
@ -105,10 +105,10 @@ export default function EstimateCopyPop({ planNo, setEstimateCopyPopupOpen }) {
}, [planNo]) }, [planNo])
useEffect(() => { useEffect(() => {
if (state?.charger) { if (estimateContextState?.charger) {
setCopyReceiveUser(state.charger) setCopyReceiveUser(estimateContextState.charger)
} }
}, [state.charger]) }, [estimateContextState?.charger])
//T01 1 //T01 1
const onInputChange = (key) => { const onInputChange = (key) => {
@ -282,7 +282,7 @@ export default function EstimateCopyPop({ planNo, setEstimateCopyPopupOpen }) {
type="text" type="text"
className="input-light" className="input-light"
required required
defaultValue={state?.charger} defaultValue={estimateContextState?.charger}
onChange={(e) => { onChange={(e) => {
setCopyReceiveUser(e.target.value) setCopyReceiveUser(e.target.value)
}} }}

View File

@ -40,9 +40,8 @@ export default function ProductFeaturesPop({ popShowSpecialNoteList, showProduct
{showSpecialNoteList.length > 0 && {showSpecialNoteList.length > 0 &&
showSpecialNoteList.map((row) => { showSpecialNoteList.map((row) => {
return ( return (
<dl> <dl key={row.code}>
<dt>{row.codeNm}</dt> <dt>{row.codeNm}</dt>
{/* <dd dangerouslySetInnerHTML={{ __html: row.remarks }}></dd> */}
<dd dangerouslySetInnerHTML={{ __html: row.remarks }} style={{ whiteSpace: 'pre-wrap' }}></dd> <dd dangerouslySetInnerHTML={{ __html: row.remarks }} style={{ whiteSpace: 'pre-wrap' }}></dd>
</dl> </dl>
) )

View File

@ -15,7 +15,6 @@ 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' import ImgLoad from '@/components/floor-plan/modal/ImgLoad'
import { EventContext } from '@/app/floor-plan/EventProvider'
export default function CanvasFrame() { export default function CanvasFrame() {
const canvasRef = useRef(null) const canvasRef = useRef(null)

View File

@ -20,7 +20,7 @@ export default function CanvasLayout({ children }) {
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { swalFire } = useSwal() const { swalFire } = useSwal()
const { plans, initCanvasPlans, modifiedPlans, loadCanvasPlanData, handleCurrentPlan, handleAddPlan, handleDeletePlan } = usePlan() const { plans, modifiedPlans, loadCanvasPlanData, handleCurrentPlan, handleAddPlan, handleDeletePlan } = usePlan()
useEffect(() => { useEffect(() => {
loadCanvasPlanData(session.userId, objectNo, pid) loadCanvasPlanData(session.userId, objectNo, pid)
@ -34,10 +34,9 @@ export default function CanvasLayout({ children }) {
<button <button
key={`plan-${plan.id}`} key={`plan-${plan.id}`}
className={`canvas-page-box ${plan.isCurrent === true ? 'on' : ''}`} className={`canvas-page-box ${plan.isCurrent === true ? 'on' : ''}`}
onClick={() => handleCurrentPlan(session.userId, plan.id)} onClick={() => handleCurrentPlan(plan.id)}
> >
<span> <span>
{!initCanvasPlans.some((initCanvasPlans) => initCanvasPlans.id === plan.id) && 'New '}
{`Plan ${plan.ordering}`} {`Plan ${plan.ordering}`}
{modifiedPlans.some((modifiedPlan) => modifiedPlan === plan.id) && ' [ M ]'} {modifiedPlans.some((modifiedPlan) => modifiedPlan === plan.id) && ' [ M ]'}
</span> </span>
@ -45,10 +44,7 @@ export default function CanvasLayout({ children }) {
className="close" className="close"
onClick={(e) => onClick={(e) =>
swalFire({ swalFire({
text: text: `Plan ${plan.ordering} ` + getMessage('plan.message.confirm.delete'),
(!initCanvasPlans.some((initCanvasPlans) => initCanvasPlans.id === plan.id) ? 'New ' : '') +
`Plan ${plan.ordering} ` +
getMessage('plan.message.confirm.delete'),
type: 'confirm', type: 'confirm',
confirmFn: () => { confirmFn: () => {
handleDeletePlan(e, plan.id) handleDeletePlan(e, plan.id)

View File

@ -40,6 +40,8 @@ import EstimateCopyPop from '../estimate/popup/EstimateCopyPop'
import { pwrGnrSimTypeState } from '@/store/simulatorAtom' import { pwrGnrSimTypeState } from '@/store/simulatorAtom'
import { useAxios } from '@/hooks/useAxios' import { useAxios } from '@/hooks/useAxios'
import { useModuleBasicSetting } from '@/hooks/module/useModuleBasicSetting'
export default function CanvasMenu(props) { export default function CanvasMenu(props) {
const { menuNumber, setMenuNumber } = props const { menuNumber, setMenuNumber } = props
const pathname = usePathname() const pathname = usePathname()
@ -49,7 +51,7 @@ export default function CanvasMenu(props) {
const [type, setType] = useRecoilState(menuTypeState) const [type, setType] = useRecoilState(menuTypeState)
const [verticalHorizontalMode, setVerticalHorizontalMode] = useRecoilState(verticalHorizontalModeState) const [verticalHorizontalMode, setVerticalHorizontalMode] = useRecoilState(verticalHorizontalModeState)
const [appMessageState, setAppMessageState] = useRecoilState(appMessageStore) const [appMessageState, setAppMessageState] = useRecoilState(appMessageStore)
const setCurrentMenu = useSetRecoilState(currentMenuState) const [currentMenu, setCurrentMenu] = useRecoilState(currentMenuState)
const setOuterLinePoints = useSetRecoilState(outerLinePointsState) const setOuterLinePoints = useSetRecoilState(outerLinePointsState)
const setPlacementPoints = useSetRecoilState(placementShapeDrawingPointsState) const setPlacementPoints = useSetRecoilState(placementShapeDrawingPointsState)
const canvasSetting = useRecoilValue(canvasSettingState) const canvasSetting = useRecoilValue(canvasSettingState)
@ -66,7 +68,7 @@ export default function CanvasMenu(props) {
const [estimateCopyPopupOpen, setEstimateCopyPopupOpen] = useState(false) const [estimateCopyPopupOpen, setEstimateCopyPopupOpen] = useState(false)
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { currentCanvasPlan, saveCanvas } = usePlan() const { saveCanvas } = usePlan()
const { swalFire } = useSwal() const { swalFire } = useSwal()
const { initEvent, addCanvasMouseEventListener, addDocumentEventListener } = useEvent() const { initEvent, addCanvasMouseEventListener, addDocumentEventListener } = useEvent()
// const { initEvent, addCanvasMouseEventListener, addDocumentEventListener } = useContext(EventContext) // const { initEvent, addCanvasMouseEventListener, addDocumentEventListener } = useContext(EventContext)
@ -74,6 +76,7 @@ export default function CanvasMenu(props) {
const { commonFunctions } = useCommonUtils() const { commonFunctions } = useCommonUtils()
const SelectOption = [{ name: '瓦53A' }, { name: '瓦53A' }] const SelectOption = [{ name: '瓦53A' }, { name: '瓦53A' }]
const { floorPlanState, setFloorPlanState } = useContext(FloorPlanContext) const { floorPlanState, setFloorPlanState } = useContext(FloorPlanContext)
const { restoreModuleInstArea } = useModuleBasicSetting()
const onClickNav = (menu) => { const onClickNav = (menu) => {
setMenuNumber(menu.index) setMenuNumber(menu.index)
@ -88,6 +91,10 @@ export default function CanvasMenu(props) {
setType('outline') setType('outline')
break break
case 3: case 3:
if (type === 'module') {
restoreModuleInstArea()
}
setType('surface') setType('surface')
break break
case 4: case 4:
@ -111,13 +118,7 @@ export default function CanvasMenu(props) {
// (btn08) // (btn08)
const handleSaveCanvas = async () => { const handleSaveCanvas = async () => {
// swalFire({ await saveCanvas()
// text: `${currentCanvasPlan.name} ` + getMessage('plan.message.confirm.save'),
// type: 'confirm',
// confirmFn: async () => {
await saveCanvas(sessionState.userId)
// },
// })
} }
const [placementInitialId, setPlacementInitialId] = useState(uuidv4()) const [placementInitialId, setPlacementInitialId] = useState(uuidv4())
@ -317,11 +318,11 @@ export default function CanvasMenu(props) {
<div className="ico-btn-from"> <div className="ico-btn-from">
<button className="btn-frame gray ico-flx" onClick={() => setEstimatePopupOpen(true)}> <button className="btn-frame gray ico-flx" onClick={() => setEstimatePopupOpen(true)}>
<span className="ico ico01"></span> <span className="ico ico01"></span>
<span>{getMessage('plan.menu.estimate.docDown')}</span> <span className="name">{getMessage('plan.menu.estimate.docDown')}</span>
</button> </button>
<button className="btn-frame gray ico-flx" onClick={handleEstimateSubmit}> <button className="btn-frame gray ico-flx" onClick={handleEstimateSubmit}>
<span className="ico ico02"></span> <span className="ico ico02"></span>
<span>{getMessage('plan.menu.estimate.save')}</span> <span className="name">{getMessage('plan.menu.estimate.save')}</span>
</button> </button>
<button <button
className="btn-frame gray ico-flx" className="btn-frame gray ico-flx"
@ -330,7 +331,7 @@ export default function CanvasMenu(props) {
}} }}
> >
<span className="ico ico03"></span> <span className="ico ico03"></span>
<span>{getMessage('plan.menu.estimate.reset')}</span> <span className="name">{getMessage('plan.menu.estimate.reset')}</span>
</button> </button>
{estimateRecoilState?.docNo !== null && (sessionState.storeId === 'T01' || sessionState.storeLvl === '1') && ( {estimateRecoilState?.docNo !== null && (sessionState.storeId === 'T01' || sessionState.storeLvl === '1') && (
@ -341,9 +342,13 @@ export default function CanvasMenu(props) {
}} }}
> >
<span className="ico ico04"></span> <span className="ico ico04"></span>
<span>{getMessage('plan.menu.estimate.copy')}</span> <span className="name">{getMessage('plan.menu.estimate.copy')}</span>
</button> </button>
)} )}
<button className="btn-frame gray ico-flx">
<span className="ico ico05"></span>
<span className="name">{getMessage('plan.menu.estimate.unLock')}</span>
</button>
</div> </div>
</> </>
)} )}

View File

@ -8,6 +8,9 @@ import { useRefFiles } from '@/hooks/common/useRefFiles'
import { usePlan } from '@/hooks/usePlan' import { usePlan } from '@/hooks/usePlan'
import WithDraggable from '@/components/common/draggable/WithDraggable' import WithDraggable from '@/components/common/draggable/WithDraggable'
import { useCanvas } from '@/hooks/useCanvas'
import { useRecoilValue } from 'recoil'
import { canvasState } from '@/store/canvasAtom'
export default function ImgLoad() { export default function ImgLoad() {
const { const {
@ -22,13 +25,20 @@ export default function ImgLoad() {
setMapPositionAddress, setMapPositionAddress,
handleFileDelete, handleFileDelete,
handleMapImageDown, handleMapImageDown,
handleAddressDelete,
} = useRefFiles() } = useRefFiles()
const { currentCanvasPlan } = usePlan() const { currentCanvasPlan, setCurrentCanvasPlan } = usePlan()
const { getMessage } = useMessage() const { getMessage } = useMessage()
const canvas = useRecoilValue(canvasState)
const { floorPlanState, setFloorPlanState } = useContext(FloorPlanContext) const { floorPlanState, setFloorPlanState } = useContext(FloorPlanContext)
const handleModal = () => { const handleModal = () => {
setFloorPlanState({ ...floorPlanState, refFileModalOpen: false }) setFloorPlanState({ ...floorPlanState, refFileModalOpen: false, toggleRotate: false })
setCurrentCanvasPlan({
...currentCanvasPlan,
bgImageName: refImage?.name ?? null,
mapPositionAddress,
})
} }
useEffect(() => { useEffect(() => {
@ -37,8 +47,24 @@ export default function ImgLoad() {
}, [floorPlanState.refFileModalOpen]) }, [floorPlanState.refFileModalOpen])
useEffect(() => { useEffect(() => {
const refFileMethod = currentCanvasPlan?.mapPositionAddress === null ? '1' : '2' const backGroundImage = canvas?.getObjects().filter((obj) => obj.name === 'backGroundImage')
if (backGroundImage && backGroundImage.length === 1) {
backGroundImage[0].set({
lockMovementX: !floorPlanState.toggleRotate,
lockMovementY: !floorPlanState.toggleRotate,
lockRotation: !floorPlanState.toggleRotate,
lockScalingX: !floorPlanState.toggleRotate,
lockScalingY: !floorPlanState.toggleRotate,
selectable: floorPlanState.toggleRotate,
})
}
}, [floorPlanState.toggleRotate])
useEffect(() => {
if (!currentCanvasPlan) return
const refFileMethod = currentCanvasPlan?.mapPositionAddress === null || currentCanvasPlan?.mapPositionAddress?.trim() === '' ? '1' : '2'
setRefFileMethod(refFileMethod) setRefFileMethod(refFileMethod)
setMapPositionAddress(currentCanvasPlan?.mapPositionAddress ?? '')
}, [currentCanvasPlan]) }, [currentCanvasPlan])
return ( return (
@ -50,7 +76,7 @@ export default function ImgLoad() {
</div> </div>
<div className="modal-body"> <div className="modal-body">
<div className="img-flex-box"> <div className="img-flex-box">
<span className="normal-font mr10">サイズ調整と回転</span> <span className="normal-font mr10">{getMessage('modal.image.load.size.rotate')}</span>
<label className="toggle-btn"> <label className="toggle-btn">
<input <input
type="checkbox" type="checkbox"
@ -64,13 +90,13 @@ export default function ImgLoad() {
<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={'1'} onChange={(e) => handleRefFileMethod(e)} checked={refFileMethod === '1'} /> <input type="radio" name="radio03" id="ra06" value={'1'} onChange={(e) => handleRefFileMethod(e)} checked={refFileMethod === '1'} />
<label htmlFor="ra06">ファイルを読み込む</label> <label htmlFor="ra06">{getMessage('common.input.file')}</label>
</div> </div>
<div className="img-flex-box"> <div className="img-flex-box">
<div className="img-edit-wrap"> <div className="img-edit-wrap">
<label className="img-edit-btn" htmlFor="img_file"> <label className="img-edit-btn" htmlFor="img_file">
<span className="img-edit"></span> <span className="img-edit"></span>
ファイルの追加 {getMessage('common.load')}
</label> </label>
<input <input
type="file" type="file"
@ -80,21 +106,20 @@ export default function ImgLoad() {
/> />
</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
<button className="img-check"></button> */} type="text"
{currentCanvasPlan?.bgImageName === null ? ( className="input-origin al-l"
<input type="text" className="input-origin al-l" value={refImage ? refImage.name : ''} readOnly /> value={refImage ? (refImage?.name ?? '') : (currentCanvasPlan?.bgImageName ?? '')}
) : ( readOnly
<input type="text" className="input-origin al-l" value={currentCanvasPlan?.bgImageName} readOnly /> />
)} {refImage && <button className="img-check" onClick={handleFileDelete}></button>}
{(refImage || currentCanvasPlan?.bgImageName) && <button className="img-check" onClick={handleFileDelete}></button>}
</div> </div>
</div> </div>
</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="ra07" 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="ra07">アドレスを読み込む</label> <label htmlFor="ra07">{getMessage('common.input.address.load')}</label>
</div> </div>
<div className="img-flex-box for-address"> <div className="img-flex-box for-address">
<input <input
@ -106,18 +131,21 @@ export default function ImgLoad() {
onChange={(e) => setMapPositionAddress(e.target.value)} onChange={(e) => setMapPositionAddress(e.target.value)}
/> />
<div className="img-edit-wrap"> <div className="img-edit-wrap">
<button className="img-edit-btn" onClick={refFileMethod === '2' ? handleMapImageDown : () => {}}> <button
完了 className={`img-edit-btn ${mapPositionAddress.trim().length === 0 ? 'no-click' : ''}`}
onClick={refFileMethod === '2' ? handleMapImageDown : () => {}}
>
{getMessage('common.finish')}
</button> </button>
</div> </div>
{mapPositionAddress && <span className="check-address fail"></span>} {mapPositionAddress && <button className="check-address fail" onClick={handleAddressDelete}></button>}
{/* <span className="check-address success"></span> */} {/* <span className="check-address success"></span> */}
</div> </div>
</div> </div>
</div> </div>
<div className="grid-btn-wrap"> <div className="grid-btn-wrap">
<button className="btn-frame modal act" onClick={handleModal}> <button className="btn-frame modal act" onClick={handleModal}>
完了 {getMessage('common.finish')}
</button> </button>
</div> </div>
</div> </div>

View File

@ -20,7 +20,7 @@ export default function BasicSetting({ id, pos = { x: 50, y: 230 } }) {
const orientationRef = useRef(null) const orientationRef = useRef(null)
const { initEvent } = useEvent() const { initEvent } = useEvent()
// const { initEvent } = useContext(EventContext) // const { initEvent } = useContext(EventContext)
const { makeModuleInstArea, manualModuleSetup, autoModuleSetup } = useModuleBasicSetting() const { manualModuleSetup, autoModuleSetup, manualFlatroofModuleSetup, autoFlatroofModuleSetup } = useModuleBasicSetting()
const handleBtnNextStep = () => { const handleBtnNextStep = () => {
if (tabNum === 1) { if (tabNum === 1) {
orientationRef.current.handleNextStep() orientationRef.current.handleNextStep()
@ -28,20 +28,16 @@ export default function BasicSetting({ id, pos = { x: 50, y: 230 } }) {
setTabNum(tabNum + 1) setTabNum(tabNum + 1)
} }
useEffect(() => {
makeModuleInstArea() //
return () => {
initEvent() //
}
}, [])
const placementRef = { const placementRef = {
isChidori: useRef('false'), isChidori: useRef('false'),
setupLocation: useRef('center'), setupLocation: useRef('center'),
isMaxSetup: useRef('false'), isMaxSetup: useRef('false'),
} }
const placementFlatRef = {
setupLocation: useRef('south'),
}
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`}>
@ -66,7 +62,9 @@ export default function BasicSetting({ id, pos = { x: 50, y: 230 } }) {
{/*배치면 초기설정 - 입력방법: 육지붕*/} {/*배치면 초기설정 - 입력방법: 육지붕*/}
{canvasSetting.roofSizeSet && canvasSetting.roofSizeSet == 3 && tabNum === 2 && <PitchModule setTabNum={setTabNum} />} {canvasSetting.roofSizeSet && canvasSetting.roofSizeSet == 3 && tabNum === 2 && <PitchModule setTabNum={setTabNum} />}
{canvasSetting.roofSizeSet && canvasSetting.roofSizeSet == 3 && tabNum === 3 && <PitchPlacement setTabNum={setTabNum} />} {canvasSetting.roofSizeSet && canvasSetting.roofSizeSet == 3 && tabNum === 3 && (
<PitchPlacement setTabNum={setTabNum} ref={placementFlatRef} />
)}
<div className="grid-btn-wrap"> <div className="grid-btn-wrap">
{tabNum !== 1 && ( {tabNum !== 1 && (
@ -80,14 +78,29 @@ export default function BasicSetting({ id, pos = { x: 50, y: 230 } }) {
Next Next
</button> </button>
)} )}
{tabNum === 3 && ( {tabNum === 3 && (
<> <>
<button className="btn-frame modal mr5" onClick={manualModuleSetup}> {canvasSetting.roofSizeSet && canvasSetting.roofSizeSet != 3 && (
{getMessage('modal.module.basic.setting.passivity.placement')} <>
</button> <button className="btn-frame modal mr5" onClick={manualModuleSetup}>
<button className="btn-frame modal act" onClick={() => autoModuleSetup(placementRef)}> {getMessage('modal.module.basic.setting.passivity.placement')}
{getMessage('modal.module.basic.setting.auto.placement')} </button>
</button> <button className="btn-frame modal act" onClick={() => autoModuleSetup(placementRef)}>
{getMessage('modal.module.basic.setting.auto.placement')}
</button>
</>
)}
{canvasSetting.roofSizeSet && canvasSetting.roofSizeSet === 3 && (
<>
<button className="btn-frame modal mr5" onClick={() => manualFlatroofModuleSetup(placementFlatRef)}>
{getMessage('modal.module.basic.setting.passivity.placement')}
</button>
<button className="btn-frame modal act" onClick={() => autoFlatroofModuleSetup(placementFlatRef)}>
{getMessage('modal.module.basic.setting.auto.placement')}
</button>
</>
)}
</> </>
)} )}
</div> </div>

View File

@ -34,7 +34,7 @@ export const Orientation = forwardRef(({ tabNum }, ref) => {
onClick={() => setCompasDeg(15 * (12 + index))} onClick={() => setCompasDeg(15 * (12 + index))}
> >
{index === 0 && <i>180°</i>} {index === 0 && <i>180°</i>}
{index === 6 && <i>270°</i>} {index === 6 && <i>-90°</i>}
</div> </div>
))} ))}
{Array.from({ length: 180 / 15 }).map((dot, index) => ( {Array.from({ length: 180 / 15 }).map((dot, index) => (
@ -56,7 +56,7 @@ export const Orientation = forwardRef(({ tabNum }, ref) => {
<div className="center-wrap"> <div className="center-wrap">
<div className="d-check-box pop"> <div className="d-check-box pop">
<input type="checkbox" id="ch99" checked={!hasAnglePassivity} onChange={() => setHasAnglePassivity(!hasAnglePassivity)} /> <input type="checkbox" id="ch99" checked={!hasAnglePassivity} onChange={() => setHasAnglePassivity(!hasAnglePassivity)} />
<label htmlFor="ch99">{getMessage('modal.module.basic.setting.orientation.setting.angle.passivity')}0360</label> <label htmlFor="ch99">{getMessage('modal.module.basic.setting.orientation.setting.angle.passivity')}-180 180</label>
</div> </div>
<div className="outline-form"> <div className="outline-form">
<div className="input-grid mr10" style={{ width: '160px' }}> <div className="input-grid mr10" style={{ width: '160px' }}>

View File

@ -1,5 +1,6 @@
import { forwardRef, useEffect, useState } from 'react'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { forwardRef, useState } from 'react' import { useModuleBasicSetting } from '@/hooks/module/useModuleBasicSetting'
const Placement = forwardRef((props, refs) => { const Placement = forwardRef((props, refs) => {
const { getMessage } = useMessage() const { getMessage } = useMessage()
@ -7,6 +8,12 @@ const Placement = forwardRef((props, refs) => {
const [setupLocation, setSetupLocation] = useState('center') const [setupLocation, setSetupLocation] = useState('center')
const [isMaxSetup, setIsMaxSetup] = useState('false') const [isMaxSetup, setIsMaxSetup] = useState('false')
const { makeModuleInstArea } = useModuleBasicSetting()
useEffect(() => {
makeModuleInstArea()
}, [])
const moduleData = { const moduleData = {
header: [ header: [
{ type: 'check', name: '', prop: 'check', width: 70 }, { type: 'check', name: '', prop: 'check', width: 70 },

View File

@ -1,7 +1,26 @@
import { forwardRef, useState, useEffect } from 'react'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { useModuleBasicSetting } from '@/hooks/module/useModuleBasicSetting'
import { compasDegAtom } from '@/store/orientationAtom'
import { canvasState } from '@/store/canvasAtom'
import { useRecoilValue } from 'recoil'
import { POLYGON_TYPE } from '@/common/common'
export default function PitchPlacement() { const PitchPlacement = forwardRef((props, refs) => {
const { getMessage } = useMessage() const { getMessage } = useMessage()
const [setupLocation, setSetupLocation] = useState('south')
const { makeModuleInstArea } = useModuleBasicSetting()
const compasDeg = useRecoilValue(compasDegAtom)
const canvas = useRecoilValue(canvasState)
useEffect(() => {
makeModuleInstArea()
}, [])
useEffect(() => {
handleChangeSetupLocation()
}, [setupLocation])
const moduleData = { const moduleData = {
header: [ header: [
{ type: 'check', name: '', prop: 'check', width: 70 }, { type: 'check', name: '', prop: 'check', width: 70 },
@ -24,6 +43,45 @@ export default function PitchPlacement() {
}, },
], ],
} }
const handleSetupLocation = (e) => {
refs.setupLocation.current = e.target
setSetupLocation(e.target.value)
}
const handleChangeSetupLocation = () => {
if (setupLocation === 'south') {
canvas.getObjects().forEach((obj) => obj.name === 'flatExcretaLine' && canvas.remove(obj))
return null
} else {
const moduleSetupSurfaces = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE) //
moduleSetupSurfaces.forEach((surface, index) => {
console.log(`surface ${index} : `, surface)
const excretaLine = surface.lines
excretaLine.forEach((line) => {
line.set({
stroke: '#642EFB',
strokeWidth: 5,
surfaceId: surface.surfaceId,
name: 'flatExcretaLine',
})
canvas.add(line)
line.on('selected', () => {
excretaLine.forEach((obj) => obj.set({ stroke: '#642EFB', isSelected: false }))
if (!line.isSelected) {
line.set({ stroke: 'red', isSelected: true })
} else {
line.set({ stroke: '#642EFB', isSelected: false })
}
})
})
})
}
}
return ( return (
<> <>
<div className="module-table-box mb10"> <div className="module-table-box mb10">
@ -88,11 +146,26 @@ export default function PitchPlacement() {
<div className="hexagonal-item"> <div className="hexagonal-item">
<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"
value={'south'}
checked={setupLocation === 'south'}
defaultChecked
onChange={handleSetupLocation}
/>
<label htmlFor="ra01">{getMessage('modal.module.basic.setting.pitch.module.placement.standard.setting.south')}</label> <label htmlFor="ra01">{getMessage('modal.module.basic.setting.pitch.module.placement.standard.setting.south')}</label>
</div> </div>
<div className="d-check-radio pop"> <div className="d-check-radio pop">
<input type="radio" name="radio01" id="ra02" /> <input
type="radio"
name="radio01"
id="ra02"
value={'excreta'}
checked={setupLocation === 'excreta'}
onChange={handleSetupLocation}
/>
<label htmlFor="ra02">{getMessage('modal.module.basic.setting.pitch.module.placement.standard.setting.select')}</label> <label htmlFor="ra02">{getMessage('modal.module.basic.setting.pitch.module.placement.standard.setting.select')}</label>
</div> </div>
</div> </div>
@ -102,4 +175,6 @@ export default function PitchPlacement() {
</div> </div>
</> </>
) )
} })
export default PitchPlacement

View File

@ -1,54 +1,44 @@
import WithDraggable from '@/components/common/draggable/WithDraggable' import WithDraggable from '@/components/common/draggable/WithDraggable'
import QSelectBox from '@/components/common/select/QSelectBox' import QSelectBox from '@/components/common/select/QSelectBox'
import { useEffect, useState } from 'react' import { useEffect } from 'react'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { canvasState, dotLineGridSettingState, dotLineIntervalSelector } from '@/store/canvasAtom' import { canvasState } from '@/store/canvasAtom'
import { useRecoilState, useRecoilValue, useResetRecoilState, useSetRecoilState } from 'recoil' import { useRecoilValue } from 'recoil'
import { onlyNumberInputChange } from '@/util/input-utils' import { onlyNumberInputChange } from '@/util/input-utils'
import { settingModalGridOptionsState } from '@/store/settingAtom'
import { useAxios } from '@/hooks/useAxios'
import { useSwal } from '@/hooks/useSwal'
import { usePopup } from '@/hooks/usePopup' import { usePopup } from '@/hooks/usePopup'
import { useCanvasSetting } from '@/hooks/option/useCanvasSetting'
import { useSwal } from '@/hooks/useSwal'
const TYPE = { const TYPE = {
DOT: 'DOT', DOT: 'DOT',
LINE: 'LINE', LINE: 'LINE',
} }
const defaultDotLineGridSetting = {
INTERVAL: {
type: 2, // 1: , , 2:
ratioInterval: 910,
verticalInterval: 910,
horizontalInterval: 910,
dimension: 1, //
},
DOT: false,
LINE: false,
}
export default function DotLineGrid(props) { export default function DotLineGrid(props) {
// const [modalOption, setModalOption] = useRecoilState(modalState); //modal state // const [modalOption, setModalOption] = useRecoilState(modalState); //modal state
const [objectNo, setObjectNo] = useState('test123240912001') // //const interval = useRecoilValue(dotLineIntervalSelector)
const [close, setClose] = useState(false)
const { id, setIsShow, pos = { x: 840, y: -815 }, isConfig = false } = props const { id, setIsShow, pos = { x: 840, y: -815 }, isConfig = false } = props
const setSettingModalGridOptions = useSetRecoilState(settingModalGridOptionsState)
const canvas = useRecoilValue(canvasState) const canvas = useRecoilValue(canvasState)
const [dotLineGridSetting, setDotLineGridSettingState] = useRecoilState(dotLineGridSettingState)
const [currentSetting, setCurrentSetting] = useState(
JSON.stringify(dotLineGridSetting) === JSON.stringify(defaultDotLineGridSetting) ? { ...defaultDotLineGridSetting } : { ...dotLineGridSetting },
)
const resetDotLineGridSetting = useResetRecoilState(dotLineGridSettingState)
const interval = useRecoilValue(dotLineIntervalSelector)
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { get, post } = useAxios()
const { swalFire } = useSwal()
const { closePopup } = usePopup() const { closePopup } = usePopup()
const { swalFire } = useSwal()
const {
selectOption,
setSelectOption,
SelectOptions,
currentSetting,
setCurrentSetting,
dotLineGridSettingState,
setSettingModalGridOptions,
setDotLineGridSettingState,
} = useCanvasSetting()
//
useEffect(() => { useEffect(() => {
console.log('DotLineGrid useEffect 실행')
return () => { return () => {
setSettingModalGridOptions((prev) => { setSettingModalGridOptions((prev) => {
const newSettingOptions = [...prev] const newSettingOptions = [...prev]
@ -58,20 +48,6 @@ export default function DotLineGrid(props) {
} }
}, []) }, [])
const SelectOption = [
{ id: 1, name: getMessage('modal.canvas.setting.grid.dot.line.setting.line.origin'), value: 1 },
{ id: 2, name: '1/2', value: 1 / 2 },
{ id: 3, name: '1/4', value: 1 / 4 },
{ id: 4, name: '1/10', value: 1 / 10 },
]
const [selectOption, setSelectOption] = useState(SelectOption[0])
//
useEffect(() => {
console.log('DotLineGrid useEffect 실행')
fetchGridSettings()
}, [objectNo])
const HandleClickClose = () => { const HandleClickClose = () => {
// setClose(true) // setClose(true)
// setTimeout(() => { // setTimeout(() => {
@ -90,61 +66,31 @@ export default function DotLineGrid(props) {
}) })
} }
// Canvas Grid Setting
const fetchGridSettings = async () => {
try {
const res = await get({ url: `/api/canvas-management/canvas-grid-settings/by-object/${objectNo}` })
const patternData = {
INTERVAL: {
type: res.gridType,
horizontalInterval: res.gridHorizon * 10,
verticalInterval: res.gridVertical * 10,
ratioInterval: res.gridRatio * 10,
},
dimension: res.gridDimen,
DOT: res.dotGridDisplay,
LINE: res.lineGridDisplay,
}
const matchedOption = SelectOption.find((option) => option.value == res.gridDimen)
// dimension
setSelectOption(matchedOption)
//
setCurrentSetting(patternData)
} catch (error) {
console.error('Data fetching error:', error)
}
}
const handleSave = async () => { const handleSave = async () => {
if (!currentSetting.DOT && !currentSetting.LINE) { if (!currentSetting.DOT && !currentSetting.LINE) {
swalFire({ text: '배치할 그리드를 설정해주세요.' }) swalFire({ text: '배치할 그리드를 설정해주세요.' })
return return
} }
try {
const patternData = {
objectNo,
dotGridDisplay: currentSetting.DOT,
lineGridDisplay: currentSetting.LINE,
gridType: currentSetting.INTERVAL.type,
gridHorizon: currentSetting.INTERVAL.horizontalInterval / 10,
gridVertical: currentSetting.INTERVAL.verticalInterval / 10,
gridRatio: currentSetting.INTERVAL.ratioInterval / 10,
gridDimen: currentSetting.INTERVAL.dimension,
}
// HTTP POST setDotLineGridSettingState((prev) => {
await post({ url: `/api/canvas-management/canvas-grid-settings`, data: patternData }).then((res) => { return {
swalFire({ text: getMessage(res.returnMessage) }) ...prev,
setDotLineGridSettingState({ ...currentSetting }) INTERVAL: {
closePopup(id, isConfig) type: currentSetting.INTERVAL.type,
}) horizontalInterval: currentSetting.INTERVAL.horizontalInterval,
} catch (error) { verticalInterval: currentSetting.INTERVAL.verticalInterval,
swalFire({ text: getMessage(res.returnMessage), icon: 'error' }) ratioInterval: currentSetting.INTERVAL.ratioInterval,
} dimension: currentSetting.INTERVAL.dimension,
},
DOT: currentSetting.DOT,
LINE: currentSetting.LINE,
flag: true,
}
//setDotLineGridSettingState({ ...currentSetting })
})
setIsShow(false)
closePopup(id, isConfig)
} }
const handleRadioChange = (e) => { const handleRadioChange = (e) => {
@ -198,8 +144,19 @@ export default function DotLineGrid(props) {
.filter((obj) => obj.name === 'dotGrid') .filter((obj) => obj.name === 'dotGrid')
.forEach((obj) => canvas?.remove(obj)) .forEach((obj) => canvas?.remove(obj))
resetDotLineGridSetting() // resetDotLineGridSetting()
setSelectOption(SelectOption[0]) setCurrentSetting({
INTERVAL: {
type: 2, // 1: , , 2:
ratioInterval: 910,
verticalInterval: 910,
horizontalInterval: 910,
dimension: 1, //
},
DOT: false,
LINE: false,
})
setSelectOption(SelectOptions[0])
} }
return ( return (
@ -296,7 +253,7 @@ export default function DotLineGrid(props) {
<span>mm</span> <span>mm</span>
</div> </div>
<div className="grid-select"> <div className="grid-select">
<QSelectBox options={SelectOption} onChange={changeDimension} value={selectOption} /> <QSelectBox options={SelectOptions} onChange={changeDimension} value={selectOption} />
</div> </div>
</div> </div>
</div> </div>

View File

@ -17,8 +17,7 @@ export default function GridCopy(props) {
const currentObject = useRecoilValue(currentObjectState) const currentObject = useRecoilValue(currentObjectState)
const { copy } = useGrid() const { copy } = useGrid()
const handleApply = () => { const handleApply = () => {
// copy(currentObject, ) copy(currentObject, ['↑', '←'].includes(arrow) ? +length * -1 : +length)
copy(currentObject, ['↑', '←'].includes(arrow) ? Number(length) * -1 : Number(length))
} }
return ( return (
<WithDraggable isShow={true} pos={pos}> <WithDraggable isShow={true} pos={pos}>

View File

@ -5,12 +5,10 @@ import { useMessage } from '@/hooks/useMessage'
import WithDraggable from '@/components/common/draggable/WithDraggable' import WithDraggable from '@/components/common/draggable/WithDraggable'
import { usePopup } from '@/hooks/usePopup' import { usePopup } from '@/hooks/usePopup'
import { contextPopupPositionState } from '@/store/popupAtom' import { contextPopupPositionState } from '@/store/popupAtom'
import { useRef, useState, useEffect, useContext } from 'react' import { useRef, useState } from 'react'
import { useObjectBatch } from '@/hooks/object/useObjectBatch' import { useObjectBatch } from '@/hooks/object/useObjectBatch'
import { useEvent } from '@/hooks/useEvent'
import { BATCH_TYPE, POLYGON_TYPE } from '@/common/common' import { BATCH_TYPE, POLYGON_TYPE } from '@/common/common'
import { useSurfaceShapeBatch } from '@/hooks/surface/useSurfaceShapeBatch' import { useSurfaceShapeBatch } from '@/hooks/surface/useSurfaceShapeBatch'
import { EventContext } from '@/app/floor-plan/EventProvider'
export default function SizeSetting(props) { export default function SizeSetting(props) {
const contextPopupPosition = useRecoilValue(contextPopupPositionState) const contextPopupPosition = useRecoilValue(contextPopupPositionState)

View File

@ -1,113 +1,26 @@
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { useRecoilState } from 'recoil'
import { canvasSettingState } from '@/store/canvasAtom'
import { basicSettingState } from '@/store/settingAtom'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { useAxios } from '@/hooks/useAxios'
import { useSwal } from '@/hooks/useSwal'
import { usePopup } from '@/hooks/usePopup' 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 { useCanvasSetting } from '@/hooks/option/useCanvasSetting'
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 [showSizeGuideModal, setShowSizeGuidModal] = useState(false) const [showSizeGuideModal, setShowSizeGuidModal] = useState(false)
const [showMaterialGuideModal, setShowMaterialGuidModal] = useState(false) const [showMaterialGuideModal, setShowMaterialGuidModal] = useState(false)
const [selectedRoofMaterial, setSelectedRoofMaterial] = useState(1)
const [canvasSetting, setCanvasSetting] = useRecoilState(canvasSettingState)
const { closePopup } = usePopup() const { closePopup } = usePopup()
const [basicSetting, setBasicSettings] = useRecoilState(basicSettingState)
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { get, post } = useAxios()
const { swalFire } = useSwal() const { basicSetting, setBasicSettings, fetchBasicSettings, basicSettingSave } = useCanvasSetting()
// //
useEffect(() => { useEffect(() => {
console.log('PlacementShapeSetting useEffect 실행') fetchBasicSettings()
}, [])
fetchSettings()
}, [objectNo])
// PlacementShapeSetting
const fetchSettings = async () => {
try {
await get({ url: `/api/canvas-management/canvas-basic-settings/by-object/${objectNo}` }).then((res) => {
if (res.length == 0) return
// 'roofs'
const roofsRow = res.map((item) => {
return {
roofSizeSet: item.roofSizeSet,
roofAngleSet: item.roofAngleSet,
}
})
const roofsArray = res.some((item) => !item.roofSeq)
? // roofsArray
res.map(() => ({
roofApply: true,
roofSeq: 1,
roofType: 1,
roofWidth: 200,
roofHeight: 200,
roofHajebichi: 200,
roofGap: 0,
roofLayout: 'parallel',
}))
: res.map((item) => ({
roofApply: item.roofApply === '' || item.roofApply === false ? false : true,
roofSeq: item.roofSeq,
roofType: item.roofType,
roofWidth: item.roofWidth,
roofHeight: item.roofHeight,
roofHajebichi: item.roofHajebichi,
roofGap: item.roofGap,
roofLayout: item.roofLayout,
}))
console.log('roofsArray ', roofsArray)
// 'roofs' patternData
const patternData = {
roofSizeSet: roofsRow[0].roofSizeSet, //
roofAngleSet: roofsRow[0].roofAngleSet, //
roofs: roofsArray, // roofs
}
//
setBasicSettings({ ...patternData })
})
} catch (error) {
console.error('Data fetching error:', error)
}
if (!(Object.keys(canvasSetting).length === 0 && canvasSetting.constructor === Object)) {
setBasicSettings({ ...canvasSetting })
}
}
const submitCanvasConfig = async () => {
try {
const patternData = {
objectNo,
roofSizeSet: basicSetting.roofSizeSet,
roofAngleSet: basicSetting.roofAngleSet,
roofMaterialsAddList: basicSetting.roofs,
}
await post({ url: `/api/canvas-management/canvas-basic-settings`, data: patternData }).then((res) => {
swalFire({ text: getMessage(res.returnMessage) })
})
//Recoil
setCanvasSetting({ ...basicSetting })
} catch (error) {
swalFire({ text: getMessage(res.returnMessage), icon: 'error' })
}
}
// Function to update the roofType and corresponding values // Function to update the roofType and corresponding values
const handleRoofTypeChange = (index, value) => { const handleRoofTypeChange = (index, value) => {
@ -122,7 +35,7 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, set
roofWidth: 265, roofWidth: 265,
roofHeight: 235, roofHeight: 235,
roofGap: 455, roofGap: 455,
hajebichi: 0, roofHajebichi: 0,
} }
} else if (roofType === 2) { } else if (roofType === 2) {
updatedRoofs[index] = { updatedRoofs[index] = {
@ -490,7 +403,7 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, set
</table> </table>
</div> </div>
<div className="grid-btn-wrap"> <div className="grid-btn-wrap">
<button className="btn-frame modal act" onClick={() => submitCanvasConfig()}> <button className="btn-frame modal act" onClick={() => basicSettingSave()}>
{getMessage('modal.common.save')} {getMessage('modal.common.save')}
</button> </button>
</div> </div>

View File

@ -14,7 +14,7 @@ export default function SizeGuide({ setShowSizeGuidModal }) {
<div className="placement-table light"> <div className="placement-table light">
<table> <table>
<colgroup> <colgroup>
<col style={{ width: '60px' }} /> <col style={{ width: '65px' }} />
<col /> <col />
</colgroup> </colgroup>
<tbody> <tbody>

View File

@ -3,11 +3,14 @@ import { useCanvasSetting } from '@/hooks/option/useCanvasSetting'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { POLYGON_TYPE } from '@/common/common' import { POLYGON_TYPE } from '@/common/common'
import { setSurfaceShapePattern } from '@/util/canvas-util' import { setSurfaceShapePattern } from '@/util/canvas-util'
import { useEvent } from '@/hooks/useEvent'
export default function FirstOption() { export default function FirstOption(props) {
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { canvas, settingModalFirstOptions, setSettingModalFirstOptions } = useCanvasSetting() // const { canvas, settingModalFirstOptions, setSettingModalFirstOptions, settingsData, setSettingsData } = useCanvasSetting()
let { canvas, settingModalFirstOptions, setSettingModalFirstOptions, settingsData, setSettingsData } = props
const { option1, option2, dimensionDisplay } = settingModalFirstOptions const { option1, option2, dimensionDisplay } = settingModalFirstOptions
const { initEvent } = useEvent()
// //
useEffect(() => { useEffect(() => {
@ -16,19 +19,27 @@ export default function FirstOption() {
const onClickOption = async (item) => { const onClickOption = async (item) => {
// ( ) // ( )
let dimensionDisplay = settingModalFirstOptions?.dimensionDisplay
let option1 = settingModalFirstOptions?.option1
let option2 = settingModalFirstOptions?.option2
if (item.column === 'corridorDimension' || item.column === 'realDimension' || item.column === 'noneDimension') { if (item.column === 'corridorDimension' || item.column === 'realDimension' || item.column === 'noneDimension') {
const options = settingModalFirstOptions?.dimensionDisplay.map((option) => { dimensionDisplay = settingModalFirstOptions?.dimensionDisplay.map((option) => {
option.selected = option.id === item.id option.selected = option.id === item.id
return option return option
}) })
// setSettingModalFirstOptions({ ...settingModalFirstOptions, dimensionDisplay: [...options] })
// ( ) // ( )
} else if (item.column === 'onlyBorder' || item.column === 'lineHatch' || item.column === 'allPainted') { } else if (item.column === 'onlyBorder' || item.column === 'lineHatch' || item.column === 'allPainted') {
const options2 = settingModalFirstOptions?.option2.map((option2) => { option2 = settingModalFirstOptions?.option2.map((option2) => {
option2.selected = option2.id === item.id option2.selected = option2.id === item.id
return option2 return option2
}) })
// setSettingModalFirstOptions({ ...settingModalFirstOptions, option2: [...options] })
const polygons = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF) const polygons = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
polygons.forEach((polygon) => { polygons.forEach((polygon) => {
@ -36,12 +47,24 @@ export default function FirstOption() {
}) })
// ( ) // ( )
} else { } else {
item.selected = !item.selected option1 = settingModalFirstOptions?.option1.map((opt) => {
if (opt.id === item.id) {
opt.selected = !opt.selected
}
return opt
})
// setSettingModalFirstOptions({ ...settingModalFirstOptions, option1: [...options] })
} }
setSettingModalFirstOptions({ ...settingModalFirstOptions, option1, option2, dimensionDisplay, fontFlag: true }) setSettingsData({ ...settingsData, option1: [...option1], option2: [...option2], dimensionDisplay: [...dimensionDisplay] })
} }
// useEffect(() => {
// console.log('🚀 ~ useEffect ~ initEvent:')
// initEvent()
// }, [onClickOption])
return ( return (
<> <>
<div className="modal-check-btn-wrap"> <div className="modal-check-btn-wrap">

View File

@ -4,28 +4,32 @@ import { settingModalGridOptionsState } from '@/store/settingAtom'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { adsorptionPointAddModeState } from '@/store/canvasAtom' import { adsorptionPointAddModeState } from '@/store/canvasAtom'
import { useTempGrid } from '@/hooks/useTempGrid' import { useTempGrid } from '@/hooks/useTempGrid'
import { gridColorState } from '@/store/gridAtom'
import { useColor } from 'react-color-palette'
import ColorPickerModal from '@/components/common/color-picker/ColorPickerModal' import ColorPickerModal from '@/components/common/color-picker/ColorPickerModal'
import { usePopup } from '@/hooks/usePopup' import { usePopup } from '@/hooks/usePopup'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
import DotLineGrid from '@/components/floor-plan/modal/grid/DotLineGrid' import DotLineGrid from '@/components/floor-plan/modal/grid/DotLineGrid'
import { useCanvasSetting } from '@/hooks/option/useCanvasSetting'
import { useEvent } from '@/hooks/useEvent'
export default function GridOption() { export default function GridOption(props) {
const [gridOptions, setGridOptions] = useRecoilState(settingModalGridOptionsState) const [gridOptions, setGridOptions] = useRecoilState(settingModalGridOptionsState)
const [adsorptionPointAddMode, setAdsorptionPointAddMode] = useRecoilState(adsorptionPointAddModeState) const [adsorptionPointAddMode, setAdsorptionPointAddMode] = useRecoilState(adsorptionPointAddModeState)
const setSettingModalGridOptions = useSetRecoilState(settingModalGridOptionsState) const setSettingModalGridOptions = useSetRecoilState(settingModalGridOptionsState)
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { tempGridMode, setTempGridMode } = useTempGrid() const { tempGridMode, setTempGridMode } = useTempGrid()
const [gridColor, setGridColor] = useRecoilState(gridColorState)
const [color, setColor] = useColor(gridColor)
const [showColorPickerModal, setShowColorPickerModal] = useState(false) const [showColorPickerModal, setShowColorPickerModal] = useState(false)
const [showDotLineGridModal, setShowDotLineGridModal] = useState(false) const [showDotLineGridModal, setShowDotLineGridModal] = useState(false)
const { addPopup, closePopup, closePopups } = usePopup() const { addPopup, closePopup, closePopups } = usePopup()
const [colorId, setColorId] = useState(uuidv4()) const [colorId, setColorId] = useState(uuidv4())
const [dotLineId, setDotLineId] = useState(uuidv4()) const [dotLineId, setDotLineId] = useState(uuidv4())
// const { gridColor, setGridColor, color } = useCanvasSetting()
const { gridColor, setGridColor, color } = props
const { initEvent } = useEvent()
useEffect(() => { useEffect(() => {
console.log('GridOption useEffect 실행')
setGridColor(color.hex) setGridColor(color.hex)
}, [color]) }, [color])
@ -91,6 +95,11 @@ export default function GridOption() {
setGridOptions(newGridOptions) setGridOptions(newGridOptions)
} }
useEffect(() => {
console.log('🚀 ~ useEffect ~ initEvent:')
initEvent()
}, [gridOptions])
const dotLineGridProps = { const dotLineGridProps = {
id: dotLineId, id: dotLineId,
setIsShow: setShowDotLineGridModal, setIsShow: setShowDotLineGridModal,

View File

@ -8,8 +8,9 @@ import PlanSizeSetting from '@/components/floor-plan/modal/setting01/planSize/Pl
import { useCanvasSetting } from '@/hooks/option/useCanvasSetting' import { useCanvasSetting } from '@/hooks/option/useCanvasSetting'
import { useRecoilState, useRecoilValue } from 'recoil' import { useRecoilState, useRecoilValue } from 'recoil'
import { fontSelector, globalFontAtom } from '@/store/fontAtom' import { fontSelector, globalFontAtom } from '@/store/fontAtom'
import { useEvent } from '@/hooks/useEvent'
export default function SecondOption() { export default function SecondOption(props) {
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { addPopup, closePopup } = usePopup() const { addPopup, closePopup } = usePopup()
const [showFontSettingModal, setShowFontSettingModal] = useState(false) const [showFontSettingModal, setShowFontSettingModal] = useState(false)
@ -24,6 +25,18 @@ export default function SecondOption() {
const [fontId, setFontId] = useState(uuidv4()) const [fontId, setFontId] = useState(uuidv4())
const [planSizeId, setPlanSizeId] = useState(uuidv4()) const [planSizeId, setPlanSizeId] = useState(uuidv4())
const { initEvent } = useEvent()
// const {
// fetchSettings,
// planSizeSettingMode,
// setPlanSizeSettingMode,
// settingModalSecondOptions,
// setSettingModalSecondOptions,
// adsorptionPointMode,
// setAdsorptionPointMode,
// setAdsorptionRange,
// } = useCanvasSetting()
const { const {
fetchSettings, fetchSettings,
planSizeSettingMode, planSizeSettingMode,
@ -33,7 +46,7 @@ export default function SecondOption() {
adsorptionPointMode, adsorptionPointMode,
setAdsorptionPointMode, setAdsorptionPointMode,
setAdsorptionRange, setAdsorptionRange,
} = useCanvasSetting() } = props
const { option3, option4 } = settingModalSecondOptions const { option3, option4 } = settingModalSecondOptions
// //
@ -189,6 +202,11 @@ export default function SecondOption() {
setAdsorptionRange(50) setAdsorptionRange(50)
} }
useEffect(() => {
console.log('🚀 ~ useEffect ~ initEvent:')
initEvent()
}, [adsorptionPointMode])
return ( return (
<> <>
<div className="modal-check-btn-wrap"> <div className="modal-check-btn-wrap">

View File

@ -9,6 +9,7 @@ import GridOption from '@/components/floor-plan/modal/setting01/GridOption'
import { canGridOptionSeletor } from '@/store/canvasAtom' import { canGridOptionSeletor } from '@/store/canvasAtom'
import { useRecoilValue } from 'recoil' import { useRecoilValue } from 'recoil'
import { usePopup } from '@/hooks/usePopup' import { usePopup } from '@/hooks/usePopup'
import { useCanvasSetting } from '@/hooks/option/useCanvasSetting'
export default function SettingModal01(props) { export default function SettingModal01(props) {
const { id } = props const { id } = props
@ -17,6 +18,37 @@ export default function SettingModal01(props) {
const canGridOptionSeletorValue = useRecoilValue(canGridOptionSeletor) const canGridOptionSeletorValue = useRecoilValue(canGridOptionSeletor)
const { closePopup } = usePopup() const { closePopup } = usePopup()
const {
canvas,
settingModalFirstOptions,
setSettingModalFirstOptions,
settingsData,
setSettingsData,
fetchSettings,
planSizeSettingMode,
setPlanSizeSettingMode,
settingModalSecondOptions,
setSettingModalSecondOptions,
adsorptionPointMode,
setAdsorptionPointMode,
setAdsorptionRange,
gridColor,
setGridColor,
color,
} = useCanvasSetting()
const firstProps = { canvas, settingModalFirstOptions, setSettingModalFirstOptions, settingsData, setSettingsData }
const secondProps = {
fetchSettings,
planSizeSettingMode,
setPlanSizeSettingMode,
settingModalSecondOptions,
setSettingModalSecondOptions,
adsorptionPointMode,
setAdsorptionPointMode,
setAdsorptionRange,
}
const gridProps = { gridColor, setGridColor, color }
const handleBtnClick = (num) => { const handleBtnClick = (num) => {
setButtonAct(num) setButtonAct(num)
} }
@ -46,9 +78,9 @@ export default function SettingModal01(props) {
</button> </button>
)} )}
</div> </div>
{buttonAct === 1 && <FirstOption />} {buttonAct === 1 && <FirstOption {...firstProps} />}
{buttonAct === 2 && <SecondOption />} {buttonAct === 2 && <SecondOption {...secondProps} />}
{buttonAct === 3 && <GridOption />} {buttonAct === 3 && <GridOption {...gridProps} />}
</div> </div>
</div> </div>
</WithDraggable> </WithDraggable>

View File

@ -75,8 +75,6 @@ export default function PlanSizeSetting(props) {
className="input-origin block" className="input-origin block"
name={`originHorizon`} name={`originHorizon`}
value={planSizeSettingMode.originHorizon} value={planSizeSettingMode.originHorizon}
//onChange={(e) => setPlanSizeSettingMode({ ...planSizeSettingMode, originHorizon: Number(e.target.value), flag: false })}
//onFocus={(e) => (originHorizon.current.value = '')}
onChange={(e) => onlyNumberInputChange(e, changeInput)} onChange={(e) => onlyNumberInputChange(e, changeInput)}
/> />
</div> </div>
@ -90,8 +88,6 @@ export default function PlanSizeSetting(props) {
className="input-origin block" className="input-origin block"
name={`originVertical`} name={`originVertical`}
value={planSizeSettingMode.originVertical} value={planSizeSettingMode.originVertical}
//onChange={(e) => setPlanSizeSettingMode({ ...planSizeSettingMode, originVertical: Number(e.target.value), flag: false })}
//onFocus={(e) => (originVertical.current.value = '')}
onChange={(e) => onlyNumberInputChange(e, changeInput)} onChange={(e) => onlyNumberInputChange(e, changeInput)}
/> />
</div> </div>

View File

@ -96,7 +96,8 @@ export default function Header(props) {
name: 'header.menus.management', name: 'header.menus.management',
url: '', url: '',
children: [ children: [
{ id: 3, name: 'header.menus.management.newStuff', url: '/management/stuff/tempdetail', children: [] }, // { id: 3, name: 'header.menus.management.newStuff', url: '/management/stuff/tempdetail', children: [] },
{ id: 3, name: 'header.menus.management.newStuff', url: '/management/stuff/tempReg', children: [] },
{ id: 4, name: 'header.menus.management.stuffList', url: '/management/stuff', children: [] }, { id: 4, name: 'header.menus.management.stuffList', url: '/management/stuff', children: [] },
], ],
}, },
@ -129,6 +130,14 @@ export default function Header(props) {
} }
} }
// Home
const moveHome = () => {
setStuffSearch({
...stuffSearch,
code: 'DELETE',
})
}
const getMenuTemplate = (menus) => { const getMenuTemplate = (menus) => {
return menus.map((menu) => { return menus.map((menu) => {
return ( return (
@ -139,7 +148,13 @@ export default function Header(props) {
onMouseLeave={(e) => ToggleonMouse(e, 'remove', 'nav > ul')} onMouseLeave={(e) => ToggleonMouse(e, 'remove', 'nav > ul')}
> >
{menu.children.length === 0 ? ( {menu.children.length === 0 ? (
<Link key={`${menu.id}`} href={menu.url}> <Link
key={`${menu.id}`}
href={menu.url}
onClick={() => {
moveHome()
}}
>
{getMessage(menu.name)} {getMessage(menu.name)}
</Link> </Link>
) : ( ) : (
@ -154,7 +169,9 @@ export default function Header(props) {
onMouseEnter={(e) => ToggleonMouse(e, 'add', 'li > ul')} onMouseEnter={(e) => ToggleonMouse(e, 'add', 'li > ul')}
onMouseLeave={(e) => ToggleonMouse(e, 'remove', 'li > ul')} onMouseLeave={(e) => ToggleonMouse(e, 'remove', 'li > ul')}
> >
<Link href={m.url}>{getMessage(m.name)}</Link> <Link scroll={false} href={m.url}>
{getMessage(m.name)}
</Link>
</li> </li>
) )
})} })}

View File

@ -6,8 +6,9 @@ import { useRecoilValue, useRecoilState } from 'recoil'
import { useAxios } from '@/hooks/useAxios' import { useAxios } from '@/hooks/useAxios'
import { globalLocaleStore } from '@/store/localeAtom' import { globalLocaleStore } from '@/store/localeAtom'
import { useRouter } from 'next/navigation' import { useRouter } from 'next/navigation'
import { setSession } from '@/lib/authActions'
export default function ChangePasswordPop() { export default function ChangePasswordPop(props) {
const globalLocaleState = useRecoilValue(globalLocaleStore) const globalLocaleState = useRecoilValue(globalLocaleStore)
const { patch } = useAxios(globalLocaleState) const { patch } = useAxios(globalLocaleState)
@ -77,6 +78,7 @@ export default function ChangePasswordPop() {
alert(getMessage('main.popup.login.success')) alert(getMessage('main.popup.login.success'))
setSessionState({ ...sessionState, pwdInitYn: 'Y' }) setSessionState({ ...sessionState, pwdInitYn: 'Y' })
// //
props.setChagePasswordPopOpen(false)
router.push('/') router.push('/')
} else { } else {
alert(res.result.resultMsg) alert(res.result.resultMsg)

View File

@ -22,19 +22,12 @@ export default function MainContents() {
const globalLocaleState = useRecoilValue(globalLocaleStore) const globalLocaleState = useRecoilValue(globalLocaleStore)
const { promiseGet } = useAxios(globalLocaleState) const { promiseGet } = useAxios(globalLocaleState)
//
// const [objectList, setObjectList] = useState([])
// //
const [recentNoticeList, setRecentNoticeList] = useState([]) const [recentNoticeList, setRecentNoticeList] = useState([])
//FAQ //FAQ
const [recentFaqList, setRecentFaqList] = useState([]) const [recentFaqList, setRecentFaqList] = useState([])
//Sales Contact info
// const [businessCharger, setBusinessCharger] = useState(null)
// const [businessChargerMail, setBusinessChargerMail] = useState(null)
const { qcastState } = useContext(QcastContext) const { qcastState } = useContext(QcastContext)
const { fetchObjectList, initObjectList } = useMainContentsController() const { fetchObjectList, initObjectList } = useMainContentsController()
@ -47,28 +40,6 @@ export default function MainContents() {
} }
}, []) }, [])
// / Sales Contact info
// const fetchObjectList = async () => {
// try {
// const apiUrl = `/api/main-page/object/${session?.storeId}/list`
// await promiseGet({
// url: apiUrl,
// }).then((res) => {
// if (res.status === 200) {
// setObjectList(res.data.objectList)
// setBusinessCharger(res.data.businessCharger)
// setBusinessChargerMail(res.data.businessChargerMail)
// } else {
// setObjectList([])
// setBusinessCharger(null)
// setBusinessChargerMail(null)
// }
// })
// } catch (error) {
// console.error('MAIN API fetching error:', error)
// }
// }
// //
const fetchNoticeList = async () => { const fetchNoticeList = async () => {
try { try {
@ -128,9 +99,9 @@ export default function MainContents() {
className="recently-item" className="recently-item"
onClick={() => { onClick={() => {
if (row.tempFlg === '0') { if (row.tempFlg === '0') {
router.push(`/management/stuff/detail?objectNo=${row.objectNo.toString()}`) router.push(`/management/stuff/detail?objectNo=${row.objectNo.toString()}`, { scroll: false })
} else { } else {
router.push(`/management/stuff/tempdetail?objectNo=${row.objectNo.toString()}`) router.push(`/management/stuff/tempdetail?objectNo=${row.objectNo.toString()}`, { scroll: false })
} }
}} }}
> >

View File

@ -6,7 +6,7 @@ export default function ProductItem({ num, name, children }) {
// //
const pageMove = (num) => { const pageMove = (num) => {
if (num === 1) { if (num === 1) {
router.push('/management/stuff') router.push('/management/stuff', { scroll: false })
} else if (num === 2) { } else if (num === 2) {
router.push('/community/notice') router.push('/community/notice')
} else { } else {

View File

@ -211,7 +211,6 @@ export default function Stuff() {
fetchData() fetchData()
} else if (stuffSearchParams?.code === 'M') { } else if (stuffSearchParams?.code === 'M') {
const params = { const params = {
saleStoreId: session?.storeId,
schObjectNo: stuffSearchParams.schObjectNo, schObjectNo: stuffSearchParams.schObjectNo,
schAddress: '', schAddress: '',
schObjectName: '', schObjectName: '',
@ -225,7 +224,7 @@ 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', code: 'E',
pageNo: 1, pageNo: 1,
pageSize: 100, pageSize: 100,
} }
@ -261,6 +260,7 @@ export default function Stuff() {
fetchData() fetchData()
} else if (stuffSearchParams?.code === 'C') { } else if (stuffSearchParams?.code === 'C') {
resetStuffRecoil() resetStuffRecoil()
setIsGlobalLoading(false)
} else if (stuffSearchParams?.code === 'FINISH') { } else if (stuffSearchParams?.code === 'FINISH') {
stuffSearchParams.startRow = 1 stuffSearchParams.startRow = 1
stuffSearchParams.endRow = 1 * pageSize stuffSearchParams.endRow = 1 * pageSize
@ -278,6 +278,7 @@ export default function Stuff() {
setTotalCount(0) setTotalCount(0)
} }
}) })
setIsGlobalLoading(false)
} }
fetchData() fetchData()
} else if (stuffSearchParams?.code === 'DELETE') { } else if (stuffSearchParams?.code === 'DELETE') {
@ -305,6 +306,11 @@ export default function Stuff() {
setStuffSearch({ setStuffSearch({
...newParams, ...newParams,
}) })
setIsGlobalLoading(false)
} else {
stuffSearchParams.code = 'DELETE'
setIsGlobalLoading(false)
} }
}, [stuffSearchParams]) }, [stuffSearchParams])
@ -320,7 +326,7 @@ export default function Stuff() {
setPageSize(e.target.value) setPageSize(e.target.value)
setStuffSearch({ setStuffSearch({
...stuffSearch, ...stuffSearch,
code: 'S', code: 'E',
startRow: startRow, startRow: startRow,
endRow: 1 * e.target.value, endRow: 1 * e.target.value,
pageSize: e.target.value, pageSize: e.target.value,
@ -331,7 +337,6 @@ export default function Stuff() {
// //
const onChangeSortType = (e) => { const onChangeSortType = (e) => {
// let startRow = (stuffSearchParams.pageNo - 1) * pageSize + 1
let startRow = (1 - 1) * stuffSearchParams.pageSize + 1 let startRow = (1 - 1) * stuffSearchParams.pageSize + 1
stuffSearchParams.startRow = startRow stuffSearchParams.startRow = startRow
stuffSearchParams.endRow = startRow * stuffSearchParams.pageSize stuffSearchParams.endRow = startRow * stuffSearchParams.pageSize
@ -345,7 +350,7 @@ export default function Stuff() {
setStuffSearch({ setStuffSearch({
...stuffSearch, ...stuffSearch,
code: 'S', code: 'E',
startRow: startRow, startRow: startRow,
endRow: startRow * stuffSearchParams.pageSize, endRow: startRow * stuffSearchParams.pageSize,
pageSize: stuffSearchParams.pageSize, pageSize: stuffSearchParams.pageSize,
@ -371,7 +376,7 @@ export default function Stuff() {
setStuffSearch({ setStuffSearch({
...stuffSearch, ...stuffSearch,
code: 'S', code: 'E',
startRow: (page - 1) * pageSize + 1, startRow: (page - 1) * pageSize + 1,
endRow: page * stuffSearchParams?.pageSize, endRow: page * stuffSearchParams?.pageSize,
pageNo: page, pageNo: page,

View File

@ -7,7 +7,7 @@ import Select, { components } from 'react-select'
import Link from 'next/link' import Link from 'next/link'
import { useAxios } from '@/hooks/useAxios' import { useAxios } from '@/hooks/useAxios'
import { globalLocaleStore } from '@/store/localeAtom' import { globalLocaleStore } from '@/store/localeAtom'
import { isEmptyArray, isNotEmptyArray, isObjectNotEmpty } from '@/util/common-utils' import { isEmptyArray, isNotEmptyArray, isObjectNotEmpty, queryStringFormatter } 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, useResetRecoilState, useRecoilState } from 'recoil' import { useRecoilValue, useSetRecoilState, useResetRecoilState, useRecoilState } from 'recoil'
@ -75,7 +75,7 @@ export default function StuffDetail() {
installHeight: '', // installHeight: '', //
conType: '0', //( / ) conType: '0', //( / )
remarks: '', // remarks: '', //
tempFlag: 'T', //(1) (0) tempFlg: 'T', //(1) (0)
} }
const { register, setValue, getValues, handleSubmit, resetField, control, watch } = useForm({ const { register, setValue, getValues, handleSubmit, resetField, control, watch } = useForm({
defaultValues: formInitValue, defaultValues: formInitValue,
@ -108,7 +108,6 @@ export default function StuffDetail() {
const [editMode, setEditMode] = useState('NEW') const [editMode, setEditMode] = useState('NEW')
const { managementState, setManagementState } = useContext(ManagementContext) const { managementState, setManagementState } = useContext(ManagementContext)
const [planGridProps, setPlanGridProps] = useState({ const [planGridProps, setPlanGridProps] = useState({
planGridData: [], planGridData: [],
isPageable: false, isPageable: false,
@ -135,6 +134,7 @@ export default function StuffDetail() {
field: 'moduleModel', field: 'moduleModel',
headerName: getMessage('stuff.detail.planGridHeader.moduleModel'), headerName: getMessage('stuff.detail.planGridHeader.moduleModel'),
flex: 1, flex: 1,
wrapText: true,
cellStyle: { justifyContent: 'flex-start' /* 좌측정렬*/ }, cellStyle: { justifyContent: 'flex-start' /* 좌측정렬*/ },
}, },
{ {
@ -283,6 +283,7 @@ export default function StuffDetail() {
{getMessage('stuff.detail.planGrid.btn1')} {getMessage('stuff.detail.planGrid.btn1')}
</button> </button>
<button <button
style={buttonStyle}
type="button" type="button"
className="grid-btn" className="grid-btn"
onClick={() => { onClick={() => {
@ -309,6 +310,7 @@ export default function StuffDetail() {
useEffect(() => { useEffect(() => {
if (objectNo) { if (objectNo) {
setManagementState({})
setEditMode('EDIT') setEditMode('EDIT')
if (objectNo.substring(0, 1) !== 'T') { if (objectNo.substring(0, 1) !== 'T') {
// .. // ..
@ -321,7 +323,7 @@ export default function StuffDetail() {
} else { } else {
setManagementState({}) setManagementState({})
alert(getMessage('stuff.detail.header.notExistObjectNo')) alert(getMessage('stuff.detail.header.notExistObjectNo'))
router.push('/management/stuff') router.push('/management/stuff', { scroll: false })
} }
if (isNotEmptyArray(res.data.planList)) { if (isNotEmptyArray(res.data.planList)) {
setPlanGridProps({ ...planGridProps, planGridData: res.data.planList }) setPlanGridProps({ ...planGridProps, planGridData: res.data.planList })
@ -333,7 +335,7 @@ export default function StuffDetail() {
setPlanGridProps({ ...planGridProps, planGridData: [] }) setPlanGridProps({ ...planGridProps, planGridData: [] })
alert(getMessage('stuff.detail.header.notExistObjectNo')) alert(getMessage('stuff.detail.header.notExistObjectNo'))
router.push('/management/stuff') router.push('/management/stuff', { scroll: false })
} }
}) })
} else { } else {
@ -516,7 +518,6 @@ export default function StuffDetail() {
firstList = res firstList = res
favList = res.filter((row) => row.priority !== 'B') favList = res.filter((row) => row.priority !== 'B')
otherList = res.filter((row) => row.firstAgentYn === 'N') otherList = res.filter((row) => row.firstAgentYn === 'N')
setSaleStoreList(firstList) setSaleStoreList(firstList)
setFavoriteStoreList(firstList) setFavoriteStoreList(firstList)
setShowSaleStoreList(firstList) setShowSaleStoreList(firstList)
@ -547,6 +548,9 @@ export default function StuffDetail() {
form.setValue('otherSaleStoreLevel', managementState.saleStoreLevel) form.setValue('otherSaleStoreLevel', managementState.saleStoreLevel)
form.setValue('saleStoreLevel', '1') form.setValue('saleStoreLevel', '1')
form.setValue('saleStoreId', managementState.firstAgentId)
setSelOptions(managementState.firstAgentId)
} }
//No. //No.
@ -896,6 +900,8 @@ export default function StuffDetail() {
// //
const setPlanReqInfo = (info) => { const setPlanReqInfo = (info) => {
// console.log('session :::::::', session)
// console.log(' :::::::', info)
form.setValue('planReqNo', info.planReqNo) form.setValue('planReqNo', info.planReqNo)
form.setValue('objectStatusId', info.building) form.setValue('objectStatusId', info.building)
setSelectObjectStatusId(info.building) setSelectObjectStatusId(info.building)
@ -912,7 +918,11 @@ export default function StuffDetail() {
}) })
// WL_ // WL_
form.setValue('standardWindSpeedId', `WL_${info.windSpeed}`) if (info.windSpeed !== '') {
form.setValue('standardWindSpeedId', `WL_${info.windSpeed}`)
} else {
form.setValue('standardWindSpeedId', `WL_${info.windSpeed}`)
}
form.setValue('verticalSnowCover', info.verticalSnowCover) form.setValue('verticalSnowCover', info.verticalSnowCover)
form.setValue('surfaceType', info.surfaceType) form.setValue('surfaceType', info.surfaceType)
@ -1041,7 +1051,7 @@ export default function StuffDetail() {
errors.prefId = true errors.prefId = true
} }
if (!formData.areaId) { if (!formData.areaId || formData.areaId === '0') {
errors.areaId = true errors.areaId = true
} }
@ -1285,23 +1295,34 @@ export default function StuffDetail() {
} }
if (editMode === 'NEW') { if (editMode === 'NEW') {
await promisePost({ url: apiUrl, data: params }).then((res) => { await promisePost({ url: apiUrl, data: params })
// .then((res) => {
if (res.status === 201) { //
alert(getMessage('stuff.detail.save')) if (res.status === 201) {
setFloorPlanObjectNo({ floorPlanObjectNo: objectNo }) alert(getMessage('stuff.detail.save'))
router.push(`/management/stuff/detail?objectNo=${res.data.objectNo.toString()}`, { scroll: false }) setFloorPlanObjectNo({ floorPlanObjectNo: objectNo })
} router.push(`/management/stuff/detail?objectNo=${res.data.objectNo.toString()}`, { scroll: false })
}) }
})
.catch((error) => {
console.log('error::::::', error)
alert(error?.response.data.message)
})
} else { } else {
// PUT // PUT
await promisePut({ url: apiUrl, data: params }).then((res) => { // await promisePut({ url: apiUrl, data: params }).then((res) => {
if (res.status === 201) { await promisePut({ url: apiUrl, data: params })
setFloorPlanObjectNo({ floorPlanObjectNo: res.data.objectNo }) .then((res) => {
alert(getMessage('stuff.detail.save')) if (res.status === 201) {
router.push(`/management/stuff/detail?objectNo=${res.data.objectNo.toString()}`, { scroll: false }) setFloorPlanObjectNo({ floorPlanObjectNo: res.data.objectNo })
} alert(getMessage('stuff.detail.save'))
}) router.push(`/management/stuff/detail?objectNo=${res.data.objectNo.toString()}`, { scroll: false })
}
})
.catch((error) => {
console.log('error::::::', error)
alert(error?.response.data.message)
})
} }
} }
@ -1346,38 +1367,57 @@ export default function StuffDetail() {
const apiUrl = '/api/object/save-object' const apiUrl = '/api/object/save-object'
if (objectNo) { if (objectNo) {
await promisePut({ url: apiUrl, data: params }).then((res) => { await promisePut({ url: apiUrl, data: params })
if (res.status === 201) { .then((res) => {
alert(getMessage('stuff.detail.tempSave.message1')) if (res.status === 201) {
router.push(`/management/stuff/tempdetail?objectNo=${res.data.objectNo.toString()}`, { scroll: false }) alert(getMessage('stuff.detail.tempSave.message1'))
} router.push(`/management/stuff/tempdetail?objectNo=${res.data.objectNo.toString()}`, { scroll: false })
}) }
})
.catch((error) => {
console.log('error::::::', error)
alert(error?.response.data.message)
})
} else { } else {
await promisePost({ url: apiUrl, data: params }).then((res) => { await promisePost({ url: apiUrl, data: params })
if (res.status === 201) { .then((res) => {
alert(getMessage('stuff.detail.tempSave.message1')) if (res.status === 201) {
router.push(`/management/stuff/tempdetail?objectNo=${res.data.objectNo.toString()}`, { scroll: false }) alert(getMessage('stuff.detail.tempSave.message1'))
} router.push(`/management/stuff/tempdetail?objectNo=${res.data.objectNo.toString()}`, { scroll: false })
}) }
})
.catch((error) => {
console.log('error::::::', error)
alert(error?.response.data.message)
})
} }
} }
// //
const onDelete = () => { const onDelete = () => {
const specificationConfirmDate = managementState.specificationConfirmDate const specificationConfirmDate = managementState.specificationConfirmDate
const delParams = {
userId: session.userId,
}
if (specificationConfirmDate != null) { if (specificationConfirmDate != null) {
alert(getMessage('stuff.detail.delete.message1')) alert(getMessage('stuff.detail.delete.message1'))
} else { } else {
if (confirm(getMessage('common.message.data.delete'))) { if (confirm(getMessage('common.message.data.delete'))) {
del({ url: `/api/object/${objectNo}` }).then(() => { // setIsGlobalLoading(true)
setFloorPlanObjectNo({ floorPlanObjectNo: '' }) del({ url: `/api/object/${objectNo}?${queryStringFormatter(delParams)}` })
if (session.storeId === 'T01') { .then(() => {
stuffSearchParams.code = 'DELETE' setFloorPlanObjectNo({ floorPlanObjectNo: '' })
} else { if (session.storeId === 'T01') {
resetStuffRecoil() stuffSearchParams.code = 'DELETE'
} } else {
router.push('/management/stuff') resetStuffRecoil()
}) }
// setIsGlobalLoading(false)
router.push('/management/stuff', { scroll: false })
})
.catch(() => {
// setIsGlobalLoading(false)
})
} }
} }
} }
@ -1388,6 +1428,11 @@ export default function StuffDetail() {
input.value = input.value.replace(/[^0-9]/g, '') input.value = input.value.replace(/[^0-9]/g, '')
} }
const handleBlur = (e) => {
let input = e.target
input.value = input.value.replace(/[^0-9]/g, '')
}
// .. // ..
const NoOptionsMessage = (props) => { const NoOptionsMessage = (props) => {
return ( return (
@ -1775,7 +1820,8 @@ export default function StuffDetail() {
<input <input
type="text" type="text"
className="input-light" className="input-light"
onKeyUp={handleKeyUp} onInput={handleKeyUp}
onBlur={handleBlur}
value={form.watch('verticalSnowCover') || ''} value={form.watch('verticalSnowCover') || ''}
{...register('verticalSnowCover')} {...register('verticalSnowCover')}
/> />
@ -1842,7 +1888,8 @@ export default function StuffDetail() {
<input <input
type="text" type="text"
className="input-light" className="input-light"
onKeyUp={handleKeyUp} onInput={handleKeyUp}
onBlur={handleBlur}
value={form.watch('installHeight') || ''} value={form.watch('installHeight') || ''}
{...register('installHeight')} {...register('installHeight')}
/> />
@ -2032,8 +2079,8 @@ export default function StuffDetail() {
onChange={onSelectionChange} onChange={onSelectionChange}
getOptionLabel={(x) => x.saleStoreName} getOptionLabel={(x) => x.saleStoreName}
getOptionValue={(x) => x.saleStoreId} getOptionValue={(x) => x.saleStoreId}
isClearable={managementState.tempFlg === '0' ? false : session?.storeLvl === '1' ? true : false} isClearable={managementState?.tempFlg === '0' ? false : session?.storeLvl === '1' ? true : false}
isDisabled={managementState.tempFlg === '0' ? true : session?.storeLvl !== '1' ? true : false} isDisabled={managementState?.tempFlg === '0' ? true : session?.storeLvl !== '1' ? true : false}
value={saleStoreList.filter(function (option) { value={saleStoreList.filter(function (option) {
return option.saleStoreId === selOptions return option.saleStoreId === selOptions
})} })}
@ -2066,7 +2113,7 @@ export default function StuffDetail() {
getOptionValue={(x) => x.saleStoreId} getOptionValue={(x) => x.saleStoreId}
isClearable={false} isClearable={false}
isDisabled={ isDisabled={
managementState.tempFlg === '0' managementState?.tempFlg === '0'
? true ? true
: session?.storeLvl !== '1' : session?.storeLvl !== '1'
? true ? true
@ -2151,9 +2198,13 @@ export default function StuffDetail() {
getOptionLabel={(x) => x.saleStoreName} getOptionLabel={(x) => x.saleStoreName}
getOptionValue={(x) => x.saleStoreId} getOptionValue={(x) => x.saleStoreId}
isDisabled={ isDisabled={
managementState.tempFlg === '0' ? true : session?.storeLvl === '1' && form.watch('saleStoreId') != '' ? false : true managementState?.tempFlg === '0'
? true
: session?.storeLvl === '1' && form.watch('saleStoreId') != ''
? false
: true
} }
isClearable={managementState.tempFlg === '0' ? false : session?.storeLvl === '1' ? true : false} isClearable={managementState?.tempFlg === '0' ? false : session?.storeLvl === '1' ? true : false}
value={otherSaleStoreList.filter(function (option) { value={otherSaleStoreList.filter(function (option) {
return option.saleStoreId === otherSelOptions return option.saleStoreId === otherSelOptions
})} })}
@ -2293,7 +2344,8 @@ export default function StuffDetail() {
<input <input
type="text" type="text"
className="input-light" className="input-light"
onKeyUp={handleKeyUp} onInput={handleKeyUp}
onBlur={handleBlur}
value={form.watch('verticalSnowCover') || ''} value={form.watch('verticalSnowCover') || ''}
{...register('verticalSnowCover')} {...register('verticalSnowCover')}
/> />
@ -2365,7 +2417,8 @@ export default function StuffDetail() {
<input <input
type="text" type="text"
className="input-light" className="input-light"
onKeyUp={handleKeyUp} onInput={handleKeyUp}
onBlur={handleBlur}
value={form.watch('installHeight') || ''} value={form.watch('installHeight') || ''}
{...register('installHeight')} {...register('installHeight')}
/> />

View File

@ -83,7 +83,6 @@ export default function StuffSearchCondition() {
} }
setIsGlobalLoading(true) setIsGlobalLoading(true)
if (stuffSearch.code === 'S') { if (stuffSearch.code === 'S') {
if (stuffSearch.pageNo !== 1) { if (stuffSearch.pageNo !== 1) {
setStuffSearch({ setStuffSearch({
@ -254,6 +253,7 @@ export default function StuffSearchCondition() {
// //
const resetRecoil = () => { const resetRecoil = () => {
setIsGlobalLoading(false)
//T01 //T01
objectNoRef.current.value = '' objectNoRef.current.value = ''
saleStoreNameRef.current.value = '' saleStoreNameRef.current.value = ''
@ -291,17 +291,39 @@ export default function StuffSearchCondition() {
schSortType: 'R', schSortType: 'R',
pageNo: 1, pageNo: 1,
pageSize: 100, pageSize: 100,
code: 'S', // code: 'S',
}) })
} else { } else {
if (otherSaleStoreList.length > 1) { if (otherSaleStoreList.length > 1) {
handleClear2() handleClear2()
setOtherSaleStoreId('') setOtherSaleStoreId('')
stuffSearch.schObjectNo = '' stuffSearch.schObjectNo = ''
stuffSearch.schAddress = ''
stuffSearch.schObjectName = ''
stuffSearch.schSaleStoreName = ''
stuffSearch.schReceiveUser = ''
stuffSearch.schDispCompanyName = ''
stuffSearch.schDateType = 'U' stuffSearch.schDateType = 'U'
stuffSearch.startRow = 1
stuffSearch.endRow = 100
stuffSearch.schSortType = 'R'
stuffSearch.pageNo = 1
stuffSearch.pageSize = 100
} else { } else {
stuffSearch.schObjectNo = '' stuffSearch.schObjectNo = ''
stuffSearch.schAddress = ''
stuffSearch.schObjectName = ''
stuffSearch.schSaleStoreName = ''
stuffSearch.schReceiveUser = ''
stuffSearch.schDispCompanyName = ''
stuffSearch.schDateType = 'U' stuffSearch.schDateType = 'U'
stuffSearch.startRow = 1
stuffSearch.endRow = 100
stuffSearch.schSortType = 'R'
stuffSearch.pageNo = 1
stuffSearch.pageSize = 100
} }
} }
} }
@ -491,7 +513,6 @@ export default function StuffSearchCondition() {
} }
useEffect(() => { useEffect(() => {
//X42
if (session?.storeId === 'T01') { if (session?.storeId === 'T01') {
if (stuffSearch.code === 'DELETE') { if (stuffSearch.code === 'DELETE') {
setObjectNo('') setObjectNo('')
@ -523,6 +544,37 @@ export default function StuffSearchCondition() {
stuffSearch.pageNo = 1 stuffSearch.pageNo = 1
stuffSearch.pageSize = 100 stuffSearch.pageSize = 100
setSchSelSaleStoreId('')
setOtherSaleStoreId('')
} else if (stuffSearch.code === 'S') {
setObjectNo('')
setSaleStoreName('')
setAddress('')
setobjectName('')
setDispCompanyName('')
setReceiveUser('')
objectNoRef.current.value = ''
saleStoreNameRef.current.value = ''
addressRef.current.value = ''
objectNameRef.current.value = ''
dispCompanyNameRef.current.value = ''
receiveUserRef.current.value = ''
stuffSearch.schObjectNo = ''
stuffSearch.schAddress = ''
stuffSearch.schObjectName = ''
stuffSearch.schSaleStoreName = ''
stuffSearch.schReceiveUser = ''
stuffSearch.schDispCompanyName = ''
stuffSearch.schDateType = 'U'
stuffSearch.schFromDt = dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD')
stuffSearch.schToDt = dayjs(new Date()).format('YYYY-MM-DD')
stuffSearch.startRow = 1
stuffSearch.endRow = 100
stuffSearch.schSelSaleStoreId = ''
stuffSearch.schOtherSelSaleStoreId = ''
stuffSearch.schSortType = 'R'
stuffSearch.pageNo = 1
stuffSearch.pageSize = 100
setSchSelSaleStoreId('') setSchSelSaleStoreId('')
setOtherSaleStoreId('') setOtherSaleStoreId('')
} }
@ -564,7 +616,8 @@ export default function StuffSearchCondition() {
<h3>{getMessage('stuff.search.title')}</h3> <h3>{getMessage('stuff.search.title')}</h3>
</div> </div>
<div className="left-unit-box"> <div className="left-unit-box">
<Link href="/management/stuff/tempdetail" scroll={false}> <Link href="/management/stuff/tempReg" 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.btn.register')} {getMessage('stuff.search.btn.register')}
</button> </button>

View File

@ -1,19 +1,26 @@
'use client' 'use client'
import { useEffect } from 'react' import { useContext, useEffect } from 'react'
import Link from 'next/link' import Link from 'next/link'
import Image from 'next/image' import Image from 'next/image'
import { useMessage } from '@/hooks/useMessage'
import { useRouter, useSearchParams } from 'next/navigation' import { useRouter, useSearchParams } from 'next/navigation'
import { floorPlanObjectState } from '@/store/floorPlanObjectAtom'
import { useSetRecoilState } from 'recoil' import { useSetRecoilState } from 'recoil'
import { QcastContext } from '@/app/QcastProvider'
import { useMessage } from '@/hooks/useMessage'
import { floorPlanObjectState } from '@/store/floorPlanObjectAtom'
import { queryStringFormatter } from '@/util/common-utils' import { queryStringFormatter } from '@/util/common-utils'
export default function StuffSubHeader({ type }) { export default function StuffSubHeader({ type }) {
const { getMessage } = useMessage() const { getMessage } = useMessage()
const router = useRouter() const router = useRouter()
const setFloorPlanObjectNo = useSetRecoilState(floorPlanObjectState) const setFloorPlanObjectNo = useSetRecoilState(floorPlanObjectState)
const { isGlobalLoading } = useContext(QcastContext)
useEffect(() => { useEffect(() => {
window.scrollTo(0, 0) window.scrollTo(0, 0)
}, []) }, [])
@ -35,85 +42,87 @@ export default function StuffSubHeader({ type }) {
} }
return ( return (
<> !isGlobalLoading && (
<div className="sub-header"> <>
<div className="sub-header-inner"> <div className="sub-header">
{type === 'list' && ( <div className="sub-header-inner">
<> {type === 'list' && (
<Link href={'#'}> <>
<h1 className="sub-header-title">{getMessage('header.menus.management')}</h1> <Link href={'#'}>
</Link> <h1 className="sub-header-title">{getMessage('header.menus.management')}</h1>
<ul className="sub-header-location"> </Link>
<li className="location-item"> <ul className="sub-header-location">
<span className="home"> <li className="location-item">
<Image src="/static/images/main/home_icon.svg" alt="react" width={16} height={16} /> <span className="home">
</span> <Image src="/static/images/main/home_icon.svg" alt="react" width={16} height={16} />
</li> </span>
<li className="location-item"> </li>
<span>{getMessage('header.menus.management')}</span> <li className="location-item">
</li> <span>{getMessage('header.menus.management')}</span>
<li className="location-item"> </li>
<span>{getMessage('header.menus.management.stuffList')}</span> <li className="location-item">
</li> <span>{getMessage('header.menus.management.stuffList')}</span>
</ul> </li>
</> </ul>
)} </>
{type === 'temp' && ( )}
<> {type === 'temp' && (
<ul className="sub-header-title-wrap"> <>
<li className="title-item"> <ul className="sub-header-title-wrap">
<Link className="sub-header-title" href={'#'}> <li className="title-item">
{getMessage('stuff.temp.subTitle')} <Link className="sub-header-title" href={'#'}>
</Link> {getMessage('stuff.temp.subTitle')}
</li> </Link>
</ul> </li>
<ul className="sub-header-location"> </ul>
<li className="location-item"> <ul className="sub-header-location">
<span className="home"> <li className="location-item">
<Image src="/static/images/main/home_icon.svg" alt="react" width={16} height={16} /> <span className="home">
</span> <Image src="/static/images/main/home_icon.svg" alt="react" width={16} height={16} />
</li> </span>
<li className="location-item"> </li>
<span>{getMessage('header.menus.management')}</span> <li className="location-item">
</li> <span>{getMessage('header.menus.management')}</span>
<li className="location-item"> </li>
<span>{getMessage('header.menus.management.newStuff')}</span> <li className="location-item">
</li> <span>{getMessage('header.menus.management.newStuff')}</span>
</ul> </li>
</> </ul>
)} </>
{type === 'detail' && ( )}
<> {type === 'detail' && (
<ul className="sub-header-title-wrap"> <>
<li className="title-item"> <ul className="sub-header-title-wrap">
<Link className="sub-header-title" href={'#'}> <li className="title-item">
{getMessage('stuff.temp.subTitle')} <Link className="sub-header-title" href={'#'}>
</Link> {getMessage('stuff.temp.subTitle')}
</li> </Link>
<li className="title-item"> </li>
<a className="sub-header-title" onClick={moveFloorPlan}> <li className="title-item">
<span className="icon drawing"></span> <a className="sub-header-title" onClick={moveFloorPlan}>
{getMessage('stuff.temp.subTitle2')} <span className="icon drawing"></span>
</a> {getMessage('stuff.temp.subTitle2')}
</li> </a>
</ul> </li>
<ul className="sub-header-location"> </ul>
<li className="location-item"> <ul className="sub-header-location">
<span className="home"> <li className="location-item">
<Image src="/static/images/main/home_icon.svg" alt="react" width={16} height={16} /> <span className="home">
</span> <Image src="/static/images/main/home_icon.svg" alt="react" width={16} height={16} />
</li> </span>
<li className="location-item"> </li>
<span>{getMessage('header.menus.management')}</span> <li className="location-item">
</li> <span>{getMessage('header.menus.management')}</span>
<li className="location-item"> </li>
<span>{getMessage('header.menus.management.detail')}</span> <li className="location-item">
</li> <span>{getMessage('header.menus.management.detail')}</span>
</ul> </li>
</> </ul>
)} </>
)}
</div>
</div> </div>
</div> </>
</> )
) )
} }

View File

@ -5,14 +5,28 @@ import { globalLocaleStore } from '@/store/localeAtom'
import { isObjectNotEmpty } from '@/util/common-utils' import { isObjectNotEmpty } from '@/util/common-utils'
import { useAxios } from '../useAxios' import { useAxios } from '../useAxios'
/**
* 공통코드 관리를 위한 커스텀
*
* @description
* - 공통코드를 전역 상태로 관리하고 필요한 곳에서 사용할 있도록
* - 최초 1회만 API를 호출하여 공통코드를 가져옴
* - 언어 설정에 따라 한국어/일본어 코드명을 자동으로 변환
*
* @returns {Object}
* - commonCode: 전체 공통코드 객체(recoil state)
* - findCommonCode: 특정 공통코드 그룹을 조회하는 함수
*
* @example
* const { commonCode, findCommonCode } = useCommonCode();
* const honorificCodes = findCommonCode(200800);
*/
export const useCommonCode = () => { export const useCommonCode = () => {
const [commonCode, setCommonCode] = useRecoilState(commonCodeState) const [commonCode, setCommonCode] = useRecoilState(commonCodeState)
const globalLocale = useRecoilValue(globalLocaleStore) const globalLocale = useRecoilValue(globalLocaleStore)
const { promiseGet } = useAxios() const { promiseGet } = useAxios()
const findCommonCode = (key) => { const findCommonCode = (key) => {
// const arr = commonCode[key]
// return arr.sort((a, b) => a.clPriority - b.clPriority)
const resultCodes = commonCode[key]?.map((code) => { const resultCodes = commonCode[key]?.map((code) => {
const result = { const result = {
clHeadCd: code.clHeadCd, clHeadCd: code.clHeadCd,

View File

@ -1,83 +1,161 @@
import { useEffect, useRef, useState } from 'react' import { useEffect, useRef, useState } from 'react'
import { useRecoilState } from 'recoil' import { useRecoilState, useRecoilValue } from 'recoil'
import { v4 as uuidv4 } from 'uuid'
import { useSwal } from '@/hooks/useSwal' import { useSwal } from '@/hooks/useSwal'
import { useAxios } from '../useAxios' import { useAxios } from '../useAxios'
import { currentCanvasPlanState } from '@/store/canvasAtom' import { canvasState, currentCanvasPlanState } from '@/store/canvasAtom'
import { convertDwgToPng, writeImageBuffer } from '@/lib/fileAction' import { convertDwgToPng, removeImage, writeImageBuffer, readImage } from '@/lib/fileAction'
import { useCanvas } from '@/hooks/useCanvas'
export function useRefFiles() { export function useRefFiles() {
const converterUrl = process.env.NEXT_PUBLIC_CONVERTER_API_URL const converterUrl = process.env.NEXT_PUBLIC_CONVERTER_API_URL
const [refImage, setRefImage] = useState(null) const [refImage, setRefImage] = useState(null)
const [refFileMethod, setRefFileMethod] = useState('1') const [refFileMethod, setRefFileMethod] = useState('1')
const [mapPositionAddress, setMapPositionAddress] = useState('') const [mapPositionAddress, setMapPositionAddress] = useState('')
const [currentCanvasPlan, setCurrentCanvasPlan] = useRecoilState(currentCanvasPlanState) const [currentBgImage, setCurrentBgImage] = useState(null)
const queryRef = useRef(null) const queryRef = useRef(null)
const [currentCanvasPlan, setCurrentCanvasPlan] = useRecoilState(currentCanvasPlanState)
const canvas = useRecoilValue(canvasState)
const { handleBackImageLoadToCanvas } = useCanvas()
const { swalFire } = useSwal() const { swalFire } = useSwal()
const { get, promisePut, promisePost } = useAxios() const { get, post, promisePost } = useAxios()
// const { currentCanvasPlan, setCurrentCanvasPlan } = usePlan()
useEffect(() => {
if (refFileMethod === '1') {
// 파일 불러오기
setMapPositionAddress('')
} else {
setRefImage(null)
}
}, [refFileMethod])
/**
* 현재 플랜이 변경되면 플랜 상태 저장
*/
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])
/** /**
* 파일 불러오기 버튼 컨트롤 * 파일 불러오기 버튼 컨트롤
* @param {*} file * @param {*} file
*/ */
const handleRefFile = (file) => { const handleRefFile = (file) => {
setRefImage(file) console.log('handleRefFile', file)
/** console.log('refImage', refImage)
* 파일 확장자가 dwg일 경우 변환하여 이미지로 저장
* 파일 확장자가 이미지일 경우 이미지 저장 if (refImage) {
*/ swalFire({
file.name.split('.').pop() === 'dwg' ? handleUploadConvertRefFile(file) : handleUploadImageRefFile(file) text: '파일을 변경하시겠습니까?',
// handleUploadRefFile(file) type: 'confirm',
confirmFn: () => {
refFileSetting(file)
// setRefImage(file)
// file.name.split('.').pop() === 'dwg' ? handleUploadConvertRefFile(file) : handleUploadImageRefFile(file)
},
})
} else {
refFileSetting(file)
}
} }
const refFileSetting = (file) => {
if (file && ['image/png', 'image/jpg', 'image/jpeg', 'image/bmp', 'image/gif'].includes(file.type)) {
// setRefImage(file)
file.name.split('.').pop() === 'dwg' ? handleUploadConvertRefFile(file) : handleUploadImageRefFile(file)
} else {
swalFire({
text: '이미지가 아닙니다.',
type: 'alert',
icon: 'error',
})
}
}
/** /**
* 파일 삭제 * 파일 삭제
*/ */
const handleFileDelete = () => { const handleFileDelete = () => {
setRefImage(null) swalFire({
setCurrentCanvasPlan((prev) => ({ ...prev, bgFileName: null })) text: '삭제하시겠습니까?',
type: 'confirm',
confirmFn: () => {
setRefImage(null)
setCurrentCanvasPlan((prev) => ({ ...prev, bgFileName: null }))
removeImage(currentCanvasPlan.id).then((res) => {
console.log(res)
})
},
})
} }
/** /**
* 주소 삭제 * 주소 삭제
*/ */
const handleAddressDelete = () => { const handleAddressDelete = () => {
setCurrentCanvasPlan((prev) => ({ ...prev, mapPositionAddress: null })) swalFire({
text: '삭제하시겠습니까?',
type: 'confirm',
confirmFn: () => {
setMapPositionAddress('')
setCurrentCanvasPlan((prev) => ({ ...prev, mapPositionAddress: null }))
removeImage(currentCanvasPlan.id).then((res) => {
console.log(res)
})
},
})
} }
/** /**
* 주소로 구글 이미지 다운로드 * 주소로 구글 이미지 다운로드
*/ */
const handleMapImageDown = async () => { const handleMapImageDown = async () => {
console.log('🚀 ~ handleMapImageDown ~ handleMapImageDown:')
if (queryRef.current.value === '' || queryRef.current.value === null) { if (queryRef.current.value === '' || queryRef.current.value === null) {
return return
} }
const res = await get({ url: `http://localhost:3000/api/html2canvas?q=${queryRef.current.value}&fileNm=${uuidv4()}&zoom=20` }) const res = await get({
url: `http://localhost:3000/api/html2canvas?q=${queryRef.current.value}&fileNm=${currentCanvasPlan.id}&zoom=20`,
})
console.log('🚀 ~ handleMapImageDown ~ res:', res) console.log('🚀 ~ handleMapImageDown ~ res:', res)
setCurrentCanvasPlan((prev) => ({ ...prev, bgImageName: res.fileNm, mapPositionAddress: queryRef.current.value })) const file = await readImage(res.fileNm)
console.log('🚀 ~ handleMapImageDown ~ file:', file)
setCurrentBgImage(file)
// handleBackImageLoadToCanvas(`plan-images/${currentCanvasPlan.id}.png`)
// setCurrentCanvasPlan((prev) => ({ ...prev, bgImageName: currentCanvasPlan.id, mapPositionAddress: queryRef.current.value }))
} }
useEffect(() => {
if (!currentBgImage) {
return
}
console.log('🚀 ~ useEffect ~ currentBgImage:', currentBgImage)
handleBackImageLoadToCanvas(`plan-images/${currentCanvasPlan.id}.png`)
setCurrentCanvasPlan((prev) => ({ ...prev, bgImageName: refImage?.name ?? null, mapPositionAddress: queryRef.current.value }))
}, [currentBgImage])
/** /**
* 이미지 파일 업로드 * 이미지 파일 업로드
* @param {*} file * @param {*} file
*/ */
const handleUploadImageRefFile = async (file) => { const handleUploadImageRefFile = async (file) => {
console.log('🚀 ~ handleUploadImageRefFile ~ file:', file)
const formData = new FormData() const formData = new FormData()
formData.append('file', file) formData.append('file', file)
formData.append('fileName', currentCanvasPlan.id)
const response = await fetch('http://localhost:3000/api/image-upload', { const res = await post({ url: 'http://localhost:3000/api/image-upload', data: formData })
method: 'POST', console.log('🚀 ~ handleUploadImageRefFile ~ res:', res)
body: formData, const image = await readImage(res.fileNm)
}) console.log('🚀 ~ handleUploadImageRefFile ~ file:', image)
const result = await response.json() setCurrentBgImage(image)
console.log('🚀 ~ handleUploadImageRefFile ~ res:', result) setRefImage(file)
// writeImageBuffer(file)
} }
/** /**
@ -92,6 +170,7 @@ export function useRefFiles() {
.then((res) => { .then((res) => {
convertDwgToPng(res.data.Files[0].FileName, res.data.Files[0].FileData) convertDwgToPng(res.data.Files[0].FileName, res.data.Files[0].FileData)
swalFire({ text: '파일 변환 성공' }) swalFire({ text: '파일 변환 성공' })
setRefImage(res.data.Files[0].FileData)
}) })
.catch((err) => { .catch((err) => {
swalFire({ text: '파일 변환 실패', icon: 'error' }) swalFire({ text: '파일 변환 실패', icon: 'error' })
@ -106,19 +185,6 @@ 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

@ -3,7 +3,7 @@ import { useContext, useEffect, useReducer, useState } from 'react'
import { useRecoilState, useRecoilValue } from 'recoil' import { useRecoilState, useRecoilValue } from 'recoil'
import { globalLocaleStore } from '@/store/localeAtom' import { globalLocaleStore } from '@/store/localeAtom'
import { estimateState, floorPlanObjectState } from '@/store/floorPlanObjectAtom' import { estimateState, floorPlanObjectState } from '@/store/floorPlanObjectAtom'
import { isObjectNotEmpty, isNotEmptyArray } from '@/util/common-utils' import { isObjectNotEmpty, isEmptyArray, isNotEmptyArray } 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' import { useRouter } from 'next/navigation'
@ -18,6 +18,9 @@ const updateItemInList = (itemList, dispOrder, updates) => {
} }
export const useEstimateController = (planNo) => { export const useEstimateController = (planNo) => {
const [fileList, setFileList] = useState([])
const [deleteFileList, setDeleteFileList] = useState([])
const router = useRouter() const router = useRouter()
const { session } = useContext(SessionContext) const { session } = useContext(SessionContext)
const globalLocaleState = useRecoilValue(globalLocaleStore) const globalLocaleState = useRecoilValue(globalLocaleStore)
@ -39,6 +42,12 @@ export const useEstimateController = (planNo) => {
} }
}, []) }, [])
useEffect(() => {
if (fileList.length > 0) {
realSave(fileList)
}
}, [fileList])
// 상세 조회 // 상세 조회
const fetchSetting = async (objectNo, planNo) => { const fetchSetting = async (objectNo, planNo) => {
try { try {
@ -50,6 +59,14 @@ export const useEstimateController = (planNo) => {
item.delFlg = '0' item.delFlg = '0'
}) })
} }
if (res.data.pkgAsp === null || res.data.pkgAsp == undefined) {
res.data.pkgAsp = '0.00'
} else {
const number = parseFloat(res.data.pkgAsp)
const roundedNumber = isNaN(number) ? '0.00' : number.toFixed(2)
res.data.pkgAsp = roundedNumber.toString()
}
setEstimateContextState(res.data) setEstimateContextState(res.data)
} }
} }
@ -68,7 +85,7 @@ export const useEstimateController = (planNo) => {
} }
const addItem = () => { const addItem = () => {
let newItemDispOrder = Math.max(...estimateContextState.itemList.map((item) => item.dispOrder)) let newItemDispOrder = estimateContextState.itemList.length === 0 ? 0 : Math.max(...estimateContextState.itemList.map((item) => item.dispOrder))
newItemDispOrder = (Math.floor(newItemDispOrder / 100) + 1) * 100 newItemDispOrder = (Math.floor(newItemDispOrder / 100) + 1) * 100
setEstimateContextState({ setEstimateContextState({
itemList: [ itemList: [
@ -89,6 +106,7 @@ export const useEstimateController = (planNo) => {
partAdd: '1', //NEW 체인지 플래그 partAdd: '1', //NEW 체인지 플래그
delFlg: '0', //삭제 플래그 0 삭제하면 1 delFlg: '0', //삭제 플래그 0 삭제하면 1
addFlg: true, addFlg: true,
paDispOrder: null,
}, },
], ],
}) })
@ -127,6 +145,7 @@ export const useEstimateController = (planNo) => {
const handleEstimateSubmit = async () => { const handleEstimateSubmit = async () => {
//0. 필수체크 //0. 필수체크
let flag = true let flag = true
let originFileFlg = false
let fileFlg = true let fileFlg = true
let itemFlg = true let itemFlg = true
if (estimateData.charger.trim().length === 0) { if (estimateData.charger.trim().length === 0) {
@ -144,23 +163,44 @@ export const useEstimateController = (planNo) => {
return alert(getMessage('estimate.detail.save.requiredEstimateDate')) return alert(getMessage('estimate.detail.save.requiredEstimateDate'))
} }
//첨부파일을 첨부안했는데 if (estimateData.estimateType === 'YJSS') {
//아이템 fileUploadFlg가1(첨부파일 필수)이 1개라도 있는데 후일 자료 제출(fileFlg) 체크안했으면(0) alert 저장안돼 let pkgAsp = estimateData.pkgAsp
if (pkgAsp === '0') {
flag = false
return alert(getMessage('estimate.detail.save.requiredPkgAsp'))
}
}
//기존에 첨부된 파일이 있으면 파일첨부관련 통과
if (estimateData?.originFiles?.length > 0) {
let cnt = estimateData.originFiles.filter((file) => file.delFlg === '0').length
if (cnt == 0) {
originFileFlg = false
} else {
originFileFlg = true
}
}
if (flag) { if (flag) {
if (estimateData.fileList.length < 1) { if (!originFileFlg) {
if (estimateData.itemList.length > 1) { //기존에 첨부된 파일이 없으면
estimateData.itemList.map((row) => { if (isEmptyArray(estimateData.newFileList)) {
if (row.delFlg === '0') { //새로 첨부한 파일이 없으면
if (row.fileUploadFlg === '1') { if (estimateData.itemList.length > 1) {
if (fileFlg) { estimateData.itemList.map((row) => {
if (estimateData.fileFlg === '0') { if (row.delFlg === '0') {
fileFlg = false if (row.fileUploadFlg === '1') {
return alert(getMessage('estimate.detail.save.requiredFileUpload')) if (fileFlg) {
if (estimateData.fileFlg === '0') {
fileFlg = false
return alert(getMessage('estimate.detail.save.requiredFileUpload'))
}
} }
} }
} }
} })
}) }
} }
} }
} }
@ -168,10 +208,16 @@ export const useEstimateController = (planNo) => {
if (fileFlg) { if (fileFlg) {
estimateData.itemList.map((item) => { estimateData.itemList.map((item) => {
if (item.delFlg === '0') { if (item.delFlg === '0') {
item.amount = item.amount?.replaceAll(',', '') if (item.addFlg) {
item.salePrice = parseFloat(item.salePrice?.replaceAll(',', '')).toFixed(2) if (item.itemId === '') {
item.saleTotPrice = parseFloat(item.saleTotPrice?.replaceAll(',', '')).toFixed(2) itemFlg = false
return alert(getMessage('estimate.detail.save.requiredItemId'))
}
}
item.amount = item.amount?.replaceAll(',', '')
item.salePrice = Number(item.salePrice?.replaceAll(',', '')).toFixed(2)
item.saleTotPrice = Number(item.saleTotPrice?.replaceAll(',', '')).toFixed(2)
if (!item.paDispOrder) { if (!item.paDispOrder) {
if (itemFlg) { if (itemFlg) {
if (isNaN(item.amount)) { if (isNaN(item.amount)) {
@ -188,39 +234,27 @@ export const useEstimateController = (planNo) => {
item.salePrice = '0' item.salePrice = '0'
} }
if (item.salePrice < 1) { if (item.openFlg !== '1') {
itemFlg = false if (item.salePrice < 1) {
return alert(getMessage('estimate.detail.save.requiredSalePrice')) itemFlg = false
return alert(getMessage('estimate.detail.save.requiredSalePrice'))
}
} }
estimateData.pkgAsp = '0' estimateData.pkgAsp = '0'
estimateData.pkgTotPrice = '0' estimateData.pkgTotPrice = '0'
} else {
if (item.pkgMaterialFlg === '1') {
if (isNaN(item.salePrice)) {
itemFlg = false
return alert(getMessage('estimate.detail.save.requiredSalePrice'))
}
}
} }
} }
} }
} }
}) })
}
if (flag && fileFlg && itemFlg) {
//1. 첨부파일 저장시작
const formData = new FormData()
if (isNotEmptyArray(estimateData.tempFileList) > 1) {
estimateData.tempFileList.forEach((file) => {
formData.append('files', file)
})
formData.append('objectNo', estimateData.objectNo)
formData.append('planNo', estimateData.planNo)
formData.append('category', '10')
formData.append('userId', estimateData.userId)
await post({ url: '/api/file/fileUpload', data: formData })
}
//첨부파일저장끝
//제품라인 추가했는데 아이템 안고르고 저장하면itemId=''은 날리고 나머지 저장하기
// estimateData.itemList = estimateData.itemList.filter((item) => item.itemId !== '')
estimateData.itemList = estimateData.itemList.filter((item) => item.delFlg === '0' || !item.addFlg) estimateData.itemList = estimateData.itemList.filter((item) => item.delFlg === '0' || !item.addFlg)
let delCnt = 0 let delCnt = 0
@ -232,30 +266,104 @@ export const useEstimateController = (planNo) => {
if (delCnt === estimateData.itemList.length) { if (delCnt === estimateData.itemList.length) {
return alert(getMessage('estimate.detail.save.requiredItem')) return alert(getMessage('estimate.detail.save.requiredItem'))
} }
}
let option = [] if (flag && fileFlg && itemFlg) {
estimateData.itemList.forEach((item) => { //1. 첨부파일 저장시작
if (item.specialNoteCd) { const formData = new FormData()
let split2 = item.specialNoteCd.split('、') if (estimateData?.newFileList?.length > 0) {
option = option.concat(split2) estimateData.newFileList.forEach((file) => {
formData.append('files', file)
})
formData.append('objectNo', estimateData.objectNo)
formData.append('planNo', estimateData.planNo)
formData.append('category', '10')
formData.append('userId', estimateData.userId)
await post({ url: '/api/file/fileUpload', data: formData }).then((res) => {
setFileList(res)
})
} else {
setFileList([])
realSave()
}
}
}
const realSave = async (fileList) => {
//첨부파일저장끝
let option = []
estimateData.itemList.forEach((item) => {
if (item.specialNoteCd) {
let split2 = item.specialNoteCd.split('、')
option = option.concat(split2)
}
})
let estimateOptions = ''
let estimateOptionsArray
estimateData.specialNoteList.map((item) => {
if (item.pkgYn === '0') {
if (item.check) {
if (estimateOptions === '') {
estimateOptions = item.code
} else {
estimateOptions += '、' + item.code
}
}
} else {
if (item.check) {
let flg = '0'
for (let i = 0; i < estimateData.uniqueData.length; i++) {
if (item.code.indexOf(estimateData.uniqueData[i]) > -1) {
flg = '1'
}
if (flg === '1') {
estimateOptions += '、' + estimateData.uniqueData[i]
}
}
}
}
})
estimateOptionsArray = estimateOptions.split('、').sort()
estimateOptionsArray = Array.from(new Set(estimateOptionsArray))
estimateOptions = estimateOptionsArray.join('、')
estimateData.estimateOption = estimateOptions
// console.log('최종아이템:::', estimateData.itemList)
if (fileList?.length > 0) {
estimateData.fileList = fileList
} else {
estimateData.fileList = []
}
if (estimateData.originFiles?.length > 0) {
estimateData.deleteFileList = estimateData.originFiles?.filter((item) => item.delFlg === '1')
} else {
estimateData.deleteFileList = []
}
if (estimateData.estimateType === 'YJSS') {
estimateData.pkgAsp = estimateData.pkgAsp.replaceAll(',', '')
}
console.log('최종저장::', estimateData)
//2. 상세데이터 저장
// return
try {
await promisePost({ url: `${ESTIMATE_API_ENDPOINT}/save-estimate`, data: estimateData }).then((res) => {
if (res.status === 201) {
estimateData.newFileList = []
// estimateData.originFiles = []
alert(getMessage('estimate.detail.save.alertMsg'))
//어디로 보낼지
fetchSetting(objectRecoil.floorPlanObjectNo, estimateData.planNo)
} }
}) })
} catch (e) {
console.log('아이템리스트::', estimateData.itemList) console.log('error::::::::::::', e.response.data.message)
console.log('최종 정보::;', estimateData)
//2. 상세데이터 저장
// return
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)
}
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,16 +1,43 @@
import { useCallback, useEffect, useState } from 'react' import { useCallback, useEffect, useState } from 'react'
import { useRecoilState, useRecoilValue } from 'recoil' import { useRecoilState, useRecoilValue, useResetRecoilState, useSetRecoilState } from 'recoil'
import { adsorptionPointModeState, adsorptionRangeState, canvasState, planSizeSettingState } from '@/store/canvasAtom' import {
adsorptionPointModeState,
adsorptionRangeState,
canvasState,
planSizeSettingState,
dotLineGridSettingState,
canvasSettingState,
} from '@/store/canvasAtom'
import { globalLocaleStore } from '@/store/localeAtom' import { globalLocaleStore } from '@/store/localeAtom'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { useAxios } from '@/hooks/useAxios' import { useAxios } from '@/hooks/useAxios'
import { useSwal } from '@/hooks/useSwal' import { useSwal } from '@/hooks/useSwal'
import { correntObjectNoState, corridorDimensionSelector, settingModalFirstOptionsState, settingModalSecondOptionsState } from '@/store/settingAtom' import {
correntObjectNoState,
corridorDimensionSelector,
settingModalFirstOptionsState,
settingModalSecondOptionsState,
settingModalGridOptionsState,
basicSettingState,
settingsState,
} from '@/store/settingAtom'
import { POLYGON_TYPE } from '@/common/common' import { POLYGON_TYPE } from '@/common/common'
import { globalFontAtom } from '@/store/fontAtom' import { globalFontAtom } from '@/store/fontAtom'
import { dimensionLineSettingsState } from '@/store/commonUtilsAtom' import { dimensionLineSettingsState } from '@/store/commonUtilsAtom'
import { gridColorState } from '@/store/gridAtom'
import { useColor } from 'react-color-palette'
let objectNo const defaultDotLineGridSetting = {
INTERVAL: {
type: 2, // 1: 가로,세로 간격 수동, 2: 비율 간격
ratioInterval: 910,
verticalInterval: 910,
horizontalInterval: 910,
dimension: 1, // 치수
},
DOT: false,
LINE: false,
}
export function useCanvasSetting() { export function useCanvasSetting() {
const canvas = useRecoilValue(canvasState) const canvas = useRecoilValue(canvasState)
@ -20,8 +47,11 @@ export function useCanvasSetting() {
const [settingModalFirstOptions, setSettingModalFirstOptions] = useRecoilState(settingModalFirstOptionsState) const [settingModalFirstOptions, setSettingModalFirstOptions] = useRecoilState(settingModalFirstOptionsState)
const [settingModalSecondOptions, setSettingModalSecondOptions] = useRecoilState(settingModalSecondOptionsState) const [settingModalSecondOptions, setSettingModalSecondOptions] = useRecoilState(settingModalSecondOptionsState)
// const [settingsData, setSettingsData] = useRecoilState(settingsState)
const [settingsData, setSettingsData] = useState({ ...settingModalFirstOptions, ...settingModalSecondOptions })
const { option1, option2, dimensionDisplay } = settingModalFirstOptions const { option1, option2, dimensionDisplay } = settingModalFirstOptions
const { option3, option4 } = settingModalSecondOptions const { option4 } = settingModalSecondOptions
const corridorDimension = useRecoilValue(corridorDimensionSelector) const corridorDimension = useRecoilValue(corridorDimensionSelector)
@ -42,6 +72,27 @@ export function useCanvasSetting() {
const [globalFont, setGlobalFont] = useRecoilState(globalFontAtom) const [globalFont, setGlobalFont] = useRecoilState(globalFontAtom)
const [dimensionLineSettings, setDimensionLineSettings] = useRecoilState(dimensionLineSettingsState) const [dimensionLineSettings, setDimensionLineSettings] = useRecoilState(dimensionLineSettingsState)
const setSettingModalGridOptions = useSetRecoilState(settingModalGridOptionsState)
const [dotLineGridSetting, setDotLineGridSettingState] = useRecoilState(dotLineGridSettingState)
const resetDotLineGridSetting = useResetRecoilState(dotLineGridSettingState)
const [currentSetting, setCurrentSetting] = useState(
JSON.stringify(dotLineGridSetting) === JSON.stringify(defaultDotLineGridSetting) ? { ...defaultDotLineGridSetting } : { ...dotLineGridSetting },
)
const [gridColor, setGridColor] = useRecoilState(gridColorState)
const [color, setColor] = useColor(gridColor ?? '#FF0000')
const [colorTemp, setColorTemp] = useState()
const [canvasSetting, setCanvasSetting] = useRecoilState(canvasSettingState)
const [basicSetting, setBasicSettings] = useRecoilState(basicSettingState)
const SelectOptions = [
{ id: 1, name: getMessage('modal.canvas.setting.grid.dot.line.setting.line.origin'), value: 1 },
{ id: 2, name: '1/2', value: 1 / 2 },
{ id: 3, name: '1/4', value: 1 / 4 },
{ id: 4, name: '1/10', value: 1 / 10 },
]
const [selectOption, setSelectOption] = useState(SelectOptions[0])
useEffect(() => { useEffect(() => {
if (!canvas) { if (!canvas) {
return return
@ -72,51 +123,66 @@ export function useCanvasSetting() {
canvas?.renderAll() canvas?.renderAll()
}, [corridorDimension]) }, [corridorDimension])
// 배치면 초기설정 변경 시
useEffect(() => { useEffect(() => {
console.log('useCanvasSetting useEffect 실행1', correntObjectNo) //console.log('useCanvasSetting canvasSetting 실행', canvasSetting)
}, []) if (canvasSetting.flag) {
basicSettingSave()
}
}, [canvasSetting])
useEffect(() => {
console.log('🚀 ~ useEffect ~ settingsData:', settingsData)
}, [settingsData])
//흡착점 ON/OFF 변경 시 //흡착점 ON/OFF 변경 시
useEffect(() => { // useEffect(() => {
console.log('useCanvasSetting useEffect 실행2', adsorptionPointMode.fontFlag, correntObjectNo) // //console.log('useCanvasSetting 실행2', adsorptionPointMode.fontFlag, correntObjectNo)
// if (adsorptionPointMode.fontFlag) {
if (adsorptionPointMode.fontFlag) { // onClickOption2()
onClickOption2() // }
frontSettings() // }, [adsorptionPointMode])
fetchSettings()
}
}, [adsorptionPointMode])
// 1 과 2 변경 시 // 1 과 2 변경 시
useEffect(() => { // useEffect(() => {
console.log('useCanvasSetting useEffect 실행3', settingModalFirstOptions.fontFlag, settingModalSecondOptions.fontFlag, correntObjectNo) // //console.log('useCanvasSetting 실행3', settingModalFirstOptions.fontFlag, settingModalSecondOptions.fontFlag, correntObjectNo)
if (settingModalFirstOptions.fontFlag || settingModalSecondOptions.fontFlag) { // if (settingModalFirstOptions.fontFlag || settingModalSecondOptions.fontFlag) {
onClickOption2() // onClickOption2()
frontSettings() // }
fetchSettings() // }, [settingModalFirstOptions, settingModalSecondOptions])
}
}, [settingModalFirstOptions, settingModalSecondOptions])
// 글꼴 변경 시 // 글꼴 변경 시
useEffect(() => { // useEffect(() => {
console.log('useCanvasSetting useEffect 실행4', globalFont.fontFlag, correntObjectNo) // //console.log('useCanvasSetting 실행4', globalFont.fontFlag, correntObjectNo)
if (globalFont.fontFlag) { // if (globalFont.fontFlag) {
onClickOption2() // onClickOption2()
frontSettings() // }
fetchSettings() // }, [globalFont])
}
}, [globalFont])
// 도명크기 변경 시 // 도명크기 변경 시
useEffect(() => { // useEffect(() => {
console.log('useCanvasSetting useEffect 실행5', planSizeSettingMode.flag, correntObjectNo) // //console.log('useCanvasSetting 실행5', planSizeSettingMode.flag, correntObjectNo)
// if (planSizeSettingMode.flag) {
// onClickOption2()
// }
// }, [planSizeSettingMode])
if (planSizeSettingMode.flag) { // 점/선 그리드 변경 시
onClickOption2() // useEffect(() => {
frontSettings() // //console.log('useCanvasSetting 실행6', dotLineGridSetting.flag)
fetchSettings() // if (dotLineGridSetting.flag) {
} // onClickOption2()
}, [planSizeSettingMode]) // }
// }, [dotLineGridSetting])
// 그리드 색 설정 변경 시
// useEffect(() => {
// console.log('useCanvasSetting 실행7', colorTemp, gridColor)
// //colorTemp는 변경 전.. 값이 있고 변경된 컬러와 다를 때 실행
// if (colorTemp !== undefined && colorTemp !== gridColor) {
// onClickOption2()
// }
// }, [color])
const getFonts = (itemValue) => { const getFonts = (itemValue) => {
if (!itemValue) return { id: 1, name: 'MS PGothic', value: 'MS PGothic' } if (!itemValue) return { id: 1, name: 'MS PGothic', value: 'MS PGothic' }
@ -189,6 +255,95 @@ export function useCanvasSetting() {
} }
} }
// 기본설정(PlacementShapeSetting) 조회 및 초기화
const fetchBasicSettings = async () => {
try {
await get({ url: `/api/canvas-management/canvas-basic-settings/by-object/${correntObjectNo}` }).then((res) => {
console.log('fetchBasicSettings res ', res)
if (res.length == 0) return
// 'roofs' 배열을 생성하여 각 항목을 추가
const roofsRow = res.map((item) => {
return {
roofSizeSet: item.roofSizeSet,
roofAngleSet: item.roofAngleSet,
}
})
const roofsArray = res.some((item) => !item.roofSeq)
? //최초 지붕재 추가 정보의 경우 roofsArray를 초기화 설정
res.map(() => ({
flag: false,
roofApply: true,
roofSeq: 1,
roofType: 1,
roofWidth: 265,
roofHeight: 235,
roofHajebichi: 0,
roofGap: 455,
// roofType: 1,
// roofWidth: 200,
// roofHeight: 200,
// roofHajebichi: 200,
// roofGap: 0,
roofLayout: 'parallel',
}))
: res.map((item) => ({
flag: false,
roofApply: item.roofApply === '' || item.roofApply === false ? false : true,
roofSeq: item.roofSeq,
roofType: item.roofType,
roofWidth: item.roofWidth,
roofHeight: item.roofHeight,
roofHajebichi: item.roofHajebichi,
roofGap: item.roofGap,
roofLayout: item.roofLayout,
}))
console.log('roofsArray ', roofsArray)
// 나머지 데이터와 함께 'roofs' 배열을 patternData에 넣음
const patternData = {
roofSizeSet: roofsRow[0].roofSizeSet, // 첫 번째 항목의 값을 사용
roofAngleSet: roofsRow[0].roofAngleSet, // 첫 번째 항목의 값을 사용
roofs: roofsArray, // 만들어진 roofs 배열
}
//console.error('patternData', patternData)
// 데이터 설정
setBasicSettings({ ...patternData })
})
} catch (error) {
console.error('Data fetching error:', error)
}
if (!(Object.keys(canvasSetting).length === 0 && canvasSetting.constructor === Object)) {
setBasicSettings({ ...canvasSetting })
}
//setCanvasSetting({ ...basicSetting })
}
// 기본설정(PlacementShapeSetting) 저장
const basicSettingSave = async () => {
try {
const patternData = {
objectNo: correntObjectNo,
roofSizeSet: basicSetting.roofSizeSet,
roofAngleSet: basicSetting.roofAngleSet,
roofMaterialsAddList: basicSetting.roofs,
}
await post({ url: `/api/canvas-management/canvas-basic-settings`, data: patternData }).then((res) => {
swalFire({ text: getMessage(res.returnMessage) })
})
//Recoil 설정
setCanvasSetting({ ...basicSetting, flag: false })
} catch (error) {
swalFire({ text: getMessage(res.returnMessage), icon: 'error' })
}
}
// CanvasSetting 조회 및 초기화
const fetchSettings = async () => { const fetchSettings = async () => {
try { try {
const res = await get({ url: `/api/canvas-management/canvas-settings/by-object/${correntObjectNo}` }) const res = await get({ url: `/api/canvas-management/canvas-settings/by-object/${correntObjectNo}` })
@ -269,10 +424,35 @@ export function useCanvasSetting() {
//글꼴 설정 Flag //글꼴 설정 Flag
fontFlag: false, fontFlag: false,
} }
console.log('fontPatternData', fontPatternData)
//조회된 글꼴 데이터 set //조회된 글꼴 데이터 set
setGlobalFont(fontPatternData) setGlobalFont(fontPatternData)
//점/선 그리드
const patternData = {
INTERVAL: {
type: res.gridType,
horizontalInterval: res.gridHorizon * 10,
verticalInterval: res.gridVertical * 10,
ratioInterval: res.gridRatio * 10,
dimension: res.gridDimen,
},
DOT: res.dotGridDisplay,
LINE: res.lineGridDisplay,
flag: false,
}
const matchedOption = SelectOptions.find((option) => option.value == res.gridDimen)
// dimension 값에 맞는 옵션을 선택
setSelectOption(matchedOption)
setDotLineGridSettingState(patternData)
//setCurrentSetting(patternData)
//그리드 색 설정
setGridColor(res.gridColor)
setColorTemp(res.gridColor)
} else { } else {
//조회된 글꼴 데이터가 없는 경우 //조회된 글꼴 데이터가 없는 경우
@ -299,6 +479,14 @@ export function useCanvasSetting() {
}) })
setGlobalFont({ ...globalFont, fontFlag: false }) setGlobalFont({ ...globalFont, fontFlag: false })
//점/선 그리드
setDotLineGridSettingState({ ...defaultDotLineGridSetting, flag: false })
//setCurrentSetting({ ...defaultDotLineGridSetting })
//그리드 색 설정
setGridColor('#FF0000')
setColorTemp('#FF0000')
} }
frontSettings() frontSettings()
} catch (error) { } catch (error) {
@ -306,8 +494,8 @@ export function useCanvasSetting() {
} }
} }
// 옵션 클릭 후 저장 // CanvasSetting 옵션 클릭 후 저장
const onClickOption2 = useCallback(async () => { const onClickOption2 = async () => {
// 서버에 전송할 데이터 // 서버에 전송할 데이터
const dataToSend = { const dataToSend = {
firstOption1: option1.map((item) => ({ firstOption1: option1.map((item) => ({
@ -394,13 +582,26 @@ export function useCanvasSetting() {
originPixel: dimensionLineSettings.pixel, originPixel: dimensionLineSettings.pixel,
originColor: dimensionLineSettings.color, originColor: dimensionLineSettings.color,
//치수선 설정 //도면크기 설정
originHorizon: planSizeSettingMode.originHorizon, originHorizon: planSizeSettingMode.originHorizon,
originVertical: planSizeSettingMode.originVertical, originVertical: planSizeSettingMode.originVertical,
dotGridDisplay: dotLineGridSetting.DOT,
lineGridDisplay: dotLineGridSetting.LINE,
gridType: dotLineGridSetting.INTERVAL.type,
gridHorizon: dotLineGridSetting.INTERVAL.horizontalInterval / 10,
gridVertical: dotLineGridSetting.INTERVAL.verticalInterval / 10,
gridRatio: dotLineGridSetting.INTERVAL.ratioInterval / 10,
gridDimen: dotLineGridSetting.INTERVAL.dimension,
//gridColor: gridColor.gridColor,
gridColor: gridColor,
} }
console.log('patternData ', patternData) console.log('patternData ', patternData)
setColorTemp(gridColor)
// HTTP POST 요청 보내기 // HTTP POST 요청 보내기
await post({ url: `/api/canvas-management/canvas-settings`, data: patternData }) await post({ url: `/api/canvas-management/canvas-settings`, data: patternData })
.then((res) => { .then((res) => {
@ -408,13 +609,15 @@ export function useCanvasSetting() {
// Canvas 디스플레이 설정 시 해당 옵션 적용 // Canvas 디스플레이 설정 시 해당 옵션 적용
frontSettings() frontSettings()
// 저장 후 재조회
fetchSettings()
}) })
.catch((error) => { .catch((error) => {
swalFire({ text: getMessage(res.returnMessage), icon: 'error' }) swalFire({ text: getMessage(res.returnMessage), icon: 'error' })
}) })
//setAdsorptionRange(item.range) //setAdsorptionRange(item.range)
}, [settingModalFirstOptions, settingModalSecondOptions, adsorptionPointMode, globalFont, planSizeSettingMode]) }
// Canvas 디스플레이 설정 시 해당 옵션 적용 // Canvas 디스플레이 설정 시 해당 옵션 적용
const frontSettings = async () => { const frontSettings = async () => {
@ -491,6 +694,7 @@ export function useCanvasSetting() {
return { return {
canvas, canvas,
correntObjectNo,
settingModalFirstOptions, settingModalFirstOptions,
setSettingModalFirstOptions, setSettingModalFirstOptions,
settingModalSecondOptions, settingModalSecondOptions,
@ -500,7 +704,6 @@ export function useCanvasSetting() {
adsorptionRange, adsorptionRange,
setAdsorptionRange, setAdsorptionRange,
fetchSettings, fetchSettings,
//onClickOption,
frontSettings, frontSettings,
globalFont, globalFont,
setGlobalFont, setGlobalFont,
@ -516,5 +719,26 @@ export function useCanvasSetting() {
setDimensionLineSettings, setDimensionLineSettings,
planSizeSettingMode, planSizeSettingMode,
setPlanSizeSettingMode, setPlanSizeSettingMode,
selectOption,
setSelectOption,
SelectOptions,
currentSetting,
setCurrentSetting,
dotLineGridSettingState,
setSettingModalGridOptions,
setDotLineGridSettingState,
resetDotLineGridSetting,
gridColor,
setGridColor,
color,
setColor,
canvasSetting,
setCanvasSetting,
basicSetting,
setBasicSettings,
fetchBasicSettings,
basicSettingSave,
settingsData,
setSettingsData,
} }
} }

View File

@ -1,127 +0,0 @@
import { settingModalFirstOptionsState, settingModalSecondOptionsState } from '@/store/settingAtom'
import { useEffect, useState } from 'react'
import { useRecoilState } from 'recoil'
export const useCanvasSettingController = () => {
const [settingModalFirstOptions, setSettingModalFirstOptions] = useRecoilState(settingModalFirstOptionsState)
const [settingModalSecondOptions, setSettingModalSecondOptions] = useRecoilState(settingModalSecondOptionsState)
const [objectNo, setObjectNo] = useState('test123240912001') // 이후 삭제 필요
const { get } = useAxios()
useEffect(() => {
fetchSettings()
}, [objectNo])
useEffect(() => {
fetchSettings()
}, [])
useEffect(() => {
onClickOnlyOne()
fetchSettings()
}, [settingModalFirstOptions, settingModalSecondOptions])
const fetchSettings = async () => {
try {
const res = await get({ url: `/api/canvas-management/canvas-settings/by-object/${objectNo}` })
const optionData1 = settingModalFirstOptions.option1.map((item) => ({ ...item, selected: res[item.column] }))
const optionData2 = settingModalFirstOptions.option2.map((item) => ({ ...item, selected: res[item.column] }))
const optionData3 = settingModalSecondOptions.option3.map((item) => ({ ...item }))
const optionData4 = settingModalSecondOptions.option4.map((item) => ({ ...item, selected: res[item.column] }))
const optionData5 = settingModalFirstOptions.dimensionDisplay.map((item) => ({
...item,
}))
// 데이터 설정
setSettingModalFirstOptions({
option1: optionData1,
option2: optionData2,
dimensionDisplay: optionData5,
})
setSettingModalSecondOptions({
option3: optionData3,
option4: optionData4,
})
} catch (error) {
console.error('Data fetching error:', error)
}
}
//
const onClickOption = async (option) => {
option.selected = !option.selected
setSettingModalFirstOptions({ option1, option2, dimensionDisplay })
setSettingModalSecondOptions({ option3, option4 })
try {
// 서버에 전송할 데이터
const dataToSend = {
firstOption1: option1.map((item) => ({
column: item.column,
selected: item.selected,
})),
firstOption2: option2.map((item) => ({
column: item.column,
selected: item.selected,
})),
firstOption3: dimensionDisplay.map((item) => ({
column: item.column,
selected: item.selected,
})),
// secondOption1: secondOptions[0].option1.map((item) => ({
// name: item.id,
// name: item.name,
// // 필요한 경우 데이터 항목 추가
// })),
secondOption2: option4.map((item) => ({
column: item.column,
selected: item.selected,
})),
}
const patternData = {
objectNo,
//디스플레이 설정(다중)
allocDisplay: dataToSend.firstOption1[0].selected,
outlineDisplay: dataToSend.firstOption1[1].selected,
gridDisplay: dataToSend.firstOption1[2].selected,
lineDisplay: dataToSend.firstOption1[3].selected,
wordDisplay: dataToSend.firstOption1[4].selected,
circuitNumDisplay: dataToSend.firstOption1[5].selected,
flowDisplay: dataToSend.firstOption1[6].selected,
trestleDisplay: dataToSend.firstOption1[7].selected,
totalDisplay: dataToSend.firstOption1[8].selected,
//차수 표시(다건)
corridorDimension: dataToSend.firstOption3[0].selected,
realDimension: dataToSend.firstOption3[1].selected,
noneDimension: dataToSend.firstOption3[2].selected,
//화면 표시(다중)
onlyBorder: dataToSend.firstOption2[0].selected,
lineHatch: dataToSend.firstOption2[1].selected,
allPainted: dataToSend.firstOption2[2].selected,
//흡착범위 설정(단건)
adsorpRangeSmall: dataToSend.secondOption2[0].selected,
adsorpRangeSmallSemi: dataToSend.secondOption2[1].selected,
adsorpRangeMedium: dataToSend.secondOption2[2].selected,
adsorpRangeLarge: dataToSend.secondOption2[3].selected,
}
// HTTP POST 요청 보내기
await post({ url: `/api/canvas-management/canvas-settings`, data: patternData }).then((res) => {
swalFire({ text: getMessage(res.returnMessage) })
})
} catch (error) {
swalFire({ text: getMessage(res.returnMessage), icon: 'error' })
}
}
return {
fetchSettings,
settingModalFirstOptions,
setSettingModalFirstOptions,
settingModalSecondOptions,
setSettingModalSecondOptions,
onClickOption,
ß,
}
}

View File

@ -1,4 +1,4 @@
import { useContext, useEffect, useRef } from 'react' import { useEffect, useRef } from 'react'
import { distanceBetweenPoints } from '@/util/canvas-util' import { distanceBetweenPoints } from '@/util/canvas-util'
import { useRecoilState, useRecoilValue, useResetRecoilState, useSetRecoilState } from 'recoil' import { useRecoilState, useRecoilValue, useResetRecoilState, useSetRecoilState } from 'recoil'
import { import {
@ -31,7 +31,6 @@ import { fabric } from 'fabric'
import { outlineDisplaySelector } from '@/store/settingAtom' import { outlineDisplaySelector } from '@/store/settingAtom'
import { usePopup } from '@/hooks/usePopup' import { usePopup } from '@/hooks/usePopup'
import PropertiesSetting from '@/components/floor-plan/modal/outerlinesetting/PropertiesSetting' import PropertiesSetting from '@/components/floor-plan/modal/outerlinesetting/PropertiesSetting'
import { EventContext } from '@/app/floor-plan/EventProvider'
//외벽선 그리기 //외벽선 그리기
export function useOuterLineWall(id, propertiesId) { export function useOuterLineWall(id, propertiesId) {

View File

@ -507,18 +507,33 @@ export function useCanvas(id) {
* cad 파일 사용시 이미지 로딩 함수 * cad 파일 사용시 이미지 로딩 함수
*/ */
const handleBackImageLoadToCanvas = (url) => { const handleBackImageLoadToCanvas = (url) => {
console.log('image load url: ', url) canvas
.getObjects()
fabric.Image.fromURL(url, function (img) { .filter((obj) => obj.name === 'backGroundImage')
.forEach((img) => {
canvas.remove(img)
canvas?.renderAll()
})
fabric.Image.fromURL(`${url}?${new Date().getTime()}`, function (img) {
console.log(img)
img.set({ img.set({
left: 0, left: 0,
top: 0, top: 0,
width: 1500, width: img.width,
height: 1500, height: img.height,
selectable: true, name: 'backGroundImage',
selectable: false,
hasRotatingPoint: false, // 회전 핸들 활성화
lockMovementX: false,
lockMovementY: false,
lockRotation: false,
lockScalingX: false,
lockScalingY: false,
}) })
canvas.add(img) // image = img
canvas.renderAll() canvas?.add(img)
canvas?.sendToBack(img)
canvas?.renderAll()
setBackImg(img) setBackImg(img)
}) })
} }

View File

@ -1,4 +1,4 @@
import { useCallback, useEffect, useRef } from 'react' import { useRef } from 'react'
import { useRecoilValue, useSetRecoilState } from 'recoil' import { useRecoilValue, useSetRecoilState } from 'recoil'
import { canvasState, canvasZoomState, currentMenuState, textModeState } from '@/store/canvasAtom' import { canvasState, canvasZoomState, currentMenuState, textModeState } from '@/store/canvasAtom'
import { fabric } from 'fabric' import { fabric } from 'fabric'
@ -20,9 +20,15 @@ export function useEvent() {
const textMode = useRecoilValue(textModeState) const textMode = useRecoilValue(textModeState)
useEffect(() => { // 이벤트 초기화 위치 수정 -> useCanvasSetting에서 세팅값 불러오고 나서 초기화 함수 호출
initEvent() // useEffect(() => {
}, [currentMenu, canvas, adsorptionPointAddMode, adsorptionPointMode, adsorptionRange, dotLineGridSetting, tempGridMode]) // initEvent()
// }, [currentMenu, canvas, adsorptionPointAddMode, adsorptionPointMode, adsorptionRange, dotLineGridSetting])
// 임시 그리드 모드 변경 시 이벤트 초기화 호출 위치 수정 -> GridOption 컴포넌트에서 임시 그리드 모드 변경 시 이벤트 초기화 함수 호출
// useEffect(() => {
// initEvent()
// }, [tempGridMode])
const initEvent = () => { const initEvent = () => {
if (!canvas) { if (!canvas) {

View File

@ -1,11 +1,13 @@
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { useRecoilState } from 'recoil' import { useRecoilState } from 'recoil'
import { v4 as uuidv4, validate as isValidUUID } from 'uuid' import { v4 as uuidv4 } from 'uuid'
import { canvasState, currentCanvasPlanState, initCanvasPlansState, plansState, modifiedPlansState, modifiedPlanFlagState } from '@/store/canvasAtom' import { canvasState, currentCanvasPlanState, plansState, modifiedPlansState, modifiedPlanFlagState } from '@/store/canvasAtom'
import { useAxios } from '@/hooks/useAxios' import { useAxios } from '@/hooks/useAxios'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { useSwal } from '@/hooks/useSwal' import { useSwal } from '@/hooks/useSwal'
import { SAVE_KEY } from '@/common/common' import { SAVE_KEY } from '@/common/common'
import { readImage, removeImage } from '@/lib/fileAction'
import { useCanvas } from '@/hooks/useCanvas'
export function usePlan() { export function usePlan() {
const [planNum, setPlanNum] = useState(0) const [planNum, setPlanNum] = useState(0)
@ -13,9 +15,9 @@ export function usePlan() {
const [currentCanvasStatus, setCurrentCanvasStatus] = useState(null) const [currentCanvasStatus, setCurrentCanvasStatus] = useState(null)
const [canvas, setCanvas] = useRecoilState(canvasState) const [canvas, setCanvas] = useRecoilState(canvasState)
const [currentCanvasPlan, setCurrentCanvasPlan] = useRecoilState(currentCanvasPlanState) const [currentCanvasPlan, setCurrentCanvasPlan] = useRecoilState(currentCanvasPlanState)
const [initCanvasPlans, setInitCanvasPlans] = useRecoilState(initCanvasPlansState) // DB에 저장된 plan const [plans, setPlans] = useRecoilState(plansState) // 전체 plan
const [plans, setPlans] = useRecoilState(plansState) // 전체 plan (DB에 저장된 plan + 저장 안된 새로운 plan)
const [modifiedPlans, setModifiedPlans] = useRecoilState(modifiedPlansState) // 변경된 canvas plan const [modifiedPlans, setModifiedPlans] = useRecoilState(modifiedPlansState) // 변경된 canvas plan
const [modifiedPlanFlag, setModifiedPlanFlag] = useRecoilState(modifiedPlanFlagState) // 캔버스 실시간 오브젝트 이벤트 감지 flag const [modifiedPlanFlag, setModifiedPlanFlag] = useRecoilState(modifiedPlanFlagState) // 캔버스 실시간 오브젝트 이벤트 감지 flag
@ -108,22 +110,22 @@ export function usePlan() {
setCurrentCanvasPlan((prev) => ({ ...prev, canvasStatus: currentCanvasStatus })) setCurrentCanvasPlan((prev) => ({ ...prev, canvasStatus: currentCanvasStatus }))
} }
}, [currentCanvasStatus]) }, [currentCanvasStatus])
/** /**
* 현재 캔버스 상태와 DB에 저장된 캔버스 상태를 비교하여 수정 여부를 판단 * 현재 캔버스 상태와 DB에 저장된 캔버스 상태를 비교하여 수정 여부를 판단
*/ */
const checkModifiedCanvasPlan = (planId) => { const checkModifiedCanvasPlan = (planId) => {
const canvasStatus = currentCanvasData() const planData = plans.find((plan) => plan.id === planId)
if (isValidUUID(planId)) { if (planData.canvasStatus === '') {
// 새로운 캔버스 // 빈 상태로 저장된 캔버스
return JSON.parse(canvasStatus).objects.length > 0 return true
} else {
// 저장된 캔버스
// 각각 object들의 uuid 목록을 추출하여 비교
const canvasObjsUuids = getObjectUuids(JSON.parse(canvasStatus).objects)
const initPlanData = initCanvasPlans.find((plan) => plan.id === planId)
const dbObjsUuids = getObjectUuids(JSON.parse(initPlanData.canvasStatus).objects)
return canvasObjsUuids.length !== dbObjsUuids.length || !canvasObjsUuids.every((uuid, index) => uuid === dbObjsUuids[index])
} }
// 각각 object들의 uuid 목록을 추출하여 비교
const canvasStatus = currentCanvasData()
const canvasObjsUuids = getObjectUuids(JSON.parse(canvasStatus).objects)
const dbObjsUuids = getObjectUuids(JSON.parse(planData.canvasStatus).objects)
return canvasObjsUuids.length !== dbObjsUuids.length || !canvasObjsUuids.every((uuid, index) => uuid === dbObjsUuids[index])
} }
const getObjectUuids = (objects) => { const getObjectUuids = (objects) => {
return objects return objects
@ -140,16 +142,12 @@ export function usePlan() {
/** /**
* 캔버스에 저장되지 않은 변경사항이 있을때 저장 여부를 확인 저장 * 캔버스에 저장되지 않은 변경사항이 있을때 저장 여부를 확인 저장
*/ */
const checkUnsavedCanvasPlan = async (userId) => { const checkUnsavedCanvasPlan = async () => {
swalFire({ swalFire({
text: text: `Plan ${currentCanvasPlan.ordering} ` + getMessage('plan.message.confirm.save.modified'),
(!initCanvasPlans.some((initCanvasPlans) => initCanvasPlans.id === currentCanvasPlan.id) ? 'New ' : '') +
`Plan ${currentCanvasPlan.ordering}의 변경 사항을 저장하시겠습니까?`,
type: 'confirm', type: 'confirm',
confirmFn: async () => { confirmFn: async () => {
initCanvasPlans.some((plan) => plan.id === currentCanvasPlan.id) await putCanvasStatus(currentCanvasPlan.canvasStatus)
? await putCanvasStatus(currentCanvasPlan.canvasStatus)
: await postCanvasStatus(userId, currentCanvasPlan.canvasStatus)
}, },
}) })
resetModifiedPlans() resetModifiedPlans()
@ -172,18 +170,16 @@ export function usePlan() {
/** /**
* 페이지 캔버스를 저장 * 페이지 캔버스를 저장
*/ */
const saveCanvas = async (userId) => { const saveCanvas = async () => {
const canvasStatus = currentCanvasData('save') const canvasStatus = currentCanvasData('save')
initCanvasPlans.some((plan) => plan.id === currentCanvasPlan.id) await putCanvasStatus(canvasStatus)
? await putCanvasStatus(canvasStatus)
: await postCanvasStatus(userId, canvasStatus)
} }
/** /**
* objectNo에 해당하는 canvas 목록을 조회 * objectNo에 해당하는 canvas 목록을 조회
*/ */
const getCanvasByObjectNo = async (userId, objectNo) => { const getCanvasByObjectNo = async (userId, objectNo) => {
return get({ url: `/api/canvas-management/canvas-statuses/by-object/${objectNo}/${userId}` }).then((res) => return await get({ url: `/api/canvas-management/canvas-statuses/by-object/${objectNo}/${userId}` }).then((res) =>
res.map((item, index) => ({ res.map((item, index) => ({
id: item.id, id: item.id,
userId: item.userId, userId: item.userId,
@ -199,28 +195,20 @@ export function usePlan() {
/** /**
* 신규 canvas 데이터를 저장 * 신규 canvas 데이터를 저장
*/ */
const postCanvasStatus = async (userId, canvasStatus) => { const postCanvasStatus = async (userId, objectNo, canvasStatus) => {
const planData = { const planData = {
userId: userId, userId: userId,
imageName: 'image_name', // api 필수항목이여서 임시로 넣음, 이후 삭제 필요 imageName: 'image_name', // api 필수항목이여서 임시로 넣음, 이후 삭제 필요
objectNo: currentCanvasPlan.objectNo, objectNo: objectNo,
bgImageName: currentCanvasPlan?.bgImageName ?? null,
mapPositionAddress: currentCanvasPlan?.mapPositionAddress ?? null,
canvasStatus: canvasToDbFormat(canvasStatus), canvasStatus: canvasToDbFormat(canvasStatus),
} }
await promisePost({ url: '/api/canvas-management/canvas-statuses', data: planData }) await promisePost({ url: '/api/canvas-management/canvas-statuses', data: planData })
.then((res) => { .then((res) => {
setInitCanvasPlans((initCanvasPlans) => [...initCanvasPlans, { id: res.data, canvasStatus: canvasStatus }]) setPlans([...plans, { id: res.data, objectNo: objectNo, userId: userId, canvasStatus: canvasStatus, ordering: planNum + 1 }])
setPlans((plans) => handleCurrentPlan(res.data)
plans.map((plan) => setPlanNum(planNum + 1)
plan.id === currentCanvasPlan.id
? {
...plan,
id: res.data,
canvasStatus: canvasStatus,
}
: plan,
),
)
setModifiedPlans((modifiedPlans) => modifiedPlans.filter((planId) => planId !== currentCanvasPlan.id))
}) })
.catch((error) => { .catch((error) => {
swalFire({ text: error.message, icon: 'error' }) swalFire({ text: error.message, icon: 'error' })
@ -233,13 +221,12 @@ export function usePlan() {
const putCanvasStatus = async (canvasStatus) => { const putCanvasStatus = async (canvasStatus) => {
const planData = { const planData = {
id: currentCanvasPlan.id, id: currentCanvasPlan.id,
bgImageName: currentCanvasPlan?.bgImageName ?? null,
mapPositionAddress: currentCanvasPlan?.mapPositionAddress ?? null,
canvasStatus: canvasToDbFormat(canvasStatus), canvasStatus: canvasToDbFormat(canvasStatus),
} }
await promisePut({ url: '/api/canvas-management/canvas-statuses', data: planData }) await promisePut({ url: '/api/canvas-management/canvas-statuses', data: planData })
.then((res) => { .then((res) => {
setInitCanvasPlans((initCanvasPlans) =>
initCanvasPlans.map((plan) => (plan.id === currentCanvasPlan.id ? { ...plan, canvasStatus: canvasStatus } : plan)),
)
setPlans((plans) => plans.map((plan) => (plan.id === currentCanvasPlan.id ? { ...plan, canvasStatus: canvasStatus } : plan))) setPlans((plans) => plans.map((plan) => (plan.id === currentCanvasPlan.id ? { ...plan, canvasStatus: canvasStatus } : plan)))
setModifiedPlans((modifiedPlans) => modifiedPlans.filter((planId) => planId !== currentCanvasPlan.id)) setModifiedPlans((modifiedPlans) => modifiedPlans.filter((planId) => planId !== currentCanvasPlan.id))
}) })
@ -251,40 +238,30 @@ export function usePlan() {
/** /**
* id에 해당하는 canvas 데이터를 삭제 * id에 해당하는 canvas 데이터를 삭제
*/ */
const delCanvasById = (id) => { const delCanvasById = async (id) => {
return promiseDel({ url: `/api/canvas-management/canvas-statuses/by-id/${id}` }) return await promiseDel({ url: `/api/canvas-management/canvas-statuses/by-id/${id}` })
} }
/** /**
* objectNo에 해당하는 canvas 데이터들을 삭제 * objectNo에 해당하는 canvas 데이터들을 삭제
*/ */
const delCanvasByObjectNo = (objectNo) => { const delCanvasByObjectNo = async (objectNo) => {
return promiseDel({ url: `/api/canvas-management/canvas-statuses/by-object/${objectNo}` }) return await promiseDel({ url: `/api/canvas-management/canvas-statuses/by-object/${objectNo}` })
} }
/** /**
* plan 이동 * plan 이동
* 현재 plan의 작업상태를 확인, 저장 이동 * 현재 plan의 작업상태를 확인, 저장 이동
*/ */
const handleCurrentPlan = async (userId, newCurrentId) => { const handleCurrentPlan = async (newCurrentId) => {
if (!currentCanvasPlan || currentCanvasPlan.id !== newCurrentId) { if (!currentCanvasPlan || currentCanvasPlan.id !== newCurrentId) {
if (currentCanvasPlan?.id && modifiedPlans.some((modifiedPlan) => modifiedPlan === currentCanvasPlan.id)) { if (currentCanvasPlan?.id && modifiedPlans.some((modifiedPlan) => modifiedPlan === currentCanvasPlan.id)) {
// swalFire({ await saveCanvas()
// text: `${currentCanvasPlan.name} ` + getMessage('plan.message.confirm.save'),
// type: 'confirm',
// confirmFn: async () => {
// await saveCanvas(userId)
// updateCurrentPlan(newCurrentId)
// },
// denyFn: () => {
// updateCurrentPlan(newCurrentId)
// },
// })
await saveCanvas(userId)
} }
updateCurrentPlan(newCurrentId) updateCurrentPlan(newCurrentId)
} }
} }
const updateCurrentPlan = (newCurrentId) => { const updateCurrentPlan = (newCurrentId) => {
setPlans((plans) => setPlans((plans) =>
plans.map((plan) => { plans.map((plan) => {
@ -295,29 +272,35 @@ export function usePlan() {
useEffect(() => { useEffect(() => {
setCurrentCanvasPlan(plans.find((plan) => plan.isCurrent) || null) setCurrentCanvasPlan(plans.find((plan) => plan.isCurrent) || null)
setSelectedPlan(plans.find((plan) => plan.isCurrent)) setSelectedPlan(plans.find((plan) => plan.isCurrent))
// setBgImage()
}, [plans]) }, [plans])
const setBgImage = () => {
// readImage(selectedPlan?.id)
}
/** /**
* 새로운 plan 생성 * 새로운 plan 생성
* 현재 plan의 데이터가 있을 경우 복제 여부를 확인 * 현재 plan의 데이터가 있을 경우 복제 여부를 확인
*/ */
const handleAddPlan = (userId, objectNo) => { const handleAddPlan = async (userId, objectNo) => {
JSON.parse(currentCanvasData()).objects.length > 0 JSON.parse(currentCanvasData()).objects.length > 0
? swalFire({ ? swalFire({
text: text: `Plan ${currentCanvasPlan.ordering} ` + getMessage('plan.message.confirm.copy'),
(!initCanvasPlans.some((initCanvasPlans) => initCanvasPlans.id === currentCanvasPlan.id) ? 'New ' : '') +
`Plan ${currentCanvasPlan.ordering} ` +
getMessage('plan.message.confirm.copy'),
type: 'confirm', type: 'confirm',
confirmFn: () => { confirmFn: async () => {
addPlan(userId, objectNo, currentCanvasData()) await postCanvasStatus(userId, objectNo, currentCanvasData())
}, },
denyFn: () => { denyFn: async () => {
addPlan(userId, objectNo, '') await postCanvasStatus(userId, objectNo, '')
}, },
}) })
: addPlan(userId, objectNo, '') : await postCanvasStatus(userId, objectNo, '')
} }
/**
* DB에 추가하지 않고 화면 상에서 plan을 추가하는 함수
*/
const addPlan = (userId, objectNo, canvasStatus) => { const addPlan = (userId, objectNo, canvasStatus) => {
const id = uuidv4() const id = uuidv4()
const newPlan = { const newPlan = {
@ -328,32 +311,26 @@ export function usePlan() {
ordering: planNum + 1, ordering: planNum + 1,
} }
setPlans([...plans, newPlan]) setPlans([...plans, newPlan])
handleCurrentPlan(userId, id) handleCurrentPlan(id)
setPlanNum(planNum + 1) setPlanNum(planNum + 1)
} }
/** /**
* plan 삭제 * plan 삭제
*/ */
const handleDeletePlan = (e, id) => { const handleDeletePlan = async (e, id) => {
e.stopPropagation() // 이벤트 버블링 방지 e.stopPropagation() // 이벤트 버블링 방지
if (initCanvasPlans.some((plan) => plan.id === id)) { await delCanvasById(id)
delCanvasById(id) .then((res) => {
.then((res) => { setPlans((plans) => plans.filter((plan) => plan.id !== id))
setInitCanvasPlans((initCanvasPlans) => initCanvasPlans.filter((plan) => plan.id !== id)) setModifiedPlans((modifiedPlans) => modifiedPlans.filter((planId) => planId !== currentCanvasPlan.id))
setPlans((plans) => plans.filter((plan) => plan.id !== id)) removeImage(currentCanvasPlan.id)
setModifiedPlans((modifiedPlans) => modifiedPlans.filter((planId) => planId !== currentCanvasPlan.id)) swalFire({ text: getMessage('plan.message.delete') })
swalFire({ text: getMessage('plan.message.delete') }) })
}) .catch((error) => {
.catch((error) => { swalFire({ text: error.message, icon: 'error' })
swalFire({ text: error.message, icon: 'error' }) })
})
} else {
setPlans((plans) => plans.filter((plan) => plan.id !== id))
setModifiedPlans((modifiedPlans) => modifiedPlans.filter((planId) => planId !== currentCanvasPlan.id))
swalFire({ text: getMessage('plan.message.delete') })
}
// 삭제 후 last 데이터에 포커싱 // 삭제 후 last 데이터에 포커싱
const lastPlan = plans.filter((plan) => plan.id !== id).at(-1) const lastPlan = plans.filter((plan) => plan.id !== id).at(-1)
@ -368,16 +345,15 @@ export function usePlan() {
/** /**
* plan 조회 * plan 조회
*/ */
const loadCanvasPlanData = (userId, objectNo, pid) => { const loadCanvasPlanData = async (userId, objectNo, pid) => {
getCanvasByObjectNo(userId, objectNo).then((res) => { await getCanvasByObjectNo(userId, objectNo).then((res) => {
// console.log('canvas 목록 ', res) // console.log('canvas 목록 ', res)
if (res.length > 0) { if (res.length > 0) {
setInitCanvasPlans(res)
setPlans(res) setPlans(res)
updateCurrentPlan(res[pid - 1].id) updateCurrentPlan(res[pid - 1].id)
setPlanNum(res.length) setPlanNum(res.length)
} else { } else {
addPlan(userId, objectNo, '') postCanvasStatus(userId, objectNo, '')
} }
}) })
} }
@ -385,9 +361,9 @@ export function usePlan() {
return { return {
canvas, canvas,
plans, plans,
initCanvasPlans,
selectedPlan, selectedPlan,
currentCanvasPlan, currentCanvasPlan,
setCurrentCanvasPlan,
modifiedPlans, modifiedPlans,
modifiedPlanFlag, modifiedPlanFlag,
setModifiedPlanFlag, setModifiedPlanFlag,

View File

@ -4,6 +4,7 @@ import fs from 'fs/promises'
const CAD_FILE_PATH = 'public/cad-images' const CAD_FILE_PATH = 'public/cad-images'
const IMAGE_FILE_PATH = 'public/plan-bg-images' const IMAGE_FILE_PATH = 'public/plan-bg-images'
const FILE_PATH = 'public/plan-images'
/** /**
* 파일 변환 & 저장 * 파일 변환 & 저장
@ -39,25 +40,56 @@ const writeImageBase64 = async (title, data) => {
return fs.writeFile(`${IMAGE_FILE_PATH}/${title}.png`, data, 'base64') return fs.writeFile(`${IMAGE_FILE_PATH}/${title}.png`, data, 'base64')
} }
/** // /**
* 이미지 저장 // * 이미지 저장
* Buffer 형식으로 저장 // * Buffer 형식으로 저장
* @param {*} title // * @param {*} title
* @param {*} data // * @param {*} data
* @returns // * @returns
*/ // */
const writeImageBuffer = async (file) => { // const writeImageBuffer = async (file) => {
// 해당 경로에 Directory 가 없다면 생성 // // 해당 경로에 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)
// }
const writeImage = async (fileName, file) => {
try { try {
await fs.readdir(IMAGE_FILE_PATH) await fs.readdir(FILE_PATH)
} catch { } catch {
await fs.mkdir(IMAGE_FILE_PATH) await fs.mkdir(FILE_PATH)
} }
const arrayBuffer = await fileURLToPath.arrayBuffer() return fs.writeFile(`${FILE_PATH}/${fileName}.png`, file)
const buffer = new Uint8Array(arrayBuffer)
return fs.writeFile(`${IMAGE_FILE_PATH}/${file.fileName}`, buffer)
} }
export { convertDwgToPng, writeImageBase64, writeImageBuffer } const readImage = async (fileName) => {
const file = await fs.readFile(`${FILE_PATH}/${fileName}`)
// .then((res) => {
// console.log('readImage-then', res)
// })
// .catch((e) => {
// console.log('readImage-catch', e)
// })
console.log('🚀 ~ readImage ~ file:', file)
return file
}
const removeImage = async (fileName) => {
try {
await fs.rm(`${FILE_PATH}/${fileName}.png`)
} catch (e) {
console.log(e)
}
}
export { convertDwgToPng, writeImageBase64, writeImage, readImage, removeImage }

View File

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

View File

@ -164,6 +164,7 @@
"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.estimate.unLock": "ロック解除",
"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",
@ -294,6 +295,7 @@
"modal.actual.size.setting.plane.size.length": "廊下の寸法の長さ", "modal.actual.size.setting.plane.size.length": "廊下の寸法の長さ",
"modal.actual.size.setting.actual.size.length": "実寸長", "modal.actual.size.setting.actual.size.length": "実寸長",
"plan.message.confirm.save": "PLAN을 저장하시겠습니까?", "plan.message.confirm.save": "PLAN을 저장하시겠습니까?",
"plan.message.confirm.save.modified": "PLAN의 변경사항을 저장하시겠습니까?",
"plan.message.confirm.copy": "PLAN을 복사하시겠습니까?", "plan.message.confirm.copy": "PLAN을 복사하시겠습니까?",
"plan.message.confirm.delete": "PLAN을 삭제하시겠습니까?", "plan.message.confirm.delete": "PLAN을 삭제하시겠습니까?",
"plan.message.save": "저장되었습니다.", "plan.message.save": "저장되었습니다.",
@ -341,6 +343,7 @@
"modal.panel.column.insert.info": "挿入する方向を選択してください。", "modal.panel.column.insert.info": "挿入する方向を選択してください。",
"modal.panel.column.insert.type.left": "左挿入", "modal.panel.column.insert.type.left": "左挿入",
"modal.panel.column.insert.type.right": "右挿入", "modal.panel.column.insert.type.right": "右挿入",
"modal.image.load.size.rotate": "サイズ調整と回転",
"contextmenu.column.insert": "列の挿入", "contextmenu.column.insert": "列の挿入",
"contextmenu.row.move": "단 이동(JA)", "contextmenu.row.move": "단 이동(JA)",
"contextmenu.row.copy": "단 복사(JA)", "contextmenu.row.copy": "단 복사(JA)",
@ -483,9 +486,13 @@
"common.message.writeToConfirm": "作成解除を実行しますか?", "common.message.writeToConfirm": "作成解除を実行しますか?",
"common.message.password.init.success": "パスワード [{0}] に初期化されました。", "common.message.password.init.success": "パスワード [{0}] に初期化されました。",
"common.message.no.edit.save": "この文書は変更できません。", "common.message.no.edit.save": "この文書は変更できません。",
"common.load": "ファイルの追加",
"common.input.file": "ファイルを読み込む", "common.input.file": "ファイルを読み込む",
"common.input.file.load": "ファイルの追加", "common.input.file.load": "ファイルの追加",
"common.input.image.load": "이미지 불러오기",
"common.input.address.load": "アドレスを読み込む",
"common.require": "必須", "common.require": "必須",
"common.finish": "完了",
"common.ok": "確認", "common.ok": "確認",
"commons.west": "立つ", "commons.west": "立つ",
"commons.east": "ドン", "commons.east": "ドン",
@ -835,6 +842,7 @@
"estimate.detail.fileList.btn": "ファイル選択", "estimate.detail.fileList.btn": "ファイル選択",
"estimate.detail.fileList.extCheck": "そのファイルはイメージファイルではありません", "estimate.detail.fileList.extCheck": "そのファイルはイメージファイルではありません",
"estimate.detail.header.fileList2": "添付ファイル一覧", "estimate.detail.header.fileList2": "添付ファイル一覧",
"estimate.detail.fileList2.btn.return": "復元",
"estimate.detail.header.specialEstimate": "見積もりの具体的な", "estimate.detail.header.specialEstimate": "見積もりの具体的な",
"estimate.detail.header.specialEstimateProductInfo": "製品情報", "estimate.detail.header.specialEstimateProductInfo": "製品情報",
"estimate.detail.sepcialEstimateProductInfo.totAmount": "数量 (PCS)", "estimate.detail.sepcialEstimateProductInfo.totAmount": "数量 (PCS)",
@ -897,10 +905,14 @@
"estimate.detail.save.requiredItem": "製品は1つ以上登録する必要があります.", "estimate.detail.save.requiredItem": "製品は1つ以上登録する必要があります.",
"estimate.detail.save.requiredCharger": "担当者は必須です.", "estimate.detail.save.requiredCharger": "担当者は必須です.",
"estimate.detail.save.requiredObjectName": "案件名は必須です.", "estimate.detail.save.requiredObjectName": "案件名は必須です.",
"estimate.detail.save.requiredPkgAsp": "住宅pkg単価は0より大きい値を入力してください.",
"estimate.detail.save.requiredEstimateDate": "見積日は必須です.", "estimate.detail.save.requiredEstimateDate": "見積日は必須です.",
"estimate.detail.save.requiredItemId": "製品を選択してください.",
"estimate.detail.save.requiredAmount": "数量は0より大きい値を入力してください.", "estimate.detail.save.requiredAmount": "数量は0より大きい値を入力してください.",
"estimate.detail.save.requiredSalePrice": "単価は0より大きい値を入力してください.", "estimate.detail.save.requiredSalePrice": "単価は0より大きい値を入力してください.",
"estimate.detail.reset.confirmMsg": "保存した見積書情報が初期化され、図面情報が反映されます。本当に初期化しますか?", "estimate.detail.reset.confirmMsg": "保存した見積書情報が初期化され、図面情報が反映されます。本当に初期化しますか?",
"estimate.detail.alert.delFile": "添付ファイルを完全に削除するには[保存]ボタンをクリックしてください",
"estimate.detail.alert.selectDelItem": "削除する商品を選択してください.",
"simulator.title.sub1": "物件番号", "simulator.title.sub1": "物件番号",
"simulator.title.sub2": "作成日", "simulator.title.sub2": "作成日",
"simulator.title.sub3": "システム容量", "simulator.title.sub3": "システム容量",

View File

@ -168,6 +168,7 @@
"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.estimate.unLock": "잠금 해제",
"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",
@ -299,6 +300,7 @@
"modal.actual.size.setting.plane.size.length": "복도치수 길이", "modal.actual.size.setting.plane.size.length": "복도치수 길이",
"modal.actual.size.setting.actual.size.length": "실제치수 길이", "modal.actual.size.setting.actual.size.length": "실제치수 길이",
"plan.message.confirm.save": "PLAN을 저장하시겠습니까?", "plan.message.confirm.save": "PLAN을 저장하시겠습니까?",
"plan.message.confirm.save.modified": "PLAN의 변경사항을 저장하시겠습니까?",
"plan.message.confirm.copy": "PLAN을 복사하시겠습니까?", "plan.message.confirm.copy": "PLAN을 복사하시겠습니까?",
"plan.message.confirm.delete": "PLAN을 삭제하시겠습니까?", "plan.message.confirm.delete": "PLAN을 삭제하시겠습니까?",
"plan.message.save": "저장되었습니다.", "plan.message.save": "저장되었습니다.",
@ -348,6 +350,7 @@
"modal.panel.column.insert.info": "삽입할 방향을 선택해주세요.", "modal.panel.column.insert.info": "삽입할 방향을 선택해주세요.",
"modal.panel.column.insert.type.left": "왼쪽 삽입", "modal.panel.column.insert.type.left": "왼쪽 삽입",
"modal.panel.column.insert.type.right": "오른쪽 삽입", "modal.panel.column.insert.type.right": "오른쪽 삽입",
"modal.image.load.size.rotate": "크기 조절 및 회전",
"contextmenu.row.move": "단 이동", "contextmenu.row.move": "단 이동",
"contextmenu.row.copy": "단 복사", "contextmenu.row.copy": "단 복사",
"contextmenu.row.remove": "단 삭제", "contextmenu.row.remove": "단 삭제",
@ -492,9 +495,13 @@
"common.message.writeToConfirm": "작성 해제를 실행하시겠습니까?", "common.message.writeToConfirm": "작성 해제를 실행하시겠습니까?",
"common.message.password.init.success": "비밀번호 [{0}]로 초기화 되었습니다.", "common.message.password.init.success": "비밀번호 [{0}]로 초기화 되었습니다.",
"common.message.no.edit.save": "This document cannot be changed.", "common.message.no.edit.save": "This document cannot be changed.",
"common.load": "불러오기",
"common.input.file": "파일 불러오기", "common.input.file": "파일 불러오기",
"common.input.file.load": "불러오기", "common.input.file.load": "불러오기",
"common.input.image.load": "이미지 불러오기",
"common.input.address.load": "주소 불러오기",
"common.require": "필수", "common.require": "필수",
"common.finish": "완료",
"common.ok": "확인", "common.ok": "확인",
"commons.west": "서", "commons.west": "서",
"commons.east": "동", "commons.east": "동",
@ -845,6 +852,7 @@
"estimate.detail.fileList.btn": "파일선택", "estimate.detail.fileList.btn": "파일선택",
"estimate.detail.fileList.extCheck": "해당 파일은 이미지 파일이 아닙니다", "estimate.detail.fileList.extCheck": "해당 파일은 이미지 파일이 아닙니다",
"estimate.detail.header.fileList2": "첨부파일 목록", "estimate.detail.header.fileList2": "첨부파일 목록",
"estimate.detail.fileList2.btn.return": "복원",
"estimate.detail.header.specialEstimate": "견적특이사항", "estimate.detail.header.specialEstimate": "견적특이사항",
"estimate.detail.header.specialEstimateProductInfo": "제품정보", "estimate.detail.header.specialEstimateProductInfo": "제품정보",
"estimate.detail.sepcialEstimateProductInfo.totAmount": "수량 (PCS)", "estimate.detail.sepcialEstimateProductInfo.totAmount": "수량 (PCS)",
@ -907,10 +915,14 @@
"estimate.detail.save.requiredItem": "제품은 1개이상 등록해야 됩니다.", "estimate.detail.save.requiredItem": "제품은 1개이상 등록해야 됩니다.",
"estimate.detail.save.requiredCharger": "담당자는 필수값 입니다.", "estimate.detail.save.requiredCharger": "담당자는 필수값 입니다.",
"estimate.detail.save.requiredObjectName": "안건명은 필수값 입니다.", "estimate.detail.save.requiredObjectName": "안건명은 필수값 입니다.",
"estimate.detail.save.requiredPkgAsp": "주택pkg 단가는 0보다 큰 값을 입력하세요.",
"estimate.detail.save.requiredEstimateDate": "견적일은 필수값 입니다.", "estimate.detail.save.requiredEstimateDate": "견적일은 필수값 입니다.",
"estimate.detail.save.requiredItemId": "제품을 선택해주세요.",
"estimate.detail.save.requiredAmount": "수량은 0보다 큰값을 입력해주세요.", "estimate.detail.save.requiredAmount": "수량은 0보다 큰값을 입력해주세요.",
"estimate.detail.save.requiredSalePrice": "단가는 0보다 큰값을 입력해주세요.", "estimate.detail.save.requiredSalePrice": "단가는 0보다 큰값을 입력해주세요.",
"estimate.detail.reset.confirmMsg": "저장된 견적서 정보가 초기화되고, 도면정보가 반영됩니다. 정말로 초기화 하시겠습니까?", "estimate.detail.reset.confirmMsg": "저장된 견적서 정보가 초기화되고, 도면정보가 반영됩니다. 정말로 초기화 하시겠습니까?",
"estimate.detail.alert.delFile": "첨부파일을 완전히 삭제하려면 [저장]버튼을 클릭하십시오.",
"estimate.detail.alert.selectDelItem": "삭제할 제품을 선택하세요.",
"simulator.title.sub1": "물건번호", "simulator.title.sub1": "물건번호",
"simulator.title.sub2": "작성일", "simulator.title.sub2": "작성일",
"simulator.title.sub3": "시스템 용량", "simulator.title.sub3": "시스템 용량",

View File

@ -260,12 +260,6 @@ export const dotLineIntervalSelector = selector({
}, },
}) })
// canvas plan 초기 목록
export const initCanvasPlansState = atom({
key: 'initCanvasPlans',
default: [],
})
// 현재 canvas plan // 현재 canvas plan
export const currentCanvasPlanState = atom({ export const currentCanvasPlanState = atom({
key: 'currentCanvasPlan', key: 'currentCanvasPlan',

View File

@ -1,8 +1,7 @@
import { atom } from 'recoil' import { atom } from 'recoil'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { v1 } from 'uuid'
export const stuffSearchState = atom({ export const stuffSearchState = atom({
key: `stuffSearchState/${v1()}`, key: `stuffSearchState`,
default: { default: {
schObjectNo: '', //물건번호 schObjectNo: '', //물건번호
schAddress: '', //물건주소 schAddress: '', //물건주소

View File

@ -139,6 +139,7 @@
&.ico02{background-image: url(../../public/static/images/canvas/ico-flx02.svg);} &.ico02{background-image: url(../../public/static/images/canvas/ico-flx02.svg);}
&.ico03{background-image: url(../../public/static/images/canvas/ico-flx03.svg);} &.ico03{background-image: url(../../public/static/images/canvas/ico-flx03.svg);}
&.ico04{background-image: url(../../public/static/images/canvas/ico-flx04.svg);} &.ico04{background-image: url(../../public/static/images/canvas/ico-flx04.svg);}
&.ico05{background-image: url(../../public/static/images/canvas/ico-flx05.svg);}
} }
.name{ .name{
font-size: 12px; font-size: 12px;
@ -720,6 +721,7 @@
&.one{ &.one{
.estimate-box{ .estimate-box{
&:last-child{ &:last-child{
flex: 1;
min-width: unset; min-width: unset;
} }
} }
@ -803,6 +805,7 @@
} }
} }
.file-list{ .file-list{
min-height: 52px;
.file-item{ .file-item{
margin-bottom: 15px; margin-bottom: 15px;
span{ span{
@ -827,6 +830,47 @@
&:last-child{ &:last-child{
margin-bottom: 0; margin-bottom: 0;
} }
.file-item-wrap{
display: flex;
align-items: center;
gap: 30px;
.return-wrap{
display: flex;
align-items: center;
}
.return{
padding: 0;
font-size: 13px;
color: #B0BCCD;
text-decoration: line-through;
}
.return-btn{
flex: none;
position: relative;
top: 0;
left: 0;
transform: none;
display: flex;
align-items: center;
height: 24px;
padding: 0 9px;
margin-left: 10px;
background: none;
border: 1px solid #B0BCCD;
border-radius: 2px;
font-size: 12px;
color: #B0BCCD;
font-weight: 500;
.return-ico{
display: block;
width: 14px;
height: 14px;
background: url(../../public/static/images/canvas/return-btn.svg)no-repeat center;
background-size: contain;
margin-right: 5px;
}
}
}
} }
} }
} }
@ -877,6 +921,16 @@
&.act{ &.act{
background-color: #F7F9FA; background-color: #F7F9FA;
} }
.special-note-check-box{
display: flex;
align-items: center;
.check-name{
font-size: 13px;
color: #45576F;
cursor: pointer;
line-height: 1.3;
}
}
} }
} }
@ -884,7 +938,7 @@
border: 1px solid #ECF0F4; border: 1px solid #ECF0F4;
border-radius: 3px; border-radius: 3px;
padding: 24px; padding: 24px;
max-height: 350px; height: 350px;
overflow-y: auto; overflow-y: auto;
margin-bottom: 30px; margin-bottom: 30px;
dl{ dl{

View File

@ -1308,23 +1308,23 @@ $alert-color: #101010;
height: 253px; height: 253px;
.circle{ .circle{
top: 86%; top: 86%;
&:nth-child(1), // &:nth-child(1),
&:nth-child(7), // &:nth-child(7),
&:nth-child(13), // &:nth-child(13),
&:nth-child(19){ // &:nth-child(19){
&::before{ // &::before{
content: ''; // content: '';
position: absolute; // position: absolute;
top: 20px; // top: 20px;
left: 50%; // left: 50%;
transform: translateX(-50%); // transform: translateX(-50%);
width: 1px; // width: 1px;
height: 6px; // height: 6px;
background-color: #8B8B8B; // background-color: #8B8B8B;
} // }
} // }
i{ i{
top: 32px; top: 22px;
} }
&.act{ &.act{
i{color: #8B8B8B;} i{color: #8B8B8B;}

View File

@ -1,109 +1,45 @@
.spinner-wrap { .spinner-wrap{
width: 100%; width: 100%;
height: 100vh; height: 100vh;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
background-color: #fff; background-color: #fff;
.loader { .loader {
font-size: 10px; font-size: 10px;
width: 1.2em; width: 1.2em;
height: 1.2em; height: 1.2em;
border-radius: 50%; border-radius: 50%;
position: relative; position: relative;
text-indent: -9999em; text-indent: -9999em;
animation: mulShdSpin 1.1s infinite ease; animation: mulShdSpin 1.1s infinite ease;
transform: translateZ(0); transform: translateZ(0);
}
@keyframes mulShdSpin {
0%,
100% {
box-shadow:
0em -2.6em 0em 0em #101010,
1.8em -1.8em 0 0em rgba(16, 16, 16, 0.2),
2.5em 0em 0 0em rgba(16, 16, 16, 0.2),
1.75em 1.75em 0 0em rgba(16, 16, 16, 0.2),
0em 2.5em 0 0em rgba(16, 16, 16, 0.2),
-1.8em 1.8em 0 0em rgba(16, 16, 16, 0.2),
-2.6em 0em 0 0em rgba(16, 16, 16, 0.5),
-1.8em -1.8em 0 0em rgba(16, 16, 16, 0.7);
} }
12.5% { @keyframes mulShdSpin {
box-shadow: 0%,
0em -2.6em 0em 0em rgba(16, 16, 16, 0.7), 100% {
1.8em -1.8em 0 0em #101010, box-shadow: 0em -2.6em 0em 0em #101010, 1.8em -1.8em 0 0em rgba(16, 16, 16, 0.2), 2.5em 0em 0 0em rgba(16, 16, 16, 0.2), 1.75em 1.75em 0 0em rgba(16, 16, 16, 0.2), 0em 2.5em 0 0em rgba(16, 16, 16, 0.2), -1.8em 1.8em 0 0em rgba(16, 16, 16, 0.2), -2.6em 0em 0 0em rgba(16, 16, 16, 0.5), -1.8em -1.8em 0 0em rgba(16, 16, 16, 0.7);
2.5em 0em 0 0em rgba(16, 16, 16, 0.2), }
1.75em 1.75em 0 0em rgba(16, 16, 16, 0.2), 12.5% {
0em 2.5em 0 0em rgba(16, 16, 16, 0.2), box-shadow: 0em -2.6em 0em 0em rgba(16, 16, 16, 0.7), 1.8em -1.8em 0 0em #101010, 2.5em 0em 0 0em rgba(16, 16, 16, 0.2), 1.75em 1.75em 0 0em rgba(16, 16, 16, 0.2), 0em 2.5em 0 0em rgba(16, 16, 16, 0.2), -1.8em 1.8em 0 0em rgba(16, 16, 16, 0.2), -2.6em 0em 0 0em rgba(16, 16, 16, 0.2), -1.8em -1.8em 0 0em rgba(16, 16, 16, 0.5);
-1.8em 1.8em 0 0em rgba(16, 16, 16, 0.2), }
-2.6em 0em 0 0em rgba(16, 16, 16, 0.2), 25% {
-1.8em -1.8em 0 0em rgba(16, 16, 16, 0.5); box-shadow: 0em -2.6em 0em 0em rgba(16, 16, 16, 0.5), 1.8em -1.8em 0 0em rgba(16, 16, 16, 0.7), 2.5em 0em 0 0em #101010, 1.75em 1.75em 0 0em rgba(16, 16, 16, 0.2), 0em 2.5em 0 0em rgba(16, 16, 16, 0.2), -1.8em 1.8em 0 0em rgba(16, 16, 16, 0.2), -2.6em 0em 0 0em rgba(16, 16, 16, 0.2), -1.8em -1.8em 0 0em rgba(16, 16, 16, 0.2);
} }
25% { 37.5% {
box-shadow: box-shadow: 0em -2.6em 0em 0em rgba(16, 16, 16, 0.2), 1.8em -1.8em 0 0em rgba(16, 16, 16, 0.5), 2.5em 0em 0 0em rgba(16, 16, 16, 0.7), 1.75em 1.75em 0 0em #101010, 0em 2.5em 0 0em rgba(16, 16, 16, 0.2), -1.8em 1.8em 0 0em rgba(16, 16, 16, 0.2), -2.6em 0em 0 0em rgba(16, 16, 16, 0.2), -1.8em -1.8em 0 0em rgba(16, 16, 16, 0.2);
0em -2.6em 0em 0em rgba(16, 16, 16, 0.5), }
1.8em -1.8em 0 0em rgba(16, 16, 16, 0.7), 50% {
2.5em 0em 0 0em #101010, box-shadow: 0em -2.6em 0em 0em rgba(16, 16, 16, 0.2), 1.8em -1.8em 0 0em rgba(16, 16, 16, 0.2), 2.5em 0em 0 0em rgba(16, 16, 16, 0.5), 1.75em 1.75em 0 0em rgba(16, 16, 16, 0.7), 0em 2.5em 0 0em #101010, -1.8em 1.8em 0 0em rgba(16, 16, 16, 0.2), -2.6em 0em 0 0em rgba(16, 16, 16, 0.2), -1.8em -1.8em 0 0em rgba(16, 16, 16, 0.2);
1.75em 1.75em 0 0em rgba(16, 16, 16, 0.2), }
0em 2.5em 0 0em rgba(16, 16, 16, 0.2), 62.5% {
-1.8em 1.8em 0 0em rgba(16, 16, 16, 0.2), box-shadow: 0em -2.6em 0em 0em rgba(16, 16, 16, 0.2), 1.8em -1.8em 0 0em rgba(16, 16, 16, 0.2), 2.5em 0em 0 0em rgba(16, 16, 16, 0.2), 1.75em 1.75em 0 0em rgba(16, 16, 16, 0.5), 0em 2.5em 0 0em rgba(16, 16, 16, 0.7), -1.8em 1.8em 0 0em #101010, -2.6em 0em 0 0em rgba(16, 16, 16, 0.2), -1.8em -1.8em 0 0em rgba(16, 16, 16, 0.2);
-2.6em 0em 0 0em rgba(16, 16, 16, 0.2), }
-1.8em -1.8em 0 0em rgba(16, 16, 16, 0.2); 75% {
} box-shadow: 0em -2.6em 0em 0em rgba(16, 16, 16, 0.2), 1.8em -1.8em 0 0em rgba(16, 16, 16, 0.2), 2.5em 0em 0 0em rgba(16, 16, 16, 0.2), 1.75em 1.75em 0 0em rgba(16, 16, 16, 0.2), 0em 2.5em 0 0em rgba(16, 16, 16, 0.5), -1.8em 1.8em 0 0em rgba(16, 16, 16, 0.7), -2.6em 0em 0 0em #101010, -1.8em -1.8em 0 0em rgba(16, 16, 16, 0.2);
37.5% { }
box-shadow: 87.5% {
0em -2.6em 0em 0em rgba(16, 16, 16, 0.2), box-shadow: 0em -2.6em 0em 0em rgba(16, 16, 16, 0.2), 1.8em -1.8em 0 0em rgba(16, 16, 16, 0.2), 2.5em 0em 0 0em rgba(16, 16, 16, 0.2), 1.75em 1.75em 0 0em rgba(16, 16, 16, 0.2), 0em 2.5em 0 0em rgba(16, 16, 16, 0.2), -1.8em 1.8em 0 0em rgba(16, 16, 16, 0.5), -2.6em 0em 0 0em rgba(16, 16, 16, 0.7), -1.8em -1.8em 0 0em #101010;
1.8em -1.8em 0 0em rgba(16, 16, 16, 0.5), }
2.5em 0em 0 0em rgba(16, 16, 16, 0.7), }
1.75em 1.75em 0 0em #101010, }
0em 2.5em 0 0em rgba(16, 16, 16, 0.2),
-1.8em 1.8em 0 0em rgba(16, 16, 16, 0.2),
-2.6em 0em 0 0em rgba(16, 16, 16, 0.2),
-1.8em -1.8em 0 0em rgba(16, 16, 16, 0.2);
}
50% {
box-shadow:
0em -2.6em 0em 0em rgba(16, 16, 16, 0.2),
1.8em -1.8em 0 0em rgba(16, 16, 16, 0.2),
2.5em 0em 0 0em rgba(16, 16, 16, 0.5),
1.75em 1.75em 0 0em rgba(16, 16, 16, 0.7),
0em 2.5em 0 0em #101010,
-1.8em 1.8em 0 0em rgba(16, 16, 16, 0.2),
-2.6em 0em 0 0em rgba(16, 16, 16, 0.2),
-1.8em -1.8em 0 0em rgba(16, 16, 16, 0.2);
}
62.5% {
box-shadow:
0em -2.6em 0em 0em rgba(16, 16, 16, 0.2),
1.8em -1.8em 0 0em rgba(16, 16, 16, 0.2),
2.5em 0em 0 0em rgba(16, 16, 16, 0.2),
1.75em 1.75em 0 0em rgba(16, 16, 16, 0.5),
0em 2.5em 0 0em rgba(16, 16, 16, 0.7),
-1.8em 1.8em 0 0em #101010,
-2.6em 0em 0 0em rgba(16, 16, 16, 0.2),
-1.8em -1.8em 0 0em rgba(16, 16, 16, 0.2);
}
75% {
box-shadow:
0em -2.6em 0em 0em rgba(16, 16, 16, 0.2),
1.8em -1.8em 0 0em rgba(16, 16, 16, 0.2),
2.5em 0em 0 0em rgba(16, 16, 16, 0.2),
1.75em 1.75em 0 0em rgba(16, 16, 16, 0.2),
0em 2.5em 0 0em rgba(16, 16, 16, 0.5),
-1.8em 1.8em 0 0em rgba(16, 16, 16, 0.7),
-2.6em 0em 0 0em #101010,
-1.8em -1.8em 0 0em rgba(16, 16, 16, 0.2);
}
87.5% {
box-shadow:
0em -2.6em 0em 0em rgba(16, 16, 16, 0.2),
1.8em -1.8em 0 0em rgba(16, 16, 16, 0.2),
2.5em 0em 0 0em rgba(16, 16, 16, 0.2),
1.75em 1.75em 0 0em rgba(16, 16, 16, 0.2),
0em 2.5em 0 0em rgba(16, 16, 16, 0.2),
-1.8em 1.8em 0 0em rgba(16, 16, 16, 0.5),
-2.6em 0em 0 0em rgba(16, 16, 16, 0.7),
-1.8em -1.8em 0 0em #101010;
}
}
}

View File

@ -824,6 +824,8 @@ export function setSurfaceShapePattern(polygon, mode = 'onlyBorder', trestleMode
ctx.strokeStyle = 'black' ctx.strokeStyle = 'black'
ctx.lineWidth = 0.2 ctx.lineWidth = 0.2
ctx.fillStyle = 'rgba(0, 0, 0, 0.1)' ctx.fillStyle = 'rgba(0, 0, 0, 0.1)'
} else {
ctx.fillStyle = 'rgba(255, 255, 255, 1)'
} }
if (polygon.direction === 'east' || polygon.direction === 'west') { if (polygon.direction === 'east' || polygon.direction === 'west') {

View File

@ -93,3 +93,27 @@ export const inputNumberCheck = (e) => {
input.value = input.value.replace(/[^\d]/g, '') input.value = input.value.replace(/[^\d]/g, '')
} }
} }
/**
* 파이프함수 정의
* @param {...any} fns 순수함수들
* @returns
*/
export const pipe =
(...fns) =>
(x) =>
fns.reduce((v, f) => f(v), x)
/**
* 캔버스 각도에 따른 흐름 방향 계산
* @param {number} canvasAngle
* @returns {object} 흐름 방향 객체
*/
export const calculateFlowDirection = (canvasAngle) => {
return {
down: -canvasAngle,
up: 180 - canvasAngle,
left: 90 - canvasAngle < 180 ? 90 - canvasAngle : 90 - canvasAngle - 360,
right: -90 - canvasAngle < -180 ? -90 - canvasAngle + 360 : -90 - canvasAngle,
}
}

617
yarn.lock

File diff suppressed because it is too large Load Diff