Merge branch 'dev' into feature/test-jy

# Conflicts:
#	src/util/qpolygon-utils.js
This commit is contained in:
Jaeyoung Lee 2024-10-18 10:33:54 +09:00
commit fd92766a4f
126 changed files with 9917 additions and 3195 deletions

View File

@ -9,3 +9,6 @@ DATABASE_URL="sqlserver://mssql.devgrr.kr:1433;database=qcast;user=qcast;passwor
SESSION_SECRET="i3iHH1yp2/2SpQSIySQ4bpyc4g0D+zCF9FAn5xUG0+Y=" SESSION_SECRET="i3iHH1yp2/2SpQSIySQ4bpyc4g0D+zCF9FAn5xUG0+Y="
NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_bV5zuYMyyIYFlOb3" NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_bV5zuYMyyIYFlOb3"
NEXT_PUBLIC_Q_ORDER_AUTO_LOGIN_URL="http://q-order-local.q-cells.jp:8120/eos/login/autoLogin"
NEXT_PUBLIC_Q_MUSUBI_AUTO_LOGIN_URL="http://q-musubi-local.q-cells.jp:8120/qm/login/autoLogin"

View File

@ -7,3 +7,6 @@ DATABASE_URL=""
SESSION_SECRET="i3iHH1yp2/2SpQSIySQ4bpyc4g0D+zCF9FAn5xUG0+Y=" SESSION_SECRET="i3iHH1yp2/2SpQSIySQ4bpyc4g0D+zCF9FAn5xUG0+Y="
NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_bV5zuYMyyIYFlOb3" NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_bV5zuYMyyIYFlOb3"
NEXT_PUBLIC_Q_ORDER_AUTO_LOGIN_URL="http://q-order-local.q-cells.jp:8120/eos/login/autoLogin"
NEXT_PUBLIC_Q_MUSUBI_AUTO_LOGIN_URL="http://q-musubi-local.q-cells.jp:8120/qm/login/autoLogin"

View File

@ -17,6 +17,7 @@
"framer-motion": "^11.2.13", "framer-motion": "^11.2.13",
"fs": "^0.0.1-security", "fs": "^0.0.1-security",
"iron-session": "^8.0.2", "iron-session": "^8.0.2",
"js-cookie": "^3.0.5",
"mathjs": "^13.0.2", "mathjs": "^13.0.2",
"mssql": "^11.0.1", "mssql": "^11.0.1",
"next": "14.2.3", "next": "14.2.3",
@ -43,7 +44,7 @@
"prettier": "^3.3.3", "prettier": "^3.3.3",
"prisma": "^5.18.0", "prisma": "^5.18.0",
"react-color-palette": "^7.2.2", "react-color-palette": "^7.2.2",
"react-dropdown-select": "^4.11.3", "react-select": "^5.8.1",
"sass": "^1.77.8", "sass": "^1.77.8",
"tailwindcss": "^3.4.1" "tailwindcss": "^3.4.1"
} }

View File

@ -0,0 +1,4 @@
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="0.5" y="0.5" width="14" height="14" rx="7" stroke="white"/>
<path d="M9.741 10.559L7.509 8.327L5.277 10.559L4.512 9.794L6.744 7.562L4.512 5.33L5.277 4.565L7.509 6.797L9.741 4.565L10.506 5.33L8.274 7.562L10.506 9.794L9.741 10.559Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 358 B

View File

@ -1,5 +1,5 @@
<svg width="154" height="155" viewBox="0 0 154 155" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="154" height="140" viewBox="0 0 154 140" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_4103_3996)"> <g clip-path="url(#clip0_24_15)">
<path d="M1.65636 139L70 9.14709L138.344 139H1.65636Z" stroke="black" stroke-width="2"/> <path d="M1.65636 139L70 9.14709L138.344 139H1.65636Z" stroke="black" stroke-width="2"/>
<rect x="69" y="11" width="2" height="127" fill="black"/> <rect x="69" y="11" width="2" height="127" fill="black"/>
<circle cx="70" cy="8" r="7.5" stroke="#FF0000"/> <circle cx="70" cy="8" r="7.5" stroke="#FF0000"/>
@ -9,13 +9,8 @@
<path d="M144 134L148.5 138L153 134" stroke="black"/> <path d="M144 134L148.5 138L153 134" stroke="black"/>
<rect x="144" width="9" height="1" fill="#FF0000"/> <rect x="144" width="9" height="1" fill="#FF0000"/>
<rect x="144" y="139" width="9" height="1" fill="#FF0000"/> <rect x="144" y="139" width="9" height="1" fill="#FF0000"/>
<rect x="2" y="150" width="1" height="136" transform="rotate(-90 2 150)" fill="black"/>
<path d="M6 154L2 149.5L6 145" stroke="black"/>
<path d="M134 154L138 149.5L134 145" stroke="black"/>
<rect y="154" width="9" height="1" transform="rotate(-90 0 154)" fill="#FF0000"/>
<rect x="139" y="154" width="9" height="1" transform="rotate(-90 139 154)" fill="#FF0000"/>
<defs> <defs>
<clipPath id="clip0_4103_3996"> <clipPath id="clip0_24_15">
<rect width="140" height="140" fill="white"/> <rect width="140" height="140" fill="white"/>
</clipPath> </clipPath>
</defs> </defs>

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 716 B

View File

@ -0,0 +1,6 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="elements">
<path id="Vector" d="M6.05566 9.94445L9.94455 6.05556" stroke="#45576F" stroke-width="1.5" stroke-linecap="round"/>
<path id="Vector_2" d="M11.7693 10.0296L13.799 8C15.4003 6.39865 15.4003 3.80236 13.799 2.20101C12.1976 0.599663 9.60135 0.599663 8 2.20101L5.97035 4.23066M10.0296 11.7693L8 13.799C6.39865 15.4003 3.80236 15.4003 2.20101 13.799C0.599663 12.1976 0.599663 9.60135 2.20101 8L4.23066 5.97035" stroke="#45576F" stroke-width="1.5" stroke-linecap="round"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 592 B

View File

@ -0,0 +1,5 @@
<svg width="10" height="10" viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="elements">
<path id="Vector" d="M8.99915 9L1 1M1.00085 9L9 1" stroke="#101010" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 260 B

View File

@ -0,0 +1,11 @@
<svg width="21" height="21" viewBox="0 0 21 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="element" clip-path="url(#clip0_4262_4496)">
<path id="&#237;&#140;&#168;&#236;&#138;&#164; 171" d="M17.879 17.9735L19.879 19.9735" stroke="#101010" stroke-width="1.5" stroke-linecap="round"/>
<path id="Vector" d="M9.98901 18.4039C14.6834 18.4039 18.489 14.5989 18.489 9.90519C18.489 5.21149 14.6834 1.40649 9.98901 1.40649C5.29459 1.40649 1.48901 5.21149 1.48901 9.90519C1.48901 14.5989 5.29459 18.4039 9.98901 18.4039Z" stroke="#101010" stroke-width="1.5"/>
</g>
<defs>
<clipPath id="clip0_4262_4496">
<rect width="21" height="21" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 677 B

View File

@ -0,0 +1,6 @@
<svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="0.5" y="0.5" width="35" height="35" rx="17.5" fill="white"/>
<rect x="0.5" y="0.5" width="35" height="35" rx="17.5" stroke="#45576F"/>
<path d="M9 20L9.23384 20.6626C10.144 23.2413 10.5991 24.5307 11.6374 25.2654C12.6758 26 14.0431 26 16.7778 26H19.2222C21.9569 26 23.3242 26 24.3626 25.2654C25.4009 24.5307 25.856 23.2413 26.7662 20.6626L27 20" stroke="#45576F" stroke-width="1.5" stroke-linecap="round"/>
<path d="M18 20L18 10M18 20C17.2998 20 15.9915 18.0057 15.5 17.5M18 20C18.7002 20 20.0085 18.0057 20.5 17.5" stroke="#45576F" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 713 B

View File

@ -0,0 +1,6 @@
<svg width="15" height="14" viewBox="0 0 15 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="elements">
<path id="Vector 4232" d="M0.499939 8.5556L0.681818 9.07093C1.38972 11.0766 1.74367 12.0795 2.55127 12.6509C3.35887 13.2223 4.42235 13.2223 6.54932 13.2223H8.45058C10.5775 13.2223 11.641 13.2223 12.4486 12.6509C13.2562 12.0795 13.6102 11.0766 14.3181 9.07093L14.5 8.5556" stroke="#94A0AD" stroke-linecap="round"/>
<path id="Vector" d="M7.49981 8.55556V0.777771M7.49981 8.55556C6.95518 8.55556 5.93766 7.00444 5.55536 6.61111M7.49981 8.55556C8.04443 8.55556 9.06195 7.00444 9.44425 6.61111" stroke="#94A0AD" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 682 B

View File

@ -0,0 +1,8 @@
<svg width="15" height="16" viewBox="0 0 15 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="elements">
<path id="Rectangle 2175" d="M12.5556 6.25V5.41C12.5497 3.44799 12.487 2.40784 11.8095 1.73809C11.0629 1 9.8612 1 7.45783 1L6.09832 1C3.69495 1 2.49326 1 1.74663 1.73809C1 2.47618 1 3.66412 1 6.04L1 9.26C1 11.6359 1 12.8238 1.74663 13.5619C2.40945 14.2171 3.4309 14.2907 5.33333 14.299" stroke="#94A0AD" stroke-linecap="round"/>
<path id="Vector" d="M12.321 13.382L13.9999 15M13.2777 11.15C13.2777 9.4103 11.8226 8 10.0277 8C8.23278 8 6.77771 9.4103 6.77771 11.15C6.77771 12.8897 8.23278 14.3 10.0277 14.3C11.8226 14.3 13.2777 12.8897 13.2777 11.15Z" stroke="#94A0AD" stroke-linecap="round" stroke-linejoin="round"/>
<path id="Vector 4435" d="M3.88879 4.5H9.66657" stroke="#94A0AD" stroke-linecap="round"/>
<path id="Vector 4436" d="M3.88879 7.29993H6.05546" stroke="#94A0AD" stroke-linecap="round"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 927 B

View File

@ -0,0 +1,4 @@
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="7" cy="7" r="6.5" fill="white" stroke="#F16A6A"/>
<path d="M6.507 8.728L6.276 4.669L6.221 3.129H7.849L7.794 4.669L7.563 8.728H6.507ZM7.035 11.654C6.74167 11.654 6.49967 11.5513 6.309 11.346C6.12567 11.1407 6.034 10.8913 6.034 10.598C6.034 10.29 6.12567 10.037 6.309 9.839C6.49967 9.641 6.74167 9.542 7.035 9.542C7.32833 9.542 7.56667 9.641 7.75 9.839C7.94067 10.037 8.036 10.29 8.036 10.598C8.036 10.8913 7.94067 11.1407 7.75 11.346C7.56667 11.5513 7.32833 11.654 7.035 11.654Z" fill="#F16A6A"/>
</svg>

After

Width:  |  Height:  |  Size: 611 B

View File

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

View File

@ -7,10 +7,7 @@ export default async function CommunityArchivePage() {
return ( return (
<> <>
<Hero title="자료 다운로드" /> <Archive />
<div className="container flex flex-wrap items-center justify-between mx-auto p-4 m-4 border">
<Archive />
</div>
</> </>
) )
} }

View File

@ -1,4 +1,3 @@
import Hero from '@/components/Hero'
import Faq from '@/components/community/Faq' import Faq from '@/components/community/Faq'
import { initCheck } from '@/util/session-util' import { initCheck } from '@/util/session-util'
@ -7,10 +6,7 @@ export default async function CommunityFaqPage() {
return ( return (
<> <>
<Hero title="FAQ" /> <Faq />
<div className="container flex flex-wrap items-center justify-between mx-auto p-4 m-4 border">
<Faq />
</div>
</> </>
) )
} }

View File

@ -1,4 +1,3 @@
import Hero from '@/components/Hero'
import Notice from '@/components/community/Notice' import Notice from '@/components/community/Notice'
import { initCheck } from '@/util/session-util' import { initCheck } from '@/util/session-util'
@ -7,10 +6,7 @@ export default async function CommunityNoticePage() {
return ( return (
<> <>
<Hero title="공지사항" /> <Notice />
<div className="container flex flex-wrap items-center justify-between mx-auto p-4 m-4 border">
<Notice />
</div>
</> </>
) )
} }

View File

@ -1,19 +1,9 @@
'use client' import JoinComplete from '@/components/auth/JoinComplete'
import { useMessage } from '@/hooks/useMessage'
export default function CompletePage() {
const { getMessage } = useMessage()
export default function JoinCompletePage() {
return ( return (
<> <>
<div className="flex min-h-full flex-1 flex-col justify-center px-6 py-12 lg:px-8"> <JoinComplete />
<h1 className="text-center text-4xl font-bold mb-10">{getMessage('join.complete.title')}</h1>
<div className="mt-10 mb-10 w-full text-center text-2xl">{getMessage('join.complete.contents')}</div>
<div className="mt-10 w-full text-center">
{getMessage('join.complete.email_comment')} :&nbsp;{getMessage('join.complete.email')}
</div>
</div>
</> </>
) )
} }

View File

@ -4,7 +4,6 @@ import { headers } from 'next/headers'
import { redirect } from 'next/navigation' import { redirect } from 'next/navigation'
import { getSession } from '@/lib/authActions' import { getSession } from '@/lib/authActions'
import RecoilRootWrapper from './RecoilWrapper' import RecoilRootWrapper from './RecoilWrapper'
import UIProvider from './UIProvider'
import { ToastContainer } from 'react-toastify' import { ToastContainer } from 'react-toastify'
@ -14,7 +13,9 @@ import QModal from '@/components/common/modal/QModal'
import './globals.css' import './globals.css'
import '../styles/style.scss' import '../styles/style.scss'
import '../styles/contents.scss'
import Dimmed from '@/components/ui/Dimmed' import Dimmed from '@/components/ui/Dimmed'
import SessionProvider from './SessionProvider'
// const inter = Inter({ subsets: ['latin'] }) // const inter = Inter({ subsets: ['latin'] })
@ -48,6 +49,8 @@ export default async function RootLayout({ children }) {
telNo: session.telNo, telNo: session.telNo,
fax: session.fax, fax: session.fax,
email: session.email, email: session.email,
storeLvl: session.storeLvl,
groupId: session.groupId,
pwdInitYn: session.pwdInitYn, pwdInitYn: session.pwdInitYn,
isLoggedIn: session.isLoggedIn, isLoggedIn: session.isLoggedIn,
} }
@ -61,21 +64,26 @@ export default async function RootLayout({ children }) {
<RecoilRootWrapper> <RecoilRootWrapper>
<html lang="en"> <html lang="en">
<body> <body>
{headerPathname !== '/login' ? ( {headerPathname === '/login' || headerPathname === '/join' ? (
<QcastProvider>{children}</QcastProvider>
) : (
<div className="wrap"> <div className="wrap">
<Header userSession={sessionProps} /> <Header userSession={sessionProps} />
<UIProvider> <div className="content">
<div className="content"> <Dimmed />
<Dimmed /> <QcastProvider>
<QcastProvider>{children}</QcastProvider> <SessionProvider useSession={sessionProps}>{children}</SessionProvider>
</QcastProvider>
</div>
<footer>
<div className="footer-inner">
<span>COPYRIGHT©2024 Hanwha Japan All Rights Reserved.</span>
</div> </div>
</UIProvider> </footer>
</div> </div>
) : (
<QcastProvider>{children}</QcastProvider>
)} )}
<ToastContainer /> <ToastContainer />
{/* <QModal /> */} <QModal />
</body> </body>
</html> </html>
</RecoilRootWrapper> </RecoilRootWrapper>

View File

@ -1,10 +1,9 @@
import Login from '@/components/auth/Login' import Login from '@/components/auth/Login'
import NewLogin from '@/components/auth/NewLogin'
export default function LoginPage() { export default function LoginPage() {
return ( return (
<> <>
<NewLogin /> <Login />
</> </>
) )
} }

View File

@ -94,10 +94,21 @@ export const LineType = {
// 오브젝트 배치 > 개구배치, 그림자배치 // 오브젝트 배치 > 개구배치, 그림자배치
export const BATCH_TYPE = { export const BATCH_TYPE = {
OPENING: 'opening', OPENING: 'opening',
OPENING_TEMP: 'openingTemp',
SHADOW: 'shadow', SHADOW: 'shadow',
SHADOW_TEMP: 'shadowTemp',
TRIANGLE_DORMER: 'triangleDormer',
TRIANGLE_DORMER_TEMP: 'triangleDormerTemp',
PENTAGON_DORMER: 'pentagonDormer',
PENTAGON_DORMER_TEMP: 'pentagonDormerTemp',
} }
// 오브젝트 배치 > 프리입력, 치수입력 // 오브젝트 배치 > 프리입력, 치수입력
export const INPUT_TYPE = { export const INPUT_TYPE = {
FREE: 'free', FREE: 'free',
DIMENSION: 'dimension', DIMENSION: 'dimension',
} }
export const POLYGON_TYPE = {
ROOF: 'roof',
TRESTLE: 'trestle',
}

View File

@ -13,6 +13,7 @@ import { stuffSearchState } from '@/store/stuffAtom'
import { useForm } from 'react-hook-form' import { useForm } from 'react-hook-form'
import '@/styles/contents.scss' import '@/styles/contents.scss'
import ChangePasswordPop from './main/ChangePasswordPop' import ChangePasswordPop from './main/ChangePasswordPop'
import { searchState } from '@/store/boardAtom'
export default function MainPage() { export default function MainPage() {
const [sessionState, setSessionState] = useRecoilState(sessionStore) const [sessionState, setSessionState] = useRecoilState(sessionStore)
@ -36,6 +37,8 @@ export default function MainPage() {
const [stuffSearch, setStuffSearch] = useRecoilState(stuffSearchState) const [stuffSearch, setStuffSearch] = useRecoilState(stuffSearchState)
const [searchForm, setSearchForm] = useRecoilState(searchState)
useEffect(() => { useEffect(() => {
if (sessionState.pwdInitYn === 'Y') { if (sessionState.pwdInitYn === 'Y') {
fetchObjectList() fetchObjectList()
@ -72,14 +75,8 @@ export default function MainPage() {
}) })
router.push('/management/stuff') router.push('/management/stuff')
} else { } else {
alert('작업중') setSearchForm({ ...searchForm, searchValue: searchTxt, mainFlag: 'Y' })
return router.push('/community/faq')
//FAQ
//faq
//searchValue= e.target.value
//mainFlag:'Y'
// router.push('/community/faq')
} }
} }
} }
@ -100,13 +97,8 @@ export default function MainPage() {
router.push('/management/stuff') router.push('/management/stuff')
} else { } else {
alert('작업중') setSearchForm({ ...searchForm, searchValue: searchTxt, mainFlag: 'Y' })
return router.push('/community/faq')
//FAQ
//faq
//searchValue= e.target.value
//mainFlag:'Y'
// router.push('/community/faq')
} }
} }

View File

@ -20,6 +20,7 @@ import Image from 'next/image'
import QInput from './common/input/Qinput' import QInput from './common/input/Qinput'
import QSelect from './common/select/QSelect' import QSelect from './common/select/QSelect'
import QPagination from './common/pagination/QPagination'
export default function Playground() { export default function Playground() {
const [useCadFile, setUseCadFile] = useRecoilState(useCadFileState) const [useCadFile, setUseCadFile] = useRecoilState(useCadFileState)
@ -29,7 +30,7 @@ export default function Playground() {
const fileRef = useRef(null) const fileRef = useRef(null)
const queryRef = useRef(null) const queryRef = useRef(null)
const [zoom, setZoom] = useState(20) const [zoom, setZoom] = useState(20)
const { get, promisePost } = useAxios() const { get, promiseGet, promisePost } = useAxios()
const testVar = process.env.NEXT_PUBLIC_TEST const testVar = process.env.NEXT_PUBLIC_TEST
const converterUrl = process.env.NEXT_PUBLIC_CONVERTER_API_URL const converterUrl = process.env.NEXT_PUBLIC_CONVERTER_API_URL
const { getMessage } = useMessage() const { getMessage } = useMessage()
@ -41,6 +42,9 @@ export default function Playground() {
const [radioInput, setRadioInput] = useState('') const [radioInput, setRadioInput] = useState('')
const [checkboxInput, setCheckboxInput] = useState([]) const [checkboxInput, setCheckboxInput] = useState([])
const [selectedValue, setSelectedValue] = useState('') const [selectedValue, setSelectedValue] = useState('')
const [users, setUsers] = useState([])
useEffect(() => { useEffect(() => {
console.log('textInput:', textInput) console.log('textInput:', textInput)
}, [textInput]) }, [textInput])
@ -130,6 +134,20 @@ export default function Playground() {
}) })
} }
const paginationProps = {
pageNo: 1,
pageSize: 10,
pagePerBlock: 10,
totalCount: 501,
handleChangePage: (page) => {
console.log('page', page)
},
}
useEffect(() => {
console.log('users:', users)
}, [users])
return ( return (
<> <>
<div className="container mx-auto p-4 m-4 border"> <div className="container mx-auto p-4 m-4 border">
@ -290,6 +308,35 @@ export default function Playground() {
Sweetalert - confirm Sweetalert - confirm
</Button> </Button>
</div> </div>
<div className="my-2">
<QPagination {...paginationProps} />
</div>
<div className="my-2">
<Button
onClick={() => {
promiseGet({ url: 'http://localhost:8080/api/user' }).then((res) => setUsers(res.data))
}}
>
axios get test
</Button>
</div>
<div className="my-2">
<Button
onClick={() => {
const result = promisePost({
url: 'http://localhost:8080/api/user',
data: {
firstName: 'Yoo',
lastName: 'Sangwook',
email: 'yoo1757@naver.com',
age: 46,
},
}).then((res) => console.log('res', res))
}}
>
axios post test
</Button>
</div>
</div> </div>
</> </>
) )

View File

@ -3,7 +3,8 @@
import React, { useEffect, useState } from 'react' import React, { useEffect, useState } from 'react'
import { Button } from '@nextui-org/react' import { Button } from '@nextui-org/react'
import { get, post } from '@/lib/Axios' import { useAxios } from '@/hooks/useAxios'
import { useRecoilState } from 'recoil' import { useRecoilState } from 'recoil'
import { customSettingsState } from '@/store/canvasAtom' import { customSettingsState } from '@/store/canvasAtom'
import { modalContent, modalState } from '@/store/modalAtom' import { modalContent, modalState } from '@/store/modalAtom'
@ -20,6 +21,8 @@ export default function Settings() {
const [open, setOpen] = useRecoilState(modalState) const [open, setOpen] = useRecoilState(modalState)
const [contents, setContent] = useRecoilState(modalContent) const [contents, setContent] = useRecoilState(modalContent)
const { get, post } = useAxios()
const handleSavePopup = () => { const handleSavePopup = () => {
console.log('color ', color) console.log('color ', color)
} }

View File

@ -1,30 +1,28 @@
'use client' 'use client'
import { useAxios } from '@/hooks/useAxios' import { useAxios } from '@/hooks/useAxios'
import { redirect } from 'next/navigation' import { useRouter } from 'next/navigation'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import Cookies from 'js-cookie'
export default function Join() { export default function Join() {
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { post } = useAxios() const { promisePost } = useAxios()
const router = useRouter()
//
const joinProcess = async (e) => {
e.preventDefault()
const formData = new FormData(e.target)
const joinProcess = async (formData) => {
const param = { const param = {
langCd: 'JA',
lastEditUser: formData.get('userId'),
storeQcastNm: formData.get('storeQcastNm'), storeQcastNm: formData.get('storeQcastNm'),
storeQcastNmKana: formData.get('storeQcastNmKana'), storeQcastNmKana: formData.get('storeQcastNmKana'),
postCd: formData.get('postCd'), postCd: formData.get('postCd'),
addr: formData.get('addr'), addr: formData.get('addr'),
telNo: formData.get('telNo'), telNo: formData.get('telNo'),
fax: formData.get('fax'), fax: formData.get('fax'),
payTermsCd: 'JB02', bizNo: formData.get('bizNo'),
kamId: 'E1101011',
qtCompNm: formData.get('qtCompNm'),
qtPostCd: formData.get('qtPostCd'),
qtAddr: formData.get('qtAddr'),
qtTelNo: formData.get('qtTelNo'),
qtFax: formData.get('qtFax'),
userInfo: { userInfo: {
userId: formData.get('userId'), userId: formData.get('userId'),
userNm: formData.get('userNm'), userNm: formData.get('userNm'),
@ -36,287 +34,268 @@ export default function Join() {
}, },
} }
await post({ url: '/api/login/v1.0/user/join', data: param }).then((res) => { await promisePost({ url: '/api/login/v1.0/user/join', data: param })
if (res) { .then((res) => {
if (res.result.resultCode == 'S') { if (res) {
redirect('/join/complete') if (res.data.result.resultCode == 'S') {
} else { Cookies.set('joinEmail', formData.get('email'), { expires: 1 })
alert(res.result.resultMsg) router.push('/join/complete')
} else {
alert(res.data.result.resultMsg)
}
} }
} })
}) .catch((error) => {
alert(error.response.data.message)
})
} }
return ( return (
<div className="flex min-h-full flex-1 flex-col justify-center px-6 py-12 lg:px-8"> <div className="center-page-wrap">
<h1 className="text-center text-4xl font-bold">{getMessage('join.title')}</h1> <div className="center-page-inner">
<form action={joinProcess}> <form onSubmit={joinProcess}>
<div className="mt-10"> <div className="center-page-tit">{getMessage('join.title')}</div>
<div> <div className="sub-table-box signup">
{getMessage('join.sub1.title')} (*{getMessage('common.require')}) <span>{getMessage('join.sub1.comment')}</span> <div className="table-box-title-wrap">
<div className="title-wrap">
<h3>
{getMessage('join.sub1.title')} <span className="important">(*{getMessage('common.require')})</span>
</h3>
<span className="option">{getMessage('join.sub1.comment')}</span>
</div>
</div>
<div className="common-table">
<table>
<colgroup>
<col style={{ width: '180px' }} />
<col />
</colgroup>
<tbody>
<tr>
<th>
{getMessage('join.sub1.storeQcastNm')} <span className="important">*</span>
</th>
<td>
<div className="input-wrap" style={{ width: '700px' }}>
<input
type="text"
id="storeQcastNm"
name="storeQcastNm"
required
alt={getMessage('join.sub1.storeQcastNm')}
className="input-light"
placeholder={getMessage('join.sub1.storeQcastNm_placeholder')}
/>
</div>
</td>
</tr>
<tr>
<th>
{getMessage('join.sub1.storeQcastNmKana')} <span className="important">*</span>
</th>
<td>
<div className="input-wrap" style={{ width: '700px' }}>
<input
type="text"
id="storeQcastNmKana"
name="storeQcastNmKana"
required
className="input-light"
placeholder={getMessage('join.sub1.storeQcastNmKana_placeholder')}
/>
</div>
</td>
</tr>
<tr>
<th>
{getMessage('join.sub1.postCd')}/{getMessage('join.sub1.addr')} <span className="important">*</span>
</th>
<td>
<div className="flx-box">
<div className="input-wrap mr5" style={{ width: '200px' }}>
<input
type="text"
id="postCd"
name="postCd"
required
className="input-light"
placeholder={getMessage('join.sub1.postCd_placeholder')}
/>
</div>
<div className="input-wrap" style={{ width: '495px' }}>
<input
type="text"
id="addr"
name="addr"
required
className="input-light"
placeholder={getMessage('join.sub1.addr_placeholder')}
/>
</div>
</div>
</td>
</tr>
<tr>
<th>
{getMessage('join.sub1.telNo')} <span className="important">*</span>
</th>
<td>
<div className="input-wrap" style={{ width: '200px' }}>
<input
type="text"
id="telNo"
name="telNo"
required
className="input-light"
placeholder={getMessage('join.sub1.telNo_placeholder')}
></input>
</div>
</td>
</tr>
<tr>
<th>
{getMessage('join.sub1.fax')} <span className="important">*</span>
</th>
<td>
<div className="input-wrap" style={{ width: '200px' }}>
<input
type="text"
id="fax"
name="fax"
required
className="input-light"
placeholder={getMessage('join.sub1.fax_placeholder')}
></input>
</div>
</td>
</tr>
<tr>
<th>{getMessage('join.sub1.bizNo')}</th>
<td>
<div className="input-wrap" style={{ width: '200px' }}>
<input type="text" id="bizNo" name="bizNo" className="input-light" />
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div> </div>
<table className="w-full"> <div className="sub-table-box signup">
<colgroup> <div className="table-box-title-wrap">
<col style={{ width: '20%' }} /> <div className="title-wrap">
<col style={{ width: '80%' }} /> <h3>
</colgroup> {getMessage('join.sub2.title')} <span className="important">(*{getMessage('common.require')})</span>
<tbody> </h3>
<tr> </div>
<th style={{ border: '1px solid gray', padding: '5px' }}>{getMessage('join.sub1.storeQcastNm')} *</th> </div>
<td style={{ border: '1px solid gray', padding: '5px' }}> <div className="common-table">
<input <table>
type="text" <colgroup>
id="storeQcastNm" <col style={{ width: '180px' }} />
name="storeQcastNm" <col />
required </colgroup>
alt={getMessage('join.sub1.storeQcastNm')} <tbody>
className="block w-full border-0 py-1.5 ring-1 ring-inset ring-gray-300" <tr>
placeholder={getMessage('join.sub1.storeQcastNm_placeholder')} <th>
></input> {getMessage('join.sub2.userNm')} <span className="important">*</span>
</td> </th>
</tr> <td>
<tr> <div className="input-wrap" style={{ width: '200px' }}>
<th style={{ border: '1px solid gray', padding: '5px' }}>{getMessage('join.sub1.storeQcastNmKana')} *</th> <input type="text" id="userNm" name="userNm" className="input-light" required />
<td style={{ border: '1px solid gray', padding: '5px' }}> </div>
<input </td>
type="text" </tr>
id="storeQcastNmKana" <tr>
name="storeQcastNmKana" <th>{getMessage('join.sub2.userNmKana')}</th>
required <td>
className="block w-full border-0 py-1.5 ring-1 ring-inset ring-gray-300" <div className="input-wrap" style={{ width: '200px' }}>
placeholder={getMessage('join.sub1.storeQcastNmKana_placeholder')} <input type="text" id="userNmKana" name="userNmKana" className="input-light" />
></input> </div>
</td> </td>
</tr> </tr>
<tr> <tr>
<th style={{ border: '1px solid gray', padding: '5px' }}> <th>
{getMessage('join.sub1.postCd')}/{getMessage('join.sub1.addr')} * {getMessage('join.sub2.userId')} <span className="important">*</span>
</th> </th>
<td style={{ border: '1px solid gray', padding: '5px' }}> <td>
<input <div className="input-wrap" style={{ width: '200px' }}>
type="text" <input type="text" id="userId" name="userId" className="input-light" required />
id="postCd" </div>
name="postCd" </td>
required </tr>
className="block border-0 py-1.5 ring-1 ring-inset ring-gray-300" <tr>
placeholder={getMessage('join.sub1.postCd_placeholder')} <th>
></input> {getMessage('join.sub2.email')} <span className="important">*</span>
<input </th>
type="text" <td>
id="addr" <div className="input-wrap" style={{ width: '200px' }}>
name="addr" <input type="text" id="email" name="email" className="input-light" required />
required </div>
className="block w-full border-0 py-1.5 ring-1 ring-inset ring-gray-300" </td>
placeholder={getMessage('join.sub1.addr_placeholder')} </tr>
></input> <tr>
</td> <th>
</tr> {getMessage('join.sub2.telNo')} <span className="important">*</span>
<tr> </th>
<th style={{ border: '1px solid gray', padding: '5px' }}>{getMessage('join.sub1.telNo')} *</th> <td>
<td style={{ border: '1px solid gray', padding: '5px' }}> <div className="input-wrap" style={{ width: '200px' }}>
<input <input
type="text" type="text"
id="telNo" id="userTelNo"
name="telNo" name="userTelNo"
required className="input-light"
className="block w-full border-0 py-1.5 ring-1 ring-inset ring-gray-300" placeholder={getMessage('join.sub2.telNo_placeholder')}
placeholder={getMessage('join.sub1.telNo_placeholder')} required
></input> />
</td> </div>
</tr> </td>
<tr> </tr>
<th style={{ border: '1px solid gray', padding: '5px' }}>{getMessage('join.sub1.fax')} *</th> <tr>
<td style={{ border: '1px solid gray', padding: '5px' }}> <th>
<input {getMessage('join.sub2.fax')} <span className="important">*</span>
type="text" </th>
id="fax" <td>
name="fax" <div className="input-wrap" style={{ width: '200px' }}>
required <input
className="block w-full border-0 py-1.5 ring-1 ring-inset ring-gray-300" type="text"
placeholder={getMessage('join.sub1.fax_placeholder')} id="userFax"
></input> name="userFax"
</td> className="input-light"
</tr> placeholder={getMessage('join.sub1.fax_placeholder')}
</tbody> required
</table> />
</div>
<div className="mt-5"> </td>
{getMessage('join.sub2.title')} (*{getMessage('common.require')}) </tr>
<tr>
<th>{getMessage('join.sub2.category')}</th>
<td>
<div className="input-wrap" style={{ width: '200px' }}>
<input type="text" id="category" name="category" className="input-light" />
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div> </div>
<table className="w-full"> <div className="sign-up-btn-wrap">
<colgroup> <button
<col style={{ width: '20%' }} /> type="button"
<col style={{ width: '80%' }} /> className="btn-origin grey mr5"
</colgroup> onClick={() => {
<tbody> router.push('/login')
<tr> }}
<th style={{ border: '1px solid gray', padding: '5px' }}>{getMessage('join.sub2.userNm')} *</th> >
<td style={{ border: '1px solid gray', padding: '5px' }}> {getMessage('join.btn.login_page')}
<input </button>
type="text" <button type="submit" className="btn-origin navy">
id="userNm" {getMessage('join.btn.approval_request')}
name="userNm" </button>
required
className="block w-full border-0 py-1.5 ring-1 ring-inset ring-gray-300"
></input>
</td>
</tr>
<tr>
<th style={{ border: '1px solid gray', padding: '5px' }}>{getMessage('join.sub2.userNmKana')} *</th>
<td style={{ border: '1px solid gray', padding: '5px' }}>
<input
type="text"
id="userNmKana"
name="userNmKana"
required
className="block w-full border-0 py-1.5 ring-1 ring-inset ring-gray-300"
></input>
</td>
</tr>
<tr>
<th style={{ border: '1px solid gray', padding: '5px' }}>{getMessage('join.sub2.userId')} *</th>
<td style={{ border: '1px solid gray', padding: '5px' }}>
<input
type="text"
id="userId"
name="userId"
required
className="block w-full border-0 py-1.5 ring-1 ring-inset ring-gray-300"
></input>
</td>
</tr>
<tr>
<th style={{ border: '1px solid gray', padding: '5px' }}>{getMessage('join.sub2.email')} *</th>
<td style={{ border: '1px solid gray', padding: '5px' }}>
<input
type="email"
id="email"
name="email"
required
className="block w-full border-0 py-1.5 ring-1 ring-inset ring-gray-300"
autoComplete="email"
></input>
</td>
</tr>
<tr>
<th style={{ border: '1px solid gray', padding: '5px' }}>{getMessage('join.sub2.telNo')} *</th>
<td style={{ border: '1px solid gray', padding: '5px' }}>
<input
type="text"
id="userTelNo"
name="userTelNo"
required
className="block w-full border-0 py-1.5 ring-1 ring-inset ring-gray-300"
placeholder={getMessage('join.sub2.telNo_placeholder')}
></input>
</td>
</tr>
<tr>
<th style={{ border: '1px solid gray', padding: '5px' }}>{getMessage('join.sub2.fax')} *</th>
<td style={{ border: '1px solid gray', padding: '5px' }}>
<input
type="text"
id="userFax"
name="userFax"
required
className="block w-full border-0 py-1.5 ring-1 ring-inset ring-gray-300"
placeholder={getMessage('join.sub1.fax_placeholder')}
></input>
</td>
</tr>
<tr>
<th style={{ border: '1px solid gray', padding: '5px' }}>{getMessage('join.sub2.category')}</th>
<td style={{ border: '1px solid gray', padding: '5px' }}>
<input id="category" name="category" className="block w-full border-0 py-1.5 ring-1 ring-inset ring-gray-300"></input>
</td>
</tr>
</tbody>
</table>
<div className="mt-5">
{getMessage('join.sub3.title')} (*{getMessage('common.require')})
</div> </div>
<table className="w-full"> </form>
<colgroup> </div>
<col style={{ width: '20%' }} />
<col style={{ width: '80%' }} />
</colgroup>
<tbody>
<tr>
<th style={{ border: '1px solid gray', padding: '5px' }}>{getMessage('join.sub3.qtCompNm')}</th>
<td style={{ border: '1px solid gray', padding: '5px' }}>
<input type="text" id="qtCompNm" name="qtCompNm" className="block w-full border-0 py-1.5 ring-1 ring-inset ring-gray-300"></input>
</td>
</tr>
<tr>
<th style={{ border: '1px solid gray', padding: '5px' }}>
{getMessage('join.sub3.qtPostCd')}/{getMessage('join.sub3.qtAddr')}
</th>
<td style={{ border: '1px solid gray', padding: '5px' }}>
<input
type="text"
id="qtPostCd"
name="qtPostCd"
className="block border-0 py-1.5 ring-1 ring-inset ring-gray-300"
placeholder={getMessage('join.sub3.qtPostCd_placeholder')}
></input>
<input
type="text"
id="qtAddr"
name="qtAddr"
className="block w-full border-0 py-1.5 ring-1 ring-inset ring-gray-300"
placeholder={getMessage('join.sub3.qtAddr_placeholder')}
></input>
</td>
</tr>
<tr>
<th style={{ border: '1px solid gray', padding: '5px' }}>{getMessage('join.sub3.qtEmail')}</th>
<td style={{ border: '1px solid gray', padding: '5px' }}>
<input
type="email"
id="qtEmail"
name="qtEmail"
className="block w-full border-0 py-1.5 ring-1 ring-inset ring-gray-300"
autoComplete="email"
></input>
</td>
</tr>
<tr>
<th style={{ border: '1px solid gray', padding: '5px' }}>{getMessage('join.sub3.qtTelNo')}</th>
<td style={{ border: '1px solid gray', padding: '5px' }}>
<input
type="text"
id="qtTelNo"
name="qtTelNo"
className="block w-full border-0 py-1.5 ring-1 ring-inset ring-gray-300"
placeholder={getMessage('join.sub3.qtTelNo_placeholder')}
></input>
</td>
</tr>
<tr>
<th style={{ border: '1px solid gray', padding: '5px' }}>{getMessage('join.sub3.qtFax')}</th>
<td style={{ border: '1px solid gray', padding: '5px' }}>
<input
type="text"
id="qtFax"
name="qtFax"
className="block w-full border-0 py-1.5 ring-1 ring-inset ring-gray-300"
placeholder={getMessage('join.sub3.qtFax_placeholder')}
></input>
</td>
</tr>
</tbody>
</table>
</div>
<div className="mt-10 mb-10">
<button
type="submit"
className="w-full justify-center rounded-md bg-indigo-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
>
{getMessage('join.btn.approval_request')}
</button>
</div>
</form>
</div> </div>
) )
} }

View File

@ -0,0 +1,44 @@
'use client'
import { useMessage } from '@/hooks/useMessage'
import { useRouter } from 'next/navigation'
import { useState } from 'react'
import Cookies from 'js-cookie'
export default function JoinComplete() {
const router = useRouter()
const { getMessage } = useMessage()
const [emailText, setEmailText] = useState(Cookies.get('joinEmail'))
return (
<>
<div className="center-page-wrap">
<div className="center-page-inner complete">
<div className="sub-table-box">
<div className="complete-box-wrap">
<div className="complete-tit">{getMessage('join.complete.title')}</div>
<div className="complete-txt">{getMessage('join.complete.contents')}</div>
<div className="complete-email-wrap">
<div className="email-info">
{getMessage('join.complete.email_comment')} <span>{emailText}</span>
</div>
</div>
<div className="complete-btn">
<button
tyep="button"
className="btn-origin navy"
onClick={() => {
router.push('/login')
}}
>
{getMessage('join.btn.login_page')}
</button>
</div>
</div>
</div>
</div>
</div>
</>
)
}

View File

@ -1,62 +1,80 @@
'use client' 'use client'
import { useState } from 'react' import { useState, useEffect } from 'react'
import Image from 'next/image'
import Link from 'next/link'
import { useRecoilState } from 'recoil'
import { useAxios } from '@/hooks/useAxios' import { useAxios } from '@/hooks/useAxios'
import { setSession } from '@/lib/authActions' import { setSession } from '@/lib/authActions'
import { redirect } from 'next/navigation'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { Button, Switch } from '@nextui-org/react'
import { useRecoilState } from 'recoil'
import { globalLocaleStore } from '@/store/localeAtom' import { globalLocaleStore } from '@/store/localeAtom'
import { modalContent, modalState } from '@/store/modalAtom'
import { sessionStore } from '@/store/commonAtom' import { sessionStore } from '@/store/commonAtom'
import { useRouter } from 'next/navigation'
import Cookies from 'js-cookie'
import { useSearchParams } from 'next/navigation'
export default function Login() { export default function Login() {
const { patch } = useAxios() //
const initParams = useSearchParams()
const autoLoginParam = initParams.get('autoLoginParam1')
useEffect(() => {
if (autoLoginParam) {
autoLoginProcess(autoLoginParam)
}
}, [])
const autoLoginProcess = async (autoLoginParam) => {
await promisePost({ url: '/api/login/v1.0/user/login/autoLoginDecryptData', data: { loginId: autoLoginParam } })
.then((res) => {
if (res) {
if (res.data) {
post({ url: '/api/login/v1.0/user', data: { loginId: res.data } }).then((response) => {
if (response) {
const result = { ...response, storeLvl: response.groupId === '60000' ? '1' : '2', pwdInitYn: 'Y' }
setSession(result)
setSessionState(result)
router.push('/')
} else {
router.push('/login')
}
})
}
}
})
.catch((error) => {
router.push('/login')
})
}
const [userId, setUserId] = useState('')
const [checkId, setCheckId] = useState('')
const [checkEmail, setCheckEmail] = useState('')
useEffect(() => {
if (Cookies.get('chkLoginId')) {
setUserId(Cookies.get('chkLoginId'))
}
}, [])
const [chkLoginId, setChkLoginId] = useState(Cookies.get('chkLoginId') ? true : false)
const [passwordVisible, setPasswordVisible] = useState(false)
const router = useRouter()
const { getMessage } = useMessage() const { getMessage } = useMessage()
const [globalLocaleState, setGlbalLocaleState] = useRecoilState(globalLocaleStore) const [globalLocaleState, setGlbalLocaleState] = useRecoilState(globalLocaleStore)
const [sessionState, setSessionState] = useRecoilState(sessionStore) const [sessionState, setSessionState] = useRecoilState(sessionStore)
const [isSelected, setIsSelected] = useState(globalLocaleState === 'ko' ? true : false)
const handleSelected = () => { const [passwordReset, setPasswordReset] = useState(1)
if (isSelected) {
setGlbalLocaleState('ja')
} else {
setGlbalLocaleState('ko')
}
setIsSelected(!isSelected) const { promisePost, promisePatch, post } = useAxios(globalLocaleState)
}
// login process // login process
const loginProcess = async (formData) => { const loginProcess = async (e) => {
const param = { e.preventDefault()
// langCd: currentLocale const formData = new FormData(e.target)
langCd: globalLocaleState,
lastEditUser: formData.get('id'),
loginId: formData.get('id'),
pwd: formData.get('password'),
}
// await post({ url: '/api/login/v1.0/login', data: param }).then((res) => {
// if (res) {
// if (res.result.resultCode == 'S') {
// // console.log('res.data', res.data)
// //
// // if (res.data.pwdInitYn != 'Y') {
// // alert(' ')
// // } else {
// setSession(res.data)
// redirect('/')
// // }
// } else {
// alert(res.result.resultMsg)
// }
// }
// })
///////////////////////////////////////////////////////////
// //
setSession({ setSession({
userId: 'NEW016610', userId: 'NEW016610',
@ -71,9 +89,10 @@ export default function Login() {
telNo: '336610', telNo: '336610',
fax: null, fax: null,
email: 't10t@naver.com', email: 't10t@naver.com',
pwdInitYn: 'N', pwdInitYn: 'Y',
storeLvl: '1',
groupId: '60000',
}) })
setSessionState({ setSessionState({
userId: 'NEW016610', userId: 'NEW016610',
saleStoreId: null, saleStoreId: null,
@ -87,155 +106,230 @@ export default function Login() {
telNo: '336610', telNo: '336610',
fax: null, fax: null,
email: 't10t@naver.com', email: 't10t@naver.com',
pwdInitYn: 'N', pwdInitYn: 'Y',
storeLvl: '1',
groupId: '60000',
}) })
if (chkLoginId) {
redirect('/') Cookies.set('chkLoginId', formData.get('id'), { expires: 7 })
} else {
Cookies.remove('chkLoginId')
}
router.push('/')
// //
///////////////////////////////////////////////////////////
// - ** **
// const param = {
// loginId: formData.get('id'),
// pwd: formData.get('password'),
// }
// await promisePost({ url: '/api/login/v1.0/login', data: param })
// .then((res) => {
// if (res) {
// if (res.data.result.resultCode === 'S') {
// setSession(res.data.data)
// setSessionState(res.data.data)
// // ID SAVE ,
// if (chkLoginId) {
// Cookies.set('chkLoginId', formData.get('id'), { expires: 7 })
// } else {
// Cookies.remove('chkLoginId')
// }
// router.push('/')
// } else {
// alert(res.data.result.resultMsg)
// }
// }
// })
// .catch((error) => {
// alert(error.response.data.message)
// })
} }
// //
const [open, setOpen] = useRecoilState(modalState) const initPasswordProcess = async () => {
const [contents, setContent] = useRecoilState(modalContent)
const initPasswordProcess = async (formData) => {
const param = { const param = {
langCd: currentLocale, loginId: checkId,
lastEditUser: formData.get('checkId'), email: checkEmail,
loginId: formData.get('checkId'),
email: formData.get('checkEmail'),
} }
await promisePatch({
await patch({ url: '/api/login/v1.0/user/init-password', data: param }).then((res) => { url: '/api/login/v1.0/user/init-password',
if (res) { data: param,
if (res.result.resultCode == 'S') {
alert(getMessage('login.init_password.complete_message'))
redirect('/login')
} else {
alert(res.result.resultMsg)
}
}
}) })
.then((res) => {
if (res) {
if (res.data.result.resultCode == 'S') {
alert(getMessage('login.init_password.complete_message'))
setCheckId('')
setCheckEmail('')
setPasswordReset(1)
} else {
alert(res.data.result.resultMsg)
}
}
})
.catch((error) => {
alert(error.response.data.message)
})
} }
const initPasswordContent = (
<div className="flex min-h-full flex-1 flex-col justify-center px-6 py-12 lg:px-8">
<form action={initPasswordProcess} className="space-y-6">
<h2 className="text-center text-2xl font-bold leading-9 tracking-tight text-gray-900">{getMessage('login.init_password.title')}</h2>
<h2 className="text-center text-1xl font-bold leading-9 tracking-tight text-gray-900">{getMessage('login.init_password.sub_title')}</h2>
<div>
<label htmlFor="checkId" className="block text-sm font-medium leading-6 text-gray-900">
ID
</label>
<div className="mt-2">
<input
id="checkId"
name="checkId"
type="text"
required
className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
/>
</div>
</div>
<div>
<div className="flex items-center justify-between">
<label htmlFor="checkEmail" className="block text-sm font-medium leading-6 text-gray-900">
E-Mail
</label>
</div>
<div className="mt-2">
<input
id="checkEmail"
name="checkEmail"
type="email"
required
className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
/>
</div>
</div>
<p className="mt-5 text-center text-sm text-gray-500">
<Button type="submit" className="font-semibold leading-6 text-indigo-600 hover:text-indigo-500">
{getMessage('login.init_password.btn')}
</Button>
</p>
</form>
</div>
)
return ( return (
<div className="flex flex-col align-center"> <div className="login-wrap">
<div className="flex min-h-full flex-1 flex-col justify-center px-6 py-12 lg:px-8"> <div className="login-inner">
<div className="mt-10 sm:mx-auto sm:w-full sm:max-w-sm"> <Link href={'/login'} className="login-logo">
<h1 className="text-center text-4xl font-bold leading-9 tracking-tight text-gray-900">{getMessage('site.name')}</h1> <Image src="/static/images/main/login-logo.svg" alt="react" width={236} height={43} styles={{ width: '236px', height: '43px' }} priority />
<h2 className="mt-5 text-center text-2xl font-bold leading-9 tracking-tight text-gray-900">{getMessage('site.sub_name')}</h2> </Link>
</div>
<div className="mt-5 sm:mx-auto sm:w-full sm:max-w-sm"> {passwordReset === 1 && (
<form action={loginProcess} className="space-y-6"> <>
<div> <div className="login-input-frame">
<label htmlFor="userId" className="block text-sm font-medium leading-6 text-gray-900"> <form onSubmit={loginProcess} className="space-y-6">
ID <div className="login-frame-tit">
</label> <span>{getMessage('site.name')}</span>
<div className="mt-2"> {getMessage('site.sub_name')}
<input </div>
id="userId" <div className="login-input-wrap">
name="id" <div className="login-area id">
type="text" <input
required type="text"
// autoComplete="email" className="login-input"
className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" id="userId"
/> name="id"
required
value={userId}
placeholder={getMessage('login.id.placeholder')}
onChange={(e) => {
setUserId(e.target.value)
}}
/>
<button
type="button"
className="id-delete"
onClick={(e) => {
setUserId('')
}}
></button>
</div>
<div className="login-area password">
<input
type={passwordVisible ? 'text' : 'password'}
className="login-input"
id="password"
name="password"
required
autoComplete="current-password"
placeholder={getMessage('login.password.placeholder')}
onChange={(e) => {
setPasswordVisible(passwordVisible)
}}
/>
<button
className={`password-hidden ${passwordVisible ? 'visible' : ''}`}
onClick={(e) => {
e.preventDefault()
setPasswordVisible(!passwordVisible)
}}
></button>
</div>
<div className="d-check-box login">
<input
type="checkbox"
id="ch01"
name="chkLoginId"
checked={chkLoginId}
onChange={(e) => {
setChkLoginId(e.target.checked)
}}
/>
<label htmlFor="ch01">{getMessage('login.id.save')}</label>
</div>
<div className="login-btn-box">
<button type="submit" className="login-btn">
{getMessage('login')}
</button>
</div>
<div className="reset-password">
<button type="button" onClick={() => setPasswordReset(2)}>
{getMessage('login.init_password.btn')}
</button>
</div>
</div>
</form>
</div>
<div className="login-guide-wrap">
<span></span>
{getMessage('login.guide.text')}
<br />
{getMessage('login.guide.sub1')} <Link href={'../join'}>{getMessage('login.guide.join.btn')}</Link>
{getMessage('login.guide.sub2')}
</div>
</>
)}
{passwordReset === 2 && (
<>
<div className="login-input-frame">
<div className="login-frame-tit pw-reset">
<span>{getMessage('login.init_password.title')}</span>
{getMessage('login.init_password.sub_title')}
</div>
<div className="login-input-wrap">
<div className="login-area id">
<input
type="text"
id="checkId"
name="checkId"
value={checkId}
required
className="login-input"
placeholder={getMessage('login.init_password.id.placeholder')}
onChange={(e) => {
setCheckId(e.target.value)
}}
/>
<button
type="button"
className="id-delete"
onClick={() => {
setCheckId('')
}}
></button>
</div>
<div className="login-area email">
<input
id="checkEmail"
name="checkEmail"
type="email"
required
className="login-input"
value={checkEmail}
onChange={(e) => {
setCheckEmail(e.target.value)
}}
placeholder={getMessage('login.init_password.email.placeholder')}
/>
<button
type="button"
className="id-delete"
onClick={() => {
setCheckEmail('')
}}
></button>
</div>
<div className="pwreset-btn-box">
<button type="button" className="login-btn light mr5" onClick={() => setPasswordReset(1)}>
{getMessage('login.init_password.btn.back')}
</button>
<button type="button" className="login-btn" onClick={initPasswordProcess}>
{getMessage('login.init_password.btn')}
</button>
</div>
</div> </div>
</div> </div>
</>
<div> )}
<div className="flex items-center justify-between">
<label htmlFor="password" className="block text-sm font-medium leading-6 text-gray-900">
Password
</label>
</div>
<div className="mt-2">
<input
id="password"
name="password"
type="password"
required
autoComplete="current-password"
className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
/>
</div>
</div>
<div>
<button
type="submit"
className="mt-10 flex w-full justify-center rounded-md bg-indigo-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
>
{getMessage('login')}
</button>
</div>
</form>
<p className="mt-5 text-center text-sm text-gray-500">
<Button
onClick={() => {
setContent(initPasswordContent)
setOpen(true)
}}
className="font-semibold leading-6 text-indigo-600 hover:text-indigo-500"
>
{getMessage('login.init_password.btn')}
</Button>
</p>
<div className="flex align-center mt-2">
<Switch isSelected={isSelected} onValueChange={handleSelected}>
{isSelected ? 'Current Locale: KO' : 'Current Locale: JA'}
</Switch>
</div>
</div>
</div> </div>
<div className="login-copyright">COPYRIGHT©2024 Hanwha Japan All Rights Reserved.</div>
</div> </div>
) )
} }

View File

@ -1,244 +0,0 @@
'use client'
import { useState, useRef, useEffect } from 'react'
import Image from 'next/image'
import Link from 'next/link'
import { redirect } from 'next/navigation'
import { useRecoilState } from 'recoil'
import { useAxios } from '@/hooks/useAxios'
import { setSession } from '@/lib/authActions'
import { useMessage } from '@/hooks/useMessage'
import { globalLocaleStore } from '@/store/localeAtom'
import { sessionStore } from '@/store/commonAtom'
import { modalContent, modalState } from '@/store/modalAtom'
import '@/styles/style.scss'
import { useRouter } from 'next/navigation'
export default function NewLogin() {
const [passwordVisible, setPasswordVisible] = useState(false)
const passwordRef = useRef(null)
const router = useRouter()
useEffect(() => {
setOpen(false)
}, [])
useEffect(() => {
if (passwordVisible) {
passwordRef.current.type = 'text'
} else {
passwordRef.current.type = 'password'
}
}, [passwordVisible])
const { patch } = useAxios()
const { getMessage } = useMessage()
const [globalLocaleState, setGlbalLocaleState] = useRecoilState(globalLocaleStore)
const [sessionState, setSessionState] = useRecoilState(sessionStore)
const [isSelected, setIsSelected] = useState(globalLocaleState === 'ko' ? true : false)
const handleSelected = () => {
if (isSelected) {
setGlbalLocaleState('ja')
} else {
setGlbalLocaleState('ko')
}
setIsSelected(!isSelected)
}
// login process
const loginProcess = async (formData) => {
const param = {
// langCd: currentLocale
langCd: globalLocaleState,
lastEditUser: formData.get('id'),
loginId: formData.get('id'),
pwd: formData.get('password'),
}
// await post({ url: '/api/login/v1.0/login', data: param }).then((res) => {
// if (res) {
// if (res.result.resultCode == 'S') {
// // console.log('res.data', res.data)
// //
// // if (res.data.pwdInitYn != 'Y') {
// // alert(' ')
// // } else {
// setSession(res.data)
// redirect('/')
// // }
// } else {
// alert(res.result.resultMsg)
// }
// }
// })
//
setSession({
userId: 'NEW016610',
saleStoreId: null,
name: null,
mail: null,
tel: null,
storeId: 'TEMP02',
userNm: 'ㅇㅇ6610',
userNmKana: '신규사용자 16610',
category: '인상6610',
telNo: '336610',
fax: null,
email: 't10t@naver.com',
pwdInitYn: 'Y',
})
setSessionState({
userId: 'NEW016610',
saleStoreId: null,
name: null,
mail: null,
tel: null,
storeId: 'TEMP02',
userNm: 'ㅇㅇ6610',
userNmKana: '신규사용자 16610',
category: '인상6610',
telNo: '336610',
fax: null,
email: 't10t@naver.com',
pwdInitYn: 'Y',
})
// redirect('/')
router.push('/')
//
}
//
const [open, setOpen] = useRecoilState(modalState)
const [contents, setContent] = useRecoilState(modalContent)
const initPasswordProcess = async (formData) => {
const param = {
langCd: currentLocale,
lastEditUser: formData.get('checkId'),
loginId: formData.get('checkId'),
email: formData.get('checkEmail'),
}
await patch({ url: '/api/login/v1.0/user/init-password', data: param }).then((res) => {
if (res) {
if (res.result.resultCode == 'S') {
alert(getMessage('login.init_password.complete_message'))
redirect('/login')
} else {
alert(res.result.resultMsg)
}
}
})
}
const initPasswordContent = (
<div className="flex min-h-full flex-1 flex-col justify-center px-6 py-12 lg:px-8">
<form action={initPasswordProcess} className="space-y-6">
<h2 className="text-center text-2xl font-bold leading-9 tracking-tight text-gray-900">{getMessage('login.init_password.title')}</h2>
<h2 className="text-center text-1xl font-bold leading-9 tracking-tight text-gray-900">{getMessage('login.init_password.sub_title')}</h2>
<div>
<label htmlFor="checkId" className="block text-sm font-medium leading-6 text-gray-900">
ID
</label>
<div className="mt-2">
<input
id="checkId"
name="checkId"
type="text"
required
className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
/>
</div>
</div>
<div>
<div className="flex items-center justify-between">
<label htmlFor="checkEmail" className="block text-sm font-medium leading-6 text-gray-900">
E-Mail
</label>
</div>
<div className="mt-2">
<input
id="checkEmail"
name="checkEmail"
type="email"
required
className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
/>
</div>
</div>
<p className="mt-5 text-center text-sm text-gray-500">
<button type="submit" className="font-semibold leading-6 text-indigo-600 hover:text-indigo-500">
{getMessage('login.init_password.btn')}
</button>
</p>
</form>
</div>
)
return (
<div className="login-wrap">
<div className="login-inner">
<Link href={'/login'} className="login-logo">
<Image src="/static/images/main/login-logo.svg" alt="react" width={236} height={43} styles={{ width: '236px', height: '43px' }} priority />
</Link>
<form action={loginProcess} className="space-y-6">
<div className="login-input-frame">
<div className="login-frame-tit">
<span>Q.CAST III</span>
太陽光発電システム図面管理サイト
</div>
<div className="login-input-wrap">
<div className="login-area id">
<input type="text" className="login-input" id="userId" name="id" required placeholder={'IDを入力してください'} />
<button className="id-delete" onClick={(e) => e.preventDefault()}></button>
</div>
<div className="login-area password">
<input
type="password"
className="login-input"
id="password"
name="password"
required
autoComplete="current-password"
ref={passwordRef}
/>
<button
className={`password-hidden ${passwordVisible ? 'visible' : ''}`}
onClick={(e) => {
e.preventDefault()
setPasswordVisible(!passwordVisible)
}}
></button>
</div>
<div className="d-check-box login">
<input type="checkbox" id="ch01" />
<label htmlFor="ch01">ID Save</label>
</div>
<div className="login-btn-box">
<button type="submit" className="login-btn">
Login
</button>
</div>
<div className="reset-password">
<Link href={'#'}>パスワードの初期化</Link>
</div>
</div>
</div>
</form>
<div className="login-guide-wrap">
<span></span>当サイトをご利用の際は事前申請が必要です
<br />
IDがない方は <Link href={'#'}>ID申請 クリックしてください</Link>
</div>
</div>
<div className="login-copyright">COPYRIGHT©2024 Hanwha Japan All Rights Reserved.</div>
</div>
)
}

View File

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

View File

@ -0,0 +1,43 @@
import usePagination from '@/hooks/usePagination'
export default function QPagination(props) {
const { handleChangePage = () => {}, pagePerBlock = 10 } = props
const { currentPage, changePage, pageGroup, totalPages, pages, startPage, endPage, pageRange } = usePagination(props)
const handlePage = (page) => {
handleChangePage(page)
changePage(page)
}
return (
<ol className="pagination">
<li className="page-item first">
<button onClick={() => handlePage(1)}></button>
</li>
<li className="page-item prev">
<button
onClick={() => {
if (currentPage === 1) return
handlePage(Math.max(1, (pageGroup - 2) * pagePerBlock + 1))
}}
></button>
</li>
{pageRange.map((page) => (
<li className={page === currentPage ? `page-item on` : `page-item`} key={page}>
<button onClick={() => handlePage(page)}>{page}</button>
</li>
))}
<li className="page-item next">
<button
onClick={() => {
if (currentPage === totalPages) return
handlePage(Math.max(1, pageGroup * pagePerBlock + 1))
}}
></button>
</li>
<li className="page-item last">
<button onClick={() => handlePage(totalPages)}></button>
</li>
</ol>
)
}

View File

@ -1,7 +1,71 @@
'use client'
import Link from 'next/link'
import Image from 'next/image'
import Search from '@/components/community/Search'
import ArchiveTable from '@/components/community/ArchiveTable'
import { useEffect, useState } from 'react'
import { useResetRecoilState } from 'recoil'
import { useMessage } from '@/hooks/useMessage'
import { searchState } from '@/store/boardAtom'
export default function Archive() { export default function Archive() {
const { getMessage } = useMessage()
const resetSearch = useResetRecoilState(searchState)
const [isInitialized, setIsInitialized] = useState(false)
useEffect(() => {
resetSearch()
setIsInitialized(true)
}, [])
if (!isInitialized) {
return null
}
const boardType = {
boardTitle: getMessage('board.archive.title'),
subTitle: getMessage('board.archive.sub.title'),
clsCode: 'DOWN',
}
return ( return (
<> <>
<h1>Community Archive</h1> <div className="sub-header">
<div className="sub-header-inner">
<ul className="sub-header-title-wrap">
<li className="title-item">
<Link className="sub-header-title" href={'#'}>
{getMessage('board.archive.title')}
</Link>
</li>
</ul>
<ul className="sub-header-location">
<li className="location-item">
<span className="home">
<Image src="/static/images/main/home_icon.svg" alt="react" width={16} height={16} />
</span>
</li>
<li className="location-item">
<span>{getMessage('header.menus.community')}</span>
</li>
<li className="location-item">
<span>{getMessage('board.archive.title')}</span>
</li>
</ul>
</div>
</div>
<div className="sub-content">
<div className="sub-content-inner">
<div className="sub-table-box">
<Search title={boardType.boardTitle} subTitle={boardType.subTitle} isSelectUse={true} />
<ArchiveTable clsCode={boardType.clsCode} />
</div>
</div>
</div>
</> </>
) )
} }

View File

@ -0,0 +1,103 @@
'use client'
import { useEffect, useState } from 'react'
import { useRecoilState } from 'recoil'
import { useAxios } from '@/hooks/useAxios'
import { searchState } from '@/store/boardAtom'
import { useMessage } from '@/hooks/useMessage'
import { handleFileDown } from '@/util/board-utils'
export default function ArchiveTable({ clsCode }) {
const { getMessage } = useMessage()
// api
const { get } = useAxios()
const [search, setSearch] = useRecoilState(searchState)
const [boardList, setBoardList] = useState([])
//
useEffect(() => {
async function fetchData() {
const url = `/api/board/list`
const params = new URLSearchParams({
schNoticeTpCd: 'QC',
schNoticeClsCd: clsCode,
schTitle: search.searchValue ? search.searchValue : '',
})
const apiUrl = `${url}?${params.toString()}`
const resultData = await get({ url: apiUrl })
if (resultData) {
if (resultData.result.code === 200) {
setBoardList(resultData.data)
if (resultData.data.length > 0) {
setSearch({ ...search, totalCount: resultData.data[0].totCnt })
} else {
setSearch({ ...search, totalCount: 0 })
}
} else {
alert(resultData.result.message)
}
}
}
fetchData()
}, [search.searchValue])
//
const handleDetailFileListDown = async (noticeNo) => {
const url = `/api/board/detail`
const params = new URLSearchParams({
noticeNo: noticeNo,
})
const apiUrl = `${url}?${params.toString()}`
const resultData = await get({ url: apiUrl })
if (resultData) {
if (resultData.result.code === 200) {
const boardDetailFileList = resultData.data.listFile
if (boardDetailFileList && Array.isArray(boardDetailFileList)) {
boardDetailFileList.forEach((boardFile) => {
handleFileDown(boardFile)
})
}
} else {
alert(resultData.result.message)
}
}
}
return (
<>
<div className="file-down-list">
{boardList?.map((board) => (
<div key={board.noticeNo} className="file-down-item">
<div className="file-item-info">
<div className="item-num">
{/* 번호 */}
{board.rowNumber}
</div>
<div className="item-name">
{/* 제목 */}
{board.title}
</div>
<div className="item-date">
{/* 등록일 */}
{getMessage('board.sub.updDt')} : {board.uptDt ? board.uptDt : board.regDt}
</div>
</div>
<div className="file-down-box">
{/* 첨부파일 */}
<button type="button" className="file-down-btn" onClick={() => handleDetailFileListDown(board.noticeNo)}></button>
</div>
</div>
))}
</div>
</>
)
}

View File

@ -1,7 +1,80 @@
'use client'
import Link from 'next/link'
import Image from 'next/image'
import Search from '@/components/community/Search'
import Pagination from '@/components/community/Pagination'
import Table from '@/components/community/Table'
import { useEffect, useState } from 'react'
import { useResetRecoilState, useRecoilValue, useRecoilState } from 'recoil'
import { useMessage } from '@/hooks/useMessage'
import { searchState } from '@/store/boardAtom'
export default function Faq() { export default function Faq() {
const { getMessage } = useMessage()
const resetSearch = useResetRecoilState(searchState)
const [isInitialized, setIsInitialized] = useState(false)
const search = useRecoilValue(searchState)
const [searchForm, setSearchForm] = useRecoilState(searchState)
useEffect(() => {
if (search.mainFlag === 'N') {
resetSearch()
} else {
setSearchForm({ ...searchForm, mainFlag: 'N' })
}
setIsInitialized(true)
}, [])
if (!isInitialized) {
return null
}
const boardType = {
boardTitle: getMessage('board.faq.title'),
subTitle: getMessage('board.faq.sub.title'),
clsCode: 'FAQ',
}
return ( return (
<> <>
<h1>Community FAQ</h1> <div className="sub-header">
<div className="sub-header-inner">
<ul className="sub-header-title-wrap">
<li className="title-item">
<Link className="sub-header-title" href={'#'}>
{getMessage('board.faq.title')}
</Link>
</li>
</ul>
<ul className="sub-header-location">
<li className="location-item">
<span className="home">
<Image src="/static/images/main/home_icon.svg" alt="react" width={16} height={16} />
</span>
</li>
<li className="location-item">
<span>{getMessage('header.menus.community')}</span>
</li>
<li className="location-item">
<span>{getMessage('board.faq.title')}</span>
</li>
</ul>
</div>
</div>
<div className="sub-content">
<div className="sub-content-inner">
<div className="sub-table-box">
<Search title={boardType.boardTitle} subTitle={boardType.subTitle} isSelectUse={true} />
<Table clsCode={boardType.clsCode} />
<Pagination />
</div>
</div>
</div>
</> </>
) )
} }

View File

@ -1,7 +1,72 @@
'use client'
import Link from 'next/link'
import Image from 'next/image'
import Search from '@/components/community/Search'
import Pagination from '@/components/community/Pagination'
import Table from '@/components/community/Table'
import { useEffect, useState } from 'react'
import { useResetRecoilState } from 'recoil'
import { useMessage } from '@/hooks/useMessage'
import { searchState } from '@/store/boardAtom'
export default function Notice() { export default function Notice() {
const { getMessage } = useMessage()
const resetSearch = useResetRecoilState(searchState)
const [isInitialized, setIsInitialized] = useState(false)
useEffect(() => {
resetSearch()
setIsInitialized(true)
}, [])
if (!isInitialized) {
return null
}
const boardType = {
boardTitle: getMessage('board.notice.title'),
subTitle: getMessage('board.notice.sub.title'),
clsCode: 'NOTICE',
}
return ( return (
<> <>
<h1>Community Notice</h1> <div className="sub-header">
<div className="sub-header-inner">
<ul className="sub-header-title-wrap">
<li className="title-item">
<Link className="sub-header-title" href={'#'}>
{getMessage('board.notice.title')}
</Link>
</li>
</ul>
<ul className="sub-header-location">
<li className="location-item">
<span className="home">
<Image src="/static/images/main/home_icon.svg" alt="react" width={16} height={16} />
</span>
</li>
<li className="location-item">
<span>{getMessage('header.menus.community')}</span>
</li>
<li className="location-item">
<span>{getMessage('board.notice.title')}</span>
</li>
</ul>
</div>
</div>
<div className="sub-content">
<div className="sub-content-inner">
<div className="sub-table-box">
<Search title={boardType.boardTitle} subTitle={boardType.subTitle} isSelectUse={true} />
<Table clsCode={boardType.clsCode} />
<Pagination />
</div>
</div>
</div>
</> </>
) )
} }

View File

@ -0,0 +1,78 @@
'use client'
import { searchState } from '@/store/boardAtom'
import { useRecoilState, useRecoilValue } from 'recoil'
import { generateBlockPagination } from '@/util/board-utils'
export default function Pagination() {
const search = useRecoilValue(searchState)
const [searchForm, setSearchForm] = useRecoilState(searchState)
const handlePagination = (pageNum) => {
setSearchForm({ ...searchForm, currentPage: pageNum })
}
const totalPages = Math.ceil(search.totalCount / search.pageBlock) > 0 ? Math.ceil(search.totalCount / search.pageBlock) : 1
const allPages = generateBlockPagination(search.currentPage, totalPages, 10)
return (
<>
<ol className="pagination">
<li className="page-item first">
<button
type="button"
disabled={search.currentPage === 1}
onClick={() => {
handlePagination(1)
}}
></button>
</li>
<li className="page-item prev">
<button
type="button"
disabled={search.currentPage <= 1}
onClick={() => {
handlePagination(search.currentPage - 1)
}}
></button>
</li>
{/* 페이지목록 */}
{allPages.map((page, index) => {
return (
<li className={`page-item ${search.currentPage === page ? 'on' : ''}`} key={index}>
<button
type="button"
onClick={() => {
handlePagination(page)
}}
>
{page}
</button>
</li>
)
})}
<li className="page-item next">
<button
type="button"
disabled={search.currentPage >= totalPages}
onClick={() => {
handlePagination(search.currentPage + 1)
}}
></button>
</li>
<li className="page-item last">
<button
type="button"
disabled={search.currentPage === totalPages}
onClick={() => {
handlePagination(totalPages)
}}
></button>
</li>
</ol>
</>
)
}

View File

@ -0,0 +1,117 @@
'use client'
import { searchState } from '@/store/boardAtom'
import { useRecoilState, useRecoilValue } from 'recoil'
import { useState } from 'react'
import { useMessage } from '@/hooks/useMessage'
export default function Search({ title = '', subTitle = '', isSelectUse = false }) {
const { getMessage } = useMessage()
const search = useRecoilValue(searchState)
const [searchForm, setSearchForm] = useRecoilState(searchState)
const [selectPageBlock, setSelectPageBlock] = useState(search.pageBlock)
const [searchValue, setSearchValue] = useState(search.searchValue)
const [searchView, setSearchView] = useState(search.searchValue ? true : false)
const [searchViewText, setSearchViewText] = useState(search.searchValue ? search.searchValue : '')
// Enter
const handleKeyDown = (e) => {
if (e.key === 'Enter') {
handleSubmit(e)
}
}
//
const handleSearch = (text, block) => {
if (text !== '') {
setSearchView(true)
setSearchViewText(text)
setSearchForm({ ...searchForm, currentPage: 1, searchValue: text, pageBlock: block, searchFlag: true })
} else {
setSearchView(false)
setSearchViewText('')
setSearchForm({ ...searchForm, currentPage: 1, searchValue: '', pageBlock: block, searchFlag: !searchForm.searchFlag })
}
//
setSearchValue('')
}
const handleSubmit = (e) => {
e.preventDefault()
handleSearch(searchValue, selectPageBlock)
}
return (
<>
<div className="community-search-warp">
<div className="community-search-box">
<input
type="text"
className="community-input"
placeholder={getMessage('board.sub.search.placeholder')}
onChange={(e) => {
setSearchValue(e.target.value)
}}
onKeyDown={handleKeyDown}
value={searchValue}
/>
<button type="button" className="community-search-ico" onClick={() => handleSearch(searchValue, selectPageBlock)}></button>
</div>
{searchView && (
<div className="community-search-keyword">
{isSelectUse ? (
<>
<div
dangerouslySetInnerHTML={{
__html: getMessage('board.sub.search.result', [`<span>${searchViewText}</span>`, `<span>${search.totalCount}</span>`]),
}}
></div>
</>
) : (
<>
<div
dangerouslySetInnerHTML={{
__html: getMessage('board.sub.search.archive.result', [`<span>${searchViewText}</span>`, `<span>${search.totalCount}</span>`]),
}}
></div>
</>
)}
</div>
)}
</div>
<div className="table-box-title-wrap">
<div className="title-wrap">
<h3>{subTitle}</h3>
<ul className="info-wrap">
<li>
{getMessage('board.sub.total')} <span className="red">{search.totalCount}</span>
</li>
</ul>
</div>
{isSelectUse && (
<div className="left-unit-box">
<div className="select-box" style={{ width: '80px' }}>
<select
className="select-light black"
value={selectPageBlock}
onChange={(e) => {
setSelectPageBlock(Number(e.target.value))
const text = searchValue ? searchValue : searchViewText
handleSearch(text, Number(e.target.value))
}}
>
<option value={100}>100</option>
<option value={200}>200</option>
<option value={300}>300</option>
<option value={400}>400</option>
<option value={500}>500</option>
</select>
</div>
</div>
)}
</div>
</>
)
}

View File

@ -0,0 +1,110 @@
'use client'
import { useEffect, useState } from 'react'
import { useRecoilState } from 'recoil'
import { searchState } from '@/store/boardAtom'
import { useAxios } from '@/hooks/useAxios'
import { useMessage } from '@/hooks/useMessage'
import BoardDetailModal from '../community/modal/BoardDetailModal'
export default function Table({ clsCode }) {
const { getMessage } = useMessage()
// api
const { get } = useAxios()
const [search, setSearch] = useRecoilState(searchState)
const [boardList, setBoardList] = useState([])
//
const [open, setOpen] = useState(false)
const [modalNoticeNo, setModalNoticeNo] = useState('')
//
useEffect(() => {
async function fetchData() {
const startRow = (search.currentPage - 1) * search.pageBlock > 0 ? (search.currentPage - 1) * search.pageBlock + 1 : 1
const endRow = search.currentPage * search.pageBlock
const url = `/api/board/list`
const params = new URLSearchParams({
schNoticeTpCd: 'QC',
schNoticeClsCd: clsCode,
schTitle: search.searchValue ? search.searchValue : '',
startRow: startRow,
endRow: endRow,
})
const apiUrl = `${url}?${params.toString()}`
const resultData = await get({ url: apiUrl })
if (resultData) {
if (resultData.result.code === 200) {
if (resultData.data.length > 0) {
setBoardList(resultData.data)
setSearch({ ...search, totalCount: resultData.data[0].totCnt })
} else {
setBoardList([])
setSearch({ ...search, totalCount: 0 })
}
} else {
alert(resultData.result.message)
}
}
}
fetchData()
}, [search.currentPage, search.searchValue, search.pageBlock, search.searchFlag])
return (
<>
<div className="community-table">
<table>
<colgroup>
<col width={100} />
<col />
<col width={200} />
</colgroup>
<tbody>
{boardList.length > 0 ? (
boardList?.map((board) => (
<tr
key={board.noticeNo}
onClick={() => {
setOpen(true)
setModalNoticeNo(board.noticeNo)
}}
>
<td className="al-c">
{/* 번호 */}
{board.rowNumber}
</td>
<td style={{ textAlign: 'center' }}>
{/* 제목 */}
<div className="text-frame">
<div className="text-overflow">{board.title}</div>
{board.attachYn && <span className="clip"></span>}
</div>
</td>
<td className="al-c">
{/* 등록일 */}
{board.regDt.split(' ')[0]}
</td>
</tr>
))
) : (
<tr>
<td colSpan={3} className="al-c">
{getMessage('common.message.no.data')}
</td>
</tr>
)}
</tbody>
</table>
</div>
{open && <BoardDetailModal noticeNo={modalNoticeNo} setOpen={setOpen} />}
</>
)
}

View File

@ -0,0 +1,77 @@
'use client'
import { useEffect, useState } from 'react'
import { useAxios } from '@/hooks/useAxios'
import { handleFileDown } from '@/util/board-utils'
export default function BoardDetailModal({ noticeNo, setOpen }) {
// api
const { get } = useAxios()
const [boardDetail, setBoardDetail] = useState({})
useEffect(() => {
//
const fetchDetail = async (noticeNo) => {
const url = `/api/board/detail`
const params = new URLSearchParams({
noticeNo: noticeNo,
})
const apiUrl = `${url}?${params.toString()}`
const resultData = await get({ url: apiUrl })
if (resultData) {
if (resultData.result.code === 200) {
const boardDetail = resultData.data
setBoardDetail(boardDetail)
} else {
alert(resultData.result.message)
}
}
}
fetchDetail(noticeNo)
}, [])
return (
<>
<div key={noticeNo} className="modal-popup community">
<div className="modal-dialog">
<div className="modal-content">
<div className="modal-header">
<button
type="button"
className="modal-close"
onClick={() => {
setOpen(false)
}}
>
닫기
</button>
</div>
<div className="modal-body">
<div className="community_detail">
<div className="community_detail-tit">{boardDetail.title}</div>
{boardDetail.listFile && (
<dl className="community_detail-file-wrap">
<dt>첨부파일 목록</dt>
{boardDetail.listFile.map((boardFile) => (
<dd key={boardFile.encodeFileNo}>
<button type="button" className="down" onClick={() => handleFileDown(boardFile)}>
{boardFile.srcFileNm}
</button>
</dd>
))}
</dl>
)}
<div className="community_detail-inner">{boardDetail.contents}</div>
</div>
</div>
</div>
</div>
</div>
</>
)
}

View File

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

View File

@ -121,13 +121,13 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
this.on('modified', (e) => { this.on('modified', (e) => {
this.addLengthText() this.addLengthText()
if (this.arrow) { // if (this.arrow) {
drawDirectionArrow(this) // drawDirectionArrow(this)
} // }
}) })
this.on('selected', () => { this.on('selected', () => {
drawDirectionArrow(this) // drawDirectionArrow(this)
Object.keys(this.controls).forEach((controlKey) => { Object.keys(this.controls).forEach((controlKey) => {
if (controlKey !== 'ml' && controlKey !== 'mr') { if (controlKey !== 'ml' && controlKey !== 'mr') {
this.setControlVisible(controlKey, false) this.setControlVisible(controlKey, false)
@ -200,14 +200,32 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
const end = points[(i + 1) % points.length] const end = points[(i + 1) % points.length]
const dx = end.x - start.x const dx = end.x - start.x
const dy = end.y - start.y const dy = end.y - start.y
const length = Number(Math.sqrt(dx * dx + dy * dy).toFixed(1)) * 10 const length = Number(Math.sqrt(dx * dx + dy * dy).toFixed(0)) * 10
const midPoint = new fabric.Point((start.x + end.x) / 2, (start.y + end.y) / 2) let midPoint
switch (this.direction) {
case 'north':
midPoint = new fabric.Point((start.x + end.x) / 2, (start.y + end.y) / 2 - 30)
break
case 'west':
midPoint = new fabric.Point((start.x + end.x) / 2 - 30, (start.y + end.y) / 2)
break
case 'south':
midPoint = new fabric.Point((start.x + end.x) / 2, (start.y + end.y) / 2 + 30)
break
case 'east':
midPoint = new fabric.Point((start.x + end.x) / 2 + 30, (start.y + end.y) / 2)
break
default:
midPoint = new fabric.Point((start.x + end.x) / 2, (start.y + end.y) / 2)
break
}
const degree = (Math.atan2(dy, dx) * 180) / Math.PI const degree = (Math.atan2(dy, dx) * 180) / Math.PI
// Create new text object if it doesn't exist // Create new text object if it doesn't exist
const text = new fabric.Text(length.toFixed(0), { const text = new fabric.Text(length.toString(), {
left: midPoint.x, left: midPoint.x,
top: midPoint.y, top: midPoint.y,
fontSize: this.fontSize, fontSize: this.fontSize,

View File

@ -1,19 +1,19 @@
'use client' 'use client'
import { useEffect, useRef, useState } from 'react' import { useEffect, useRef } from 'react'
import { useCanvas } from '@/hooks/useCanvas' import { useCanvas } from '@/hooks/useCanvas'
import { useEvent } from '@/hooks/useEvent' import { useEvent } from '@/hooks/useEvent'
import QContextMenu from '@/components/common/context-menu/QContextMenu' import QContextMenu from '@/components/common/context-menu/QContextMenu'
import { useContextMenu } from '@/hooks/useContextMenu'
import { useRecoilValue } from 'recoil' import { useRecoilValue } from 'recoil'
import { currentMenuState } from '@/store/canvasAtom' import { currentObjectState } from '@/store/canvasAtom'
import { MENU } from '@/common/common'
export default function CanvasFrame({ plan }) { export default function CanvasFrame({ plan }) {
const canvasRef = useRef(null) const canvasRef = useRef(null)
const { canvas } = useCanvas('canvas') const { canvas } = useCanvas('canvas')
const currentMenu = useRecoilValue(currentMenuState) const { contextMenu, currentContextMenu, setCurrentContextMenu } = useContextMenu()
const [contextMenu, setContextMenu] = useState([[]])
useEvent() useEvent()
const loadCanvas = () => { const loadCanvas = () => {
@ -29,131 +29,9 @@ export default function CanvasFrame({ plan }) {
useEffect(() => { useEffect(() => {
loadCanvas() loadCanvas()
}, [plan]) }, [plan, canvas])
useEffect(() => { const onClickContextMenu = (index) => {}
switch (currentMenu) {
case MENU.PLAN_DRAWING:
setContextMenu([
[
{
name: '그리드 이동',
},
{
name: '그리드 복사',
},
{
name: '그리드 색 변경',
},
{
name: '삭제',
},
{
name: '전체 삭제',
},
],
])
break
case MENU.ROOF_COVERING.EXTERIOR_WALL_LINE:
case MENU.ROOF_COVERING.ROOF_SHAPE_SETTINGS:
case MENU.ROOF_COVERING.ROOF_SHAPE_PASSIVITY_SETTINGS:
case MENU.ROOF_COVERING.ROOF_SHAPE_EDITING:
case MENU.ROOF_COVERING.HELP_LINE_DRAWING:
case MENU.ROOF_COVERING.EAVES_KERAVA_EDIT:
case MENU.ROOF_COVERING.MOVEMENT_SHAPE_UPDOWN:
case MENU.ROOF_COVERING.OUTLINE_EDIT_OFFSET:
case MENU.ROOF_COVERING.ROOF_SHAPE_ALLOC:
case MENU.ROOF_COVERING.DEFAULT:
console.log('지붕덮개')
setContextMenu([
[
{
name: '지붕재 배치',
},
{
name: '지붕재 삭제',
},
{
name: '지붕재 전체 삭제',
},
{
name: '선택・이동',
},
{
name: '외벽선 삭제',
},
],
[
{
name: '사이즈 변경',
},
{
name: '보조선 이동(M)',
},
{
name: '보조선 복사(C)',
},
{
name: '보조선 삭제(D)',
},
{
name: '보조선 수직이등분선',
},
{
name: '보조선 절삭',
},
{
name: '보조선 전체 삭제',
},
],
])
break
case MENU.BATCH_CANVAS.SLOPE_SETTING:
case MENU.BATCH_CANVAS.BATCH_DRAWING:
case MENU.BATCH_CANVAS.SURFACE_SHAPE_BATCH:
case MENU.BATCH_CANVAS.OBJECT_BATCH:
case MENU.BATCH_CANVAS.ALL_REMOVE:
case MENU.BATCH_CANVAS.DEFAULT:
console.log('배치면')
setContextMenu([
[
{
name: '사이즈 변경',
},
{
name: '삭제(D)',
},
{
name: '이동(M)',
},
{
name: '복사(C)',
},
],
[
{
name: '지붕재 변경',
},
{
name: '각 변 속성 변경',
},
{
name: '흐름 방향 변경',
},
],
])
break
default:
console.log('default')
setContextMenu([])
break
}
}, [currentMenu])
useEffect(() => {
console.log('currentMenu', currentMenu)
console.log('contextMenu', contextMenu)
}, [contextMenu])
return ( return (
<div className="canvas-frame flex justify-center"> <div className="canvas-frame flex justify-center">
@ -162,11 +40,12 @@ export default function CanvasFrame({ plan }) {
{contextMenu.map((menus, index) => ( {contextMenu.map((menus, index) => (
<ul key={index}> <ul key={index}>
{menus.map((menu) => ( {menus.map((menu) => (
<li>{menu.name}</li> <li onClick={(e) => setCurrentContextMenu(menu)}>{menu.name}</li>
))} ))}
</ul> </ul>
))} ))}
</QContextMenu> </QContextMenu>
{currentContextMenu?.component}
</div> </div>
) )
} }

View File

@ -1,108 +1,29 @@
'use client' 'use client'
import { useEffect, useState } from 'react' import { useContext, useEffect, useState } from 'react'
import { useRecoilState, useRecoilValue } from 'recoil' import { useRecoilValue } from 'recoil'
import CanvasFrame from './CanvasFrame' import CanvasFrame from './CanvasFrame'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { useSwal } from '@/hooks/useSwal' import { useSwal } from '@/hooks/useSwal'
import { usePlan } from '@/hooks/usePlan' import { usePlan } from '@/hooks/usePlan'
import { globalLocaleStore } from '@/store/localeAtom' import { globalLocaleStore } from '@/store/localeAtom'
import { currentCanvasPlanState, initCanvasPlansState, plansState } from '@/store/canvasAtom'
import { sessionStore } from '@/store/commonAtom' import { sessionStore } from '@/store/commonAtom'
import { SessionContext } from '@/app/SessionProvider'
export default function CanvasLayout() { export default function CanvasLayout() {
const { session } = useContext(SessionContext)
console.log('session >>> ', session)
const [objectNo, setObjectNo] = useState('test123240822001') // const [objectNo, setObjectNo] = useState('test123240822001') //
const [planNum, setPlanNum] = useState(0)
const [currentCanvasPlan, setCurrentCanvasPlan] = useRecoilState(currentCanvasPlanState)
const [initCanvasPlans, setInitCanvasPlans] = useRecoilState(initCanvasPlansState)
const [plans, setPlans] = useRecoilState(plansState)
const globalLocaleState = useRecoilValue(globalLocaleStore)
const sessionState = useRecoilValue(sessionStore) const sessionState = useRecoilValue(sessionStore)
const globalLocaleState = useRecoilValue(globalLocaleStore)
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { swalFire } = useSwal() const { swalFire } = useSwal()
const { getCanvasByObjectNo, delCanvasById, checkModifiedCanvasPlan, saveCanvas } = usePlan() const { plans, loadCanvasPlanData, handleCurrentPlan, handleAddPlan, handleDeletePlan } = usePlan()
const handleCurrentPlan = (newCurrentId) => {
// console.log('currentPlan newCurrentId: ', newCurrentId)
if (!currentCanvasPlan?.id || currentCanvasPlan.id !== newCurrentId) {
if (currentCanvasPlan?.id && checkModifiedCanvasPlan()) {
swalFire({
html: getMessage('common.message.confirm.save') + `</br>${currentCanvasPlan.name}`,
type: 'confirm',
confirmFn: async () => {
await saveCanvas(sessionState.userId)
updateCurrentPlan(newCurrentId)
},
denyFn: () => {
updateCurrentPlan(newCurrentId)
},
})
} else {
updateCurrentPlan(newCurrentId)
}
}
}
const updateCurrentPlan = (newCurrentId) => {
setPlans((plans) =>
plans.map((plan) => {
return { ...plan, isCurrent: plan.id === newCurrentId }
}),
)
}
useEffect(() => {
setCurrentCanvasPlan(plans.find((plan) => plan.isCurrent) || null)
}, [plans])
const handleDeletePlan = (e, id) => {
e.stopPropagation() //
if (initCanvasPlans.some((plan) => plan.id === id)) {
delCanvasById(id)
.then((res) => {
swalFire({ text: getMessage('common.message.delete') })
// console.log('[DELETE] canvas-statuses res :::::::: %o', res)
setInitCanvasPlans((initCanvasPlans) => initCanvasPlans.filter((plan) => plan.id !== id))
setPlans((plans) => plans.filter((plan) => plan.id !== id))
})
.catch((error) => {
swalFire({ text: error.message, icon: 'error' })
// console.error('[DELETE] canvas-statuses res error :::::::: %o', error)
})
} else {
setPlans((plans) => plans.filter((plan) => plan.id !== id))
swalFire({ text: getMessage('common.message.delete') })
}
// last
const lastPlan = plans.filter((plan) => plan.id !== id).at(-1)
if (!lastPlan) {
setPlanNum(0)
setCurrentCanvasPlan(null)
} else if (id !== lastPlan.id) {
handleCurrentPlan(lastPlan.id)
}
}
const addNewPlan = () => {
setPlans([...plans, { id: planNum, name: `Plan ${planNum + 1}`, objectNo: `${objectNo}` }])
handleCurrentPlan(planNum)
setPlanNum(planNum + 1)
}
useEffect(() => { useEffect(() => {
getCanvasByObjectNo(sessionState.userId, objectNo).then((res) => { console.log('loadCanvasPlanData 실행, sessionState.userId >>> ', sessionState.userId)
console.log('canvas 목록 ', res) loadCanvasPlanData(sessionState.userId, objectNo)
if (res.length > 0) {
setInitCanvasPlans(res)
setPlans(res)
handleCurrentPlan(res.at(-1).id) // last
setPlanNum(res.length)
} else {
addNewPlan()
}
})
}, []) }, [])
return ( return (
@ -110,13 +31,17 @@ export default function CanvasLayout() {
<div className="canvas-page-list"> <div className="canvas-page-list">
<div className="canvas-plane-wrap"> <div className="canvas-plane-wrap">
{plans.map((plan) => ( {plans.map((plan) => (
<button key={plan.id} className={`canvas-page-box ${plan.isCurrent === true ? 'on' : ''}`} onClick={() => handleCurrentPlan(plan.id)}> <button
key={`plan-${plan.id}`}
className={`canvas-page-box ${plan.isCurrent === true ? 'on' : ''}`}
onClick={() => handleCurrentPlan(sessionState.userId, plan.id)}
>
<span>{plan.name}</span> <span>{plan.name}</span>
<i <i
className="close" className="close"
onClick={(e) => onClick={(e) =>
swalFire({ swalFire({
html: getMessage('common.message.confirm.delete') + `</br>${plan.name}`, text: `${plan.name} ` + getMessage('plan.message.confirm.delete'),
type: 'confirm', type: 'confirm',
confirmFn: () => { confirmFn: () => {
handleDeletePlan(e, plan.id) handleDeletePlan(e, plan.id)
@ -127,9 +52,11 @@ export default function CanvasLayout() {
</button> </button>
))} ))}
</div> </div>
<button className="plane-add" onClick={addNewPlan}> {plans.length < 10 && (
<span></span> <button className="plane-add" onClick={() => handleAddPlan(sessionState.userId, objectNo)}>
</button> <span></span>
</button>
)}
</div> </div>
<CanvasFrame plan={plans.find((plan) => plan.isCurrent === true)} /> <CanvasFrame plan={plans.find((plan) => plan.isCurrent === true)} />
</div> </div>

View File

@ -19,6 +19,7 @@ import { MENU } from '@/common/common'
import KO from '@/locales/ko.json' import KO from '@/locales/ko.json'
import JA from '@/locales/ja.json' import JA from '@/locales/ja.json'
import { settingModalFirstOptionsState } from '@/store/settingAtom' import { settingModalFirstOptionsState } from '@/store/settingAtom'
import { placementShapeDrawingPointsState } from '@/store/placementShapeDrawingAtom'
const canvasMenus = [ const canvasMenus = [
{ index: 0, name: 'plan.menu.plan.drawing', icon: 'con00', title: MENU.PLAN_DRAWING }, { index: 0, name: 'plan.menu.plan.drawing', icon: 'con00', title: MENU.PLAN_DRAWING },
@ -48,6 +49,8 @@ export default function CanvasMenu(props) {
setShowWallLineOffsetSettingModal, setShowWallLineOffsetSettingModal,
setShowRoofAllocationSettingModal, setShowRoofAllocationSettingModal,
setShowBasicSettingModal, setShowBasicSettingModal,
setShowCircuitTrestleSettingModal,
setShowPropertiesSettingModal,
} = props } = props
const [menuNumber, setMenuNumber] = useState(null) const [menuNumber, setMenuNumber] = useState(null)
@ -56,7 +59,8 @@ export default function CanvasMenu(props) {
const [verticalHorizontalMode, setVerticalHorizontalMode] = useRecoilState(verticalHorizontalModeState) const [verticalHorizontalMode, setVerticalHorizontalMode] = useRecoilState(verticalHorizontalModeState)
const [appMessageState, setAppMessageState] = useRecoilState(appMessageStore) const [appMessageState, setAppMessageState] = useRecoilState(appMessageStore)
const setCurrentMenu = useSetRecoilState(currentMenuState) const setCurrentMenu = useSetRecoilState(currentMenuState)
const setPoints = useSetRecoilState(outerLinePointsState) const setOuterLinePoints = useSetRecoilState(outerLinePointsState)
const setPlacementPoints = useSetRecoilState(placementShapeDrawingPointsState)
const [canvasZoom, setCanvasZoom] = useRecoilState(canvasZoomState) const [canvasZoom, setCanvasZoom] = useRecoilState(canvasZoomState)
const [currentCanvasPlan, setcurrentCanvasPlan] = useRecoilState(currentCanvasPlanState) const [currentCanvasPlan, setcurrentCanvasPlan] = useRecoilState(currentCanvasPlanState)
@ -104,6 +108,8 @@ export default function CanvasMenu(props) {
setShowRoofAllocationSettingModal, setShowRoofAllocationSettingModal,
setShowObjectSettingModal, setShowObjectSettingModal,
setShowBasicSettingModal, setShowBasicSettingModal,
setShowCircuitTrestleSettingModal,
setShowPropertiesSettingModal,
type, type,
} }
@ -117,14 +123,14 @@ export default function CanvasMenu(props) {
}, [menuNumber, type]) }, [menuNumber, type])
// (btn08) // (btn08)
const handleSaveCanvas = () => { const handleSaveCanvas = async () => {
swalFire({ // swalFire({
html: getMessage('common.message.confirm.save') + `</br>${currentCanvasPlan.name}`, // text: `${currentCanvasPlan.name} ` + getMessage('plan.message.confirm.save'),
type: 'confirm', // type: 'confirm',
confirmFn: () => { // confirmFn: async () => {
saveCanvas(sessionState.userId) await saveCanvas(sessionState.userId)
}, // },
}) // })
} }
const onClickPlacementInitialMenu = () => { const onClickPlacementInitialMenu = () => {
@ -136,7 +142,8 @@ export default function CanvasMenu(props) {
} }
const handleClear = () => { const handleClear = () => {
setPoints([]) setOuterLinePoints([])
setPlacementPoints([])
canvas?.clear() canvas?.clear()
} }
@ -161,7 +168,11 @@ export default function CanvasMenu(props) {
<ul className="canvas-menu-list"> <ul className="canvas-menu-list">
{canvasMenus.map((menu) => { {canvasMenus.map((menu) => {
return ( return (
<li key={menu.index} className={`canvas-menu-item ${menuNumber === menu.index ? 'active' : ''}`} onClick={() => onClickNav(menu)}> <li
key={`canvas-menu-${menu.index}`}
className={`canvas-menu-item ${menuNumber === menu.index ? 'active' : ''}`}
onClick={() => onClickNav(menu)}
>
<button> <button>
<span className={`menu-icon ${menu.icon}`}></span> <span className={`menu-icon ${menu.icon}`}></span>
{getMessage(menu.name)} {getMessage(menu.name)}
@ -193,7 +204,12 @@ export default function CanvasMenu(props) {
<button className="btn06"></button> <button className="btn06"></button>
</div> </div>
<div className="size-control"> <div className="size-control">
<button className="control-btn minus"></button> <button
className="control-btn minus"
onClick={() => {
canvas.setZoom(canvas.getZoom() - 0.1)
}}
></button>
<span>{canvasZoom}%</span> <span>{canvasZoom}%</span>
<button className="control-btn plus" onClick={handleZoomClear}></button> <button className="control-btn plus" onClick={handleZoomClear}></button>
</div> </div>

View File

@ -27,6 +27,7 @@ import RoofShapePassivitySetting from '@/components/floor-plan/modal/roofShape/R
import MovementSetting from '@/components/floor-plan/modal/movement/MovementSetting' import MovementSetting from '@/components/floor-plan/modal/movement/MovementSetting'
import RoofAllocationSetting from '@/components/floor-plan/modal/roofAllocation/RoofAllocationSetting' import RoofAllocationSetting from '@/components/floor-plan/modal/roofAllocation/RoofAllocationSetting'
import BasicSetting from '@/components/floor-plan/modal/basic/BasicSetting' import BasicSetting from '@/components/floor-plan/modal/basic/BasicSetting'
import CircuitTrestleSetting from '@/components/floor-plan/modal/circuitTrestle/CircuitTrestleSetting'
export default function FloorPlan() { export default function FloorPlan() {
const [showCanvasSettingModal, setShowCanvasSettingModal] = useState(false) const [showCanvasSettingModal, setShowCanvasSettingModal] = useState(false)
@ -45,6 +46,7 @@ export default function FloorPlan() {
const [showWallLineOffsetSettingModal, setShowWallLineOffsetSettingModal] = useState(false) const [showWallLineOffsetSettingModal, setShowWallLineOffsetSettingModal] = useState(false)
const [showRoofAllocationSettingModal, setShowRoofAllocationSettingModal] = useState(false) const [showRoofAllocationSettingModal, setShowRoofAllocationSettingModal] = useState(false)
const [showBasicSettingModal, setShowBasicSettingModal] = useState(false) const [showBasicSettingModal, setShowBasicSettingModal] = useState(false)
const [showCircuitTrestleSettingModal, setShowCircuitTrestleSettingModal] = useState(false)
const globalLocaleState = useRecoilValue(globalLocaleStore) const globalLocaleState = useRecoilValue(globalLocaleStore)
const { get } = useAxios(globalLocaleState) const { get } = useAxios(globalLocaleState)
@ -84,6 +86,8 @@ export default function FloorPlan() {
setShowWallLineOffsetSettingModal, setShowWallLineOffsetSettingModal,
setShowRoofAllocationSettingModal, setShowRoofAllocationSettingModal,
setShowBasicSettingModal, setShowBasicSettingModal,
setShowCircuitTrestleSettingModal,
setShowPropertiesSettingModal,
} }
useEffect(() => { useEffect(() => {
@ -158,6 +162,7 @@ export default function FloorPlan() {
{showObjectSettingModal && <ObjectSetting setShowObjectSettingModal={setShowObjectSettingModal} />} {showObjectSettingModal && <ObjectSetting setShowObjectSettingModal={setShowObjectSettingModal} />}
{showPlacementSurfaceSettingModal && <PlacementSurfaceSetting setShowPlacementSurfaceSettingModal={setShowPlacementSurfaceSettingModal} />} {showPlacementSurfaceSettingModal && <PlacementSurfaceSetting setShowPlacementSurfaceSettingModal={setShowPlacementSurfaceSettingModal} />}
{showBasicSettingModal && <BasicSetting setShowBasicSettingModal={setShowBasicSettingModal} />} {showBasicSettingModal && <BasicSetting setShowBasicSettingModal={setShowBasicSettingModal} />}
{showCircuitTrestleSettingModal && <CircuitTrestleSetting setShowCircuitTrestleSettingModal={setShowCircuitTrestleSettingModal} />}
</div> </div>
</div> </div>
</> </>

View File

@ -5,7 +5,7 @@ import { useEffect, useState } from 'react'
import { MENU } from '@/common/common' import { MENU } from '@/common/common'
import { currentMenuState } from '@/store/canvasAtom' import { currentMenuState } from '@/store/canvasAtom'
import { useSetRecoilState } from 'recoil' import { useSetRecoilState } from 'recoil'
import { useSurfaceShapeBatch } from '@/hooks/surface/useSurfaceShapeBatch'
export default function MenuDepth01(props) { export default function MenuDepth01(props) {
const { const {
setShowOutlineModal, setShowOutlineModal,
@ -23,10 +23,15 @@ export default function MenuDepth01(props) {
setShowRoofAllocationSettingModal, setShowRoofAllocationSettingModal,
setShowObjectSettingModal, setShowObjectSettingModal,
setShowBasicSettingModal, setShowBasicSettingModal,
setShowCircuitTrestleSettingModal,
setShowPropertiesSettingModal,
} = props } = props
const { getMessage } = useMessage() const { getMessage } = useMessage()
const [activeMenu, setActiveMenu] = useState() const [activeMenu, setActiveMenu] = useState()
const setCurrentMenu = useSetRecoilState(currentMenuState) const setCurrentMenu = useSetRecoilState(currentMenuState)
const { deleteAllSurfacesAndObjects } = useSurfaceShapeBatch()
const onClickMenu = ({ id, menu, name }) => { const onClickMenu = ({ id, menu, name }) => {
setActiveMenu(menu) setActiveMenu(menu)
setShowOutlineModal(menu === MENU.ROOF_COVERING.EXTERIOR_WALL_LINE) setShowOutlineModal(menu === MENU.ROOF_COVERING.EXTERIOR_WALL_LINE)
@ -42,6 +47,8 @@ export default function MenuDepth01(props) {
setShowWallLineOffsetSettingModal(id === 6) setShowWallLineOffsetSettingModal(id === 6)
setShowRoofAllocationSettingModal(id === 7) setShowRoofAllocationSettingModal(id === 7)
setShowPlaceShapeDrawingModal(false) setShowPlaceShapeDrawingModal(false)
setShowPropertiesSettingModal(false)
setShowCircuitTrestleSettingModal(false)
} }
if (type === 'surface') { if (type === 'surface') {
@ -53,11 +60,17 @@ export default function MenuDepth01(props) {
setShowMovementModal(false) setShowMovementModal(false)
setShowWallLineOffsetSettingModal(false) setShowWallLineOffsetSettingModal(false)
setShowRoofAllocationSettingModal(false) setShowRoofAllocationSettingModal(false)
setShowPropertiesSettingModal(false)
setShowCircuitTrestleSettingModal(false)
setShowSlopeSettingModal(id === 0) setShowSlopeSettingModal(id === 0)
setShowPlaceShapeDrawingModal(id === 1) setShowPlaceShapeDrawingModal(id === 1)
setShowPlacementSurfaceSettingModal(id === 2) setShowPlacementSurfaceSettingModal(id === 2)
setShowObjectSettingModal(id === 3) setShowObjectSettingModal(id === 3)
//
if (id === 4) {
deleteAllSurfacesAndObjects()
}
} }
if (type === 'module') { if (type === 'module') {
@ -69,7 +82,9 @@ export default function MenuDepth01(props) {
setShowMovementModal(false) setShowMovementModal(false)
setShowWallLineOffsetSettingModal(false) setShowWallLineOffsetSettingModal(false)
setShowRoofAllocationSettingModal(false) setShowRoofAllocationSettingModal(false)
setShowPropertiesSettingModal(false)
setShowBasicSettingModal(id === 0) setShowBasicSettingModal(id === 0)
setShowCircuitTrestleSettingModal(id === 1)
} }
} }

View File

@ -49,7 +49,7 @@ export default function AuxiliaryDrawing({ setShowAuxiliaryModal }) {
handleFix, handleFix,
buttonAct, buttonAct,
setButtonAct, setButtonAct,
} = useAuxiliaryDrawing() } = useAuxiliaryDrawing(setShowAuxiliaryModal)
const outerLineProps = { const outerLineProps = {
length1, length1,

View File

@ -0,0 +1,49 @@
import { useMessage } from '@/hooks/useMessage'
import WithDraggable from '@/components/common/draggable/WithDraggable'
export default function AuxiliaryMove({ setCurrentContextMenu }) {
const { getMessage } = useMessage()
return (
<WithDraggable isShow={true} pos={{ x: 0, y: 150 }}>
<div className={`modal-pop-wrap xm`}>
<div className="modal-head">
<h1 className="title">補助線の移動 </h1>
<button className="modal-close" onClick={() => setCurrentContextMenu(null)}>
닫기
</button>
</div>
<div className="modal-body">
<div className="grid-option-tit">移動する方向を入力してください</div>
<div className="grid-option-wrap">
<div className="grid-option-box">
<div className="move-form">
<p className="mb5">長さ</p>
<div className="input-move-wrap mb5">
<div className="input-move">
<input type="text" className="input-origin" defaultValue={910} />
</div>
<span>mm</span>
</div>
<div className="input-move-wrap">
<div className="input-move">
<input type="text" className="input-origin" defaultValue={910} />
</div>
<span>mm</span>
</div>
</div>
<div className="direction-move-wrap">
<button className="direction up"></button>
<button className="direction down act"></button>
<button className="direction left"></button>
<button className="direction right"></button>
</div>
</div>
</div>
<div className="grid-btn-wrap">
<button className="btn-frame modal act">保存</button>
</div>
</div>
</div>
</WithDraggable>
)
}

View File

@ -0,0 +1,61 @@
import { useMessage } from '@/hooks/useMessage'
import WithDraggable from '@/components/common/draggable/WithDraggable'
export default function AuxiliarySize({ setCurrentContextMenu }) {
const { getMessage } = useMessage()
return (
<WithDraggable isShow={true} pos={{ x: 0, y: 150 }}>
<div className={`modal-pop-wrap xm`}>
<div className="modal-head">
<h1 className="title">補助線サイズ変更 </h1>
<button className="modal-close" onClick={() => setCurrentContextMenu(null)}>
닫기
</button>
</div>
<div className="modal-body">
<div className="discrimination-box mb10">
<div className="d-check-radio pop mb10">
<input type="radio" name="radio01" id="ra01" />
<label htmlFor="ra01">1支店</label>
</div>
<div className="outline-form mb15">
<div className="input-grid mr5" style={{ flex: '1 1 auto' }}>
<input type="text" className="input-origin block" defaultValue={100} />
</div>
<span className="thin">mm</span>
</div>
<div className="outline-form">
<span style={{ width: 'auto' }}>長さ</span>
<div className="input-grid mr5">
<input type="text" className="input-origin block" defaultValue={100} />
</div>
<span className="thin">mm</span>
</div>
</div>
<div className="discrimination-box ">
<div className="d-check-radio pop mb10">
<input type="radio" name="radio01" id="ra02" />
<label htmlFor="ra02">2支店</label>
</div>
<div className="outline-form mb15">
<div className="input-grid mr5" style={{ flex: '1 1 auto' }}>
<input type="text" className="input-origin block" defaultValue={100} />
</div>
<span className="thin">mm</span>
</div>
<div className="outline-form">
<span style={{ width: 'auto' }}>長さ</span>
<div className="input-grid mr5">
<input type="text" className="input-origin block" defaultValue={100} />
</div>
<span className="thin">mm</span>
</div>
</div>
<div className="grid-btn-wrap">
<button className="btn-frame modal act">保存</button>
</div>
</div>
</div>
</WithDraggable>
)
}

View File

@ -1,14 +1,18 @@
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import WithDraggable from '@/components/common/draggable/withDraggable' import WithDraggable from '@/components/common/draggable/WithDraggable'
import { useState } from 'react' import { useState } from 'react'
import Orientation from '@/components/floor-plan/modal/basic/step/Orientation' import Orientation from '@/components/floor-plan/modal/basic/step/Orientation'
import Module from '@/components/floor-plan/modal/basic/step/Module' import Module from '@/components/floor-plan/modal/basic/step/Module'
import PitchModule from '@/components/floor-plan/modal/basic/step/pitch/PitchModule'
import PitchPlacement from '@/components/floor-plan/modal/basic/step/pitch/PitchPlacement'
import Placement from '@/components/floor-plan/modal/basic/step/Placement' import Placement from '@/components/floor-plan/modal/basic/step/Placement'
import { useRecoilState } from 'recoil'
import { canvasSettingState } from '@/store/canvasAtom'
export default function BasicSetting({ setShowBasicSettingModal }) { export default function BasicSetting({ setShowBasicSettingModal }) {
const { getMessage } = useMessage() const { getMessage } = useMessage()
const [tabNum, setTabNum] = useState(1) const [tabNum, setTabNum] = useState(1)
const [canvasSetting, setCanvasSetting] = useRecoilState(canvasSettingState)
return ( return (
<WithDraggable isShow={true} pos={{ x: 50, y: -950 }}> <WithDraggable isShow={true} pos={{ x: 50, y: -950 }}>
<div className={`modal-pop-wrap lx-2`}> <div className={`modal-pop-wrap lx-2`}>
@ -27,8 +31,13 @@ export default function BasicSetting({ setShowBasicSettingModal }) {
<div className={`module-tab-bx ${tabNum === 3 ? 'act' : ''}`}>{getMessage('modal.module.basic.setting.module.placement')}</div> <div className={`module-tab-bx ${tabNum === 3 ? 'act' : ''}`}>{getMessage('modal.module.basic.setting.module.placement')}</div>
</div> </div>
{tabNum === 1 && <Orientation setTabNum={setTabNum} />} {tabNum === 1 && <Orientation setTabNum={setTabNum} />}
{tabNum === 2 && <Module setTabNum={setTabNum} />} {/*배치면 초기설정 - 입력방법: 복시도 입력 || 실측값 입력*/}
{tabNum === 3 && <Placement setTabNum={setTabNum} />} {canvasSetting.roofSizeSet && canvasSetting.roofSizeSet != 3 && tabNum === 2 && <Module setTabNum={setTabNum} />}
{canvasSetting.roofSizeSet && canvasSetting.roofSizeSet != 3 && tabNum === 3 && <Placement setTabNum={setTabNum} />}
{/*배치면 초기설정 - 입력방법: 육지붕*/}
{canvasSetting.roofSizeSet && canvasSetting.roofSizeSet == 3 && tabNum === 2 && <PitchModule setTabNum={setTabNum} />}
{canvasSetting.roofSizeSet && canvasSetting.roofSizeSet == 3 && tabNum === 3 && <PitchPlacement setTabNum={setTabNum} />}
<div className="grid-btn-wrap"> <div className="grid-btn-wrap">
{tabNum !== 1 && ( {tabNum !== 1 && (
@ -36,7 +45,7 @@ export default function BasicSetting({ setShowBasicSettingModal }) {
{getMessage('modal.module.basic.setting.prev')} {getMessage('modal.module.basic.setting.prev')}
</button> </button>
)} )}
{tabNum !== 3 && <button className="btn-frame modal act mr5">{getMessage('modal.common.save')}</button>} {/*{tabNum !== 3 && <button className="btn-frame modal act mr5">{getMessage('modal.common.save')}</button>}*/}
{tabNum !== 3 && ( {tabNum !== 3 && (
<button className="btn-frame modal" onClick={() => setTabNum(tabNum + 1)}> <button className="btn-frame modal" onClick={() => setTabNum(tabNum + 1)}>
Next Next

View File

@ -74,12 +74,15 @@ export default function Module({}) {
</tr> </tr>
</> </>
))} ))}
<tr>
<td></td> {Array.from({ length: 3 - moduleData.rows.length }).map((_, i) => (
<td></td> <tr key={i}>
<td></td> <td></td>
<td></td> <td></td>
</tr> <td></td>
<td></td>
</tr>
))}
</tbody> </tbody>
</table> </table>
</div> </div>

View File

@ -0,0 +1,91 @@
import { useMessage } from '@/hooks/useMessage'
import QSelectBox from '@/components/common/select/QSelectBox'
export default function PitchModule({}) {
const { getMessage } = useMessage()
const SelectOption01 = [{ name: '0' }, { name: '0' }, { name: '0' }, { name: '0' }]
const moduleData = {
header: [
{ name: getMessage('module'), width: 150, prop: 'module', type: 'color-box' },
{
name: `${getMessage('높이')} (mm)`,
prop: 'height',
},
{ name: `${getMessage('width')} (mm)`, prop: 'width' },
{ name: `${getMessage('output')} (W)`, prop: 'output' },
],
rows: [
{
module: { name: 'Re.RISE-G3 440', color: '#AA6768' },
height: { name: '1134' },
width: { name: '1722' },
output: { name: '440' },
},
{
module: {
name: 'Re.RISE MS-G3 290',
color: '#67A2AA',
},
height: { name: '1134' },
width: { name: '1722' },
output: { name: '240' },
},
],
}
return (
<>
<div className="module-table-box">
<div className="module-table-inner">
<div className="outline-form mb10">
<span className="mr10">{getMessage('modal.module.basic.setting.module.setting')}</span>
<div className="grid-select">
<QSelectBox title={'Search'} option={SelectOption01} />
</div>
</div>
<div className="roof-module-table">
<table>
<thead>
<tr>
{moduleData.header.map((data) => (
<th key={data.prop} style={{ width: data.width ? data.width : '' }}>
{data.name}
</th>
))}
</tr>
</thead>
<tbody>
{moduleData.rows.map((row) => (
<>
<tr>
{moduleData.header.map((header) => (
<>
{header.type === 'color-box' && (
<td>
<div className="color-wrap">
<span className="color-box" style={{ backgroundColor: row[header.prop].color }}></span>
<span className="name">{row[header.prop].name}</span>
</div>
</td>
)}
{!header.type && header.type !== 'color-box' && <td className="al-r">{row[header.prop].name}</td>}
</>
))}
</tr>
</>
))}
{Array.from({ length: 3 - moduleData.rows.length }).map((_, i) => (
<tr key={i}>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
))}
</tbody>
</table>
</div>
</div>
</div>
</>
)
}

View File

@ -0,0 +1,105 @@
import { useMessage } from '@/hooks/useMessage'
export default function PitchPlacement() {
const { getMessage } = useMessage()
const moduleData = {
header: [
{ type: 'check', name: '', prop: 'check', width: 70 },
{ type: 'color-box', name: getMessage('module'), prop: 'module' },
{ type: 'text', name: `${getMessage('output')} (W)`, prop: 'output', width: 70 },
],
rows: [
{
check: false,
module: { name: 'Re.RISE-G3 440', color: '#AA6768' },
output: { name: '440' },
},
{
check: false,
module: {
name: 'Re.RISE MS-G3 290',
color: '#67A2AA',
},
output: { name: '240' },
},
],
}
return (
<>
<div className="module-table-box mb10">
<div className="module-table-inner">
<div className="roof-module-table">
<table>
<thead>
<tr>
{moduleData.header.map((data) => (
<th key={data.prop} style={{ width: data.width ? data.width : '' }}>
{data.type === 'check' ? (
<div className="d-check-box no-text pop">
<input type="checkbox" id="ch01" disabled />
<label htmlFor="ch01"></label>
</div>
) : (
data.name
)}
</th>
))}
</tr>
</thead>
<tbody>
{moduleData.rows.map((row) => (
<>
<tr>
{moduleData.header.map((header) => (
<>
{header.type === 'color-box' && (
<td>
<div className="color-wrap">
<span className="color-box" style={{ backgroundColor: row[header.prop].color }}></span>
<span className="name">{row[header.prop].name}</span>
</div>
</td>
)}
{header.type === 'check' && (
<td className="al-c">
<div className="d-check-box no-text pop">
<input type="checkbox" id="ch02" />
<label htmlFor="ch02"></label>
</div>
</td>
)}
{header.type && header.type !== 'color-box' && header.type !== 'check' && <td className="al-r">{row[header.prop].name}</td>}
</>
))}
</tr>
</>
))}
</tbody>
</table>
</div>
</div>
</div>
<div className="module-table-box mb10">
<div className="module-table-inner">
<div className="hexagonal-wrap">
<div className="hexagonal-item">
<div className="bold-font">{getMessage('modal.module.basic.setting.pitch.module.placement.standard.setting')}</div>
</div>
<div className="hexagonal-item">
<div className="pop-form-radio">
<div className="d-check-radio pop">
<input type="radio" name="radio01" id="ra01" />
<label htmlFor="ra01">{getMessage('modal.module.basic.setting.pitch.module.placement.standard.setting.south')}</label>
</div>
<div className="d-check-radio pop">
<input type="radio" name="radio01" id="ra02" />
<label htmlFor="ra02">{getMessage('modal.module.basic.setting.pitch.module.placement.standard.setting.select')}</label>
</div>
</div>
</div>
</div>
</div>
</div>
</>
)
}

View File

@ -0,0 +1,57 @@
import WithDraggable from '@/components/common/draggable/WithDraggable'
import { useState } from 'react'
import PowerConditionalSelect from '@/components/floor-plan/modal/circuitTrestle/step/PowerConditionalSelect'
import CircuitAllocation from '@/components/floor-plan/modal/circuitTrestle/step/CircuitAllocation'
import StepUp from '@/components/floor-plan/modal/circuitTrestle/step/StepUp'
import { useMessage } from '@/hooks/useMessage'
export default function CircuitTrestleSetting({ setShowCircuitTrestleSettingModal }) {
const { getMessage } = useMessage()
const [tabNum, setTabNum] = useState(1)
const [circuitAllocationType, setCircuitAllocationType] = useState(1)
const circuitProps = {
circuitAllocationType,
setCircuitAllocationType,
}
return (
<WithDraggable isShow={true} pos={{ x: 50, y: -950 }}>
<div className={`modal-pop-wrap lx-2`}>
<div className="modal-head">
<h1 className="title">{getMessage('modal.circuit.trestle.setting')} </h1>
<button className="modal-close" onClick={() => setShowCircuitTrestleSettingModal(false)}>
닫기
</button>
</div>
<div className="modal-body">
<div className="roof-module-tab">
<div className={`module-tab-bx ${tabNum === 1 || tabNum === 2 || tabNum === 3 ? 'act' : ''}`}>
{getMessage('modal.circuit.trestle.setting.power.conditional.select')}
</div>
<span className={`tab-arr ${tabNum === 2 || tabNum === 3 ? 'act' : ''}`}></span>
<div className={`module-tab-bx ${tabNum === 2 || tabNum === 3 ? 'act' : ''}`}>
{getMessage('modal.circuit.trestle.setting.circuit.allocation')}
</div>
<span className={`tab-arr ${tabNum === 3 ? 'act' : ''}`}></span>
<div className={`module-tab-bx ${tabNum === 3 ? 'act' : ''}`}>{getMessage('modal.circuit.trestle.setting.step.up.allocation')}</div>
</div>
{tabNum === 1 && <PowerConditionalSelect />}
{tabNum === 2 && <CircuitAllocation {...circuitProps} />}
{tabNum === 3 && <StepUp />}
<div className="grid-btn-wrap">
{tabNum !== 1 && (
<button className="btn-frame modal mr5" onClick={() => setTabNum(tabNum - 1)}>
{getMessage('modal.common.prev')}
</button>
)}
{tabNum !== 3 && (
<button className="btn-frame modal act" onClick={() => setTabNum(tabNum + 1)}>
Next
</button>
)}
{tabNum === 3 && <button className="btn-frame modal act">保存 (仮割り当て)</button>}
</div>
</div>
</div>
</WithDraggable>
)
}

View File

@ -0,0 +1,25 @@
import AutoCircuitAllocation from '@/components/floor-plan/modal/circuitTrestle/step/type/AutoCircuitAllocation'
import PassivityCircuitAllocation from '@/components/floor-plan/modal/circuitTrestle/step/type/PassivityCircuitAllocation'
import { useMessage } from '@/hooks/useMessage'
export default function CircuitAllocation(props) {
const { getMessage } = useMessage()
const { circuitAllocationType, setCircuitAllocationType } = props
return (
<>
<div className="module-box-tab">
<button className={`module-btn ${circuitAllocationType === 1 ? 'act' : ''}`} onClick={() => setCircuitAllocationType(1)}>
{getMessage('modal.circuit.trestle.setting.circuit.allocation.auto')}
</button>
<button className={`module-btn ${circuitAllocationType === 2 ? 'act' : ''}`} onClick={() => setCircuitAllocationType(2)}>
{getMessage('modal.circuit.trestle.setting.circuit.allocation.passivity')}
</button>
</div>
<div className="properties-setting-wrap outer">
<div className="setting-tit">{getMessage('modal.circuit.trestle.setting.circuit.allocation')}</div>
{circuitAllocationType === 1 && <AutoCircuitAllocation />}
{circuitAllocationType === 2 && <PassivityCircuitAllocation />}
</div>
</>
)
}

View File

@ -0,0 +1,146 @@
import QSelectBox from '@/components/common/select/QSelectBox'
import { useMessage } from '@/hooks/useMessage'
import { useState } from 'react'
const SelectOption01 = [{ name: '0' }, { name: '0' }, { name: '0' }, { name: '0' }]
export default function PowerConditionalSelect({ setTabNum }) {
const { getMessage } = useMessage()
const [selectedRowIndex, setSelectedRowIndex] = useState(null)
const [powerConditions, setPowerConditions] = useState([])
const seriesData = {
header: [
{ name: getMessage('명칭'), width: '15%', prop: 'name', type: 'color-box' },
{
name: `${getMessage('modal.circuit.trestle.setting.power.conditional.select.rated.output')} (kW)`,
width: '10%',
prop: 'ratedOutput',
},
{
name: `${getMessage('modal.circuit.trestle.setting.power.conditional.select.circuit.amount')}`,
width: '10%',
prop: 'circuitAmount',
},
{
name: `${getMessage('modal.circuit.trestle.setting.power.conditional.select.max.connection')}`,
width: '10%',
prop: 'maxConnection',
},
{
name: `${getMessage('modal.circuit.trestle.setting.power.conditional.select.max.overload')}`,
width: '10%',
prop: 'maxOverload',
},
{
name: `${getMessage('modal.circuit.trestle.setting.power.conditional.select.output.current')}`,
width: '10%',
prop: 'outputCurrent',
},
],
rows: [
{
name: { name: 'PCSオプションマスター', color: '#AA6768' },
ratedOutput: { name: '2' },
circuitAmount: { name: '2' },
maxConnection: { name: '-' },
maxOverload: { name: '-' },
outputCurrent: { name: '-' },
},
{
name: { name: 'HQJP-KA40-5', color: '#AA6768' },
ratedOutput: { name: '2' },
circuitAmount: { name: '2' },
maxConnection: { name: '-' },
maxOverload: { name: '-' },
outputCurrent: { name: '-' },
},
{
name: { name: 'Re.RISE-G3 440', color: '#AA6768' },
ratedOutput: { name: '2' },
circuitAmount: { name: '2' },
maxConnection: { name: '-' },
maxOverload: { name: '-' },
outputCurrent: { name: '-' },
},
],
}
return (
<>
<div className="outline-form mb15">
<span className="mr10">分類 (余剰)</span>
<div className="grid-select mr10">
<QSelectBox title={'HQJPシリーズ'} option={SelectOption01} />
</div>
<span className="thin">寒冷地仕様</span>
</div>
<div className="module-table-box mb10">
<div className="module-table-inner">
<div className="circuit-check-inner">
<div className="d-check-box pop mb15 sel">
<input type="checkbox" id="ch01" />
<label htmlFor="ch01">屋内PCSHQJP-KA-5シリーズ</label>
</div>
<div className="d-check-box pop sel">
<input type="checkbox" id="ch02" />
<label htmlFor="ch02">屋外マルチPCSHQJP-RA5シリーズ</label>
</div>
</div>
</div>
</div>
<div className="module-table-box mb15">
<div className="module-table-inner">
<div className="x-scroll-table mb10">
<div className="roof-module-table">
<table>
<thead>
<tr>
{seriesData.header.map((header) => (
<th key={header.prop} style={{ width: header.width }}>
{header.name}
</th>
))}
</tr>
</thead>
<tbody>
{seriesData.rows.map((row, index) => (
<tr key={index} onClick={() => setSelectedRowIndex(index)} className={index === selectedRowIndex ? 'on' : ''}>
{seriesData.header.map((header) => (
<td>{row[header.prop].name}</td>
))}
</tr>
))}
</tbody>
</table>
</div>
</div>
<div className="circuit-right-wrap mb10">
<button className="btn-frame self mr5">追加</button>
</div>
<div className="circuit-data-form">
<span className="normal-font">
HQJP-KA40-5 <button className="del"></button>
</span>
<span className="normal-font">
HQJP-KA40-5 <button className="del"></button>
</span>
<span className="normal-font">
HQJP-KA40-5 <button className="del"></button>
</span>
</div>
</div>
</div>
<div className="slope-wrap">
<div className="d-check-box pop mb15">
<input type="checkbox" id="ch03" />
<label htmlFor="ch03">同一傾斜同一方面の面積の場合同じ面として回路を分ける</label>
</div>
<div className="d-check-box pop">
<input type="checkbox" id="ch04" />
<label className="red" htmlFor="ch04">
MAX接続過積で回路を分ける
</label>
</div>
</div>
</>
)
}

View File

@ -0,0 +1,414 @@
import QSelectBox from '@/components/common/select/QSelectBox'
import { useMessage } from '@/hooks/useMessage'
const SelectOption01 = [{ name: '0' }, { name: '0' }, { name: '0' }, { name: '0' }]
export default function StepUp({}) {
const { getMessage } = useMessage()
return (
<>
<div className="properties-setting-wrap outer">
<div className="setting-tit">{getMessage('modal.circuit.trestle.setting.step.up.allocation')}</div>
<div className="circuit-overflow">
<div className="module-table-box mb10">
<div className="module-table-inner">
<div className="mb-box">
<div className="circuit-table-tit">HQJP-KA55-5</div>
<div className="roof-module-table overflow-y">
<table>
<thead>
<tr>
<th>シリアル枚数</th>
<th>総回路数</th>
</tr>
</thead>
<tbody>
<tr>
<td className="al-r">10</td>
<td className="al-r">0</td>
</tr>
<tr>
<td className="al-r">10</td>
<td className="al-r">0</td>
</tr>
<tr>
<td className="al-r">10</td>
<td className="al-r">0</td>
</tr>
<tr>
<td className="al-r">10</td>
<td className="al-r">0</td>
</tr>
<tr>
<td className="al-r">10</td>
<td className="al-r">0</td>
</tr>
</tbody>
</table>
</div>
</div>
<div className="circuit-table-flx-wrap">
<div className="circuit-table-flx-box">
<div className="bold-font mb10">接続する</div>
<div className="roof-module-table mb10">
<table>
<thead>
<tr>
<th style={{ width: '140px' }}>名称</th>
<th>回路数</th>
<th>昇圧回路数</th>
</tr>
</thead>
<tbody>
<tr>
<td className="al-c">KTN-CBD4C</td>
<td className="al-r">4</td>
<td className="al-r">0</td>
</tr>
<tr>
<td className="al-c">KTN-CBD4C</td>
<td className="al-r">4</td>
<td className="al-r">0</td>
</tr>
<tr>
<td className="al-c">KTN-CBD4C</td>
<td className="al-r">4</td>
<td className="al-r">0</td>
</tr>
</tbody>
</table>
</div>
<div className="bottom-wrap">
<div className="circuit-right-wrap mb10">
<button className="btn-frame self mr5">追加</button>
</div>
<div className="circuit-data-form">
<span className="normal-font">
HQJP-KA40-5<button className="del"></button>
</span>
</div>
</div>
</div>
<div className="circuit-table-flx-box">
<div className="bold-font mb10">昇圧オプション</div>
<div className="roof-module-table mb10">
<table>
<thead>
<tr>
<th>名称</th>
<th>昇圧回路数</th>
</tr>
</thead>
<tbody>
<tr>
<td className="al-c">-</td>
<td className="al-c">-</td>
</tr>
<tr>
<td className="al-c">-</td>
<td className="al-c">-</td>
</tr>
</tbody>
</table>
</div>
<div className="bottom-wrap">
<div className="circuit-right-wrap mb10">
<button className="btn-frame self mr5">追加</button>
</div>
<div className="circuit-data-form">
<span className="normal-font">
HQJP-KA40-5<button className="del"></button>
</span>
</div>
</div>
</div>
</div>
<div className="circuit-count-input">
<span className="normal-font">綿調道区分</span>
<div className="input-grid mr5" style={{ width: '40px' }}>
<input type="text" className="input-origin block" />
</div>
<span className="normal-font">回路</span>
<span className="normal-font">(二重昇圧回路数</span>
<div className="input-grid mr5" style={{ width: '40px' }}>
<input type="text" className="input-origin block" />
</div>
<span className="normal-font">回路)</span>
</div>
</div>
</div>
<div className="module-table-box mb10">
<div className="module-table-inner">
<div className="mb-box">
<div className="circuit-table-tit">HQJP-KA55-5</div>
<div className="roof-module-table overflow-y">
<table>
<thead>
<tr>
<th>シリアル枚数</th>
<th>総回路数</th>
</tr>
</thead>
<tbody>
<tr>
<td className="al-r">10</td>
<td className="al-r">0</td>
</tr>
<tr>
<td className="al-r">10</td>
<td className="al-r">0</td>
</tr>
<tr>
<td className="al-r">10</td>
<td className="al-r">0</td>
</tr>
<tr>
<td className="al-r">10</td>
<td className="al-r">0</td>
</tr>
<tr>
<td className="al-r">10</td>
<td className="al-r">0</td>
</tr>
</tbody>
</table>
</div>
</div>
<div className="circuit-table-flx-wrap">
<div className="circuit-table-flx-box">
<div className="bold-font mb10">接続する</div>
<div className="roof-module-table mb10">
<table>
<thead>
<tr>
<th style={{ width: '140px' }}>名称</th>
<th>回路数</th>
<th>昇圧回路数</th>
</tr>
</thead>
<tbody>
<tr>
<td className="al-c">KTN-CBD4C</td>
<td className="al-r">4</td>
<td className="al-r">0</td>
</tr>
<tr>
<td className="al-c">KTN-CBD4C</td>
<td className="al-r">4</td>
<td className="al-r">0</td>
</tr>
<tr>
<td className="al-c">KTN-CBD4C</td>
<td className="al-r">4</td>
<td className="al-r">0</td>
</tr>
</tbody>
</table>
</div>
<div className="bottom-wrap">
<div className="circuit-right-wrap mb10">
<button className="btn-frame self mr5">追加</button>
</div>
<div className="circuit-data-form">
<span className="normal-font">
HQJP-KA40-5<button className="del"></button>
</span>
</div>
</div>
</div>
<div className="circuit-table-flx-box">
<div className="bold-font mb10">昇圧オプション</div>
<div className="roof-module-table mb10">
<table>
<thead>
<tr>
<th>名称</th>
<th>昇圧回路数</th>
</tr>
</thead>
<tbody>
<tr>
<td className="al-c">-</td>
<td className="al-c">-</td>
</tr>
<tr>
<td className="al-c">-</td>
<td className="al-c">-</td>
</tr>
</tbody>
</table>
</div>
<div className="bottom-wrap">
<div className="circuit-right-wrap mb10">
<button className="btn-frame self mr5">追加</button>
</div>
<div className="circuit-data-form">
<span className="normal-font">
HQJP-KA40-5<button className="del"></button>
</span>
</div>
</div>
</div>
</div>
<div className="circuit-count-input">
<span className="normal-font">綿調道区分</span>
<div className="input-grid mr5" style={{ width: '40px' }}>
<input type="text" className="input-origin block" />
</div>
<span className="normal-font">回路</span>
<span className="normal-font">(二重昇圧回路数</span>
<div className="input-grid mr5" style={{ width: '40px' }}>
<input type="text" className="input-origin block" />
</div>
<span className="normal-font">回路)</span>
</div>
</div>
</div>
<div className="module-table-box ">
<div className="module-table-inner">
<div className="mb-box">
<div className="circuit-table-tit">HQJP-KA55-5</div>
<div className="roof-module-table overflow-y">
<table>
<thead>
<tr>
<th>シリアル枚数</th>
<th>総回路数</th>
</tr>
</thead>
<tbody>
<tr>
<td className="al-r">10</td>
<td className="al-r">0</td>
</tr>
<tr>
<td className="al-r">10</td>
<td className="al-r">0</td>
</tr>
<tr>
<td className="al-r">10</td>
<td className="al-r">0</td>
</tr>
<tr>
<td className="al-r">10</td>
<td className="al-r">0</td>
</tr>
<tr>
<td className="al-r">10</td>
<td className="al-r">0</td>
</tr>
</tbody>
</table>
</div>
</div>
<div className="circuit-table-flx-wrap">
<div className="circuit-table-flx-box">
<div className="bold-font mb10">接続する</div>
<div className="roof-module-table mb10">
<table>
<thead>
<tr>
<th style={{ width: '140px' }}>名称</th>
<th>回路数</th>
<th>昇圧回路数</th>
</tr>
</thead>
<tbody>
<tr>
<td className="al-c">KTN-CBD4C</td>
<td className="al-r">4</td>
<td className="al-r">0</td>
</tr>
<tr>
<td className="al-c">KTN-CBD4C</td>
<td className="al-r">4</td>
<td className="al-r">0</td>
</tr>
<tr>
<td className="al-c">KTN-CBD4C</td>
<td className="al-r">4</td>
<td className="al-r">0</td>
</tr>
</tbody>
</table>
</div>
<div className="bottom-wrap">
<div className="circuit-right-wrap mb10">
<button className="btn-frame self mr5">追加</button>
</div>
<div className="circuit-data-form">
<span className="normal-font">
HQJP-KA40-5<button className="del"></button>
</span>
</div>
</div>
</div>
<div className="circuit-table-flx-box">
<div className="bold-font mb10">昇圧オプション</div>
<div className="roof-module-table mb10">
<table>
<thead>
<tr>
<th>名称</th>
<th>昇圧回路数</th>
</tr>
</thead>
<tbody>
<tr>
<td className="al-c">-</td>
<td className="al-c">-</td>
</tr>
<tr>
<td className="al-c">-</td>
<td className="al-c">-</td>
</tr>
</tbody>
</table>
</div>
<div className="bottom-wrap">
<div className="circuit-right-wrap mb10">
<button className="btn-frame self mr5">追加</button>
</div>
<div className="circuit-data-form">
<span className="normal-font">
HQJP-KA40-5<button className="del"></button>
</span>
</div>
</div>
</div>
</div>
<div className="circuit-count-input">
<span className="normal-font">綿調道区分</span>
<div className="input-grid mr5" style={{ width: '40px' }}>
<input type="text" className="input-origin block" />
</div>
<span className="normal-font">回路</span>
<span className="normal-font">(二重昇圧回路数</span>
<div className="input-grid mr5" style={{ width: '40px' }}>
<input type="text" className="input-origin block" />
</div>
<span className="normal-font">回路)</span>
</div>
</div>
</div>
</div>
<div className="slope-wrap">
<div className="outline-form">
<span className="mr10" style={{ width: 'auto' }}>
モニターの選択
</span>
<div className="grid-select mr10">
<QSelectBox title={'電力検出ユニット (モニター付き)'} option={SelectOption01} />
</div>
</div>
</div>
</div>
{/*<div className="grid-btn-wrap">*/}
{/* <button className="btn-frame modal mr5" onClick={() => setTabNum(2)}>*/}
{/* 以前*/}
{/* </button>*/}
{/* <button className="btn-frame modal act">保存 (仮割り当て)</button>*/}
{/*</div>*/}
</>
)
}

View File

@ -0,0 +1,17 @@
import { useMessage } from '@/hooks/useMessage'
export default function AutoCircuitAllocation() {
const { getMessage } = useMessage()
return (
<div className="module-table-box">
<div className="module-table-inner">
<div className="circuit-check-inner">
<div className="d-check-radio pop">
<input type="radio" name="radio01" id="ra01" checked={true} />
<label htmlFor="ra01">{getMessage('modal.circuit.trestle.setting.circuit.allocation.auto')}</label>
</div>
</div>
</div>
</div>
)
}

View File

@ -0,0 +1,104 @@
import { useMessage } from '@/hooks/useMessage'
export default function PassivityCircuitAllocation() {
const { getMessage } = useMessage()
const moduleData = {
header: [
{ name: getMessage('屋根面'), prop: 'roofShape' },
{
name: getMessage('Q.TRON M-G2'),
prop: 'moduleName',
},
{
name: getMessage('発電量 (kW)'),
prop: 'powerGeneration',
},
],
rows: [
{
roofShape: { name: 'M 1' },
moduleName: { name: '8' },
powerGeneration: { name: '3,400' },
},
{
roofShape: { name: 'M 1' },
moduleName: { name: '8' },
powerGeneration: { name: '3,400' },
},
],
}
return (
<>
<div className="module-table-box mb10">
<div className="module-table-inner">
<div className="bold-font mb10">{getMessage('modal.circuit.trestle.setting.circuit.allocation.passivity')}</div>
<div className="normal-font mb15">{getMessage('modal.circuit.trestle.setting.circuit.allocation.passivity.info')}</div>
<div className="roof-module-table overflow-y">
<table>
<thead>
<tr>
{moduleData.header.map((header) => (
<th key={header.prop}>{header.name}</th>
))}
</tr>
</thead>
<tbody>
{moduleData.rows.map((row, index) => (
<tr key={index}>
{moduleData.header.map((header) => (
<td key={header.prop}>{row[header.prop].name}</td>
))}
</tr>
))}
</tbody>
</table>
</div>
</div>
</div>
<div className="module-table-box mb10">
<div className="module-table-inner">
<div className="hexagonal-wrap">
<div className="hexagonal-item">
<div className="bold-font">{getMessage('modal.circuit.trestle.setting.circuit.allocation.passivity.selected.power.conditional')}</div>
</div>
<div className="hexagonal-item">
<div className="d-check-radio pop mb10">
<input type="radio" name="radio01" id="ra01" />
<label htmlFor="ra01">HQJP-KA55-5 (標準回路2枚10)</label>
</div>
<div className="d-check-radio pop mb10">
<input type="radio" name="radio01" id="ra02" />
<label htmlFor="ra02">HQJP-KA55-5 (標準回路2枚10)</label>
</div>
<div className="d-check-radio pop">
<input type="radio" name="radio01" id="ra03" />
<label htmlFor="ra03">HQJP-KA55-5 (標準回路2枚10)</label>
</div>
</div>
</div>
</div>
</div>
<div className="slope-wrap">
<div className="circuit-right-wrap mb15">
<div className="outline-form">
<span className="mr10" style={{ width: 'auto' }}>
{getMessage('modal.circuit.trestle.setting.circuit.allocation.passivity.circuit.num')}
</span>
<div className="input-grid mr5" style={{ width: '70px' }}>
<input type="text" className="input-origin block" />
</div>
</div>
</div>
<div className="circuit-right-wrap">
<button className="btn-frame roof mr5">
{getMessage('modal.circuit.trestle.setting.circuit.allocation.passivity.selected.power.conditional.reset')}
</button>
<button className="btn-frame roof mr5">
{getMessage('modal.circuit.trestle.setting.circuit.allocation.passivity.all.power.conditional.reset')}
</button>
<button className="btn-frame roof">{getMessage('modal.circuit.trestle.setting.circuit.allocation.passivity.circuit.num.fix')}</button>
</div>
</div>
</>
)
}

View File

@ -10,7 +10,7 @@ import { useEavesGableEdit } from '@/hooks/roofcover/useEavesGableEdit'
export default function EavesGableEdit({ setShowEavesGableEditModal }) { export default function EavesGableEdit({ setShowEavesGableEditModal }) {
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { type, setType, buttonMenu, TYPES, pitchRef, offsetRef, widthRef, radioTypeRef } = useEavesGableEdit() const { type, setType, buttonMenu, TYPES, pitchRef, offsetRef, widthRef, radioTypeRef } = useEavesGableEdit(setShowEavesGableEditModal)
const eavesProps = { const eavesProps = {
pitchRef, pitchRef,
offsetRef, offsetRef,

View File

@ -1,6 +1,14 @@
'use client'
import { useState, useRef, useEffect } from 'react'
import { useRecoilValue } from 'recoil'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { useEvent } from '@/hooks/useEvent'
import { canvasState } from '@/store/canvasAtom'
import { useSwal } from '@/hooks/useSwal'
import { useObjectBatch } from '@/hooks/object/useObjectBatch'
import { POLYGON_TYPE } from '@/common/common'
import WithDraggable from '@/components/common/draggable/WithDraggable' import WithDraggable from '@/components/common/draggable/WithDraggable'
import { useState } from 'react'
import OpenSpace from '@/components/floor-plan/modal/object/type/OpenSpace' import OpenSpace from '@/components/floor-plan/modal/object/type/OpenSpace'
import Shadow from '@/components/floor-plan/modal/object/type/Shadow' import Shadow from '@/components/floor-plan/modal/object/type/Shadow'
import TriangleDormer from '@/components/floor-plan/modal/object/type/TriangleDormer' import TriangleDormer from '@/components/floor-plan/modal/object/type/TriangleDormer'
@ -8,7 +16,56 @@ import PentagonDormer from '@/components/floor-plan/modal/object/type/PentagonDo
export default function ObjectSetting({ setShowObjectSettingModal }) { export default function ObjectSetting({ setShowObjectSettingModal }) {
const { getMessage } = useMessage() const { getMessage } = useMessage()
const canvas = useRecoilValue(canvasState)
const [buttonAct, setButtonAct] = useState(1) const [buttonAct, setButtonAct] = useState(1)
const { swalFire } = useSwal()
const { applyOpeningAndShadow, applyDormers } = useObjectBatch()
const surfaceShapePolygons = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
//
useEffect(() => {
canvas.discardActiveObject()
surfaceShapePolygons.forEach((obj) => {
obj.set({ selectable: false })
})
}, [])
/**
* 개구배치, 그림자배치
*/
const objectPlacement = {
typeRef: useRef([]), //,
widthRef: useRef(null),
heightRef: useRef(null),
isCrossRef: useRef(null),
}
const dormerPlacement = {
widthRef: useRef(null),
heightRef: useRef(null),
pitchRef: useRef(null),
offsetRef: useRef(null),
directionRef: useRef(null),
}
const applyObject = () => {
// if (surfaceShapePolygons.length === 0) {
// swalFire({ text: ' ', icon: 'error' })
// return
// }
//,
console.log(surfaceShapePolygons)
if (buttonAct === 1 || buttonAct === 2) {
applyOpeningAndShadow(objectPlacement, buttonAct, surfaceShapePolygons)
} else {
applyDormers(dormerPlacement, buttonAct, surfaceShapePolygons)
}
}
const buttonMenu = [ const buttonMenu = [
{ id: 1, name: getMessage('modal.object.setting.type.open.space.placement') }, { id: 1, name: getMessage('modal.object.setting.type.open.space.placement') },
{ id: 2, name: getMessage('modal.object.setting.type.shadow.placement') }, { id: 2, name: getMessage('modal.object.setting.type.shadow.placement') },
@ -34,13 +91,20 @@ export default function ObjectSetting({ setShowObjectSettingModal }) {
</div> </div>
<div className="properties-setting-wrap outer"> <div className="properties-setting-wrap outer">
<div className="setting-tit">{getMessage('setting')}</div> <div className="setting-tit">{getMessage('setting')}</div>
{buttonAct === 1 && <OpenSpace />} {buttonAct === 1 && <OpenSpace ref={objectPlacement} />}
{buttonAct === 2 && <Shadow />} {buttonAct === 2 && <Shadow ref={objectPlacement} />}
{buttonAct === 3 && <TriangleDormer />} {buttonAct === 3 && <TriangleDormer ref={dormerPlacement} />}
{buttonAct === 4 && <PentagonDormer />} {buttonAct === 4 && <PentagonDormer ref={dormerPlacement} />}
</div> </div>
<div className="grid-btn-wrap"> <div className="grid-btn-wrap">
<button className="btn-frame modal act">{getMessage('write')}</button> <button
className="btn-frame modal act"
onClick={() => {
applyObject()
}}
>
{getMessage('write')}
</button>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,18 +1,45 @@
import { forwardRef, useState, useEffect } from 'react'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { INPUT_TYPE } from '@/common/common'
export default function OpenSpace() { const OpenSpace = forwardRef((props, refs) => {
const { getMessage } = useMessage() const { getMessage } = useMessage()
const [selectedType, setSelectedType] = useState(INPUT_TYPE.FREE)
useEffect(() => {
if (selectedType === INPUT_TYPE.FREE) {
refs.widthRef.current.value = 0
refs.heightRef.current.value = 0
}
}, [selectedType])
//
return ( return (
<div className="discrimination-box mb10"> <div className="discrimination-box mb10">
<div className="mb-box"> <div className="mb-box">
<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={INPUT_TYPE.FREE}
defaultChecked
ref={(el) => (refs.typeRef.current[0] = el)}
onClick={() => setSelectedType(INPUT_TYPE.FREE)}
/>
<label htmlFor="ra01">{getMessage('modal.object.setting.free.input')}</label> <label htmlFor="ra01">{getMessage('modal.object.setting.free.input')}</label>
</div> </div>
</div> </div>
<div className="mb-box"> <div className="mb-box">
<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={INPUT_TYPE.DIMENSION}
ref={(el) => (refs.typeRef.current[1] = el)}
onClick={() => setSelectedType(INPUT_TYPE.DIMENSION)}
/>
<label htmlFor="ra02">{getMessage('modal.object.setting.size.input')}</label> <label htmlFor="ra02">{getMessage('modal.object.setting.size.input')}</label>
</div> </div>
</div> </div>
@ -24,7 +51,13 @@ export default function OpenSpace() {
<div className="eaves-keraba-td"> <div className="eaves-keraba-td">
<div className="outline-form"> <div className="outline-form">
<div className="input-grid mr5" style={{ width: '100px' }}> <div className="input-grid mr5" style={{ width: '100px' }}>
<input type="text" className="input-origin block" defaultValue={100} /> <input
type="text"
className="input-origin block"
placeholder={0}
ref={refs.widthRef}
disabled={selectedType !== INPUT_TYPE.DIMENSION}
/>
</div> </div>
<span className="thin">mm</span> <span className="thin">mm</span>
</div> </div>
@ -35,7 +68,13 @@ export default function OpenSpace() {
<div className="eaves-keraba-td"> <div className="eaves-keraba-td">
<div className="outline-form"> <div className="outline-form">
<div className="input-grid mr5" style={{ width: '100px' }}> <div className="input-grid mr5" style={{ width: '100px' }}>
<input type="text" className="input-origin block" defaultValue={100} /> <input
type="text"
className="input-origin block"
placeholder={0}
ref={refs.heightRef}
disabled={selectedType !== INPUT_TYPE.DIMENSION}
/>
</div> </div>
<span className="thin">mm</span> <span className="thin">mm</span>
</div> </div>
@ -45,9 +84,11 @@ export default function OpenSpace() {
</div> </div>
</div> </div>
<div className="d-check-box pop"> <div className="d-check-box pop">
<input type="checkbox" id="ch99" /> <input type="checkbox" id="ch99" ref={refs.isCrossRef} />
<label htmlFor="ch99">{getMessage('modal.object.setting.area.cross')}</label> <label htmlFor="ch99">{getMessage('modal.object.setting.area.cross')}</label>
</div> </div>
</div> </div>
) )
} })
export default OpenSpace

View File

@ -1,18 +1,45 @@
import { forwardRef, useState, useEffect } from 'react'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { INPUT_TYPE } from '@/common/common'
export default function Shadow() { const Shadow = forwardRef((props, refs) => {
const { getMessage } = useMessage() const { getMessage } = useMessage()
const [selectedType, setSelectedType] = useState(INPUT_TYPE.FREE)
useEffect(() => {
if (selectedType === INPUT_TYPE.FREE) {
refs.widthRef.current.value = 0
refs.heightRef.current.value = 0
}
}, [selectedType])
return ( return (
<div className="discrimination-box mb10"> <div className="discrimination-box mb10">
<div className="mb-box"> <div className="mb-box">
<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={INPUT_TYPE.FREE}
defaultChecked
ref={(el) => (refs.typeRef.current[0] = el)}
onClick={() => setSelectedType(INPUT_TYPE.FREE)}
/>
<label htmlFor="ra01">{getMessage('modal.object.setting.free.input')}</label> <label htmlFor="ra01">{getMessage('modal.object.setting.free.input')}</label>
</div> </div>
</div> </div>
<div className="mb-box"> <div className="mb-box">
<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={INPUT_TYPE.DIMENSION}
ref={(el) => (refs.typeRef.current[1] = el)}
onClick={() => setSelectedType(INPUT_TYPE.DIMENSION)}
/>
<label htmlFor="ra02">{getMessage('modal.object.setting.size.input')}</label> <label htmlFor="ra02">{getMessage('modal.object.setting.size.input')}</label>
</div> </div>
</div> </div>
@ -24,7 +51,13 @@ export default function Shadow() {
<div className="eaves-keraba-td"> <div className="eaves-keraba-td">
<div className="outline-form"> <div className="outline-form">
<div className="input-grid mr5" style={{ width: '100px' }}> <div className="input-grid mr5" style={{ width: '100px' }}>
<input type="text" className="input-origin block" defaultValue={100} /> <input
type="text"
className="input-origin block"
placeholder={0}
ref={refs.widthRef}
disabled={selectedType !== INPUT_TYPE.DIMENSION}
/>
</div> </div>
<span className="thin">mm</span> <span className="thin">mm</span>
</div> </div>
@ -35,7 +68,13 @@ export default function Shadow() {
<div className="eaves-keraba-td"> <div className="eaves-keraba-td">
<div className="outline-form"> <div className="outline-form">
<div className="input-grid mr5" style={{ width: '100px' }}> <div className="input-grid mr5" style={{ width: '100px' }}>
<input type="text" className="input-origin block" defaultValue={100} /> <input
type="text"
className="input-origin block"
placeholder={0}
ref={refs.heightRef}
disabled={selectedType !== INPUT_TYPE.DIMENSION}
/>
</div> </div>
<span className="thin">mm</span> <span className="thin">mm</span>
</div> </div>
@ -46,4 +85,6 @@ export default function Shadow() {
</div> </div>
</div> </div>
) )
} })
export default Shadow

View File

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

View File

@ -6,7 +6,7 @@ export default function PropertiesSetting(props) {
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { setShowPropertiesSettingModal } = props const { setShowPropertiesSettingModal } = props
const { handleSetEaves, handleSetGable, handleRollback, handleFix, closeModal } = usePropertiesSetting() const { handleSetEaves, handleSetGable, handleRollback, handleFix, closeModal } = usePropertiesSetting(setShowPropertiesSettingModal)
return ( return (
<WithDraggable isShow={true} pos={{ x: 50, y: -950 }}> <WithDraggable isShow={true} pos={{ x: 50, y: -950 }}>

View File

@ -39,7 +39,7 @@ export default function WallLineSetting(props) {
outerLineDiagonalLengthRef, outerLineDiagonalLengthRef,
handleRollback, handleRollback,
handleFix, handleFix,
} = useOuterLineWall() } = useOuterLineWall(setShowOutlineModal)
const outerLineProps = { const outerLineProps = {
length1, length1,
@ -171,7 +171,7 @@ export default function WallLineSetting(props) {
<button <button
className="btn-frame modal act" className="btn-frame modal act"
onClick={() => { onClick={() => {
handleFix(setShowOutlineModal) handleFix()
setShowPropertiesSettingModal(true) setShowPropertiesSettingModal(true)
}} }}
> >

View File

@ -8,6 +8,7 @@ import Diagonal from '@/components/floor-plan/modal/lineTypes/Diagonal'
import { useOuterLineWall } from '@/hooks/roofcover/useOuterLineWall' import { useOuterLineWall } from '@/hooks/roofcover/useOuterLineWall'
import { OUTER_LINE_TYPE } from '@/store/outerLineAtom' import { OUTER_LINE_TYPE } from '@/store/outerLineAtom'
import OuterLineWall from '@/components/floor-plan/modal/lineTypes/OuterLineWall' import OuterLineWall from '@/components/floor-plan/modal/lineTypes/OuterLineWall'
import { usePlacementShapeDrawing } from '@/hooks/surface/usePlacementShapeDrawing'
export default function PlacementShapeDrawing({ setShowPlaceShapeDrawingModal }) { export default function PlacementShapeDrawing({ setShowPlaceShapeDrawingModal }) {
const { getMessage } = useMessage() const { getMessage } = useMessage()
@ -45,7 +46,7 @@ export default function PlacementShapeDrawing({ setShowPlaceShapeDrawingModal })
outerLineDiagonalLengthRef, outerLineDiagonalLengthRef,
handleRollback, handleRollback,
handleFix, handleFix,
} = useOuterLineWall() } = usePlacementShapeDrawing(setShowPlaceShapeDrawingModal)
const outerLineProps = { const outerLineProps = {
length1, length1,
@ -129,8 +130,12 @@ export default function PlacementShapeDrawing({ setShowPlaceShapeDrawingModal })
</div> </div>
<div className="modal-body"> <div className="modal-body">
<div className="modal-btn-wrap"> <div className="modal-btn-wrap">
{types.map((type) => ( {types.map((type, idx) => (
<button className={`btn-frame modal ${buttonAct === type.id ? 'act' : ''}`} onClick={() => onClickButton(type)}> <button
key={`placement-${idx}`}
className={`btn-frame modal ${buttonAct === type.id ? 'act' : ''}`}
onClick={() => onClickButton(type)}
>
{type.name} {type.name}
</button> </button>
))} ))}
@ -145,8 +150,12 @@ export default function PlacementShapeDrawing({ setShowPlaceShapeDrawingModal })
</div> </div>
<div className="grid-btn-wrap"> <div className="grid-btn-wrap">
<button className="btn-frame modal mr5">{getMessage('modal.cover.outline.rollback')}</button> <button className="btn-frame modal mr5" onClick={handleRollback}>
<button className="btn-frame modal act">{getMessage('modal.cover.outline.fix')}</button> {getMessage('modal.cover.outline.rollback')}
</button>
<button className="btn-frame modal act" onClick={handleFix}>
{getMessage('modal.cover.outline.fix')}
</button>
</div> </div>
</div> </div>
</div> </div>

View File

@ -4,17 +4,29 @@ import { useEffect, useState, useRef } from 'react'
import Image from 'next/image' import Image from 'next/image'
import PlacementSurface from '@/components/floor-plan/modal/placementSurface/PlacementSurface' import PlacementSurface from '@/components/floor-plan/modal/placementSurface/PlacementSurface'
import { useSurfaceShapeBatch } from '@/hooks/surface/useSurfaceShapeBatch' import { useSurfaceShapeBatch } from '@/hooks/surface/useSurfaceShapeBatch'
import { useRecoilValue } from 'recoil'
import { canvasState } from '@/store/canvasAtom'
import { POLYGON_TYPE } from '@/common/common'
export default function PlacementSurfaceSetting({ setShowPlacementSurfaceSettingModal }) { export default function PlacementSurfaceSetting({ setShowPlacementSurfaceSettingModal }) {
const { getMessage } = useMessage() const { getMessage } = useMessage()
const [selectedType, setSelectedType] = useState() const [selectedType, setSelectedType] = useState()
const [rotate, setRotate] = useState(0) const [rotate, setRotate] = useState(0)
const [xInversion, setXInversion] = useState(false) const [xInversion, setXInversion] = useState(false)
const [yInversion, setYInversion] = useState(false) const [yInversion, setYInversion] = useState(false)
const canvas = useRecoilValue(canvasState)
const { applySurfaceShape } = useSurfaceShapeBatch() const { applySurfaceShape } = useSurfaceShapeBatch()
const surfaceShapePolygons = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
//
useEffect(() => {
surfaceShapePolygons.forEach((obj) => {
obj.set({ selectable: true })
})
}, [])
const surfaceRefs = { const surfaceRefs = {
length1: useRef(null), length1: useRef(null),
length2: useRef(null), length2: useRef(null),

View File

@ -2,76 +2,12 @@ import { useMessage } from '@/hooks/useMessage'
import WithDraggable from '@/components/common/draggable/WithDraggable' import WithDraggable from '@/components/common/draggable/WithDraggable'
import { useState } from 'react' import { useState } from 'react'
import QSelectBox from '@/components/common/select/QSelectBox' import QSelectBox from '@/components/common/select/QSelectBox'
import { useRoofAllocationSetting } from '@/hooks/roofcover/useRoofAllocationSetting'
export default function RoofAllocationSetting({ setShowRoofAllocationSettingModal }) { export default function RoofAllocationSetting({ setShowRoofAllocationSettingModal }) {
const { getMessage } = useMessage() const { getMessage } = useMessage()
const [selectedRoofMaterial, setSelectedRoofMaterial] = useState(null) const { handleSave, onAddRoofMaterial, onDeleteRoofMaterial, values, roofMaterials, selectedRoofMaterial, setSelectedRoofMaterial } =
const [values, setValues] = useState([ useRoofAllocationSetting(setShowRoofAllocationSettingModal)
{
id: '1',
type: 'A',
roofMaterial: { name: '기와1' },
width: { name: '200' },
length: { name: '250' },
rafter: { name: '300' },
alignType: 'stairs',
},
])
const roofMaterials = [
{
id: 'A',
name: '기와1',
type: 'A',
width: '200',
length: '200',
alignType: 'parallel',
},
{
id: 'B',
name: '기와2',
type: 'B',
rafter: '200',
alignType: 'parallel',
},
{
id: 'C',
name: '기와3',
type: 'C',
hajebichi: '200',
alignType: 'stairs',
},
{
id: 'D',
name: '기와4',
type: 'D',
length: '200',
alignType: 'stairs',
},
]
const widths = [
{ name: '200', id: 'q' },
{ name: '250', id: 'q1' },
{ name: '300', id: 'q2' },
]
const lengths = [
{ name: '200', id: 'w' },
{ name: '250', id: 'w1' },
{ name: '300', id: 'w2' },
]
const rafters = [
{ name: '200', id: 'e' },
{ name: '250', id: 'e1' },
{ name: '300', id: 'e2' },
]
const onAddRoofMaterial = () => {
setValues([...values, selectedRoofMaterial])
}
const onDeleteRoofMaterial = (id) => {
setValues(values.filter((value) => value.id !== id))
}
return ( return (
<WithDraggable isShow={true} pos={{ x: 50, y: -950 }}> <WithDraggable isShow={true} pos={{ x: 50, y: -950 }}>
@ -103,7 +39,7 @@ export default function RoofAllocationSetting({ setShowRoofAllocationSettingModa
{values.map((value, index) => ( {values.map((value, index) => (
<div className="grid-option-box" key={index}> <div className="grid-option-box" key={index}>
<div className="d-check-radio pop no-text"> <div className="d-check-radio pop no-text">
<input type="radio" name="radio01" id="ra01" /> <input type="radio" name="radio01" />
<label htmlFor="ra01"></label> <label htmlFor="ra01"></label>
</div> </div>
<div className="grid-option-block-form"> <div className="grid-option-block-form">
@ -213,7 +149,9 @@ export default function RoofAllocationSetting({ setShowRoofAllocationSettingModa
))} ))}
</div> </div>
<div className="grid-btn-wrap"> <div className="grid-btn-wrap">
<button className="btn-frame modal act">{getMessage('modal.roof.alloc.apply')}</button> <button className="btn-frame modal act" onClick={handleSave}>
{getMessage('modal.roof.alloc.apply')}
</button>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,18 +1,29 @@
import { useState } from 'react'
import WithDraggable from '@/components/common/draggable/WithDraggable' import WithDraggable from '@/components/common/draggable/WithDraggable'
import Eaves from '@/components/floor-plan/modal/roofShape/passivity/Eaves' import Eaves from '@/components/floor-plan/modal/roofShape/passivity/Eaves'
import Gable from '@/components/floor-plan/modal/roofShape/passivity/Gable' import Gable from '@/components/floor-plan/modal/roofShape/passivity/Gable'
import Shed from '@/components/floor-plan/modal/roofShape/passivity/Shed' import Shed from '@/components/floor-plan/modal/roofShape/passivity/Shed'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { useRoofShapePassivitySetting } from '@/hooks/roofcover/useRoofShapePassivitySetting'
export default function RoofShapePassivitySetting({ setShowRoofShapePassivitySettingModal }) { export default function RoofShapePassivitySetting({ setShowRoofShapePassivitySettingModal }) {
const { handleSave, handleConfirm, handleRollback, buttons, type, setType, TYPES, offsetRef, pitchRef } =
useRoofShapePassivitySetting(setShowRoofShapePassivitySettingModal)
const { getMessage } = useMessage() const { getMessage } = useMessage()
const [buttonAct, setButtonAct] = useState(1)
const buttons = [ const eavesProps = {
{ id: 1, name: getMessage('eaves') }, offsetRef,
{ id: 2, name: getMessage('gable') }, pitchRef,
{ id: 3, name: getMessage('windage') }, }
]
const gableProps = {
offsetRef,
pitchRef,
}
const shedProps = {
offsetRef,
}
return ( return (
<WithDraggable isShow={true} pos={{ x: 50, y: -950 }}> <WithDraggable isShow={true} pos={{ x: 50, y: -950 }}>
<div className={`modal-pop-wrap xxm`}> <div className={`modal-pop-wrap xxm`}>
@ -25,7 +36,7 @@ export default function RoofShapePassivitySetting({ setShowRoofShapePassivitySet
<div className="modal-body"> <div className="modal-body">
<div className="modal-btn-wrap"> <div className="modal-btn-wrap">
{buttons.map((button) => ( {buttons.map((button) => (
<button id={button.id} className={`btn-frame modal ${buttonAct === button.id ? 'act' : ''}`} onClick={() => setButtonAct(button.id)}> <button key={button.id} className={`btn-frame modal ${type === button.type ? 'act' : ''}`} onClick={() => setType(button.type)}>
{button.name} {button.name}
</button> </button>
))} ))}
@ -33,17 +44,23 @@ export default function RoofShapePassivitySetting({ setShowRoofShapePassivitySet
<div className="modal-bottom-border-bx"> <div className="modal-bottom-border-bx">
<div className="setting-tit">{getMessage('setting')}</div> <div className="setting-tit">{getMessage('setting')}</div>
<div className="discrimination-box"> <div className="discrimination-box">
{buttonAct === 1 && <Eaves />} {type === TYPES.EAVES && <Eaves {...eavesProps} />}
{buttonAct === 2 && <Gable />} {type === TYPES.GABLE && <Gable {...gableProps} />}
{buttonAct === 3 && <Shed />} {type === TYPES.SHED && <Shed {...shedProps} />}
</div> </div>
<div className="grid-btn-wrap"> <div className="grid-btn-wrap">
<button className="btn-frame sub-tab mr5">{getMessage('common.setting.rollback')}</button> <button className="btn-frame sub-tab mr5" onClick={handleRollback}>
<button className="btn-frame sub-tab act">{getMessage('apply')}</button> {getMessage('common.setting.rollback')}
</button>
<button className="btn-frame sub-tab act" onClick={handleConfirm}>
{getMessage('apply')}
</button>
</div> </div>
</div> </div>
<div className="grid-btn-wrap"> <div className="grid-btn-wrap">
<button className="btn-frame modal act">{getMessage('common.setting.finish')}</button> <button className="btn-frame modal act" onClick={() => handleSave(setShowRoofShapePassivitySettingModal)}>
{getMessage('common.setting.finish')}
</button>
</div> </div>
</div> </div>
</div> </div>

View File

@ -37,7 +37,7 @@ export default function RoofShapeSetting({ setShowRoofShapeSettingModal }) {
buttonMenu, buttonMenu,
handleConfirm, handleConfirm,
handleRollBack, handleRollBack,
} = useRoofShapeSetting() } = useRoofShapeSetting(setShowRoofShapeSettingModal)
const ridgeProps = { pitch, setPitch, eavesOffset, setEavesOffset } const ridgeProps = { pitch, setPitch, eavesOffset, setEavesOffset }
const patternProps = { pitch, setPitch, eavesOffset, setEavesOffset, gableOffset, setGableOffset } const patternProps = { pitch, setPitch, eavesOffset, setEavesOffset, gableOffset, setGableOffset }

View File

@ -1,6 +1,6 @@
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
export default function Eaves() { export default function Eaves({ offsetRef, pitchRef }) {
const { getMessage } = useMessage() const { getMessage } = useMessage()
return ( return (
<> <>
@ -9,7 +9,7 @@ export default function Eaves() {
{getMessage('slope')} {getMessage('slope')}
</span> </span>
<div className="input-grid mr5"> <div className="input-grid mr5">
<input type="text" className="input-origin block" defaultValue={100} /> <input type="text" className="input-origin block" defaultValue={4} ref={pitchRef} />
</div> </div>
<span className="thin"></span> <span className="thin"></span>
</div> </div>
@ -18,7 +18,7 @@ export default function Eaves() {
{getMessage('eaves.offset')} {getMessage('eaves.offset')}
</span> </span>
<div className="input-grid mr5"> <div className="input-grid mr5">
<input type="text" className="input-origin block" defaultValue={100} /> <input type="text" className="input-origin block" defaultValue={500} ref={offsetRef} />
</div> </div>
<span className="thin">mm</span> <span className="thin">mm</span>
</div> </div>

View File

@ -1,6 +1,6 @@
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
export default function Gable() { export default function Gable({ offsetRef, pitchRef }) {
const { getMessage } = useMessage() const { getMessage } = useMessage()
return ( return (
<> <>
@ -9,7 +9,7 @@ export default function Gable() {
{getMessage('slope')} {getMessage('slope')}
</span> </span>
<div className="input-grid mr5"> <div className="input-grid mr5">
<input type="text" className="input-origin block" defaultValue={100} /> <input type="text" className="input-origin block" defaultValue={4} ref={pitchRef} />
</div> </div>
<span className="thin"></span> <span className="thin"></span>
</div> </div>
@ -18,7 +18,7 @@ export default function Gable() {
{getMessage('gable.offset')} {getMessage('gable.offset')}
</span> </span>
<div className="input-grid mr5"> <div className="input-grid mr5">
<input type="text" className="input-origin block" defaultValue={100} /> <input type="text" className="input-origin block" defaultValue={300} ref={offsetRef} />
</div> </div>
<span className="thin">mm</span> <span className="thin">mm</span>
</div> </div>

View File

@ -1,6 +1,6 @@
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
export default function Shed() { export default function Shed({ offsetRef }) {
const { getMessage } = useMessage() const { getMessage } = useMessage()
return ( return (
<> <>
@ -9,7 +9,7 @@ export default function Shed() {
{getMessage('shed.width')} {getMessage('shed.width')}
</span> </span>
<div className="input-grid mr5"> <div className="input-grid mr5">
<input type="text" className="input-origin block" defaultValue={100} /> <input type="text" className="input-origin block" defaultValue={300} ref={offsetRef} />
</div> </div>
<span className="thin">mm</span> <span className="thin">mm</span>
</div> </div>

View File

@ -7,9 +7,20 @@ import { useWallLineOffsetSetting } from '@/hooks/roofcover/useWallLineOffsetSet
export default function WallLineOffsetSetting({ setShowWallLineOffsetSettingModal }) { export default function WallLineOffsetSetting({ setShowWallLineOffsetSettingModal }) {
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { type, setType, buttonMenu, currentWallLineRef, TYPES, radioTypeRef, arrow1Ref, arrow2Ref, length1Ref, length2Ref, handleSave } = const {
useWallLineOffsetSetting() type,
const [buttonAct, setButtonAct] = useState(1) setType,
buttonMenu,
currentWallLineRef,
TYPES,
radioTypeRef,
arrow1Ref,
arrow2Ref,
length1Ref,
length2Ref,
handleSave,
wallLineEditRef,
} = useWallLineOffsetSetting(setShowWallLineOffsetSettingModal)
const wallLineProps = { const wallLineProps = {
length1Ref, length1Ref,
@ -45,7 +56,7 @@ export default function WallLineOffsetSetting({ setShowWallLineOffsetSettingModa
</div> </div>
<div className="properties-setting-wrap outer"> <div className="properties-setting-wrap outer">
<div className="setting-tit">{getMessage('setting')}</div> <div className="setting-tit">{getMessage('setting')}</div>
{type === TYPES.WALL_LINE_EDIT && <WallLine {...wallLineProps} />} {type === TYPES.WALL_LINE_EDIT && <WallLine ref={wallLineEditRef} {...wallLineProps} />}
{type === TYPES.OFFSET && <Offset {...offsetProps} />} {type === TYPES.OFFSET && <Offset {...offsetProps} />}
</div> </div>
<div className="grid-btn-wrap"> <div className="grid-btn-wrap">

View File

@ -1,8 +1,8 @@
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { useEffect, useState } from 'react' import { forwardRef, useEffect, useImperativeHandle, useState } from 'react'
import { useEvent } from '@/hooks/useEvent' import { useEvent } from '@/hooks/useEvent'
export default function WallLine({ length1Ref, length2Ref, arrow1Ref, arrow2Ref, radioTypeRef, currentWallLineRef }) { export default forwardRef(function WallLine({ length1Ref, length2Ref, arrow1Ref, arrow2Ref, radioTypeRef, currentWallLineRef }, ref) {
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { addDocumentEventListener, initEvent } = useEvent() const { addDocumentEventListener, initEvent } = useEvent()
const [type, setType] = useState(1) const [type, setType] = useState(1)
@ -10,76 +10,25 @@ export default function WallLine({ length1Ref, length2Ref, arrow1Ref, arrow2Ref,
const [arrow2, setArrow2] = useState('up') const [arrow2, setArrow2] = useState('up')
useEffect(() => { useEffect(() => {
addDocumentEventListener('keydown', document, keyDown)
return () => { return () => {
initEvent() initEvent()
} }
}, []) }, [])
useImperativeHandle(ref, () => ({
setArrow,
}))
const setArrow = () => {
setArrow1(arrow1Ref.current)
setArrow2(arrow2Ref.current)
}
const onChange = (e) => { const onChange = (e) => {
setType(Number(e.target.value)) setType(Number(e.target.value))
radioTypeRef.current = e.target.value radioTypeRef.current = e.target.value
} }
const handleBtnClick = (direction) => {
document.dispatchEvent(new KeyboardEvent('keydown', { key: direction }))
}
const keyDown = (e) => {
if (currentWallLineRef.current === null) {
alert('보조선을 먼저 선택하세요')
return
}
const key = e.key
switch (key) {
case 'Down': // IE/Edge
case 'ArrowDown': {
if (radioTypeRef.current === '1') {
setArrow1('down')
arrow1Ref.current = 'down'
} else {
setArrow2('down')
arrow2Ref.current = 'down'
}
break
}
case 'Up': // IE/Edge
case 'ArrowUp':
if (radioTypeRef.current === '1') {
setArrow1('up')
arrow1Ref.current = 'up'
} else {
setArrow2('up')
arrow2Ref.current = 'up'
}
break
case 'Left': // IE/Edge
case 'ArrowLeft':
if (radioTypeRef.current === '1') {
setArrow1('left')
arrow1Ref.current = 'left'
} else {
setArrow2('left')
arrow2Ref.current = 'left'
}
break
case 'Right': // IE/Edge
case 'ArrowRight':
if (radioTypeRef.current === '1') {
setArrow1('right')
arrow1Ref.current = 'right'
} else {
setArrow2('right')
arrow2Ref.current = 'right'
}
break
}
}
return ( return (
<> <>
<div className="outline-wrap"> <div className="outline-wrap">
@ -107,10 +56,10 @@ export default function WallLine({ length1Ref, length2Ref, arrow1Ref, arrow2Ref,
<div className="eaves-keraba-td"> <div className="eaves-keraba-td">
<div className="outline-form"> <div className="outline-form">
<div className="grid-direction"> <div className="grid-direction">
<button className={`direction up ${arrow1 === 'up' ? 'act' : ''} `} onClick={() => handleBtnClick('ArrowUp')}></button> <button className={`direction up ${arrow1 === 'up' ? 'act' : ''} `}></button>
<button className={`direction down ${arrow1 === 'down' ? 'act' : ''} `} onClick={() => handleBtnClick('ArrowDown')}></button> <button className={`direction down ${arrow1 === 'down' ? 'act' : ''} `}></button>
<button className={`direction left ${arrow1 === 'left' ? 'act' : ''} `} onClick={() => handleBtnClick('ArrowLeft')}></button> <button className={`direction left ${arrow1 === 'left' ? 'act' : ''} `}></button>
<button className={`direction right ${arrow1 === 'right' ? 'act' : ''} `} onClick={() => handleBtnClick('ArrowRight')}></button> <button className={`direction right ${arrow1 === 'right' ? 'act' : ''} `}></button>
</div> </div>
</div> </div>
</div> </div>
@ -141,10 +90,10 @@ export default function WallLine({ length1Ref, length2Ref, arrow1Ref, arrow2Ref,
<div className="eaves-keraba-td"> <div className="eaves-keraba-td">
<div className="outline-form"> <div className="outline-form">
<div className="grid-direction"> <div className="grid-direction">
<button className={`direction up ${arrow2 === 'up' ? 'act' : ''} `} onClick={() => handleBtnClick('ArrowUp')}></button> <button className={`direction up ${arrow2 === 'up' ? 'act' : ''} `}></button>
<button className={`direction down ${arrow2 === 'down' ? 'act' : ''} `} onClick={() => handleBtnClick('ArrowDown')}></button> <button className={`direction down ${arrow2 === 'down' ? 'act' : ''} `}></button>
<button className={`direction left ${arrow2 === 'left' ? 'act' : ''} `} onClick={() => handleBtnClick('ArrowLeft')}></button> <button className={`direction left ${arrow2 === 'left' ? 'act' : ''} `}></button>
<button className={`direction right ${arrow2 === 'right' ? 'act' : ''} `} onClick={() => handleBtnClick('ArrowRight')}></button> <button className={`direction right ${arrow2 === 'right' ? 'act' : ''} `}></button>
</div> </div>
</div> </div>
</div> </div>
@ -155,4 +104,4 @@ export default function WallLine({ length1Ref, length2Ref, arrow1Ref, arrow2Ref,
</div> </div>
</> </>
) )
} })

View File

@ -12,6 +12,10 @@ import { logout } from '@/lib/authActions'
import QSelectBox from '@/components/common/select/QSelectBox' import QSelectBox from '@/components/common/select/QSelectBox'
import UserInfoModal from '@/components/myInfo/UserInfoModal'
import { useAxios } from '@/hooks/useAxios'
import { globalLocaleStore } from '@/store/localeAtom'
export const ToggleonMouse = (e, act, target) => { export const ToggleonMouse = (e, act, target) => {
const listWrap = e.target.closest(target) const listWrap = e.target.closest(target)
const ListItem = Array.from(listWrap.childNodes) const ListItem = Array.from(listWrap.childNodes)
@ -28,6 +32,8 @@ export const ToggleonMouse = (e, act, target) => {
} }
export default function Header(props) { export default function Header(props) {
const [userInfoModal, setUserInfoModal] = useState(false)
const { userSession } = props const { userSession } = props
const [sessionState, setSessionState] = useRecoilState(sessionStore) const [sessionState, setSessionState] = useRecoilState(sessionStore)
const { getMessage } = useMessage() const { getMessage } = useMessage()
@ -40,12 +46,42 @@ export default function Header(props) {
const dimmedState = useRecoilValue(dimmedStore) const dimmedState = useRecoilValue(dimmedStore)
const isDimmed = dimmedState ? 'opacity-50 bg-black' : '' const isDimmed = dimmedState ? 'opacity-50 bg-black' : ''
const SelectOptions = [ // Link
{ id: 0, name: 'オンライン保証シ', link: '' }, const [globalLocaleState, setGlbalLocaleState] = useRecoilState(globalLocaleStore)
{ id: 1, name: 'ステム', link: '' }, const { promisePost } = useAxios(globalLocaleState)
{ id: 2, name: 'TEST1', link: 'https://www.weather.go.kr/w/index.do' },
{ id: 3, name: 'TEST2', link: 'https://www.google.com' }, const qOrderUrl = process.env.NEXT_PUBLIC_Q_ORDER_AUTO_LOGIN_URL
] const qMusubiUrl = process.env.NEXT_PUBLIC_Q_MUSUBI_AUTO_LOGIN_URL
const [SelectOptions, setSelectOptions] = useState(
userSession.groupId === '60000' ? [{ id: 0, name: 'Q.ORDER', link: `${qOrderUrl}` }] : [{ id: 1, name: 'Q.Musubi', link: `${qMusubiUrl}` }],
)
const getAutoLoginParam = async () => {
await promisePost({ url: '/api/login/v1.0/user/login/autoLoginEncryptData', data: { loginId: userSession.userId } })
.then((res) => {
if (res) {
setSelectOptions(
userSession.groupId === '60000'
? [{ id: 0, name: 'Q.ORDER', link: `${qOrderUrl}?autoLoginParam1=${encodeURIComponent(res.data)}` }]
: [{ id: 1, name: 'Q.Musubi', link: `${qMusubiUrl}?autoLoginParam1=${encodeURIComponent(res.data)}` }],
)
setSelected(
userSession.groupId === '60000'
? { id: 0, name: 'Q.ORDER', link: `${qOrderUrl}?autoLoginParam1=${encodeURIComponent(res.data)}` }
: { id: 1, name: 'Q.Musubi', link: `${qMusubiUrl}?autoLoginParam1=${encodeURIComponent(res.data)}` },
)
}
})
.catch((error) => {
alert(error.response.data.message)
})
}
useEffect(() => {
getAutoLoginParam()
}, [userSession])
const menus = [ const menus = [
{ id: 0, name: 'header.menus.home', url: '/', children: [] }, { id: 0, name: 'header.menus.home', url: '/', children: [] },
{ {
@ -96,7 +132,7 @@ 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} scroll={false}>
{getMessage(menu.name)} {getMessage(menu.name)}
</Link> </Link>
) : ( ) : (
@ -111,7 +147,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 href={m.url} scroll={false}>
{getMessage(m.name)}
</Link>
</li> </li>
) )
})} })}
@ -137,9 +175,15 @@ export default function Header(props) {
</div> </div>
<div className="header-left"> <div className="header-left">
<div className="profile-box"> <div className="profile-box">
<Link href="/roof2"> <Link
href="#"
onClick={() => {
setUserInfoModal(true)
}}
>
<button className="profile">{userSession.userNm}</button> <button className="profile">{userSession.userNm}</button>
</Link> </Link>
{userInfoModal && <UserInfoModal userId={sessionState.userId} userInfoModal={userInfoModal} setUserInfoModal={setUserInfoModal} />}
</div> </div>
<div className="sign-out-box"> <div className="sign-out-box">
<button className="sign-out" onClick={() => logout()}> <button className="sign-out" onClick={() => logout()}>
@ -147,7 +191,7 @@ export default function Header(props) {
</button> </button>
</div> </div>
<div className="select-box"> <div className="select-box">
<QSelectBox title={'Q.ORDER'} options={SelectOptions} onChange={onChangeSelect} /> <QSelectBox options={SelectOptions} onChange={onChangeSelect} />
</div> </div>
<div className="btn-wrap"> <div className="btn-wrap">
<button className="btn-frame small dark" onClick={() => navPage()}> <button className="btn-frame small dark" onClick={() => navPage()}>

View File

@ -1,10 +1,11 @@
import React from 'react' import React, { useState } from 'react'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { useForm } from 'react-hook-form' import { useForm } from 'react-hook-form'
import { sessionStore } from '@/store/commonAtom' import { sessionStore } from '@/store/commonAtom'
import { useRecoilValue, useRecoilState } from 'recoil' 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'
export default function ChangePasswordPop() { export default function ChangePasswordPop() {
const globalLocaleState = useRecoilValue(globalLocaleStore) const globalLocaleState = useRecoilValue(globalLocaleStore)
@ -12,7 +13,7 @@ export default function ChangePasswordPop() {
const { patch } = useAxios(globalLocaleState) const { patch } = useAxios(globalLocaleState)
const { getMessage } = useMessage() const { getMessage } = useMessage()
const [sessionState, setSessionState] = useRecoilState(sessionStore) const [sessionState, setSessionState] = useRecoilState(sessionStore)
const router = useRouter()
const formInitValue = { const formInitValue = {
password1: '', password1: '',
password2: '', password2: '',
@ -74,6 +75,8 @@ export default function ChangePasswordPop() {
if (res.result.resultCode === 'S') { if (res.result.resultCode === 'S') {
alert(getMessage('main.popup.login.success')) alert(getMessage('main.popup.login.success'))
setSessionState({ ...sessionState, pwdInitYn: 'Y' }) setSessionState({ ...sessionState, pwdInitYn: 'Y' })
//
router.push('/')
} else { } else {
alert(res.result.resultMsg) alert(res.result.resultMsg)
} }
@ -154,7 +157,6 @@ export default function ChangePasswordPop() {
className="btn-origin grey" className="btn-origin grey"
onClick={() => { onClick={() => {
router.push('/login') router.push('/login')
setOpen(false)
}} }}
> >
{getMessage('main.popup.login.btn2')} {getMessage('main.popup.login.btn2')}

View File

@ -15,6 +15,7 @@ import { convertNumberToPriceDecimal } from '@/util/common-utils'
import { appMessageStore, globalLocaleStore } from '@/store/localeAtom' import { appMessageStore, globalLocaleStore } from '@/store/localeAtom'
import KO from '@/locales/ko.json' import KO from '@/locales/ko.json'
import JA from '@/locales/ja.json' import JA from '@/locales/ja.json'
import QPagination from '../common/pagination/QPagination'
import '@/styles/grid.scss' import '@/styles/grid.scss'
import { sessionStore } from '@/store/commonAtom' import { sessionStore } from '@/store/commonAtom'
@ -24,9 +25,9 @@ export default function Stuff() {
const stuffSearchParams = useRecoilValue(stuffSearchState) const stuffSearchParams = useRecoilValue(stuffSearchState)
const [stuffSearch, setStuffSearch] = useRecoilState(stuffSearchState) const [stuffSearch, setStuffSearch] = useRecoilState(stuffSearchState)
const { getMessage } = useMessage() const { getMessage } = useMessage()
const [curPage, setCurPage] = useState(1) // const [pageNo, setPageNo] = useState(1) //
const [defaultSize, setDefaultSize] = useState(100) // const [pageSize, setPageSize] = useState(100) //
const [gridCount, setGridCount] = useState(0) // const [totalCount, setTotalCount] = useState(0) //
const [defaultSortType, setDefaultSortType] = useState('R') const [defaultSortType, setDefaultSortType] = useState('R')
const globalLocaleState = useRecoilValue(globalLocaleStore) const globalLocaleState = useRecoilValue(globalLocaleStore)
@ -53,10 +54,8 @@ export default function Stuff() {
const onDoubleClick = (e) => { const onDoubleClick = (e) => {
let objectNo = e.target.innerText let objectNo = e.target.innerText
if (objectNo.substring(0, 1) === 'R') { if (objectNo.substring(0, 1) === 'R') {
console.log('진짜')
router.push(`${pathname}/detail?objectNo=${objectNo.toString()}`) router.push(`${pathname}/detail?objectNo=${objectNo.toString()}`)
} else { } else {
console.log('임시')
router.push(`${pathname}/tempdetail?objectNo=${objectNo.toString()}`) router.push(`${pathname}/tempdetail?objectNo=${objectNo.toString()}`)
} }
} }
@ -64,28 +63,16 @@ export default function Stuff() {
const [gridProps, setGridProps] = useState({ const [gridProps, setGridProps] = useState({
gridData: [], gridData: [],
isPageable: false, isPageable: false,
// sets 10 rows per page (default is 100)
// paginationPageSize: 100,
// allows the user to select the page size from a predefined list of page sizes
// paginationPageSizeSelector: [100, 200, 300, 400],
gridColumns: [ gridColumns: [
{ {
field: 'lastEditDatetime', field: 'lastEditDatetime',
minWidth: 200,
headerName: getMessage('stuff.gridHeader.lastEditDatetime'), headerName: getMessage('stuff.gridHeader.lastEditDatetime'),
headerCheckboxSelection: true, headerCheckboxSelection: true,
headerCheckboxSelectionCurrentPageOnly: true, // headerCheckboxSelectionCurrentPageOnly: true, //
checkboxSelection: true, checkboxSelection: true,
showDisabledCheckboxes: true, showDisabledCheckboxes: true,
// .centered {
// .ag-header-cell-label {
// justify-content: center !important;
// }
// }
cellStyle: { textAlign: 'center' }, cellStyle: { textAlign: 'center' },
//suppressMovable: true, //
// width : 100
// minWidth : 100
// maxWidth : 100
valueFormatter: function (params) { valueFormatter: function (params) {
if (params.value) { if (params.value) {
return dayjs(params?.value).format('YYYY.MM.DD HH:mm:ss') return dayjs(params?.value).format('YYYY.MM.DD HH:mm:ss')
@ -96,8 +83,8 @@ export default function Stuff() {
}, },
{ {
field: 'objectNo', field: 'objectNo',
minWidth: 230,
headerName: getMessage('stuff.gridHeader.objectNo'), headerName: getMessage('stuff.gridHeader.objectNo'),
// headerClass: 'centered', //_test.scss
cellRenderer: function (params) { cellRenderer: function (params) {
if (params.data.objectNo) { if (params.data.objectNo) {
return ( return (
@ -131,7 +118,7 @@ export default function Stuff() {
headerName: getMessage('stuff.gridHeader.saleStoreId'), headerName: getMessage('stuff.gridHeader.saleStoreId'),
cellStyle: { textAlign: 'left' }, cellStyle: { textAlign: 'left' },
}, },
{ field: 'saleStoreName', headerName: getMessage('stuff.gridHeader.saleStoreName'), cellStyle: { textAlign: 'left' } }, { field: 'saleStoreName', minWidth: 300, headerName: getMessage('stuff.gridHeader.saleStoreName'), cellStyle: { textAlign: 'left' } },
{ field: 'address', headerName: getMessage('stuff.gridHeader.address'), cellStyle: { textAlign: 'left' } }, { field: 'address', headerName: getMessage('stuff.gridHeader.address'), cellStyle: { textAlign: 'left' } },
{ field: 'dispCompanyName', headerName: getMessage('stuff.gridHeader.dispCompanyName'), cellStyle: { textAlign: 'left' } }, { field: 'dispCompanyName', headerName: getMessage('stuff.gridHeader.dispCompanyName'), cellStyle: { textAlign: 'left' } },
{ field: 'receiveUser', headerName: getMessage('stuff.gridHeader.receiveUser'), cellStyle: { textAlign: 'left' } }, { field: 'receiveUser', headerName: getMessage('stuff.gridHeader.receiveUser'), cellStyle: { textAlign: 'left' } },
@ -168,14 +155,11 @@ export default function Stuff() {
if (event.column.colId === 'objectNo') { if (event.column.colId === 'objectNo') {
return return
} else { } else {
console.log(' 상세이동::::::::', event.data)
//T R //T R
if (event.data.objectNo) { if (event.data.objectNo) {
if (event.data.objectNo.substring(0, 1) === 'R') { if (event.data.objectNo.substring(0, 1) === 'R') {
console.log('진짜:::::::::')
router.push(`${pathname}/detail?objectNo=${event.data.objectNo.toString()}`) router.push(`${pathname}/detail?objectNo=${event.data.objectNo.toString()}`)
} else { } else {
console.log('임시:::::::::::::::::')
router.push(`${pathname}/tempdetail?objectNo=${event.data.objectNo.toString()}`) router.push(`${pathname}/tempdetail?objectNo=${event.data.objectNo.toString()}`)
} }
} }
@ -242,56 +226,42 @@ export default function Stuff() {
// //
useEffect(() => { useEffect(() => {
if (isObjectNotEmpty(sessionState)) { if (isObjectNotEmpty(sessionState)) {
// sessionState
if (stuffSearchParams?.code === 'S') { if (stuffSearchParams?.code === 'S') {
const params = { const params = {
schObjectNo: '', schObjectNo: stuffSearchParams?.schObjectNo,
schAddress: '', schAddress: stuffSearchParams?.schAddress,
schObjectName: '', schObjectName: stuffSearchParams?.schObjectName,
schSaleStoreName: '', schSaleStoreName: stuffSearchParams?.schSaleStoreName,
schReceiveUser: '', schReceiveUser: stuffSearchParams?.schReceiveUser,
schDispCompanyName: '', schDispCompanyName: stuffSearchParams?.schDispCompanyName,
schDateType: 'U', schDateType: stuffSearchParams.schDateType,
schFromDt: dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD'), schFromDt: dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD'),
schToDt: dayjs(new Date()).format('YYYY-MM-DD'), schToDt: dayjs(new Date()).format('YYYY-MM-DD'),
startRow: (curPage - 1) * defaultSize + 1, startRow: (pageNo - 1) * pageSize + 1,
endRow: curPage * defaultSize, endRow: pageNo * pageSize,
schSelSaleStoreId: '', schSelSaleStoreId: '',
schSortType: 'R', schSortType: stuffSearchParams.schSortType,
} }
async function fetchData() { async function fetchData() {
//api startRow, endRow // const apiUrl = `/api/object/list?saleStoreId=${sessionState?.storeId}&${queryStringFormatter(params)}`
// let startRow const apiUrl = `/api/object/list?saleStoreId=T01&${queryStringFormatter(params)}`
// let endRow
// startRow = (curPage - 1) * size + 1
// endRow = curPage * size
// console.log('startrow::', startRow)
// console.log('endRow::', endRow)
// let curPage
// let totalpage
// let totalCount
// let size
// let pageCount
// console.log(' ::::::::::', sessionState)
// const apiUrl = `/api/object/list?saleStoreId=201TES01&${queryStringFormatter(params)}`
// const apiUrl = `/api/object/list?saleStoreId=X167&${queryStringFormatter(params)}`
const apiUrl = `/api/object/list?saleStoreId=${sessionState?.storeId}&${queryStringFormatter(params)}`
await get({ await get({
url: apiUrl, url: apiUrl,
}).then((res) => { }).then((res) => {
if (!isEmptyArray(res)) { if (!isEmptyArray(res)) {
setGridProps({ ...gridProps, gridData: res, count: res[0].totCnt }) setGridProps({ ...gridProps, gridData: res, count: res[0].totCnt })
setGridCount(res[0].totCnt) setTotalCount(res[0].totCnt)
} }
}) })
} }
fetchData() fetchData()
} else { } else {
//
const params = { const params = {
schObjectNo: '', schObjectNo: stuffSearchParams.schObjectNo,
schAddress: '', schAddress: '',
schObjectName: '', schObjectName: '',
schSaleStoreName: '', schSaleStoreName: '',
@ -300,30 +270,13 @@ export default function Stuff() {
schDateType: 'U', schDateType: 'U',
schFromDt: dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD'), schFromDt: dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD'),
schToDt: dayjs(new Date()).format('YYYY-MM-DD'), schToDt: dayjs(new Date()).format('YYYY-MM-DD'),
startRow: (curPage - 1) * defaultSize + 1, startRow: (pageNo - 1) * pageSize + 1,
endRow: curPage * defaultSize, endRow: pageNo * pageSize,
schSelSaleStoreId: '', schSelSaleStoreId: '',
schSortType: 'R', schSortType: 'R',
} }
async function fetchData() { async function fetchData() {
//api startRow, endRow
// let startRow
// let endRow
// startRow = (curPage - 1) * size + 1
// endRow = curPage * size
// console.log('startrow::', startRow)
// console.log('endRow::', endRow)
// let curPage
// let totalpage
// let totalCount
// let size
// let pageCount
// console.log(' ::::::::::', sessionState)
// const apiUrl = `/api/object/list?saleStoreId=201TES01&${queryStringFormatter(params)}`
// const apiUrl = `/api/object/list?saleStoreId=X167&${queryStringFormatter(params)}`
const apiUrl = `/api/object/list?saleStoreId=${sessionState?.storeId}&${queryStringFormatter(params)}` const apiUrl = `/api/object/list?saleStoreId=${sessionState?.storeId}&${queryStringFormatter(params)}`
await get({ await get({
@ -331,34 +284,34 @@ export default function Stuff() {
}).then((res) => { }).then((res) => {
if (!isEmptyArray(res)) { if (!isEmptyArray(res)) {
setGridProps({ ...gridProps, gridData: res, count: res[0].totCnt }) setGridProps({ ...gridProps, gridData: res, count: res[0].totCnt })
setGridCount(res[0].totCnt) setTotalCount(res[0].totCnt)
} }
}) })
} }
fetchData() fetchData()
} }
} }
}, [sessionState]) }, [pageNo, sessionState])
useEffect(() => { useEffect(() => {
if (stuffSearchParams?.code === 'E') { if (stuffSearchParams?.code === 'E') {
//console.log('::::::::', stuffSearchParams) //console.log('::::::::', stuffSearchParams, sessionState)
stuffSearchParams.startRow = (curPage - 1) * defaultSize + 1 stuffSearchParams.startRow = 1
stuffSearchParams.endRow = curPage * defaultSize stuffSearchParams.endRow = 1 * pageSize
stuffSearchParams.schSortType = defaultSortType stuffSearchParams.schSortType = defaultSortType
setPageNo(1)
async function fetchData() { async function fetchData() {
// console.log(' :::::::::::::', sessionState) const apiUrl = `/api/object/list?saleStoreId=T01&${queryStringFormatter(stuffSearchParams)}`
// const apiUrl = `/api/object/list?saleStoreId=201TES01&${queryStringFormatter(stuffSearchParams)}` // const apiUrl = `/api/object/list?saleStoreId=${sessionState?.storeId}&${queryStringFormatter(stuffSearchParams)}`
// const apiUrl = `/api/object/list?saleStoreId=X167&${queryStringFormatter(stuffSearchParams)}`
const apiUrl = `/api/object/list?saleStoreId=${sessionState?.storeId}&${queryStringFormatter(stuffSearchParams)}`
await get({ url: apiUrl }).then((res) => { await get({ url: apiUrl }).then((res) => {
// console.log(' API:::::::', res)
if (!isEmptyArray(res)) { if (!isEmptyArray(res)) {
setGridProps({ ...gridProps, gridData: res, count: res[0].totCnt }) setGridProps({ ...gridProps, gridData: res, count: res[0].totCnt })
setGridCount(res[0].totCnt) setTotalCount(res[0].totCnt)
} else { } else {
setGridProps({ ...gridProps, gridData: [], count: 0 }) setGridProps({ ...gridProps, gridData: [], count: 0 })
setGridCount(0) setTotalCount(0)
} }
}) })
} }
@ -368,53 +321,58 @@ export default function Stuff() {
// //
const onChangePerPage = (e) => { const onChangePerPage = (e) => {
let startRow = (curPage - 1) * e.target.value + 1 let startRow = (1 - 1) * e.target.value + 1
stuffSearchParams.startRow = startRow stuffSearchParams.startRow = startRow
stuffSearchParams.endRow = curPage * e.target.value stuffSearchParams.endRow = 1 * e.target.value
setDefaultSize(e.target.value) setPageSize(e.target.value)
setStuffSearch({ setStuffSearch({
...stuffSearch, ...stuffSearch,
code: 'S', code: 'S',
startRow: startRow, startRow: startRow,
endRow: curPage * e.target.value, endRow: 1 * e.target.value,
}) })
// console.log(' :::', stuffSearchParams)
// console.log(' sessionState:::', sessionState) setPageNo(1)
// const apiUrl = `/api/object/list?saleStoreId=201TES01&${queryStringFormatter(stuffSearchParams)}` // const apiUrl = `/api/object/list?saleStoreId=${sessionState?.storeId}&${queryStringFormatter(stuffSearchParams)}`
// const apiUrl = `/api/object/list?saleStoreId=X167&${queryStringFormatter(stuffSearchParams)}` const apiUrl = `/api/object/list?saleStoreId=T01&${queryStringFormatter(stuffSearchParams)}`
const apiUrl = `/api/object/list?saleStoreId=${sessionState?.storeId}&${queryStringFormatter(stuffSearchParams)}`
get({ url: apiUrl }).then((res) => { get({ url: apiUrl }).then((res) => {
if (!isEmptyArray(res)) { if (!isEmptyArray(res)) {
setGridProps({ ...gridProps, gridData: res, count: res[0].totCnt }) setGridProps({ ...gridProps, gridData: res, count: res[0].totCnt })
setGridCount(res[0].totCnt) setTotalCount(res[0].totCnt)
} else { } else {
setGridProps({ ...gridProps, gridData: [], count: 0 }) setGridProps({ ...gridProps, gridData: [], count: 0 })
setGridCount(0) setTotalCount(0)
} }
}) })
} }
// //
const onChangeSortType = (e) => { const onChangeSortType = (e) => {
let startRow = (1 - 1) * pageSize + 1
stuffSearchParams.startRow = startRow
stuffSearchParams.endRow = 1 * pageSize
stuffSearchParams.schSortType = e.target.value stuffSearchParams.schSortType = e.target.value
// console.log(' :::', stuffSearchParams)
setDefaultSortType(e.target.value) setDefaultSortType(e.target.value)
setStuffSearch({ setStuffSearch({
...stuffSearch, ...stuffSearch,
code: 'S', code: 'S',
startRow: startRow,
endRow: 1 * pageSize,
schSortType: e.target.value, schSortType: e.target.value,
}) })
// console.log(' ::::::::::::', sessionState)
// const apiUrl = `/api/object/list?saleStoreId=201TES01&${queryStringFormatter(stuffSearchParams)}` setPageNo(1)
// const apiUrl = `/api/object/list?saleStoreId=X167&${queryStringFormatter(stuffSearchParams)}`
const apiUrl = `/api/object/list?saleStoreId=${sessionState?.storeId}&${queryStringFormatter(stuffSearchParams)}` const apiUrl = `/api/object/list?saleStoreId=T01&${queryStringFormatter(stuffSearchParams)}`
// const apiUrl = `/api/object/list?saleStoreId=${sessionState?.storeId}&${queryStringFormatter(stuffSearchParams)}`
get({ url: apiUrl }).then((res) => { get({ url: apiUrl }).then((res) => {
if (!isEmptyArray(res)) { if (!isEmptyArray(res)) {
setGridProps({ ...gridProps, gridData: res, count: res[0].totCnt }) setGridProps({ ...gridProps, gridData: res, count: res[0].totCnt })
setGridCount(res[0].totCnt) setTotalCount(res[0].totCnt)
} else { } else {
setGridProps({ ...gridProps, gridData: [], count: 0 }) setGridProps({ ...gridProps, gridData: [], count: 0 })
setGridCount(0) setTotalCount(0)
} }
}) })
} }
@ -427,20 +385,34 @@ export default function Stuff() {
} }
}, [globalLocaleState]) }, [globalLocaleState])
//
const handleChangePage = (page) => {
stuffSearchParams.code = 'S'
setStuffSearch({
...stuffSearch,
code: 'S',
startRow: (page - 1) * pageSize + 1,
endRow: page * pageSize,
})
setPageNo(page)
}
return ( return (
<> <>
{/* 퍼블시작 */} {/* 퍼블시작 */}
<div className="sub-table-box"> <div className="sub-table-box">
<div className="table-box-title-wrap"> <div className="table-box-title-wrap">
<div className="title-wrap"> <div className="title-wrap">
<h3>물건목록</h3> <h3>{getMessage('stuff.search.grid.title')}</h3>
<ul className="info-wrap"> <ul className="info-wrap">
<li> <li>
전체 {getMessage('stuff.search.grid.all')}
<span>{convertNumberToPriceDecimal(gridCount)}</span> <span>{convertNumberToPriceDecimal(totalCount)}</span>
</li> </li>
<li> <li>
선택 {getMessage('stuff.search.grid.selected')}
<span className="red">{convertNumberToPriceDecimal(selectedRowDataCount)}</span> <span className="red">{convertNumberToPriceDecimal(selectedRowDataCount)}</span>
</li> </li>
</ul> </ul>
@ -448,8 +420,8 @@ export default function Stuff() {
<div className="left-unit-box"> <div className="left-unit-box">
<div className="select-box mr5" style={{ width: '110px' }}> <div className="select-box mr5" style={{ width: '110px' }}>
<select className="select-light black" name="" id="" onChange={onChangeSortType}> <select className="select-light black" name="" id="" onChange={onChangeSortType}>
<option value="R">최근 등록일</option> <option value="R">{getMessage('stuff.search.grid.schSortTypeR')}</option>
<option value="U">최근 수정일</option> <option value="U">{getMessage('stuff.search.grid.schSortTypeU')}</option>
</select> </select>
</div> </div>
<div className="select-box" style={{ width: '80px' }}> <div className="select-box" style={{ width: '80px' }}>
@ -464,7 +436,9 @@ export default function Stuff() {
<div className="grid-table-wrap"> <div className="grid-table-wrap">
<div className="q-grid"> <div className="q-grid">
<StuffQGrid {...gridProps} getSelectedRowdata={getSelectedRowdata} getCellDoubleClicked={getCellDoubleClicked} gridRef={gridRef} /> <StuffQGrid {...gridProps} getSelectedRowdata={getSelectedRowdata} getCellDoubleClicked={getCellDoubleClicked} gridRef={gridRef} />
<div className="pagination-wrap">페이징 컴포넌트예정</div> <div className="pagination-wrap">
<QPagination pageNo={pageNo} pageSize={pageSize} pagePerBlock={10} totalCount={totalCount} handleChangePage={handleChangePage} />
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,23 +1,30 @@
'use client' 'use client'
import React, { useState, useEffect } from 'react' import React, { useState, useEffect, useRef } from 'react'
import { useRouter, useSearchParams } from 'next/navigation' import { useRouter, useSearchParams, usePathname } from 'next/navigation'
import { Button } from '@nextui-org/react' import { Button } from '@nextui-org/react'
import Select from 'react-dropdown-select' import Select 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 { queryStringFormatter, isEmptyArray } from '@/util/common-utils' import { isEmptyArray, isNotEmptyArray, isObjectNotEmpty } from '@/util/common-utils'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { useForm } from 'react-hook-form' import { useForm } from 'react-hook-form'
import { useRecoilValue } from 'recoil' import { useRecoilValue } from 'recoil'
import { sessionStore } from '@/store/commonAtom'
import FindAddressPop from './popup/FindAddressPop'
import PlanRequestPop from './popup/PlanRequestPop'
import WindSelectPop from './popup/WindSelectPop'
export default function StuffDetail() { export default function StuffDetail() {
const sessionState = useRecoilValue(sessionStore)
const router = useRouter() const router = useRouter()
const pathname = usePathname()
const searchParams = useSearchParams() const searchParams = useSearchParams()
const { getMessage } = useMessage() const { getMessage } = useMessage()
const globalLocaleState = useRecoilValue(globalLocaleStore) const globalLocaleState = useRecoilValue(globalLocaleStore)
const ref = useRef()
const { get, post, del } = useAxios(globalLocaleState) const { get, post, del, promisePost } = useAxios(globalLocaleState)
//form //form
const formInitValue = { const formInitValue = {
// T...() R...() // T...() R...()
@ -26,16 +33,17 @@ export default function StuffDetail() {
objectName: '', // objectName: '', //
objectNameOmit: '', // objectNameOmit: '', //
objectNameKana: '', // objectNameKana: '', //
saleStoreId: '', //ID saleStoreLevel: '', //1
saleStoreName: '', // saleStoreId: '', //1ID
otherSaleStoreId: '', saleStoreName: '', //1
otherSaleStoreName: '', otherSaleStoreId: '', //1 ID
otherSaleStoreName: '', //1
otherSaleStoreLevel: '', //1
zipNo: '', // zipNo: '', //
prefId: '', // prefId: '', //
prefName: '', prefName: '',
address: '', // address: '', //
areaId: '', //id areaId: '', //id
// areaName: '', //
windSpeed: '', // windSpeed: '', //
verticalSnowCover: '', //NEW verticalSnowCover: '', //NEW
coldRegionFlg: false, //(true : 1 / false : 0) coldRegionFlg: false, //(true : 1 / false : 0)
@ -56,20 +64,25 @@ export default function StuffDetail() {
const [prefValue, setPrefValue] = useState('') const [prefValue, setPrefValue] = useState('')
const [saleStoreList, setSaleStoreList] = useState([]) // const [saleStoreList, setSaleStoreList] = useState([]) //
const [otherSaleStoreList, setOtherSaleStoreList] = useState([]) const [otherSaleStoreList, setOtherSaleStoreList] = useState([])
const [originOtherSaleStoreList, setOriginOtherSaleStoreList] = useState([])
const [areaIdList, setAreaIdList] = useState([]) // const [areaIdList, setAreaIdList] = useState([]) //
const [windSpeedList, setWindSpeedList] = useState([]) // // const [windSpeedList, setWindSpeedList] = useState([]) //
const [isFormValid, setIsFormValid] = useState(false) //, const [isFormValid, setIsFormValid] = useState(false) //,
const [buttonValid, setButtonValid] = useState(false) // const [showAddressButtonValid, setShowAddressButtonValid] = useState(false) //
const [showDesignRequestButtonValid, setShowDesignRequestButtonValid] = useState(false) //
const [showWindSpeedButtonValid, setShowWindSpeedButtonValid] = useState(false) //
const objectNo = searchParams.get('objectNo') //url set const objectNo = searchParams.get('objectNo') //url set
const [editMode, setEditMode] = useState('NEW') const [editMode, setEditMode] = useState('NEW')
const [detailData, setDetailData] = useState({}) const [detailData, setDetailData] = useState({})
useEffect(() => { useEffect(() => {
// console.log('objectNo::', objectNo)
if (objectNo) { if (objectNo) {
//console.log('') console.log('수정화면')
setEditMode('EDIT') setEditMode('EDIT')
if (objectNo.substring(0, 1) === 'R') { if (objectNo.substring(0, 1) === 'R') {
@ -77,33 +90,9 @@ export default function StuffDetail() {
setIsFormValid(true) setIsFormValid(true)
} }
get({ url: `/api/object/${objectNo}/detail` }).then((res) => { get({ url: `/api/object/${objectNo}/detail` }).then((res) => {
// console.log(' API ') console.log('물건번호로 상세 API 호출')
if (res != null) { if (res != null) {
setDetailData(res) setDetailData(res)
// APi
// API
get({ url: '/api/object/prefecture/list' }).then((res) => {
if (!isEmptyArray(res)) {
// console.log('API :::', res)
setPrefCodeList(res)
}
})
// API /api/object/saleStore//list -
// 1 saleStoreId=201TES01
// T01
//1 : X167
get({ url: `/api/object/saleStore/X167/list` }).then((res) => {
if (!isEmptyArray(res)) {
// console.log(' :::::', res)
setSaleStoreList(res)
//1
form.setValue('saleStoreId', res[0].saleStoreId)
//1
form.setValue('saleStoreName', res[0].saleStoreId)
setOtherSaleStoreList([])
}
})
} }
}) })
} else { } else {
@ -115,60 +104,121 @@ export default function StuffDetail() {
setPrefCodeList(res) setPrefCodeList(res)
} }
}) })
// API /api/object/saleStore//list -
// 1 saleStoreId=201TES01 // 1 saleStoreId=201TES01
// T01 // T01
//1 : X167 //1 : X167
get({ url: `/api/object/saleStore/X167/list` }).then((res) => { get({ url: `/api/object/saleStore/T01/list` }).then((res) => {
// get({ url: `/api/object/saleStore/${sessionState?.storeId}/list` }).then((res) => {
if (!isEmptyArray(res)) { if (!isEmptyArray(res)) {
// console.log(' :::::', res)
const firstList = res.filter((row) => row.saleStoreLevel === '1') const firstList = res.filter((row) => row.saleStoreLevel === '1')
const otherList = res.filter((row) => row.saleStoreLevel !== '1') const otherList = res.filter((row) => row.saleStoreLevel !== '1')
// console.log('first:::::', firstList)
// console.log('otherList:::::', otherList)
//1 //1
setSaleStoreList(firstList) setSaleStoreList(firstList)
//1
form.setValue('saleStoreId', firstList[0].saleStoreId)
//1
form.setValue('saleStoreName', firstList[0].saleStoreId)
//1 //1
setOriginOtherSaleStoreList(otherList)
setOtherSaleStoreList(otherList) setOtherSaleStoreList(otherList)
form.setValue('otherSaleStoreId', otherList[0].saleStoreId)
form.setValue('otherSaleStoreName', otherList[0].saleStoreId)
} }
}) })
} }
}, [objectNo]) }, [objectNo])
useEffect(() => {
if (isObjectNotEmpty(detailData)) {
console.log('상세데이타:::::::', detailData)
// API
get({ url: '/api/object/prefecture/list' }).then((res) => {
if (!isEmptyArray(res)) {
// console.log('API :::', res)
setPrefCodeList(res)
}
})
// 1 ??
// 1 saleStoreId=201TES01
// T01
//1 : X167
get({ url: `/api/object/saleStore/T01/list` }).then((res) => {
// get({ url: `/api/object/saleStore/${sessionState?.storeId}/list` }).then((res) => {
if (!isEmptyArray(res)) {
const firstList = res.filter((row) => row.saleStoreLevel === '1')
const otherList = res.filter((row) => row.saleStoreLevel !== '1')
//1
setSaleStoreList(firstList)
//1
setOriginOtherSaleStoreList(otherList)
setOtherSaleStoreList(otherList)
}
})
}
}, [detailData])
//1 //1
const onSelectionChange = (key) => { const onSelectionChange = (key) => {
if (key == null) { if (isObjectNotEmpty(key)) {
setOtherSaleStoreList(otherSaleStoreList)
form.setValue('saleStoreId', key.saleStoreId)
form.setValue('saleStoreName', key.saleStoreName)
form.setValue('saleStoreLevel', key.saleStoreLevel)
// 1 2 list
// 
let newOtherSaleStoreList = originOtherSaleStoreList.filter((row) => row.firstAgentId === key.saleStoreId)
setOtherSaleStoreList(newOtherSaleStoreList)
} else {
//X
form.setValue('saleStoreId', '') form.setValue('saleStoreId', '')
form.setValue('saleStoreName', '') form.setValue('saleStoreName', '')
} else { form.setValue('saleStoreLevel', '')
const name = saleStoreList.find((obj) => obj.saleStoreId === key.target.value).saleStoreName form.setValue('otherSaleStoreId', '')
form.setValue('saleStoreId', key.target.value) form.setValue('otherSaleStoreName', '')
form.setValue('saleStoreName', name) form.setValue('otherSaleStoreLevel', '')
setOtherSaleStoreList(originOtherSaleStoreList)
//1 2
handleClear()
} }
} }
//2 //2
const onSelectionChange2 = (key) => { const onSelectionChange2 = (key) => {
const name = otherSaleStoreList.find((obj) => obj.saleStoreId === key.target.value).saleStoreName if (isObjectNotEmpty(key)) {
form.setValue('otherSaleStoreId', key.target.value) form.setValue('otherSaleStoreId', key.saleStoreId)
form.setValue('otherSaleStoreNm', name) form.setValue('otherSaleStoreName', key.saleStoreName)
} form.setValue('otherSaleStoreLevel', key.saleStoreLevel)
//
const _zipNo = watch('zipNo')
useEffect(() => {
if (_zipNo !== '' && _zipNo.length === 7 && !_zipNo.match(/\D/g)) {
setButtonValid(true)
} else { } else {
setButtonValid(false) form.setValue('otherSaleStoreId', '')
form.setValue('otherSaleStoreName', '')
form.setValue('otherSaleStoreLevel', '')
} }
}, [_zipNo]) }
//1 2
const handleClear = () => {
if (ref.current) {
ref.current.clearValue()
}
}
//
const setZipInfo = (info) => {
// console.log(' ::::::::', info)
setPrefValue(info.prefId)
form.setValue('prefId', info.prefId)
form.setValue('prefName', info.address1)
form.setValue('address', info.address2 + info.address3)
form.setValue('zipNo', info.zipNo)
}
//
const setPlanReqInfo = (info) => {
console.log('팝업에서 넘어온 설계의뢰 정보::: ', info)
// form.setValue('dispCompanyName', info.planReqName)
}
//
const setWindSppedInfo = (info) => {
form.setValue('windSpeed', info.windSpeed)
}
// //
// dispCompanyName: '', // // dispCompanyName: '', //
@ -192,7 +242,7 @@ export default function StuffDetail() {
const _objectName = watch('objectName') const _objectName = watch('objectName')
const _objectNameOmit = watch('objectNameOmit') const _objectNameOmit = watch('objectNameOmit')
const _saleStoreId = watch('saleStoreId') const _saleStoreId = watch('saleStoreId')
const _otherSaleStoreId = watch('otherSaleStoreId') const _saleStoreLevel = watch('saleStoreLevel')
const _prefId = watch('prefId') const _prefId = watch('prefId')
const _address = watch('address') const _address = watch('address')
const _areaId = watch('areaId') //new const _areaId = watch('areaId') //new
@ -201,68 +251,56 @@ export default function StuffDetail() {
const _installHeight = watch('installHeight') const _installHeight = watch('installHeight')
useEffect(() => { useEffect(() => {
// console.log('mode:::::', editMode)
if (editMode === 'NEW') { if (editMode === 'NEW') {
const formData = form.getValues() const formData = form.getValues()
// console.log('::::::::::::', formData) // console.log('::::::::::::', formData)
let errors = {} let errors = {}
if (!_dispCompanyName || _dispCompanyName.trim().length === 0) { if (!formData.dispCompanyName || formData.dispCompanyName.trim().length === 0) {
errors.dispCompanyName = true errors.dispCompanyName = true
} }
if (!_objectName || _objectName.trim().length === 0) { if (!formData.objectName || formData.objectName.trim().length === 0) {
errors.objectName = true errors.objectName = true
} }
if (!_objectNameOmit) { if (!formData.objectNameOmit) {
errors.objectNameOmit = true errors.objectNameOmit = true
} }
if (!_saleStoreId) { if (!formData.saleStoreId) {
errors.saleStoreId = true errors.saleStoreId = true
} }
// if (!_otherSaleStoreId) { if (!formData.prefId) {
// errors.otherSaleStoreId = true
// }
if (!_zipNo || _zipNo.length != 7) {
errors.zipCode = true
}
if (!_prefId) {
errors.prefId = true errors.prefId = true
} }
if (!_address.trim().length === 0) { if (!formData.areaId) {
errors.address = true
}
if (!_areaId) {
errors.areaId = true errors.areaId = true
} }
if (!_windSpeed) { if (!formData.windSpeed) {
errors.windSpeed = true errors.windSpeed = true
} }
if (!_verticalSnowCover) { if (!formData.verticalSnowCover) {
errors.verticalSnowCover = true errors.verticalSnowCover = true
} }
if (!_installHeight) { if (!formData.installHeight) {
errors.installHeight = true errors.installHeight = true
} }
// console.log('errors::', errors) // console.log('::', errors)
setIsFormValid(Object.keys(errors).length === 0) setIsFormValid(Object.keys(errors).length === 0)
} else { } else {
// console.log(' ') console.log('상세일때 폼체크')
} }
}, [ }, [
_dispCompanyName, _dispCompanyName,
_objectName, _objectName,
_objectNameOmit, _objectNameOmit,
_saleStoreId, _saleStoreId,
_saleStoreLevel,
// _otherSaleStoreId, // _otherSaleStoreId,
_zipNo, // _otherSaleStoreLevel,
_prefId, _prefId,
_address, _address,
_areaId, _areaId,
@ -271,52 +309,33 @@ export default function StuffDetail() {
_installHeight, _installHeight,
]) ])
// API //
const onSearchPostNumber = () => { const onSearchPostNumberPopOpen = () => {
const params = { setShowAddressButtonValid(true)
zipcode: _zipNo, }
}
get({ url: `https://zipcloud.ibsnet.co.jp/api/search?${queryStringFormatter(params)}` }).then((res) => { //
//7830060 const onSearchDesignRequestPopOpen = () => {
//9302226 setShowDesignRequestButtonValid(true)
//0790177 3 }
if (res.status === 200) {
if (res.results != null) { //
console.log('주소검색::', res.results) const onSearchWindSpeedPopOpen = () => {
// console.log('prefcode::', res.results[0].prefcode) const prefName = form.watch('prefName')
// console.log('address::', res.results[0].address2 + res.results[0].address3) if (prefName === '') {
setPrefValue(res.results[0].prefcode) alert(getMessage('stuff.windSelectPopup.error.message1'))
form.setValue('prefId', res.results[0].prefcode) } else {
form.setValue('prefName', res.results[0].address1) setShowWindSpeedButtonValid(true)
form.setValue('address', res.results[0].address2 + res.results[0].address3) }
} else {
alert('등록된 우편번호에서 주소를 찾을 수 없습니다. 다시 입력해주세요.')
form.setValue('prefId', '')
form.setValue('prefName', '')
form.setValue('address', '')
form.setValue('zipNo', '')
setPrefValue('')
setAreaIdList([])
form.setValue('areaId', '')
// form.setValue('areaName', '')
setWindSpeedList([])
form.setValue('windSpeed', '')
}
} else {
alert(res.message)
}
})
} }
useEffect(() => { useEffect(() => {
if (prefValue !== '') { if (prefValue !== '') {
// //
// /api/object/prefecture//list
get({ url: `/api/object/prefecture/${prefValue}/list` }).then((res) => { get({ url: `/api/object/prefecture/${prefValue}/list` }).then((res) => {
if (!isEmptyArray(res)) { if (!isEmptyArray(res)) {
// console.log(' ::::::::', res) // console.log(' ::::::::', res)
form.setValue('areaId', res[0].prefId) form.setValue('areaId', res[0].areaId)
form.setValue('areaName', res[0].prefName) form.setValue('areaName', res[0].prefName)
setAreaIdList(res) setAreaIdList(res)
} }
@ -329,20 +348,18 @@ export default function StuffDetail() {
form.setValue('areaId', e.target.value) form.setValue('areaId', e.target.value)
} }
useEffect(() => { // useEffect(() => {
if (!isEmptyArray(areaIdList)) { // if (!isEmptyArray(areaIdList)) {
// -> // let _prefName = form.watch('prefName')
console.log('prefName::', form.watch('prefName')) // // console.log(' API', _prefName)
let _prefName = form.watch('prefName') // get({ url: `/api/object/windSpeed/${_prefName}/list` }).then((res) => {
//http://localhost:8080/api/object/windSpeed//list // // console.log('res::', res)
get({ url: `/api/object/windSpeed/${_prefName}/list` }).then((res) => { // if (!isEmptyArray(res)) {
if (!isEmptyArray(res)) { // setWindSpeedList(res)
// console.log(':::::::::', res) // }
setWindSpeedList(res) // })
} // }
}) // }, [areaIdList])
}
}, [areaIdList])
// //
const onValid = (data) => { const onValid = (data) => {
@ -378,10 +395,11 @@ export default function StuffDetail() {
// //
const onTempSave = async () => { const onTempSave = async () => {
const formData = form.getValues() const formData = form.getValues()
// console.log('formData::', formData)
const params = { const params = {
saleStoreId: formData.saleStoreId, saleStoreId: formData.otherSaleStoreId ? formData.otherSaleStoreId : formData.saleStoreId,
saleStoreName: formData.saleStoreName, saleStoreName: formData.otherSaleStoreName ? formData.otherSaleStoreName : formData.saleStoreName,
saleStoreLevel: formData.otherSaleStoreLevel ? formData.otherSaleStoreLevel : formData.saleStoreLevel,
objectStatusId: formData.objectStatusId, objectStatusId: formData.objectStatusId,
objectName: formData.objectName, objectName: formData.objectName,
objectNameOmit: formData.objectNameOmit, objectNameOmit: formData.objectNameOmit,
@ -403,10 +421,17 @@ export default function StuffDetail() {
workNo: null, workNo: null,
workName: null, workName: null,
} }
console.log('임시저장params::', params) //1 or 2
return if (params.saleStoreId == '') {
await post({ url: '/api/object/save-object', data: params }).then((res) => { params.saleStoreId = sessionState.storeId
console.log('res::::::', res) params.saleStoreLevel = sessionState.storeLvl
}
await promisePost({ url: '/api/object/save-object', data: params }).then((res) => {
if (res.status === 201) {
alert('임시저장 되었습니다. 물건번호를 획득하려면 필수 항목을 모두 입력해 주십시오.')
router.push(`${pathname}?objectNo=${res.data.objectNo.toString()}`)
}
}) })
} }
@ -419,7 +444,7 @@ export default function StuffDetail() {
let testobj = '10' let testobj = '10'
del({ url: `/api/object/${testobj}` }).then((res) => { del({ url: `/api/object/${testobj}` }).then((res) => {
// console.log(' :::', res) console.log('삭제 결과:::', res)
router.push('/management/stuff') router.push('/management/stuff')
}) })
} }
@ -431,7 +456,8 @@ export default function StuffDetail() {
<form onSubmit={handleSubmit(onValid)}> <form onSubmit={handleSubmit(onValid)}>
<div className="sub-table-box"> <div className="sub-table-box">
<div className="promise-gudie"> <div className="promise-gudie">
<span className="important">*</span> 필수 입력항목 <span className="important">*</span>
{getMessage('stuff.detail.required')}
</div> </div>
<div className="infomation-table"> <div className="infomation-table">
<table> <table>
@ -440,9 +466,22 @@ export default function StuffDetail() {
<col /> <col />
</colgroup> </colgroup>
<tbody> <tbody>
<tr>
<th>{getMessage('stuff.detail.planReqNo')}</th>
<td>
<div className="flx-box">
<div className="input-wrap mr5" style={{ width: '200px' }}>
<input type="text" className="input-light" readOnly />
</div>
<button className="btn-origin grey" onClick={onSearchDesignRequestPopOpen}>
{getMessage('stuff.planReqPopup.title')}
</button>
</div>
</td>
</tr>
<tr> <tr>
<th> <th>
담당자 <span className="important">*</span> {getMessage('stuff.detail.dispCompanyName')} <span className="important">*</span>
</th> </th>
<td> <td>
<div className="input-wrap" style={{ width: '500px' }}> <div className="input-wrap" style={{ width: '500px' }}>
@ -452,24 +491,24 @@ export default function StuffDetail() {
</tr> </tr>
<tr> <tr>
<th> <th>
물건구분/물건명 <span className="important">*</span> {getMessage('stuff.detail.objectStatusId')} <span className="important">*</span>
</th> </th>
<td> <td>
<div className="flx-box"> <div className="flx-box">
<div className="d-check-radio light mr10"> <div className="d-check-radio light mr10">
<input type="radio" name="objectStatusId" value="0" id="objectStatus0" {...form.register('objectStatusId')} /> <input type="radio" name="objectStatusId" value="0" id="objectStatus0" {...form.register('objectStatusId')} />
<label htmlFor="objectStatus0">신축</label> <label htmlFor="objectStatus0">{getMessage('stuff.detail.objectStatus0')}</label>
</div> </div>
<div className="d-check-radio light mr10"> <div className="d-check-radio light mr10">
<input type="radio" name="objectStatusId" value="1" id="objectStatus1" {...form.register('objectStatusId')} /> <input type="radio" name="objectStatusId" value="1" id="objectStatus1" {...form.register('objectStatusId')} />
<label htmlFor="objectStatus0">기축</label> <label htmlFor="objectStatus1">{getMessage('stuff.detail.objectStatus1')}</label>
</div> </div>
<div className="input-wrap mr5" style={{ width: '545px' }}> <div className="input-wrap mr5" style={{ width: '545px' }}>
<input type="text" className="input-light" {...form.register('objectName')} /> <input type="text" className="input-light" {...form.register('objectName')} />
</div> </div>
<div className="select-wrap" style={{ width: '120px' }}> <div className="select-wrap" style={{ width: '120px' }}>
<select className="select-light" name="objectNameOmit" {...register('objectNameOmit')}> <select className="select-light" name="objectNameOmit" {...register('objectNameOmit')}>
<option value="">경칭공통코드선택</option> <option value="">경칭선택</option>
<option value="11">경칭11</option> <option value="11">경칭11</option>
<option value="22">경칭22</option> <option value="22">경칭22</option>
<option value="33">경칭33</option> <option value="33">경칭33</option>
@ -479,7 +518,7 @@ export default function StuffDetail() {
</td> </td>
</tr> </tr>
<tr> <tr>
<th>물건명 후리가나</th> <th>{getMessage('stuff.detail.objectNameKana')}</th>
<td> <td>
<div className="input-wrap" style={{ width: '789px' }}> <div className="input-wrap" style={{ width: '789px' }}>
<input type="text" className="input-light" {...form.register('objectNameKana')} /> <input type="text" className="input-light" {...form.register('objectNameKana')} />
@ -490,26 +529,27 @@ export default function StuffDetail() {
<th> <th>
<div className="flx-box"> <div className="flx-box">
<div className="title"> <div className="title">
1 판매점명 / ID {getMessage('stuff.detail.saleStoreId')}
<span className="important">*</span> <span className="important">*</span>
</div> </div>
<div className="tooltips"></div> <div className="tooltips">
<span>{getMessage('stuff.detail.tooltip.saleStoreId')}</span>
</div>
</div> </div>
</th> </th>
<td> <td>
<div className="flx-box"> <div className="flx-box">
<div className="select-wrap mr5" style={{ width: '567px' }}> <div className="select-wrap mr5" style={{ width: '567px' }}>
{saleStoreList?.length > 0 && ( <Select
<select className="select-light" onChange={onSelectionChange} value={form.watch('saleStoreId')}> className="react-select-custom"
{saleStoreList.map((row) => { classNamePrefix="custom"
return ( placeholder="Select"
<option key={row.saleStoreLevel} value={row.saleStoreId}> options={saleStoreList}
{row.saleStoreName} onChange={onSelectionChange}
</option> getOptionLabel={(x) => x.saleStoreName}
) getOptionValue={(x) => x.saleStoreId}
})} isClearable={true}
</select> />
)}
</div> </div>
<div className="input-wrap" style={{ width: '216px' }}> <div className="input-wrap" style={{ width: '216px' }}>
<input type="text" className="input-light" value={form.watch('saleStoreId')} {...form.register('saleStoreId')} readOnly /> <input type="text" className="input-light" value={form.watch('saleStoreId')} {...form.register('saleStoreId')} readOnly />
@ -520,24 +560,27 @@ export default function StuffDetail() {
<tr> <tr>
<th> <th>
<div className="flx-box"> <div className="flx-box">
<div className="title">2 판매점명 / ID</div> <div className="title">{getMessage('stuff.detail.otherSaleStoreId')}</div>
<div className="tooltips"></div> <div className="tooltips">
<span>{getMessage('stuff.detail.tooltip.saleStoreId')}</span>
</div>
</div> </div>
</th> </th>
<td> <td>
<div className="flx-box"> <div className="flx-box">
<div className="select-wrap mr5" style={{ width: '567px' }}> <div className="select-wrap mr5" style={{ width: '567px' }}>
{otherSaleStoreList?.length > 0 && ( <Select
<select className="select-light" onChange={onSelectionChange2} value={form.watch('otherSaleStoreId')}> className="react-select-custom"
{otherSaleStoreList.map((row) => { classNamePrefix="custom"
return ( placeholder="Select"
<option key={row.saleStoreId} value={row.saleStoreId} text={row.saleStoreName}> ref={ref}
{row.saleStoreName} options={otherSaleStoreList}
</option> onChange={onSelectionChange2}
) getOptionLabel={(x) => x.saleStoreName}
})} getOptionValue={(x) => x.saleStoreId}
</select> isDisabled={form.watch('saleStoreId') !== '' ? false : true}
)} isClearable={true}
/>
</div> </div>
<div className="input-wrap" style={{ width: '216px' }}> <div className="input-wrap" style={{ width: '216px' }}>
<input <input
@ -553,48 +596,37 @@ export default function StuffDetail() {
</tr> </tr>
<tr> <tr>
<th> <th>
우편번호 <span className="important">*</span> {getMessage('stuff.detail.zipNo')} <span className="important">*</span>
</th> </th>
<td> <td>
<div className="flx-box"> <div className="flx-box">
<div className="input-wrap mr5" style={{ width: '200px' }}> <div className="input-wrap mr5" style={{ width: '200px' }}>
<input <input type="text" className="input-light" disabled value={form.watch('zipNo')} />
type="text"
className="input-light"
maxLength={7}
{...form.register('zipNo', {
minLength: { value: 7, message: '7자리만가능' },
pattern: { value: /^[0-9]*$/g, message: '숫자만 입력' },
})}
/>
</div> </div>
<Button className="btn-origin grey" isDisabled={!buttonValid} onClick={onSearchPostNumber}> <Button className="btn-origin grey" onClick={onSearchPostNumberPopOpen}>
주소검색 {getMessage('stuff.detail.btn.addressPop')}
</Button> </Button>
<div className="guide">*우편번호 7자리를 입력한 , 주소검색 버튼을 클릭해 주십시오</div> <div className="guide">{getMessage('stuff.detail.btn.addressPop.guide')}</div>
</div> </div>
</td> </td>
</tr> </tr>
<tr> <tr>
<th> <th>
도도부현 / 주소 <span className="important">*</span> {getMessage('stuff.detail.prefId')}
<span className="important">*</span>
</th> </th>
<td> <td>
<div className="flx-box"> <div className="flx-box">
<div className="input-wrap mr5" style={{ width: '200px' }}> <div className="select-wrap" style={{ width: '200px' }}>
<input type="text" className="input-light" value={form.watch('prefName')} {...form.register('prefName')} readOnly /> {prefCodeList?.length > 0 && (
{/* {prefCodeList?.length > 0 && ( <select className="select-light" name="prefName" {...register('prefId')} disabled>
<Select className="max-w-xs" selectedKeys={prefValue} isDisabled {...form.register('prefId')}> {prefCodeList.map((row) => (
{prefCodeList.map((row) => { <option key={row.prefId} value={row.prefId}>
return <SelectItem key={row.prefId}>{row.prefName}</SelectItem> {row.prefName}
})} </option>
</Select> ))}
)} */}
{/* {prefCodeList?.length > 0 && (
<select>
<option value=""></option>
</select> </select>
)} */} )}
</div> </div>
<div className="input-wrap mr5" style={{ width: '580px' }}> <div className="input-wrap mr5" style={{ width: '580px' }}>
<input type="text" className="input-light" value={form.watch('address')} {...form.register('address')} /> <input type="text" className="input-light" value={form.watch('address')} {...form.register('address')} />
@ -604,17 +636,15 @@ export default function StuffDetail() {
</tr> </tr>
<tr> <tr>
<th> <th>
발전량시뮬레이션지역 <span className="important">*</span> {getMessage('stuff.detail.areaId')} <span className="important">*</span>
</th> </th>
<td> <td>
<div className="select-wrap" style={{ width: '200px' }}> <div className="select-wrap" style={{ width: '200px' }}>
<select <select
className="select-light" className="select-light"
name="areaId" name="areaId"
onChange={(e) => {
form.setValue('areaId', e.target.value)
}}
disabled={areaIdList?.length > 0 ? false : true} disabled={areaIdList?.length > 0 ? false : true}
onChange={handleAreaIdOnChange}
> >
{areaIdList.map((row) => { {areaIdList.map((row) => {
return ( return (
@ -629,11 +659,14 @@ export default function StuffDetail() {
</tr> </tr>
<tr> <tr>
<th> <th>
기준풍속 <span className="important">*</span> {getMessage('stuff.detail.windSpeed')} <span className="important">*</span>
</th> </th>
<td> <td>
<div className="flx-box"> <div className="flx-box">
<div className="select-wrap mr10" style={{ width: '200px' }}> <div className="input-wrap mr10">
<input type="text" className="input-light" readOnly value={form.watch('windSpeed')} {...register('windSpeed')} />
</div>
{/* <div className="select-wrap mr10" style={{ width: '200px' }}>
<select className="select-light" name="windSpeed" {...register('windSpeed')}> <select className="select-light" name="windSpeed" {...register('windSpeed')}>
{windSpeedList.map((row) => { {windSpeedList.map((row) => {
return ( return (
@ -643,15 +676,17 @@ export default function StuffDetail() {
) )
})} })}
</select> </select>
</div> </div> */}
<span>m/s이하</span> <span className="mr10">{getMessage('stuff.detail.windSpeedSpan')}</span>
<button className="btn-origin grey mr5">풍속선택</button> <button className="btn-origin grey" onClick={onSearchWindSpeedPopOpen}>
{getMessage('stuff.detail.btn.windSpeedPop')}
</button>
</div> </div>
</td> </td>
</tr> </tr>
<tr> <tr>
<th> <th>
수직적설량 <span className="important">*</span> {getMessage('stuff.detail.verticalSnowCover')} <span className="important">*</span>
</th> </th>
<td> <td>
<div className="flx-box"> <div className="flx-box">
@ -665,14 +700,14 @@ export default function StuffDetail() {
<span className="mr10">cm</span> <span className="mr10">cm</span>
<div className="d-check-box light"> <div className="d-check-box light">
<input type="checkbox" id="coldRegionFlg" {...form.register('coldRegionFlg')} /> <input type="checkbox" id="coldRegionFlg" {...form.register('coldRegionFlg')} />
<label htmlFor="coldRegionFlg">한랭지대책시행</label> <label htmlFor="coldRegionFlg">{getMessage('stuff.detail.coldRegionFlg')}</label>
</div> </div>
</div> </div>
</td> </td>
</tr> </tr>
<tr> <tr>
<th> <th>
면조도구분 <span className="important">*</span> {getMessage('stuff.detail.surfaceType')} <span className="important">*</span>
</th> </th>
<td> <td>
<div className="flx-box"> <div className="flx-box">
@ -686,7 +721,7 @@ export default function StuffDetail() {
</div> </div>
<div className="d-check-box light mr5"> <div className="d-check-box light mr5">
<input type="checkbox" id="saltAreaFlg" {...form.register('saltAreaFlg')} /> <input type="checkbox" id="saltAreaFlg" {...form.register('saltAreaFlg')} />
<label htmlFor="saltAreaFlg">염해지역용아이템사용</label> <label htmlFor="saltAreaFlg">{getMessage('stuff.detail.saltAreaFlg')}</label>
</div> </div>
<div className="tooltips"></div> <div className="tooltips"></div>
</div> </div>
@ -694,7 +729,7 @@ export default function StuffDetail() {
</tr> </tr>
<tr> <tr>
<th> <th>
설치높이 <span className="important">*</span> {getMessage('stuff.detail.installHeight')} <span className="important">*</span>
</th> </th>
<td> <td>
<div className="flx-box"> <div className="flx-box">
@ -711,25 +746,25 @@ export default function StuffDetail() {
</td> </td>
</tr> </tr>
<tr> <tr>
<th>계약조건</th> <th>{getMessage('stuff.detail.conType')}</th>
<td> <td>
<div className="flx-box"> <div className="flx-box">
<div className="d-check-radio light mr10"> <div className="d-check-radio light mr10">
<input type="radio" name="conType" value="0" id="conType0" {...form.register('conType')} /> <input type="radio" name="conType" value="0" id="conType0" {...form.register('conType')} />
<label htmlFor="conType0">잉여</label> <label htmlFor="conType0">{getMessage('stuff.detail.conType0')}</label>
</div> </div>
<div className="d-check-radio light mr10"> <div className="d-check-radio light mr10">
<input type="radio" name="conType" value="1" id="conType1" {...form.register('conType')} /> <input type="radio" name="conType" value="1" id="conType1" {...form.register('conType')} />
<label htmlFor="conType1">전량</label> <label htmlFor="conType1">{getMessage('stuff.detail.conType1')}</label>
</div> </div>
</div> </div>
</td> </td>
</tr> </tr>
<tr> <tr>
<th>메모</th> <th>{getMessage('stuff.detail.remarks')}</th>
<td> <td>
<div className="input-wrap"> <div className="input-wrap">
<input type="text" className="input-light" /> <input type="text" className="input-light" {...form.register('remarks')} />
</div> </div>
</td> </td>
</tr> </tr>
@ -756,34 +791,152 @@ export default function StuffDetail() {
</form> </form>
)) || ( )) || (
<> <>
<form onSubmit={handleSubmit(onValid)}>
<div className="sub-table-box">
<div className="promise-gudie">
<span className="important">*</span> {getMessage('stuff.detail.required')}
</div>
<div className="infomation-table">
<table>
<colgroup>
<col style={{ width: '200px' }} />
<col />
</colgroup>
<tbody>
<tr>
<th>{getMessage('stuff.detail.planReqNo')}</th>
<td>
<div className="flx-box">
<div className="input-wrap mr5" style={{ width: '200px' }}>
<input type="text" className="input-light" readOnly />
</div>
<button className="btn-origin grey" onClick={onSearchDesignRequestPopOpen}>
{getMessage('stuff.planReqPopup.title')}
</button>
</div>
</td>
</tr>
<tr>
<th>
{getMessage('stuff.detail.dispCompanyName')} <span className="important">*</span>
</th>
<td>
<div className="input-wrap" style={{ width: '500px' }}>
<input type="text" className="input-light" {...form.register('dispCompanyName')} value={form.watch('dispCompanyName')} />
</div>
</td>
</tr>
<tr>
<th>
{getMessage('stuff.detail.objectStatusId')} <span className="importatn">*</span>
</th>
<td>
<div className="flx-box">
<div className="d-check-radio light mr10">
<input type="radio" name="objectStatusId" value="0" id="objectStatus0" {...form.register('objectStatusId')} />
<label htmlFor="objectStatus0">{getMessage('stuff.detail.objectStatus0')}</label>
</div>
<div className="d-check-radio light mr10">
<input type="radio" name="objectStatusId" value="1" id="objectStatus1" {...form.register('objectStatusId')} />
<label htmlFor="objectStatus1">{getMessage('stuff.detail.objectStatus1')}</label>
</div>
<div className="input-wrap mr5" style={{ width: '545px' }}>
<input type="text" className="input-light" {...form.register('objectName')} />
</div>
<div className="select-wrap" style={{ width: '120px' }}>
<select className="select-light" name="objectNameOmit" {...register('objectNameOmit')}>
<option value="">경칭선택</option>
<option value="11">경칭11</option>
<option value="22">경칭22</option>
<option value="33">경칭33</option>
</select>
</div>
</div>
</td>
</tr>
<tr>
<th>{getMessage('stuff.detail.objectNameKana')}</th>
<td>
<div className="input-wrap" style={{ width: '789px' }}>
<input type="text" className="input-light" {...form.register('objectNameKana')} />
</div>
</td>
</tr>
<tr>
<th>
<div className="flx-box">
<div className="title">
{getMessage('stuff.detail.saleStoreId')}
<span className="important">*</span>
</div>
<div className="tooltips"></div>
</div>
</th>
<td>
<div className="flx-box">
<div className="select-wrap mr5" style={{ width: '567px' }}>
{/* <Select
options={saleStoreList}
value={form.watch('saleStoreId')}
onChange={onSelectionChange}
labelField="saleStoreName"
valueField="saleStoreId"
searchBy="saleStoreName"
clearable={true}
></Select> */}
</div>
<div className="input-wrap" style={{ width: '216px' }}>
<input type="text" className="input-light" value={form.watch('saleStoreId')} {...form.register('saleStoreId')} readOnly />
</div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</form>
{objectNo.substring(0, 1) === 'R' ? ( {objectNo.substring(0, 1) === 'R' ? (
<> <>
<Link href="/management/stuff"> <Link href="/management/stuff">
<button type="button" className="btn-origin grey"> <button type="button" className="btn-origin grey mr5">
R상세:물건목록 R상세:물건목록
</button> </button>
</Link> </Link>
<button type="submit" className="btn-origin navy mr5"> <button type="submit" className="btn-origin navy mr5">
R상세:저장 R상세:저장
</button> </button>
<button type="submit" className="btn-origin navy mr5" onClick={onDelete}> <button type="submit" className="btn-origin navy" onClick={onDelete}>
R상세:물건삭제 R상세:물건삭제
</button> </button>
</> </>
) : ( ) : (
<> <>
{!isFormValid ? (
<button type="submit" className="btn-origin navy mr5" onClick={onTempSave}>
TEMP상세:임시저장
</button>
) : (
<button type="submit" className="btn-origin navy mr5">
TEMP상세:저장
</button>
)}
<Link href="/management/stuff"> <Link href="/management/stuff">
<button type="button" className="btn-origin grey"> <button type="button" className="btn-origin grey">
T상세:물건목록 T상세:물건목록
</button> </button>
</Link> </Link>
<button type="submit" className="btn-origin navy mr5">
T상세:저장
</button>
</> </>
)} )}
</> </>
)} )}
{showAddressButtonValid && <FindAddressPop setShowAddressButtonValid={setShowAddressButtonValid} zipInfo={setZipInfo} />}
{showDesignRequestButtonValid && (
<PlanRequestPop setShowDesignRequestButtonValid={setShowDesignRequestButtonValid} planReqInfo={setPlanReqInfo} />
)}
{showWindSpeedButtonValid && (
<WindSelectPop setShowWindSpeedButtonValid={setShowWindSpeedButtonValid} prefName={form.watch('prefName')} windSpeedInfo={setWindSppedInfo} />
)}
</> </>
) )
} }

View File

@ -38,9 +38,8 @@ export default function StuffQGrid(props) {
flex: 1, flex: 1,
sortable: false, sortable: false,
suppressMovable: true, suppressMovable: true,
resizable: false, resizable: true,
suppressSizeToFit: false, suppressSizeToFit: false,
headerClass: 'centered', //_test.scss
} }
}, []) }, [])
@ -67,24 +66,7 @@ export default function StuffQGrid(props) {
// //
const onCellDoubleClicked = useCallback((event) => { const onCellDoubleClicked = useCallback((event) => {
// if (event.column.colId === 'company') {
// return
// } else {
props.getCellDoubleClicked(event) props.getCellDoubleClicked(event)
// }
}, [])
//
const autoSizeStrategy = useMemo(() => {
return {
type: 'fitCellContents',
}
}, [])
const onGridReady = useCallback((event) => {
// console.log('event:::', event)
// width
event.api.sizeColumnsToFit()
}, []) }, [])
// Fetch data & update rowData state // Fetch data & update rowData state
@ -92,11 +74,17 @@ export default function StuffQGrid(props) {
gridData ? setRowData(gridData) : '' gridData ? setRowData(gridData) : ''
}, [gridData]) }, [gridData])
// row
const getRowClass = (row) => {
if (row.data.objectNo.substring(0, 1) === 'T') {
return 'important_row'
}
}
return ( return (
<div className="ag-theme-quartz" style={{ height: 500 }}> <div className="ag-theme-quartz" style={{ height: 500 }}>
<AgGridReact <AgGridReact
ref={gridRef} ref={gridRef}
onGridReady={onGridReady}
rowBuffer={rowBuffer} rowBuffer={rowBuffer}
rowData={rowData} rowData={rowData}
columnDefs={colDefs} columnDefs={colDefs}
@ -107,10 +95,9 @@ export default function StuffQGrid(props) {
onSelectionChanged={onSelectionChanged} onSelectionChanged={onSelectionChanged}
onCellDoubleClicked={onCellDoubleClicked} onCellDoubleClicked={onCellDoubleClicked}
pagination={isPageable} pagination={isPageable}
//paginationPageSize={paginationPageSize}
//paginationPageSizeSelector={paginationPageSizeSelector}
autoSizeStrategy={autoSizeStrategy}
overlayNoRowsTemplate={'<span className="ag-overlay-loading-center">물건 목록이 없습니다.</span>'} overlayNoRowsTemplate={'<span className="ag-overlay-loading-center">물건 목록이 없습니다.</span>'}
getRowClass={getRowClass}
autoSizeAllColumns={true}
/> />
</div> </div>
) )

View File

@ -4,7 +4,8 @@ import React, { useEffect, useRef, useState } from 'react'
import { useAxios } from '@/hooks/useAxios' import { useAxios } from '@/hooks/useAxios'
import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil' import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil'
import { appMessageStore, globalLocaleStore } from '@/store/localeAtom' import { appMessageStore, globalLocaleStore } from '@/store/localeAtom'
import Select from 'react-dropdown-select' // import Select from 'react-dropdown-select'
import Select from 'react-select'
import KO from '@/locales/ko.json' import KO from '@/locales/ko.json'
import JA from '@/locales/ja.json' import JA from '@/locales/ja.json'
import { stuffSearchState } from '@/store/stuffAtom' import { stuffSearchState } from '@/store/stuffAtom'
@ -42,7 +43,6 @@ export default function StuffSearchCondition() {
const resetStuffRecoil = useResetRecoilState(stuffSearchState) const resetStuffRecoil = useResetRecoilState(stuffSearchState)
const [stuffSearch, setStuffSearch] = useRecoilState(stuffSearchState) const [stuffSearch, setStuffSearch] = useRecoilState(stuffSearchState)
const [objectNo, setObjectNo] = useState('') // const [objectNo, setObjectNo] = useState('') //
// const [saleStoreId, setSaleStoreId] = useState('') //ID
const [address, setAddress] = useState('') // const [address, setAddress] = useState('') //
const [objectName, setobjectName] = useState('') // const [objectName, setobjectName] = useState('') //
const [saleStoreName, setSaleStoreName] = useState('') // const [saleStoreName, setSaleStoreName] = useState('') //
@ -74,13 +74,16 @@ export default function StuffSearchCondition() {
startRow: stuffSearch?.startRow ? stuffSearch.startRow : 1, startRow: stuffSearch?.startRow ? stuffSearch.startRow : 1,
endRow: stuffSearch?.endRow ? stuffSearch.endRow : 100, endRow: stuffSearch?.endRow ? stuffSearch.endRow : 100,
schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'R', schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'R',
selObject: {
label: stuffSearch.selObject.label,
value: stuffSearch.selObject.value,
},
}) })
} }
// //
const resetRecoil = () => { const resetRecoil = () => {
setObjectNo('') setObjectNo('')
//setSaleStoreId('') //
setAddress('') setAddress('')
setobjectName('') setobjectName('')
setSaleStoreName('') setSaleStoreName('')
@ -96,12 +99,14 @@ export default function StuffSearchCondition() {
useEffect(() => { useEffect(() => {
if (isObjectNotEmpty(sessionState)) { if (isObjectNotEmpty(sessionState)) {
// console.log(' ::::::::', sessionState)
// storeId T01 1 // storeId T01 1
// get({ url: `/api/object/saleStore/201TES01/list` }).then((res) => { // get({ url: `/api/object/saleStore/201TES01/list` }).then((res) => {
get({ url: `/api/object/saleStore/${sessionState?.storeId}/list` }).then((res) => { get({ url: `/api/object/saleStore/${sessionState?.storeId}/list` }).then((res) => {
if (!isEmptyArray(res)) { if (!isEmptyArray(res)) {
// console.log(' :::::', res) res.map((row) => {
row.value = row.saleStoreId
row.label = row.saleStoreName
})
setSchSelSaleStoreList(res) setSchSelSaleStoreList(res)
} }
}) })
@ -110,18 +115,21 @@ export default function StuffSearchCondition() {
// .. // ..
const handleClear = () => { const handleClear = () => {
// console.log('ref::', ref.current.state.values) if (ref.current) {
if (ref.current.state.dropDown) { ref.current.clearValue()
ref.current.methods.dropDown()
} else {
ref.current.state.values = []
} }
} }
// //
const onSelectionChange = (key) => { const onSelectionChange = (key) => {
if (!isEmptyArray(key)) { if (isObjectNotEmpty(key)) {
setSchSelSaleStoreId(key[0].saleStoreId) setSchSelSaleStoreId(key.saleStoreId)
setStuffSearch({ ...stuffSearch, code: 'S', schSelSaleStoreId: key[0].saleStoreId }) setStuffSearch({
...stuffSearch,
code: 'S',
schSelSaleStoreId: key.saleStoreId,
selObject: { value: key.saleStoreId, label: key.saleStoreName },
})
} else { } else {
setSchSelSaleStoreId('') setSchSelSaleStoreId('')
setStuffSearch({ ...stuffSearch, schSelSaleStoreId: '' }) setStuffSearch({ ...stuffSearch, schSelSaleStoreId: '' })
@ -147,19 +155,19 @@ export default function StuffSearchCondition() {
<div className="sub-table-box"> <div className="sub-table-box">
<div className="table-box-title-wrap"> <div className="table-box-title-wrap">
<div className="title-wrap"> <div className="title-wrap">
<h3>물건현황</h3> <h3>{getMessage('stuff.search.title')}</h3>
</div> </div>
<div className="left-unit-box"> <div className="left-unit-box">
<Link href="/management/stuff/tempdetail"> <Link href="/management/stuff/tempdetail" scroll={false}>
<button type="button" className="btn-origin navy mr5"> <button type="button" className="btn-origin navy mr5">
물건신규등록 {getMessage('stuff.search.btn1')}
</button> </button>
</Link> </Link>
<button type="button" className="btn-origin navy mr5" onClick={onSubmit}> <button type="button" className="btn-origin navy mr5" onClick={onSubmit}>
조회 {getMessage('stuff.search.btn2')}
</button> </button>
<button type="button" className="btn-origin grey" onClick={resetRecoil}> <button type="button" className="btn-origin grey" onClick={resetRecoil}>
초기화 {getMessage('stuff.search.btn3')}
</button> </button>
</div> </div>
</div> </div>
@ -175,13 +183,12 @@ export default function StuffSearchCondition() {
</colgroup> </colgroup>
<tbody> <tbody>
<tr> <tr>
<th>물건번호</th> <th>{getMessage('stuff.search.schObjectNo')}</th>
<td> <td>
<div className="input-wrap"> <div className="input-wrap">
<input <input
type="text" type="text"
className="input-light" className="input-light"
placeholder="물건번호 입력"
value={stuffSearch?.code === 'E' || stuffSearch?.code === 'M' ? stuffSearch.schObjectNo : objectNo} value={stuffSearch?.code === 'E' || stuffSearch?.code === 'M' ? stuffSearch.schObjectNo : objectNo}
onChange={(e) => { onChange={(e) => {
setObjectNo(e.target.value) setObjectNo(e.target.value)
@ -190,29 +197,26 @@ export default function StuffSearchCondition() {
/> />
</div> </div>
</td> </td>
<th>판매대리점명</th> <th>{getMessage('stuff.search.schSaleStoreName')}</th>
<td> <td>
<div className="input-wrap"> <div className="input-wrap">
<input <input
type="text" type="text"
className="input-light" className="input-light"
placeholder="판매대리점명 입력"
value={stuffSearch?.schSaleStoreName ? stuffSearch.schSaleStoreName : saleStoreName} value={stuffSearch?.schSaleStoreName ? stuffSearch.schSaleStoreName : saleStoreName}
onChange={(e) => { onChange={(e) => {
//setSaleStoreId(e.target.value)
setSaleStoreName(e.target.value) setSaleStoreName(e.target.value)
setStuffSearch({ ...stuffSearch, code: 'S', schSaleStoreName: e.target.value }) setStuffSearch({ ...stuffSearch, code: 'S', schSaleStoreName: e.target.value })
}} }}
/> />
</div> </div>
</td> </td>
<th>물건주소</th> <th>{getMessage('stuff.search.schAddress')}</th>
<td> <td>
<div className="input-wrap"> <div className="input-wrap">
<input <input
type="text" type="text"
className="input-light" className="input-light"
placeholder="물건주소 입력"
value={stuffSearch?.schAddress ? stuffSearch.schAddress : address} value={stuffSearch?.schAddress ? stuffSearch.schAddress : address}
onChange={(e) => { onChange={(e) => {
setAddress(e.target.value) setAddress(e.target.value)
@ -223,13 +227,12 @@ export default function StuffSearchCondition() {
</td> </td>
</tr> </tr>
<tr> <tr>
<th>물건명</th> <th>{getMessage('stuff.search.schObjectName')}</th>
<td> <td>
<div className="input-wrap"> <div className="input-wrap">
<input <input
type="text" type="text"
className="input-light" className="input-light"
placeholder="물건명 입력"
value={stuffSearch?.schObjectName ? stuffSearch.schObjectName : objectName} value={stuffSearch?.schObjectName ? stuffSearch.schObjectName : objectName}
onChange={(e) => { onChange={(e) => {
setobjectName(e.target.value) setobjectName(e.target.value)
@ -238,13 +241,12 @@ export default function StuffSearchCondition() {
/> />
</div> </div>
</td> </td>
<th>견적처</th> <th>{getMessage('stuff.search.schDispCompanyName')}</th>
<td> <td>
<div className="input-wrap"> <div className="input-wrap">
<input <input
type="text" type="text"
className="input-light" className="input-light"
placeholder="견적처 입력"
value={stuffSearch?.schDispCompanyName ? stuffSearch.schDispCompanyName : dispCompanyName} value={stuffSearch?.schDispCompanyName ? stuffSearch.schDispCompanyName : dispCompanyName}
onChange={(e) => { onChange={(e) => {
setDispCompanyName(e.target.value) setDispCompanyName(e.target.value)
@ -253,31 +255,34 @@ export default function StuffSearchCondition() {
/> />
</div> </div>
</td> </td>
<th>판매대리점 선택</th> <th>{getMessage('stuff.search.schSelSaleStoreId')}</th>
<td> <td>
{schSelSaleStoreList?.length > 0 && ( <div className="select-wrap">
<Select {schSelSaleStoreList?.length > 0 && (
options={schSelSaleStoreList} <Select
value={stuffSearch?.schSelSaleStoreId ? stuffSearch.schSelSaleStoreId : schSelSaleStoreId} className="react-select-custom"
labelField="saleStoreName" classNamePrefix="custom"
valueField="saleStoreName" placeholder="Select"
onChange={onSelectionChange} ref={ref}
clearable={true} options={schSelSaleStoreList}
onClearAll={handleClear} onChange={onSelectionChange}
ref={ref} getOptionLabel={(x) => x.saleStoreName}
disabled={sessionState?.storeLvl === '1' ? false : true} getOptionValue={(x) => x.saleStoreId}
></Select> defaultValue={stuffSearch?.selObject?.value ? stuffSearch?.selObject : null}
)} isDisabled={sessionState?.storeLvl === '1' ? false : true}
isClearable={true}
/>
)}
</div>
</td> </td>
</tr> </tr>
<tr> <tr>
<th>담당자</th> <th>{getMessage('stuff.search.schReceiveUser')}</th>
<td> <td>
<div className="input-wrap"> <div className="input-wrap">
<input <input
type="text" type="text"
className="input-light" className="input-light"
placeholder="담당자 입력"
value={stuffSearch?.schReceiveUser ? stuffSearch.schReceiveUser : receiveUser} value={stuffSearch?.schReceiveUser ? stuffSearch.schReceiveUser : receiveUser}
onChange={(e) => { onChange={(e) => {
setReceiveUser(e.target.value) setReceiveUser(e.target.value)
@ -286,7 +291,7 @@ export default function StuffSearchCondition() {
/> />
</div> </div>
</td> </td>
<th>기간검색</th> <th>{getMessage('stuff.search.period')}</th>
<td colSpan={3}> <td colSpan={3}>
<div className="form-flex-wrap"> <div className="form-flex-wrap">
<div className="radio-wrap mr10"> <div className="radio-wrap mr10">
@ -302,7 +307,7 @@ export default function StuffSearchCondition() {
setStuffSearch({ ...stuffSearch, code: 'S', schDateType: e.target.value }) setStuffSearch({ ...stuffSearch, code: 'S', schDateType: e.target.value })
}} }}
/> />
<label htmlFor="radio_u">갱신일</label> <label htmlFor="radio_u">{getMessage('stuff.search.schDateTypeU')}</label>
</div> </div>
<div className="d-check-radio light"> <div className="d-check-radio light">
<input <input
@ -316,7 +321,7 @@ export default function StuffSearchCondition() {
setStuffSearch({ ...stuffSearch, code: 'S', schDateType: e.target.value }) setStuffSearch({ ...stuffSearch, code: 'S', schDateType: e.target.value })
}} }}
/> />
<label htmlFor="radio_r">등록일</label> <label htmlFor="radio_r">{getMessage('stuff.search.schDateTypeR')}</label>
</div> </div>
</div> </div>
<div className="date-picker-wrap"> <div className="date-picker-wrap">

View File

@ -0,0 +1,165 @@
import React, { useState } from 'react'
import { useForm } from 'react-hook-form'
import { queryStringFormatter } from '@/util/common-utils'
import { useAxios } from '@/hooks/useAxios'
import { globalLocaleStore } from '@/store/localeAtom'
import { useRecoilValue } from 'recoil'
import FindAddressPopQGrid from './FindAddressPopQGrid'
import { useMessage } from '@/hooks/useMessage'
import { isNotEmptyArray } from '@/util/common-utils'
export default function FindAddressPop(props) {
const globalLocaleState = useRecoilValue(globalLocaleStore)
const { get } = useAxios(globalLocaleState)
const { getMessage } = useMessage()
const [prefId, setPrefId] = useState(null)
const [zipNo, setZipNo] = useState(null)
const [address1, setAddress1] = useState(null)
const [address2, setAddress2] = useState(null)
const [address3, setAddress3] = useState(null)
const [gridProps, setGridProps] = useState({
gridData: [],
isPageable: false,
gridColumns: [
{
field: 'address1',
headerName: getMessage('stuff.addressPopup.gridHeader.address1'),
minWidth: 150,
checkboxSelection: true,
showDisabledCheckboxes: true,
},
{
field: 'address2',
headerName: getMessage('stuff.addressPopup.gridHeader.address2'),
minWidth: 150,
},
{
field: 'address3',
headerName: getMessage('stuff.addressPopup.gridHeader.address3'),
minWidth: 150,
},
],
})
//
const formInitValue = {
zipNo: '',
}
const { register, setValue, getValues, handleSubmit, resetField, control, watch } = useForm({
defaultValues: formInitValue,
})
const form = { register, setValue, getValues, handleSubmit, resetField, control, watch }
//
const searchPostNum = () => {
// //7830060
// //9302226
// //0790177 3
const params = {
zipcode: watch('zipNo'),
}
get({ url: `https://zipcloud.ibsnet.co.jp/api/search?${queryStringFormatter(params)}` }).then((res) => {
if (res.status === 200) {
if (isNotEmptyArray(res.results)) {
setGridProps({ ...gridProps, gridData: res.results })
} else {
alert(getMessage('stuff.addressPopup.error.message1'))
setGridProps({ ...gridProps, gridData: [] })
}
} else {
alert(getMessage('stuff.addressPopup.error.message1'))
setGridProps({ ...gridProps, gridData: [] })
}
})
}
//
const applyAddress = () => {
if (prefId == null) {
alert(getMessage('stuff.addressPopup.error.message2'))
} else {
props.zipInfo({
zipNo: zipNo,
address1: address1,
address2: address2,
address3: address3,
prefId: prefId,
})
//
props.setShowAddressButtonValid(false)
}
}
//
const handleKeyUp = (e) => {
let input = e.target
input.value = input.value.replace(/[^0-9]/g, '')
if (e.key === 'Enter') {
searchPostNum()
}
}
//
const getSelectedRowdata = (data) => {
if (isNotEmptyArray(data)) {
setAddress1(data[0].address1)
setAddress2(data[0].address2)
setAddress3(data[0].address3)
setPrefId(data[0].prefcode)
setZipNo(data[0].zipcode)
} else {
setAddress1(null)
setAddress2(null)
setAddress3(null)
setPrefId(null)
setZipNo(null)
}
}
return (
<div className="modal-popup">
<div className="modal-dialog middle">
<div className="modal-content">
<div className="modal-header">
<h1 className="title">{getMessage('stuff.addressPopup.title')}</h1>
<button className="modal-close" onClick={() => props.setShowAddressButtonValid(false)}>
닫기
</button>
</div>
<div className="modal-body">
<div className="modal-body-inner">
<div className="address-input-wrap">
<input
type="text"
className="address-input"
maxLength={7}
onKeyUp={handleKeyUp}
{...form.register('zipNo')}
placeholder={getMessage('stuff.addressPopup.placeholder')}
/>
<button className="search-btn" onClick={searchPostNum}></button>
</div>
<div className="address-grid">
<FindAddressPopQGrid {...gridProps} getSelectedRowdata={getSelectedRowdata} />
</div>
</div>
<div className="footer-btn-wrap">
<button className="btn-origin grey mr5" onClick={() => props.setShowAddressButtonValid(false)}>
{getMessage('stuff.addressPopup.btn1')}
</button>
<button className="btn-origin navy " onClick={applyAddress}>
{getMessage('stuff.addressPopup.btn2')}
</button>
</div>
</div>
</div>
</div>
</div>
)
}

View File

@ -0,0 +1,62 @@
import React from 'react'
import { useCallback, useEffect, useMemo, useState } from 'react'
import { AgGridReact } from 'ag-grid-react'
import 'ag-grid-community/styles/ag-grid.css'
import 'ag-grid-community/styles/ag-theme-quartz.css'
export default function FindAddressPopGrid(props) {
const { gridData, gridColumns, isPageable = true } = props
const [rowData, setRowData] = useState(null)
const [gridApi, setGridApi] = useState(null)
const [colDefs, setColDefs] = useState(gridColumns)
const defaultColDef = useMemo(() => {
return {
flex: 1,
minWidth: 100,
sortable: false,
suppressMovable: false,
resizable: false,
suppressSizeToFit: false,
}
}, [])
const rowBuffer = 100
useEffect(() => {
gridData ? setRowData(gridData) : ''
}, [gridData])
const onGridReady = useCallback(
(params) => {
setGridApi(params.api)
gridData ? setRowData(gridData) : ''
},
[gridData],
)
//
const onSelectionChanged = () => {
const selectedData = gridApi.getSelectedRows()
props.getSelectedRowdata(selectedData)
}
return (
<div className="ag-theme-quartz" style={{ height: 500 }}>
<AgGridReact
onGridReady={onGridReady}
rowBuffer={rowBuffer}
rowData={rowData}
columnDefs={colDefs}
defaultColDef={defaultColDef}
rowSelection={'singleRow'}
pagination={isPageable}
onSelectionChanged={onSelectionChanged}
/>
</div>
)
}

View File

@ -0,0 +1,449 @@
import React, { useState, useRef, useEffect } from 'react'
import { useForm } from 'react-hook-form'
import { useAxios } from '@/hooks/useAxios'
import { globalLocaleStore } from '@/store/localeAtom'
import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil'
import FindAddressPopQGrid from './FindAddressPopQGrid'
import { useMessage } from '@/hooks/useMessage'
import { isNotEmptyArray } from '@/util/common-utils'
import SingleDatePicker from '@/components/common/datepicker/SingleDatePicker'
import dayjs from 'dayjs'
import PlanRequestPopQGrid from './PlanRequestPopQGrid'
import { sessionStore } from '@/store/commonAtom'
import { planReqSearchState } from '@/store/planReqAtom'
import { isObjectNotEmpty, queryStringFormatter } from '@/util/common-utils'
import Select from 'react-select'
import QPagination from '@/components/common/pagination/QPagination'
export default function PlanRequestPop(props) {
const [pageNo, setPageNo] = useState(1) //
const [pageSize, setPageSize] = useState(20) //
const [totalCount, setTotalCount] = useState(0) //
const sessionState = useRecoilValue(sessionStore)
const globalLocaleState = useRecoilValue(globalLocaleStore)
const [planReqObject, setPlanReqObject] = useState({})
const { get, promiseGet } = useAxios(globalLocaleState)
const { getMessage } = useMessage()
//Select ref
const ref = useRef()
//
const [startDate, setStartDate] = useState(dayjs(new Date()).add(-3, 'month').format('YYYY-MM-DD'))
const [endDate, setEndDate] = useState(dayjs(new Date()).format('YYYY-MM-DD'))
const rangeDatePickerProps1 = {
startDate, //
setStartDate,
}
const rangeDatePickerProps2 = {
startDate: endDate, //
setStartDate: setEndDate,
}
const resetPlanReqRecoil = useResetRecoilState(planReqSearchState)
const [planReqSearch, setPlanReqSearch] = useRecoilState(planReqSearchState)
const [schPlanReqNo, setSchPlanReqNo] = useState('') //
const [schTitle, setSchTitle] = useState('') //
const [schAddress, setSchAddress] = useState('') //
const [schSaleStoreName, setSchSaleStoreName] = useState('') //
const [schPlanReqName, setSchPlanReqName] = useState('') //
const [schPlanStatCd, setSchPlanStatCd] = useState('') //
const [schDateGbn, setSchDateGbn] = useState('S') //(S/R)
//
const resetRecoil = () => {
setSchPlanReqNo('')
setSchTitle('')
setSchAddress('')
setSchSaleStoreName('')
setSchPlanReqName('')
setSchDateGbn('S')
setStartDate(dayjs(new Date()).add(-3, 'month').format('YYYY-MM-DD'))
setEndDate(dayjs(new Date()).format('YYYY-MM-DD'))
setSchPlanStatCd('')
handleClear() //
resetPlanReqRecoil()
}
//
const handleClear = () => {
if (ref.current) {
ref.current.clearValue()
}
}
//
const onSelectionChange = (key) => {
//
// console.log('E::::::::', key)
if (isObjectNotEmpty(key)) {
setSchPlanStatCd(key.value)
setPlanReqSearch({
...planReqSearch,
schPlanStatCd: key.value,
})
} else {
//X
setSchPlanStatCd('')
setPlanReqSearch({ ...planReqSearch, schPlanStatCd: '' })
}
}
useEffect(() => {
setStartDate(planReqSearch?.schStartDt ? planReqSearch.schStartDt : dayjs(new Date()).add(-3, 'month').format('YYYY-MM-DD'))
setEndDate(planReqSearch?.schEndDt ? planReqSearch.schEndDt : dayjs(new Date()).format('YYYY-MM-DD'))
}, [planReqSearch])
//
const onSubmit = (page, type) => {
const params = {
saleStoreId: 'T100',
saleStoreLevel: '1',
// saleStoreId: sessionState?.storeId,
// saleStoreLevel: sessionState?.storeLvl,
schPlanReqNo: planReqSearch?.schPlanReqNo ? planReqSearch.schPlanReqNo : schPlanReqNo,
schTitle: planReqSearch?.schTitle ? planReqSearch.schTitle : schTitle,
schAddress: planReqSearch?.schAddress ? planReqSearch.schAddress : schAddress,
schSaleStoreName: planReqSearch?.schSaleStoreName ? planReqSearch.schSaleStoreName : schSaleStoreName,
schPlanReqName: planReqSearch?.schPlanReqName ? planReqSearch.schPlanReqName : schPlanReqName,
schPlanStatCd: planReqSearch?.schPlanStatCd ? planReqSearch.schPlanStatCd : schPlanStatCd,
schDateGbn: planReqSearch?.schDateGbn ? planReqSearch.schDateGbn : schDateGbn,
// schStartDt: dayjs(startDate).format('YYYY-MM-DD'),
// schEndDt: dayjs(endDate).format('YYYY-MM-DD'),
startRow: type === 'S' ? (1 - 1) * pageSize + 1 : (page - 1) * pageSize + 1,
endRow: type === 'S' ? 1 * pageSize : page * pageSize,
}
if (type === 'S') {
setPageNo(1)
} else {
setPageNo(page)
}
const apiUrl = `/api/object/planReq/list?${queryStringFormatter(params)}`
promiseGet({ url: apiUrl }).then((res) => {
if (res.status === 200) {
if (isNotEmptyArray(res.data.data)) {
setGridProps({ ...gridProps, gridData: res.data.data, gridCount: res.data.data[0].totCnt })
setTotalCount(res.data.data[0].totCnt)
} else {
setGridProps({ ...gridProps, gridData: [], gridCount: 0 })
setTotalCount(0)
}
} else {
setGridProps({ ...gridProps, gridData: [], gridCount: 0 })
setTotalCount(0)
}
})
}
//
const handleChangePage = (page) => {
setPlanReqSearch({
...planReqSearch,
startRow: (page - 1) * pageSize + 1,
endRow: page * pageSize,
})
setPageNo(page)
onSubmit(page, 'P')
}
const [gridProps, setGridProps] = useState({
gridData: [],
isPageable: false,
gridColumns: [
{
field: 'planStatName',
headerName: getMessage('stuff.planReqPopup.gridHeader.planStatName'),
minWidth: 150,
checkboxSelection: true,
showDisabledCheckboxes: true,
},
{
field: 'planReqNo',
headerName: getMessage('stuff.planReqPopup.gridHeader.planReqNo'),
minWidth: 150,
},
{
field: 'saleStoreId',
headerName: getMessage('stuff.planReqPopup.gridHeader.saleStoreId'),
minWidth: 150,
},
{
field: 'saleStoreName',
headerName: getMessage('stuff.planReqPopup.gridHeader.saleStoreName'),
minWidth: 150,
},
{
field: 'title',
headerName: getMessage('stuff.planReqPopup.gridHeader.title'),
minWidth: 150,
},
{
field: 'address1',
headerName: getMessage('stuff.planReqPopup.gridHeader.address1'),
minWidth: 150,
},
{
field: 'houseCntName',
headerName: getMessage('stuff.planReqPopup.gridHeader.houseCntName'),
minWidth: 150,
},
{
field: 'planReqName',
headerName: getMessage('stuff.planReqPopup.gridHeader.planReqName'),
minWidth: 150,
},
{
field: 'submitDt',
headerName: getMessage('stuff.planReqPopup.gridHeader.submitDt'),
minWidth: 150,
},
],
})
//
const getSelectedRowdata = (data) => {
if (isNotEmptyArray(data)) {
setPlanReqObject(data[0])
} else {
setPlanReqObject({})
}
}
//
const applyPlanReq = () => {
if (isObjectNotEmpty(planReqObject)) {
props.planReqInfo(planReqObject)
//
props.setShowDesignRequestButtonValid(false)
} else {
alert(getMessage('stuff.planReqPopup.error.message1'))
}
}
const tempList = [
{
label: '완료',
value: 'C',
},
{
label: '저장',
value: 'I',
},
{
label: '접수',
value: 'R',
},
{
label: '제출',
value: 'S',
},
]
return (
<div className="modal-popup">
<div className="modal-dialog big">
<div className="modal-content">
<div className="modal-header">
<h1 className="title">{getMessage('stuff.planReqPopup.title')}</h1>
<button className="modal-close" onClick={() => props.setShowDesignRequestButtonValid(false)}>
닫기
</button>
</div>
<div className="modal-body">
<div className="modal-body-inner">
<div className="design-tit-wrap">
<h3>{getMessage('stuff.planReqPopup.popTitle')}</h3>
<div className="design-search-wrap">
<button
className="btn-origin navy mr5"
onClick={() => {
onSubmit(pageNo, 'S')
}}
>
{getMessage('stuff.planReqPopup.btn1')}
</button>
<button className="btn-origin grey" onClick={resetRecoil}>
{getMessage('stuff.planReqPopup.btn2')}
</button>
</div>
</div>
<div className="design-request-table">
<div className="common-table">
<table>
<colgroup>
<col style={{ width: '160px' }} />
<col />
<col style={{ width: '160px' }} />
<col />
<col style={{ width: '160px' }} />
<col />
</colgroup>
<tbody>
<tr>
<th>{getMessage('stuff.planReqPopup.search.planReqNo')}</th>
<td>
<div className="input-wrap">
<input
type="text"
className="input-light"
value={planReqSearch?.schPlanReqNo ? planReqSearch?.schPlanReqNo : schPlanReqNo}
onChange={(e) => {
setSchPlanReqNo(e.target.value)
setPlanReqSearch({ ...planReqSearch, schPlanReqNo: e.target.value })
}}
/>
</div>
</td>
<th>{getMessage('stuff.planReqPopup.search.title')}</th>
<td>
<div className="input-wrap">
<input
type="text"
className="input-light"
value={planReqSearch?.schTitle ? planReqSearch?.schTitle : schTitle}
onChange={(e) => {
setSchTitle(e.target.value)
setPlanReqSearch({ ...planReqSearch, schTitle: e.target.value })
}}
/>
</div>
</td>
<th>{getMessage('stuff.planReqPopup.search.address1')}</th>
<td>
<div className="input-wrap">
<input
type="text"
className="input-light"
value={planReqSearch?.schAddress ? planReqSearch?.schAddress : schAddress}
onChange={(e) => {
setSchAddress(e.target.value)
setPlanReqSearch({ ...planReqSearch, schAddress: e.target.value })
}}
/>
</div>
</td>
</tr>
<tr>
<th>{getMessage('stuff.planReqPopup.search.saleStoreName')}</th>
<td>
<div className="input-wrap">
<input
type="text"
className="input-light"
value={planReqSearch?.schSaleStoreName ? planReqSearch?.schSaleStoreName : schSaleStoreName}
onChange={(e) => {
setSchSaleStoreName(e.target.value)
setPlanReqSearch({ ...planReqSearch, schSaleStoreName: e.target.value })
}}
/>
</div>
</td>
<th>{getMessage('stuff.planReqPopup.search.planReqName')}</th>
<td>
<div className="input-wrap">
<input
type="text"
className="input-light"
value={planReqSearch?.schPlanReqName ? planReqSearch?.schPlanReqName : schPlanReqName}
onChange={(e) => {
setSchPlanReqName(e.target.value)
setPlanReqSearch({ ...planReqSearch, schPlanReqName: e.target.value })
}}
/>
</div>
</td>
<th>{getMessage('stuff.planReqPopup.search.planStatName')}</th>
<td>
<div className="select-wrap">
<Select
className="react-select-custom"
classNamePrefix="custom"
ref={ref}
options={tempList}
onChange={onSelectionChange}
isSearchable={false}
placeholder="Select"
isClearable={true}
/>
</div>
</td>
</tr>
<tr>
<th>{getMessage('stuff.planReqPopup.search.period')}</th>
<td colSpan={5}>
<div className="form-flex-wrap">
<div className="radio-wrap mr10">
<div className="d-check-radio light mr10">
<input
type="radio"
name="radio04"
id="ra07"
checked={planReqSearch?.schDateGbn === 'S' ? true : false}
value={'S'}
onChange={(e) => {
setSchDateGbn(e.target.value)
setPlanReqSearch({ ...planReqSearch, schDateGbn: e.target.value })
}}
/>
<label htmlFor="ra07">{getMessage('stuff.planReqPopup.search.schDateGbnS')}</label>
</div>
<div className="d-check-radio light">
<input
type="radio"
name="radio04"
id="ra08"
checked={planReqSearch?.schDateGbn === 'R' ? true : false}
value={'R'}
onChange={(e) => {
setSchDateGbn(e.target.value)
setPlanReqSearch({ ...planReqSearch, schDateGbn: e.target.value })
}}
/>
<label htmlFor="ra08">{getMessage('stuff.planReqPopup.search.schDateGbnR')}</label>
</div>
</div>
<div className="date-picker-wrap">
<div className="date-picker" style={{ flex: 1 }}>
<SingleDatePicker {...rangeDatePickerProps1} />
</div>
<span> ~ </span>
<div className="date-picker" style={{ flex: 1 }}>
<SingleDatePicker {...rangeDatePickerProps2} />
</div>
</div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div className="design-request-grid">
<div className="design-request-grid-tit">Plan List</div>
<PlanRequestPopQGrid {...gridProps} getSelectedRowdata={getSelectedRowdata} />
<div className="pagination-wrap">
<QPagination pageNo={pageNo} pageSize={pageSize} pagePerBlock={10} totalCount={totalCount} handleChangePage={handleChangePage} />
</div>
</div>
</div>
<div className="footer-btn-wrap">
<button className="btn-origin grey mr5" onClick={() => props.setShowDesignRequestButtonValid(false)}>
{getMessage('stuff.planReqPopup.btn3')}
</button>
<button className="btn-origin navy" onClick={applyPlanReq}>
{getMessage('stuff.planReqPopup.btn4')}
</button>
</div>
</div>
</div>
</div>
</div>
)
}

View File

@ -0,0 +1,62 @@
import React from 'react'
import { useCallback, useEffect, useMemo, useState } from 'react'
import { AgGridReact } from 'ag-grid-react'
import 'ag-grid-community/styles/ag-grid.css'
import 'ag-grid-community/styles/ag-theme-quartz.css'
export default function PlanRequestPopQGrid(props) {
const { gridData, gridColumns, isPageable = true } = props
const [rowData, setRowData] = useState(null)
const [gridApi, setGridApi] = useState(null)
const [colDefs, setColDefs] = useState(gridColumns)
const defaultColDef = useMemo(() => {
return {
flex: 1,
minWidth: 100,
sortable: false,
suppressMovable: false,
resizable: false,
suppressSizeToFit: false,
}
}, [])
const rowBuffer = 100
useEffect(() => {
gridData ? setRowData(gridData) : ''
}, [gridData])
const onGridReady = useCallback(
(params) => {
setGridApi(params.api)
gridData ? setRowData(gridData) : ''
},
[gridData],
)
//
const onSelectionChanged = () => {
const selectedData = gridApi.getSelectedRows()
props.getSelectedRowdata(selectedData)
}
return (
<div className="ag-theme-quartz" style={{ height: 350 }}>
<AgGridReact
onGridReady={onGridReady}
rowBuffer={rowBuffer}
rowData={rowData}
columnDefs={colDefs}
defaultColDef={defaultColDef}
rowSelection={'singleRow'}
pagination={isPageable}
onSelectionChanged={onSelectionChanged}
/>
</div>
)
}

View File

@ -0,0 +1,115 @@
import React, { useState, useEffect } from 'react'
import { useMessage } from '@/hooks/useMessage'
import { useAxios } from '@/hooks/useAxios'
import { globalLocaleStore } from '@/store/localeAtom'
import { useRecoilValue } from 'recoil'
import { isEmptyArray, isNotEmptyArray } from '@/util/common-utils'
export default function WindSelectPop(props) {
const globalLocaleState = useRecoilValue(globalLocaleStore)
const { promiseGet } = useAxios(globalLocaleState)
const [windSpeedList, setWindSpeedList] = useState([])
const [windSpeed, setWindSpeed] = useState(null)
const { getMessage } = useMessage()
//
const handleChangeRadio = (e) => {
setWindSpeed(e.target.value)
}
//
const applyWindSpeed = () => {
if (windSpeed == null) {
alert(getMessage('stuff.windSelectPopup.error.message2'))
} else {
props.windSpeedInfo({ windSpeed: windSpeed })
//
props.setShowWindSpeedButtonValid(false)
}
}
useEffect(() => {
if (props.prefName !== '') {
promiseGet({ url: `/api/object/windSpeed/${props.prefName}/list` }).then((res) => {
if (res.status === 200) {
if (!isEmptyArray(res.data)) {
setWindSpeedList(res.data)
}
}
})
}
}, [props])
return (
<div className="modal-popup">
<div className="modal-dialog middle">
<div className="modal-content">
<div className="modal-header">
<h1 className="title">{getMessage('stuff.windSelectPopup.title')}</h1>
<button className="modal-close" onClick={() => props.setShowWindSpeedButtonValid(false)}>
닫기
</button>
</div>
<div className="modal-body">
<div className="modal-body-inner">
<div className="common-table">
<table>
<colgroup>
<col style={{ width: '70px' }} />
<col />
</colgroup>
<tbody>
<tr>
<th>{getMessage('stuff.windSelectPopup.search.address1')}</th>
<td>
<div className="input-wrap">
<input type="text" className="input-light" defaultValue={props.prefName} readOnly />
</div>
</td>
</tr>
</tbody>
</table>
</div>
<div className="wind-table">
<table>
<thead>
<tr>
<th style={{ width: '50px' }}>{getMessage('stuff.windSelectPopup.table.selected')}</th>
<th style={{ width: '110px' }}>{getMessage('stuff.windSelectPopup.table.windspeed')}</th>
<th>Remarks</th>
</tr>
</thead>
<tbody>
{windSpeedList.map((row, index) => {
// console.log('row:::', row)
return (
<tr key={index}>
<td className="al-c">
<div className="d-check-radio light no-text">
<input type="radio" value={row.windSpeed} name="windS" id={row.windSpeed} onChange={handleChangeRadio} />
<label htmlFor={row.windSpeed}></label>
</div>
</td>
<td className="al-c">{row.windSpeed}</td>
<td>{row.remarks}</td>
</tr>
)
})}
</tbody>
</table>
</div>
</div>
<div className="footer-btn-wrap">
<button className="btn-origin grey mr5" onClick={() => props.setShowWindSpeedButtonValid(false)}>
{getMessage('stuff.windSelectPopup.btn1')}
</button>
<button className="btn-origin navy" onClick={applyWindSpeed}>
{getMessage('stuff.windSelectPopup.btn2')}
</button>
</div>
</div>
</div>
</div>
</div>
)
}

View File

@ -0,0 +1,287 @@
'use client'
import { useState, useRef, useEffect } from 'react'
import { useAxios } from '@/hooks/useAxios'
import { useMessage } from '@/hooks/useMessage'
export default function UserInfoModal({ userId, userInfoModal, setUserInfoModal }) {
const { getMessage } = useMessage()
// api
const { get, promisePatch, promisePost } = useAxios()
const [info, setInfo] = useState({
userId: '',
name: '',
nameKana: '',
category: '',
tel: '',
fax: '',
mail: '',
groupId: '',
groupName: '',
})
const [password, setPassword] = useState('')
const [showPwd, setShowPwd] = useState(false)
const [chkChgPwd, setChkChgPwd] = useState(false)
const [chgPwd, setChgPwd] = useState('')
const pwdInput = useRef()
const chgPwdInput = useRef()
useEffect(() => {
async function fetchData() {
const url = `/api/my-info/my-profile`
const params = new URLSearchParams({ userId: userId })
const apiUrl = `${url}?${params.toString()}`
const resultData = await get({ url: apiUrl })
if (resultData) {
setInfo(resultData)
} else {
alert(getMessage('common.message.no.data'))
}
}
if (userId) {
fetchData()
}
}, [])
const pattern = /^[\u0020-\u007E]{1,10}$/
//
const handleChangePassword = async () => {
if (chgPwd === '') {
chgPwdInput.current.focus()
return alert(getMessage('myinfo.message.validation.password4'))
}
if (password === chgPwd) {
chgPwdInput.current.focus()
return alert(getMessage('myinfo.message.validation.password2'))
}
if (!pattern.test(chgPwd)) {
chgPwdInput.current.focus()
return alert(getMessage('myinfo.message.validation.password3'))
}
const params = { loginId: info.userId, chgType: 'C', pwd: password, chgPwd: chgPwd }
await promisePatch({ url: '/api/login/v1.0/user/change-password', data: params })
.then((res) => {
if (res) {
if (res.data.result.resultCode === 'S') {
alert(getMessage('myinfo.message.save'))
setChkChgPwd(false)
setPassword(chgPwd)
setChgPwd('')
} else {
alert(res.data.result.resultMsg)
}
}
})
.catch((error) => {
alert(error.response.data.message)
})
}
// ,
const checkPasswordProcess = async (e) => {
if (password === '') {
pwdInput.current.focus()
return alert(getMessage('myinfo.message.validation.password1'))
}
const param = {
loginId: userId,
pwd: password,
}
await promisePost({ url: '/api/login/v1.0/login', data: param })
.then((res) => {
if (res) {
if (res.data.result.resultCode === 'S') {
setChkChgPwd(true)
setTimeout(() => {
chgPwdInput.current.focus()
}, 10)
} else {
alert(getMessage('myinfo.message.password.error'))
setChkChgPwd(false)
setChgPwd('')
setTimeout(() => {
pwdInput.current.focus()
}, 10)
}
}
})
.catch((error) => {
alert(error.response.data.message)
})
}
return (
<>
<div className="modal-popup">
<div className="modal-dialog ">
<div className="modal-content">
<div className="modal-header">
<h1 className="title">{getMessage('myinfo.title')}</h1>
<button
type="button"
className="modal-close"
onClick={() => {
setUserInfoModal(false)
}}
>
{getMessage('myinfo.btn.close')}
</button>
</div>
<div className="modal-body">
<div className="modal-body-inner">
<div className="common-table">
<table>
<colgroup>
<col style={{ width: '170px' }} />
<col />
</colgroup>
<tbody>
<tr>
<th>{getMessage('myinfo.info.userId')}</th>
<td>
<div className="input-wrap">
<input type="text" className="input-light" value={userId} readOnly />
</div>
</td>
</tr>
<tr>
<th>{getMessage('myinfo.info.nameKana')}</th>
<td>
<div className="input-wrap">
<input type="text" className="input-light" value={info?.nameKana} readOnly />
</div>
</td>
</tr>
<tr>
<th>{getMessage('myinfo.info.name')}</th>
<td>
<div className="input-wrap">
<input type="text" className="input-light" value={info?.name} readOnly />
</div>
</td>
</tr>
<tr>
<th>{getMessage('myinfo.info.password')}</th>
<td>
<div className="form-flex-wrap">
<div className="password-input mr10">
<input
type={`${showPwd ? 'text' : 'password'}`}
value={password}
ref={pwdInput}
onChange={(e) => {
setPassword(e.target.value)
}}
/>
<button className={`blink ${showPwd ? 'on' : ''}`} onClick={() => setShowPwd(!showPwd)}></button>
</div>
<span className="mr10">{getMessage('myinfo.sub.validation.password')}</span>
<button
className="btn-origin grey mr5"
type="button"
onClick={(e) => {
checkPasswordProcess(e)
}}
>
{getMessage('myinfo.btn.chg.password')}
</button>
</div>
</td>
</tr>
<tr style={{ display: chkChgPwd ? '' : 'none' }}>
<th>
{getMessage('myinfo.info.chg.password')} <span className="red">*</span>
</th>
<td>
<div className="form-flex-wrap">
<div className="input-wrap mr10" style={{ flex: 1 }}>
<input
className="input-light"
type="text"
ref={chgPwdInput}
value={chgPwd}
onChange={(e) => {
setChgPwd(e.target.value)
}}
/>
</div>
<button type="button" className="btn-origin grey mr5" onClick={handleChangePassword}>
{getMessage('myinfo.btn.chg')}
</button>
<button
type="button"
className="btn-origin white "
onClick={() => {
setChgPwd('')
setChkChgPwd(false)
}}
>
{getMessage('myinfo.btn.noChg')}
</button>
</div>
</td>
</tr>
<tr>
<th>{getMessage('myinfo.info.category')}</th>
<td>
<div className="input-wrap">
<input type="text" className="input-light" value={info?.groupName} readOnly />
</div>
</td>
</tr>
<tr>
<th>{getMessage('myinfo.info.tel')}</th>
<td>
<div className="input-wrap">
<input type="text" className="input-light" value={info?.tel} readOnly />
</div>
</td>
</tr>
<tr>
<th>{getMessage('myinfo.info.fax')}</th>
<td>
<div className="input-wrap">
<input type="text" className="input-light" value={info?.fax} readOnly />
</div>
</td>
</tr>
<tr>
<th>{getMessage('myinfo.info.mail')}</th>
<td>
<div className="input-wrap">
<input type="text" className="input-light" value={info?.mail} readOnly />
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div className="footer-btn-wrap">
<button
type="button"
className="btn-origin navy "
onClick={() => {
setUserInfoModal(false)
}}
>
{getMessage('myinfo.btn.confirm')}
</button>
</div>
</div>
</div>
</div>
</div>
</>
)
}

View File

@ -0,0 +1,415 @@
'use client'
import { useMessage } from '@/hooks/useMessage'
import { useRecoilState, useRecoilValue } from 'recoil'
import { canvasState } from '@/store/canvasAtom'
import { INPUT_TYPE, BATCH_TYPE } from '@/common/common'
import { useEvent } from '@/hooks/useEvent'
import {
polygonToTurfPolygon,
rectToPolygon,
triangleToPolygon,
pointsToTurfPolygon,
splitDormerTriangle,
setSurfaceShapePattern,
} from '@/util/canvas-util'
import { useSwal } from '@/hooks/useSwal'
import * as turf from '@turf/turf'
import { QPolygon } from '@/components/fabric/QPolygon'
import { drawDirectionArrow } from '@/util/qpolygon-utils'
export function useObjectBatch() {
const { getMessage } = useMessage()
const canvas = useRecoilValue(canvasState)
const { addCanvasMouseEventListener, initEvent } = useEvent()
const { swalFire } = useSwal()
const applyOpeningAndShadow = (objectPlacement, buttonAct, surfaceShapePolygons) => {
const selectedType = objectPlacement.typeRef.current.find((radio) => radio.checked).value
const isCrossChecked = buttonAct === 1 ? objectPlacement.isCrossRef.current.checked : false
const objName = buttonAct === 1 ? BATCH_TYPE.OPENING : BATCH_TYPE.SHADOW
const objTempName = buttonAct === 1 ? BATCH_TYPE.OPENING_TEMP : BATCH_TYPE.SHADOW_TEMP
let rect, isDown, origX, origY
let selectedSurface
//프리입력
if (selectedType === INPUT_TYPE.FREE) {
addCanvasMouseEventListener('mouse:down', (e) => {
isDown = true
const pointer = canvas.getPointer(e.e)
surfaceShapePolygons.forEach((surface) => {
if (surface.inPolygon({ x: pointer.x, y: pointer.y })) {
selectedSurface = surface
}
})
if (!selectedSurface) {
swalFire({ text: '지붕안에 그려야해요', icon: 'error' })
initEvent() //이벤트 초기화
return
}
origX = pointer.x
origY = pointer.y
rect = new fabric.Rect({
left: origX,
top: origY,
originX: 'left',
originY: 'top',
width: 0,
height: 0,
angle: 0,
stroke: 'black',
})
//개구냐 그림자냐에 따라 변경
rect.set({
fill: buttonAct === 1 ? 'white' : 'rgba(0, 0, 0, 0.4)',
name: objTempName,
})
canvas?.add(rect)
})
addCanvasMouseEventListener('mouse:move', (e) => {
if (!isDown) return
if (selectedSurface) {
const pointer = canvas.getPointer(e.e)
const width = pointer.x - origX
const height = pointer.y - origY
rect.set({ width: Math.abs(width), height: Math.abs(height) })
if (width < 0) {
rect.set({ left: Math.abs(pointer.x) })
}
if (height < 0) {
rect.set({ top: Math.abs(pointer.y) })
}
canvas?.renderAll()
}
})
addCanvasMouseEventListener('mouse:up', (e) => {
if (rect) {
const rectPolygon = pointsToTurfPolygon(rectToPolygon(rect))
const selectedSurfacePolygon = polygonToTurfPolygon(selectedSurface)
//지붕 밖으로 그렸을때
if (!turf.booleanWithin(rectPolygon, selectedSurfacePolygon)) {
swalFire({ text: '개구를 배치할 수 없습니다.', icon: 'error' })
//일단 지워
deleteTempObjects()
return
}
if (!isCrossChecked) {
const preObjects = canvas?.getObjects().filter((obj) => obj.name === BATCH_TYPE.OPENING || obj.name === BATCH_TYPE.SHADOW)
const preObjectsArray = preObjects.map((obj) => rectToPolygon(obj))
const isCross = preObjectsArray.some((object) => turf.booleanOverlap(pointsToTurfPolygon(object), rectPolygon))
if (isCross) {
swalFire({ text: '겹치기 불가요...', icon: 'error' })
deleteTempObjects()
return
}
}
isDown = false
rect.set({ name: objName })
rect.setCoords()
initEvent()
}
})
} else if (selectedType === INPUT_TYPE.DIMENSION) {
const width = objectPlacement.widthRef.current.value / 10
const height = objectPlacement.heightRef.current.value / 10
if (width === '' || height === '' || width <= 0 || height <= 0) {
swalFire({ text: getMessage('common.canvas.validate.size'), icon: 'error' })
return
}
// setShowObjectSettingModal(false) //메뉴보이고
addCanvasMouseEventListener('mouse:move', (e) => {
isDown = true
if (!isDown) return
canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === objTempName)) //움직일때 일단 지워가면서 움직임
const pointer = canvas.getPointer(e.e)
surfaceShapePolygons.forEach((surface) => {
if (surface.inPolygon({ x: pointer.x, y: pointer.y })) {
selectedSurface = surface
}
})
rect = new fabric.Rect({
fill: 'white',
stroke: 'black',
strokeWidth: 1,
width: width,
height: height,
left: pointer.x - width / 2,
top: pointer.y - height / 2,
selectable: true,
lockMovementX: true,
lockMovementY: true,
lockRotation: true,
lockScalingX: true,
lockScalingY: true,
})
//개구냐 그림자냐에 따라 변경
rect.set({
fill: buttonAct === 1 ? 'white' : 'rgba(0, 0, 0, 0.4)',
name: objTempName,
})
canvas?.add(rect)
})
addCanvasMouseEventListener('mouse:up', (e) => {
if (rect) {
const rectPolygon = pointsToTurfPolygon(rectToPolygon(rect))
const selectedSurfacePolygon = polygonToTurfPolygon(selectedSurface)
//지붕 밖으로 그렸을때
if (!turf.booleanWithin(rectPolygon, selectedSurfacePolygon)) {
swalFire({ text: '개구를 배치할 수 없습니다.', icon: 'error' })
//일단 지워
deleteTempObjects()
return
}
if (!isCrossChecked) {
const preObjects = canvas?.getObjects().filter((obj) => obj.name === BATCH_TYPE.OPENING || obj.name === BATCH_TYPE.SHADOW)
const preObjectsArray = preObjects.map((obj) => rectToPolygon(obj))
const isCross = preObjectsArray.some((object) => turf.booleanOverlap(pointsToTurfPolygon(object), rectPolygon))
if (isCross) {
swalFire({ text: '겹치기 불가요...', icon: 'error' })
deleteTempObjects()
return
}
}
isDown = false
rect.set({ name: objName })
rect.setCoords()
initEvent()
}
})
}
}
const applyDormers = (dormerPlacement, buttonAct, surfaceShapePolygons) => {
const dormerName = buttonAct === 3 ? BATCH_TYPE.TRIANGLE_DORMER : BATCH_TYPE.PENTAGON_DORMER
const dormerTempName = buttonAct === 3 ? BATCH_TYPE.TRIANGLE_DORMER_TEMP : BATCH_TYPE.PENTAGON_DORMER_TEMP
const height = dormerPlacement.heightRef.current.value / 10
const pitch = dormerPlacement.pitchRef.current.value
const directionRef = dormerPlacement.directionRef.current
const offsetRef = dormerPlacement.offsetRef.current.value === '' ? 0 : parseInt(dormerPlacement.offsetRef.current.value) / 10
let dormer, dormerOffset, isDown, selectedSurface
console.log('dormerPlacement', dormerPlacement)
if (height === '' || pitch === '' || height <= 0 || pitch <= 0) {
swalFire({ text: getMessage('common.canvas.validate.size'), icon: 'error' })
return
}
//삼각형 도머
if (buttonAct === 3) {
const bottomLength = height / (pitch * 0.25)
const bottomOffsetLength = (height + offsetRef) / (pitch * 0.25)
console.log(bottomOffsetLength)
addCanvasMouseEventListener('mouse:move', (e) => {
isDown = true
if (!isDown) return
canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === dormerTempName)) //움직일때 일단 지워가면서 움직임
const pointer = canvas.getPointer(e.e)
surfaceShapePolygons.forEach((surface) => {
if (surface.inPolygon({ x: pointer.x, y: pointer.y })) {
selectedSurface = surface
}
})
let angle = 0
if (directionRef === 'left') {
//서
angle = 90
} else if (directionRef === 'right') {
//동
angle = 270
} else if (directionRef === 'up') {
//북
angle = 180
}
dormer = new fabric.Triangle({
fill: 'white',
stroke: 'red',
strokeDashArray: [5, 5],
strokeWidth: 1,
width: bottomLength * 2,
height: height,
left: pointer.x,
top: pointer.y,
selectable: true,
lockMovementX: true,
lockMovementY: true,
lockRotation: true,
lockScalingX: true,
lockScalingY: true,
name: dormerTempName,
originX: 'center',
originY: 'top',
angle: angle,
})
canvas?.add(dormer)
if (offsetRef > 0) {
dormerOffset = new fabric.Triangle({
fill: 'gray',
stroke: 'red',
strokeDashArray: [5, 5],
strokeWidth: 1,
width: bottomOffsetLength * 2,
height: height + offsetRef,
left: pointer.x,
top: pointer.y,
selectable: true,
lockMovementX: true,
lockMovementY: true,
lockRotation: true,
lockScalingX: true,
lockScalingY: true,
name: dormerTempName,
originX: 'center',
originY: 'top',
angle: angle,
})
canvas?.add(dormerOffset)
}
})
addCanvasMouseEventListener('mouse:up', (e) => {
if (dormer) {
// const trianglePolygon = pointsToTurfPolygon(triangleToPolygon(dormer))
// const selectedSurfacePolygon = polygonToTurfPolygon(selectedSurface)
// //지붕 밖으로 그렸을때
// if (!turf.booleanWithin(trianglePolygon, selectedSurfacePolygon)) {
// swalFire({ text: '개구를 배치할 수 없습니다.', icon: 'error' })
// //일단 지워
// deleteTempObjects()
// return
// }
//각도 추가
let originAngle = 0 //기본 남쪽
let direction = 'south'
if (directionRef === 'left') {
//서
originAngle = 90
direction = 'west'
} else if (directionRef === 'right') {
//동
originAngle = 270
direction = 'east'
} else if (directionRef === 'up') {
//북
originAngle = 180
direction = 'north'
}
let splitedTriangle = offsetRef > 0 ? splitDormerTriangle(dormerOffset, directionRef) : splitDormerTriangle(dormer, directionRef)
canvas?.remove(offsetRef > 0 ? dormerOffset : dormer)
if (offsetRef > 0)
dormer.set({
name: dormerName,
stroke: 'black',
strokeWidth: 1,
strokeDashArray: [0],
}) //오프셋이 있을땐 같이 도머로 만든다
const leftTriangle = new QPolygon(splitedTriangle[0], {
fill: 'transparent',
stroke: 'black',
strokeWidth: 1,
selectable: true,
lockMovementX: true, // X 축 이동 잠금
lockMovementY: true, // Y 축 이동 잠금
lockRotation: true, // 회전 잠금
viewLengthText: true,
fontSize: 14,
direction: direction,
originX: 'center',
originY: 'center',
name: dormerName,
})
const rightTriangle = new QPolygon(splitedTriangle[1], {
fill: 'transparent',
stroke: 'black',
strokeWidth: 1,
selectable: true,
lockMovementX: true, // X 축 이동 잠금
lockMovementY: true, // Y 축 이동 잠금
lockRotation: true, // 회전 잠금
viewLengthText: true,
fontSize: 14,
direction: direction,
originX: 'center',
originY: 'center',
name: dormerName,
})
canvas?.add(leftTriangle)
canvas?.add(rightTriangle)
//패턴
setSurfaceShapePattern(leftTriangle)
setSurfaceShapePattern(rightTriangle)
//방향
drawDirectionArrow(leftTriangle)
drawDirectionArrow(rightTriangle)
isDown = false
initEvent()
}
})
}
}
const deleteTempObjects = () => {
const deleteTarget = canvas
?.getObjects()
.filter(
(obj) =>
obj.name === BATCH_TYPE.OPENING_TEMP ||
obj.name === BATCH_TYPE.SHADOW_TEMP ||
obj.name === BATCH_TYPE.TRIANGLE_DORMER_TEMP ||
obj.name === BATCH_TYPE.PENTAGON_DORMER_TEMP,
)
canvas?.remove(...deleteTarget)
initEvent() //이벤트 초기화
}
return {
applyOpeningAndShadow,
applyDormers,
}
}

View File

@ -23,31 +23,23 @@ import {
outerLineLength2State, outerLineLength2State,
outerLineTypeState, outerLineTypeState,
} from '@/store/outerLineAtom' } from '@/store/outerLineAtom'
import { calculateDistance, calculateIntersection, distanceBetweenPoints, findClosestPoint } from '@/util/canvas-util' import { calculateDistance, calculateIntersection, distanceBetweenPoints, findClosestPoint, polygonToTurfPolygon } from '@/util/canvas-util'
import { fabric } from 'fabric' import { fabric } from 'fabric'
import { useAdsorptionPoint } from '@/hooks/useAdsorptionPoint' import { useAdsorptionPoint } from '@/hooks/useAdsorptionPoint'
import { useSwal } from '@/hooks/useSwal'
import { booleanPointInPolygon } from '@turf/turf'
export function useAuxiliaryDrawing() { // 보조선 작성
export function useAuxiliaryDrawing(setShowAuxiliaryModal) {
const canvas = useRecoilValue(canvasState) const canvas = useRecoilValue(canvasState)
const { const { addCanvasMouseEventListener, addDocumentEventListener, removeMouseLine, initEvent } = useEvent()
addCanvasMouseEventListener,
addDocumentEventListener,
removeAllMouseEventListeners,
removeAllDocumentEventListeners,
removeMouseEvent,
removeMouseLine,
initEvent,
} = useEvent()
const { getIntersectMousePoint } = useMouse() const { getIntersectMousePoint } = useMouse()
const { addLine, removeLine } = useLine() const { addLine, removeLine } = useLine()
const { tempGridMode } = useTempGrid() const { tempGridMode } = useTempGrid()
const { swalFire } = useSwal()
const { getAdsorptionPoints } = useAdsorptionPoint() const { getAdsorptionPoints } = useAdsorptionPoint()
const verticalHorizontalMode = useRecoilValue(verticalHorizontalModeState)
const adsorptionPointAddMode = useRecoilValue(adsorptionPointAddModeState)
const adsorptionPointMode = useRecoilValue(adsorptionPointModeState)
const adsorptionRange = useRecoilValue(adsorptionRangeState) const adsorptionRange = useRecoilValue(adsorptionRangeState)
const interval = useRecoilValue(dotLineIntervalSelector) // 가로 세로 간격
const [buttonAct, setButtonAct] = useState(1) const [buttonAct, setButtonAct] = useState(1)
@ -85,12 +77,16 @@ export function useAuxiliaryDrawing() {
useEffect(() => { useEffect(() => {
typeRef.current = type typeRef.current = type
clear()
addDocumentEventListener('keydown', document, keydown[type])
}, [type]) }, [type])
useEffect(() => { useEffect(() => {
// innerLines가 있을경우 삭제 // innerLines가 있을경우 삭제
const roofs = canvas?.getObjects().filter((obj) => obj.name === 'roofBase') const roofs = canvas?.getObjects().filter((obj) => obj.name === 'roofBase')
if (roofs.length === 0) { if (roofs.length === 0) {
swalFire({ text: '지붕형상이 없습니다.' })
setShowAuxiliaryModal(false)
return return
} }
@ -101,7 +97,7 @@ export function useAuxiliaryDrawing() {
addCanvasMouseEventListener('mouse:move', mouseMove) addCanvasMouseEventListener('mouse:move', mouseMove)
addCanvasMouseEventListener('mouse:down', mouseDown) addCanvasMouseEventListener('mouse:down', mouseDown)
addDocumentEventListener('contextmenu', document, cutAuxiliary) addDocumentEventListener('contextmenu', document, cutAuxiliary)
addDocumentEventListener('keydown', document, keydown) addDocumentEventListener('keydown', document, keydown[type])
return () => { return () => {
canvas.remove(...canvas.getObjects().filter((obj) => obj.name === 'innerPoint')) canvas.remove(...canvas.getObjects().filter((obj) => obj.name === 'innerPoint'))
@ -110,12 +106,8 @@ export function useAuxiliaryDrawing() {
} }
}, []) }, [])
useEffect(() => {
clear()
addDocumentEventListener('keydown', document, keydown[type])
}, [type])
const clear = () => { const clear = () => {
addCanvasMouseEventListener('mouse:move', mouseMove)
setLength1(0) setLength1(0)
setLength2(0) setLength2(0)
@ -130,7 +122,6 @@ export function useAuxiliaryDrawing() {
const keydown = { const keydown = {
outerLine: (e) => { outerLine: (e) => {
console.log(123)
if (mousePointerArr.current.length === 0) { if (mousePointerArr.current.length === 0) {
return return
} }
@ -467,7 +458,9 @@ export function useAuxiliaryDrawing() {
} }
const mouseDown = (e) => { const mouseDown = (e) => {
canvas.renderAll()
const pointer = getIntersectMousePoint(e) const pointer = getIntersectMousePoint(e)
console.log(pointer)
mousePointerArr.current.push(pointer) mousePointerArr.current.push(pointer)
if (mousePointerArr.current.length === 2) { if (mousePointerArr.current.length === 2) {
@ -498,7 +491,6 @@ export function useAuxiliaryDrawing() {
auxiliaryLines.forEach((line1) => { auxiliaryLines.forEach((line1) => {
auxiliaryLines.forEach((line2) => { auxiliaryLines.forEach((line2) => {
const lines = [line1, line2]
if (line1 === line2) { if (line1 === line2) {
return return
} }
@ -546,59 +538,66 @@ export function useAuxiliaryDrawing() {
// 보조선 절삭 // 보조선 절삭
const cutAuxiliary = (e) => { const cutAuxiliary = (e) => {
const auxiliaryLines = canvas.getObjects().filter((obj) => obj.name === 'auxiliaryLine' && !obj.isFixed) const auxiliaryLines = canvas.getObjects().filter((obj) => obj.name === 'auxiliaryLine')
if (auxiliaryLines.length === 0) { if (auxiliaryLines.length === 0) {
return return
} }
const roofBases = canvas.getObjects().filter((obj) => obj.name === 'roofBase')
const allLines = [...auxiliaryLines]
roofBases.forEach((roofBase) => {
roofBase.lines.forEach((line) => {
allLines.push(line)
})
})
auxiliaryLines.forEach((line1) => { auxiliaryLines.forEach((line1) => {
auxiliaryLines.forEach((line2) => { allLines.forEach((line2) => {
const lines = [line1, line2]
if (line1 === line2) { if (line1 === line2) {
return return
} }
const intersectionPoint = calculateIntersection(line1, line2) const intersectionPoint = calculateIntersection(line1, line2)
if (!intersectionPoint || intersectionPoints.current.some((point) => point.x === intersectionPoint.x && point.y === intersectionPoint.y)) { if (!intersectionPoint) {
return return
} }
roofAdsorptionPoints.current.push(intersectionPoint) roofAdsorptionPoints.current.push(intersectionPoint)
intersectionPoints.current.push(intersectionPoint) intersectionPoints.current.push(intersectionPoint)
lines.forEach((line) => {
const distance1 = distanceBetweenPoints({ x: line.x1, y: line.y1 }, intersectionPoint)
const distance2 = distanceBetweenPoints({ x: line.x2, y: line.y2 }, intersectionPoint)
if (distance1 === 0 || distance2 === 0) { const distance1 = distanceBetweenPoints({ x: line1.x1, y: line1.y1 }, intersectionPoint)
return const distance2 = distanceBetweenPoints({ x: line1.x2, y: line1.y2 }, intersectionPoint)
}
//historyLine에서 기존 line을 제거한다.
lineHistory.current = lineHistory.current.filter((history) => history !== line)
let newLine if (distance1 === 0 || distance2 === 0) {
return
}
//historyLine에서 기존 line을 제거한다.
lineHistory.current = lineHistory.current.filter((history) => history !== line1)
if (distance1 >= distance2) { let newLine
newLine = addLine([line.x1, line.y1, intersectionPoint.x, intersectionPoint.y], {
stroke: 'black', if (distance1 >= distance2) {
strokeWidth: 1, newLine = addLine([line1.x1, line1.y1, intersectionPoint.x, intersectionPoint.y], {
selectable: false, stroke: 'black',
name: 'auxiliaryLine', strokeWidth: 1,
isFixed: true, selectable: false,
intersectionPoint, name: 'auxiliaryLine',
}) isFixed: true,
} else { intersectionPoint,
newLine = addLine([line.x2, line.y2, intersectionPoint.x, intersectionPoint.y], { })
stroke: 'black', } else {
strokeWidth: 1, newLine = addLine([line1.x2, line1.y2, intersectionPoint.x, intersectionPoint.y], {
selectable: false, stroke: 'black',
name: 'auxiliaryLine', strokeWidth: 1,
isFixed: true, selectable: false,
intersectionPoint, name: 'auxiliaryLine',
}) isFixed: true,
} intersectionPoint,
lineHistory.current.push(newLine) })
removeLine(line) }
}) lineHistory.current.push(newLine)
removeLine(line1)
}) })
}) })
addCanvasMouseEventListener('mouse:move', mouseMove) addCanvasMouseEventListener('mouse:move', mouseMove)
@ -624,11 +623,31 @@ export function useAuxiliaryDrawing() {
addCanvasMouseEventListener('mouse:move', mouseMove) addCanvasMouseEventListener('mouse:move', mouseMove)
} }
const handleFix = (fn) => { const handleFix = () => {
if (!confirm('지붕선 완료하시겠습니까?')) { if (!confirm('지붕선 완료하시겠습니까?')) {
return return
} }
fn(close)
const roofBases = canvas.getObjects().filter((obj) => obj.name === 'roofBase')
const innerLines = [...lineHistory.current]
roofBases.forEach((roofBase) => {
const roofInnerLines = innerLines.filter((line) => {
const turfPolygon = polygonToTurfPolygon(roofBase)
// innerLines의 두 점이 모두 polygon 안에 있는지 확인
const inPolygon1 = booleanPointInPolygon([line.x1, line.y1], turfPolygon)
const inPolygon2 = booleanPointInPolygon([line.x2, line.y2], turfPolygon)
if (inPolygon1 && inPolygon2) {
return true
}
})
roofBase.innerLines = [...roofInnerLines]
})
setShowAuxiliaryModal(false)
} }
return { return {

View File

@ -6,8 +6,11 @@ import { useEvent } from '@/hooks/useEvent'
import { LINE_TYPE } from '@/common/common' import { LINE_TYPE } from '@/common/common'
import { useLine } from '@/hooks/useLine' import { useLine } from '@/hooks/useLine'
import { useMode } from '@/hooks/useMode' import { useMode } from '@/hooks/useMode'
import { outerLineFixState } from '@/store/outerLineAtom'
import { useSwal } from '@/hooks/useSwal'
export function useEavesGableEdit() { // 처마.케라바 변경
export function useEavesGableEdit(setShowEavesGableEditModal) {
const canvas = useRecoilValue(canvasState) const canvas = useRecoilValue(canvasState)
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { addCanvasMouseEventListener, initEvent } = useEvent() const { addCanvasMouseEventListener, initEvent } = useEvent()
@ -20,6 +23,7 @@ export function useEavesGableEdit() {
const [type, setType] = useState(TYPES.EAVES) const [type, setType] = useState(TYPES.EAVES)
const typeRef = useRef(TYPES.EAVES) const typeRef = useRef(TYPES.EAVES)
const { removeLine } = useLine() const { removeLine } = useLine()
const { swalFire } = useSwal()
const { drawRoofPolygon } = useMode() const { drawRoofPolygon } = useMode()
@ -27,6 +31,7 @@ export function useEavesGableEdit() {
const offsetRef = useRef(null) const offsetRef = useRef(null)
const widthRef = useRef(null) const widthRef = useRef(null)
const radioTypeRef = useRef('1') // 각 페이지에서 사용하는 radio type const radioTypeRef = useRef('1') // 각 페이지에서 사용하는 radio type
const outerLineFix = useRecoilValue(outerLineFixState)
const buttonMenu = [ const buttonMenu = [
{ id: 1, name: getMessage('eaves'), type: TYPES.EAVES }, { id: 1, name: getMessage('eaves'), type: TYPES.EAVES },
@ -35,6 +40,14 @@ export function useEavesGableEdit() {
{ id: 4, name: getMessage('shed'), type: TYPES.SHED }, { id: 4, name: getMessage('shed'), type: TYPES.SHED },
] ]
useEffect(() => {
const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
if (!outerLineFix || outerLines.length === 0) {
swalFire({ text: '외벽선이 없습니다.' })
setShowEavesGableEditModal(false)
}
}, [])
useEffect(() => { useEffect(() => {
const wallLines = canvas.getObjects().filter((obj) => obj.name === 'wallLine') const wallLines = canvas.getObjects().filter((obj) => obj.name === 'wallLine')
wallLines.forEach((wallLine) => { wallLines.forEach((wallLine) => {

View File

@ -29,8 +29,10 @@ import {
} from '@/store/outerLineAtom' } from '@/store/outerLineAtom'
import { calculateAngle } from '@/util/qpolygon-utils' import { calculateAngle } from '@/util/qpolygon-utils'
import { fabric } from 'fabric' import { fabric } from 'fabric'
import { QLine } from '@/components/fabric/QLine'
export function useOuterLineWall() { //외벽선 그리기
export function useOuterLineWall(setShowOutlineModal) {
const canvas = useRecoilValue(canvasState) const canvas = useRecoilValue(canvasState)
const { addCanvasMouseEventListener, addDocumentEventListener, removeAllMouseEventListeners, removeAllDocumentEventListeners, removeMouseEvent } = const { addCanvasMouseEventListener, addDocumentEventListener, removeAllMouseEventListeners, removeAllDocumentEventListeners, removeMouseEvent } =
useEvent() useEvent()
@ -67,8 +69,6 @@ export function useOuterLineWall() {
const isFix = useRef(false) const isFix = useRef(false)
const closeModalFn = useRef(null)
useEffect(() => { useEffect(() => {
if (adsorptionPointAddMode || tempGridMode) { if (adsorptionPointAddMode || tempGridMode) {
return return
@ -208,18 +208,19 @@ export function useOuterLineWall() {
removeAllMouseEventListeners() removeAllMouseEventListeners()
removeAllDocumentEventListeners() removeAllDocumentEventListeners()
canvas?.renderAll() canvas?.renderAll()
closeModalFn.current(false) setOuterLineFix(true)
setShowOutlineModal(false)
} }
if (points.length < 3) { if (points.length < 3) {
return return
} }
/*if (lastPoint.x === firstPoint.x && lastPoint.y === firstPoint.y) { if (Math.abs(lastPoint.x - firstPoint.x) < 1 && Math.abs(lastPoint.y - firstPoint.y) < 1) {
return return
} }
if (lastPoint.x === firstPoint.x || lastPoint.y === firstPoint.y) { if (Math.abs(lastPoint.x - firstPoint.x) < 1 || Math.abs(lastPoint.y - firstPoint.y) < 1) {
let isAllRightAngle = true let isAllRightAngle = true
const firstPoint = points[0] const firstPoint = points[0]
@ -238,38 +239,27 @@ export function useOuterLineWall() {
if (isAllRightAngle) { if (isAllRightAngle) {
return return
} }
const line = new QLine([lastPoint.x, lastPoint.y, firstPoint.x, firstPoint.y], { const line = addLine([lastPoint.x, lastPoint.y, firstPoint.x, firstPoint.y], {
stroke: 'grey', stroke: 'grey',
strokeWidth: 1, strokeWidth: 1,
selectable: false, selectable: false,
name: 'helpGuideLine', name: 'helpGuideLine',
}) })
canvas?.add(line)
addLineText(line)
} else { } else {
const guideLine1 = new QLine([lastPoint.x, lastPoint.y, lastPoint.x, firstPoint.y], { const guideLine1 = addLine([lastPoint.x, lastPoint.y, lastPoint.x, firstPoint.y], {
stroke: 'grey', stroke: 'grey',
strokeWidth: 1, strokeWidth: 1,
strokeDashArray: [1, 1, 1], strokeDashArray: [1, 1, 1],
name: 'helpGuideLine', name: 'helpGuideLine',
}) })
const guideLine2 = new QLine([guideLine1.x2, guideLine1.y2, firstPoint.x, firstPoint.y], { const guideLine2 = addLine([guideLine1.x2, guideLine1.y2, firstPoint.x, firstPoint.y], {
stroke: 'grey', stroke: 'grey',
strokeWidth: 1, strokeWidth: 1,
strokeDashArray: [1, 1, 1], strokeDashArray: [1, 1, 1],
name: 'helpGuideLine', name: 'helpGuideLine',
}) })
if (guideLine1.length > 0) { }
canvas?.add(guideLine1)
addLineText(guideLine1)
}
canvas?.add(guideLine2)
addLineText(guideLine2)
}*/
} }
}, [points]) }, [points])
@ -748,7 +738,7 @@ export function useOuterLineWall() {
setPoints((prev) => prev.slice(0, prev.length - 1)) setPoints((prev) => prev.slice(0, prev.length - 1))
} }
const handleFix = (fn) => { const handleFix = () => {
if (points.length < 3) { if (points.length < 3) {
return return
} }
@ -778,7 +768,6 @@ export function useOuterLineWall() {
}) })
isFix.current = true isFix.current = true
closeModalFn.current = fn
} }
return { return {

View File

@ -7,7 +7,8 @@ import { usePolygon } from '@/hooks/usePolygon'
import { useLine } from '@/hooks/useLine' import { useLine } from '@/hooks/useLine'
import { outerLinePointsState } from '@/store/outerLineAtom' import { outerLinePointsState } from '@/store/outerLineAtom'
export function usePropertiesSetting() { // 외벽선 속성 설정
export function usePropertiesSetting(setShowPropertiesSettingModal) {
const canvas = useRecoilValue(canvasState) const canvas = useRecoilValue(canvasState)
const currentObject = useRecoilValue(currentObjectState) const currentObject = useRecoilValue(currentObjectState)
@ -19,17 +20,7 @@ export function usePropertiesSetting() {
const { removeLine, hideLine } = useLine() const { removeLine, hideLine } = useLine()
useEffect(() => { useEffect(() => {
if (!currentObject) {
return
}
if (currentObject.name !== 'outerLine') {
return
}
const type = currentObject.attributes?.type
const lines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') const lines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
lines.forEach((line) => { lines.forEach((line) => {
const lineType = line.attributes?.type const lineType = line.attributes?.type
if (!lineType) { if (!lineType) {
@ -39,6 +30,14 @@ export function usePropertiesSetting() {
}) })
} }
}) })
if (!currentObject) {
return
}
if (currentObject.name !== 'outerLine') {
return
}
const type = currentObject.attributes?.type
if (!type) { if (!type) {
currentObject.set({ currentObject.set({
@ -46,6 +45,8 @@ export function usePropertiesSetting() {
strokeWidth: 4, strokeWidth: 4,
}) })
} }
canvas.renderAll()
}, [currentObject]) }, [currentObject])
const history = useRef([]) const history = useRef([])
@ -106,6 +107,7 @@ export function usePropertiesSetting() {
} }
const lastLine = history.current.pop() const lastLine = history.current.pop()
delete lastLine.attributes
lastLine.set({ lastLine.set({
stroke: '#000000', stroke: '#000000',
strokeWidth: 4, strokeWidth: 4,
@ -131,7 +133,7 @@ export function usePropertiesSetting() {
hideLine(line) hideLine(line)
}) })
const wall = addPolygonByLines(lines, { name: 'WallLine', fill: 'transparent', stroke: 'black' }) const wall = addPolygonByLines(lines, { name: 'wallLine', fill: 'transparent', stroke: 'black' })
wall.lines = [...lines] wall.lines = [...lines]
@ -159,7 +161,7 @@ export function usePropertiesSetting() {
canvas.renderAll() canvas.renderAll()
setPoints([]) setPoints([])
fn(false) setShowPropertiesSettingModal(false)
} }
return { handleSetEaves, handleSetGable, handleRollback, handleFix, closeModal } return { handleSetEaves, handleSetGable, handleRollback, handleFix, closeModal }

View File

@ -0,0 +1,143 @@
import { useRecoilValue } from 'recoil'
import { canvasState } from '@/store/canvasAtom'
import { useEffect, useState } from 'react'
import { setSurfaceShapePattern } from '@/util/canvas-util'
import { drawDirectionArrow, splitPolygonWithLines } from '@/util/qpolygon-utils'
import { useSwal } from '@/hooks/useSwal'
// 지붕면 할당
export function useRoofAllocationSetting(setShowRoofAllocationSettingModal) {
const canvas = useRecoilValue(canvasState)
const { swalFire } = useSwal()
const roofMaterials = [
{
id: 'A',
name: '기와1',
type: 'A',
width: '200',
length: '200',
alignType: 'parallel',
},
{
id: 'B',
name: '기와2',
type: 'B',
rafter: '200',
alignType: 'parallel',
},
{
id: 'C',
name: '기와3',
type: 'C',
hajebichi: '200',
alignType: 'stairs',
},
{
id: 'D',
name: '기와4',
type: 'D',
length: '200',
alignType: 'stairs',
},
]
const widths = [
{ name: '200', id: 'q' },
{ name: '250', id: 'q1' },
{ name: '300', id: 'q2' },
]
const lengths = [
{ name: '200', id: 'w' },
{ name: '250', id: 'w1' },
{ name: '300', id: 'w2' },
]
const rafters = [
{ name: '200', id: 'e' },
{ name: '250', id: 'e1' },
{ name: '300', id: 'e2' },
]
const [values, setValues] = useState([
{
id: 'A',
type: 'A',
roofMaterial: { name: '기와1' },
width: { name: '200' },
length: { name: '250' },
rafter: { name: '300' },
alignType: 'stairs',
},
])
const [radioValue, setRadioValue] = useState('A')
const [selectedRoofMaterial, setSelectedRoofMaterial] = useState(roofMaterials[0])
useEffect(() => {
const roofBases = canvas.getObjects().filter((obj) => obj.name === 'roofBase')
if (roofBases.length === 0) {
swalFire({ text: '할당할 지붕이 없습니다.' })
setShowRoofAllocationSettingModal(false)
}
}, [])
const onAddRoofMaterial = () => {
setValues([...values, selectedRoofMaterial])
}
const onDeleteRoofMaterial = (id) => {
setValues(values.filter((value) => value.id !== id))
}
// 선택한 지붕재로 할당
const handleSave = () => {
const roofBases = canvas.getObjects().filter((obj) => obj.name === 'roofBase')
const wallLines = canvas.getObjects().filter((obj) => obj.name === 'wallLine')
roofBases.forEach((roofBase) => {
try {
splitPolygonWithLines(roofBase)
} catch (e) {
return
}
roofBase.innerLines.forEach((line) => {
canvas.remove(line)
})
canvas.remove(roofBase)
})
wallLines.forEach((wallLine) => {
canvas.remove(wallLine)
})
const roofs = canvas.getObjects().filter((obj) => obj.name === 'roof')
roofs.forEach((roof) => {
setSurfaceShapePattern(roof)
drawDirectionArrow(roof)
})
setShowRoofAllocationSettingModal(false)
}
const handleRadioOnChange = (e) => {
setRadioValue(e.target)
}
return {
handleSave,
onAddRoofMaterial,
onDeleteRoofMaterial,
handleRadioOnChange,
widths,
lengths,
rafters,
values,
roofMaterials,
selectedRoofMaterial,
setSelectedRoofMaterial,
radioValue,
setRadioValue,
}
}

View File

@ -0,0 +1,220 @@
import { canvasState, currentObjectState } from '@/store/canvasAtom'
import { useRecoilValue } from 'recoil'
import { useEffect, useRef, useState } from 'react'
import { useLine } from '@/hooks/useLine'
import { useMessage } from '@/hooks/useMessage'
import { useEvent } from '@/hooks/useEvent'
import { LINE_TYPE } from '@/common/common'
import { useMode } from '@/hooks/useMode'
import { usePolygon } from '@/hooks/usePolygon'
import { outerLineFixState } from '@/store/outerLineAtom'
import { useSwal } from '@/hooks/useSwal'
//지붕형상 수동 설정
export function useRoofShapePassivitySetting(setShowRoofShapePassivitySettingModal) {
const TYPES = {
EAVES: 'eaves',
GABLE: 'gable',
SHED: 'shed',
}
const canvas = useRecoilValue(canvasState)
const { getMessage } = useMessage()
const { showLine, hideLine } = useLine()
const { swalFire } = useSwal()
const { addCanvasMouseEventListener, initEvent } = useEvent()
const { drawRoofPolygon } = useMode()
const { addPolygonByLines } = usePolygon()
const currentObject = useRecoilValue(currentObjectState)
const offsetRef = useRef(null)
const pitchRef = useRef(null)
const currentLineRef = useRef(null)
const history = useRef([])
const [type, setType] = useState(TYPES.EAVES)
const isFix = useRef(false)
const initLines = useRef([])
const [isLoading, setIsLoading] = useState(false)
const buttons = [
{ id: 1, name: getMessage('eaves'), type: TYPES.EAVES },
{ id: 2, name: getMessage('gable'), type: TYPES.GABLE },
{ id: 3, name: getMessage('windage'), type: TYPES.SHED },
]
const outerLineFix = useRecoilValue(outerLineFixState)
useEffect(() => {
const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
if (!outerLineFix || outerLines.length === 0) {
swalFire({ text: '외벽선이 없습니다.' })
setShowRoofShapePassivitySettingModal(false)
return
}
setIsLoading(true)
}, [])
useEffect(() => {
if (!isLoading) return
addCanvasMouseEventListener('mouse:down', mouseDown)
const wallLines = canvas.getObjects().filter((obj) => obj.name === 'wallLine')
canvas?.remove(...wallLines)
const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
initLines.current = outerLines.map((line) => ({ ...line }))
outerLines.forEach((outerLine, idx) => {
if (idx === 0) {
currentLineRef.current = outerLine
}
outerLine.set({ selectable: true })
showLine(outerLine)
outerLine.bringToFront()
})
canvas?.renderAll()
return () => {
handleLineToPolygon()
canvas?.discardActiveObject()
initEvent()
}
}, [isLoading])
useEffect(() => {
const lines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
lines.forEach((line) => {
line.set({
stroke: '#000000',
strokeWidth: 4,
})
})
if (!currentObject) {
return
}
if (currentObject.name !== 'outerLine') {
return
}
currentObject.set({
stroke: '#EA10AC',
strokeWidth: 4,
})
currentLineRef.current = currentObject
canvas.renderAll()
}, [currentObject])
const mouseDown = (e) => {
if (!e.target) {
currentLineRef.current = null
return
}
if (e.target && e.target.name === 'outerLine') {
currentLineRef.current = e.target
}
}
const nextLineFocus = (selectedLine) => {
const lines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
const index = lines.findIndex((line) => line === selectedLine)
const nextLine = lines[index + 1] || lines[0]
canvas.setActiveObject(nextLine)
}
const handleConfirm = () => {
if (!currentLineRef.current) {
alert('선택된 외곽선이 없습니다.')
return
}
let attributes
const offset = Number(offsetRef.current.value) / 10
if (type === TYPES.EAVES) {
attributes = {
type: LINE_TYPE.WALLLINE.EAVES,
offset,
pitch: pitchRef.current.value,
}
} else if (type === TYPES.GABLE) {
attributes = {
type: LINE_TYPE.WALLLINE.GABLE,
pitch: pitchRef.current.value,
offset,
}
} else if (type === TYPES.SHED) {
attributes = {
type: LINE_TYPE.WALLLINE.SHED,
offset,
}
}
currentLineRef.current.set({
attributes,
})
history.current.push(currentLineRef.current)
nextLineFocus(currentLineRef.current)
}
const handleRollback = () => {
if (history.current.length === 0) {
return
}
const lastLine = history.current.pop()
delete lastLine.attributes
lastLine.set({
stroke: '#000000',
strokeWidth: 4,
})
canvas.setActiveObject(lastLine)
canvas.renderAll()
}
const handleSave = () => {
isFix.current = true
handleLineToPolygon()
setShowRoofShapePassivitySettingModal(false)
}
const handleLineToPolygon = () => {
const roofBases = canvas.getObjects().filter((obj) => obj.name === 'roofBase')
const exceptObjs = canvas.getObjects().filter((obj) => obj.name !== 'outerLine' && obj.parent?.name !== 'outerLine')
const lines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
exceptObjs.forEach((obj) => {
canvas.remove(obj)
})
lines.forEach((line) => {
hideLine(line)
})
let wall
if (isFix.current) {
wall = addPolygonByLines(lines, { name: 'wallLine', fill: 'transparent', stroke: 'black' })
} else {
// 그냥 닫을 경우 처리
wall = addPolygonByLines([...initLines.current], { name: 'wallLine', fill: 'transparent', stroke: 'black' })
lines.forEach((line, idx) => {
line.attributes = initLines.current[idx].attributes
})
}
wall.lines = [...lines]
// 기존 그려진 지붕이 없다면
if (roofBases.length === 0) {
return
}
const roof = drawRoofPolygon(wall)
canvas.renderAll()
}
return { handleSave, handleConfirm, buttons, type, setType, TYPES, offsetRef, pitchRef, handleRollback }
}

View File

@ -6,10 +6,14 @@ import { LINE_TYPE, MENU } from '@/common/common'
import { usePolygon } from '@/hooks/usePolygon' import { usePolygon } from '@/hooks/usePolygon'
import { useMode } from '@/hooks/useMode' import { useMode } from '@/hooks/useMode'
import { useLine } from '@/hooks/useLine' import { useLine } from '@/hooks/useLine'
import { outerLineFixState } from '@/store/outerLineAtom'
import { useSwal } from '@/hooks/useSwal'
export function useRoofShapeSetting() { // 지붕형상 설정
export function useRoofShapeSetting(setShowRoofShapeSettingModal) {
const [shapeNum, setShapeNum] = useState(1) const [shapeNum, setShapeNum] = useState(1)
const [buttonAct, setButtonAct] = useState(1) const [buttonAct, setButtonAct] = useState(1)
const { swalFire } = useSwal()
const { getMessage } = useMessage() const { getMessage } = useMessage()
const canvas = useRecoilValue(canvasState) const canvas = useRecoilValue(canvasState)
const { addPolygonByLines } = usePolygon() const { addPolygonByLines } = usePolygon()
@ -27,9 +31,18 @@ export function useRoofShapeSetting() {
const { hideLine, showLine } = useLine() const { hideLine, showLine } = useLine()
const setCurrentMenu = useSetRecoilState(currentMenuState) const setCurrentMenu = useSetRecoilState(currentMenuState)
const outerLineFix = useRecoilValue(outerLineFixState)
const history = useRef([]) const history = useRef([])
useEffect(() => {
const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
if (!outerLineFix || outerLines.length === 0) {
swalFire({ text: '외벽선이 없습니다.' })
setShowRoofShapeSettingModal(false)
}
}, [])
useEffect(() => { useEffect(() => {
if (shapeNum !== 4) { if (shapeNum !== 4) {
return return
@ -98,11 +111,7 @@ export function useRoofShapeSetting() {
{ id: 6, name: getMessage('shed') }, { id: 6, name: getMessage('shed') },
] ]
/** const handleSave = () => {
*
* @param fn 모달 닫기 위한 함수
*/
const handleSave = (fn) => {
//기존 wallLine 삭제 //기존 wallLine 삭제
let outerLines let outerLines
@ -235,6 +244,14 @@ export function useRoofShapeSetting() {
} }
} }
// 기존 wallLine, roofBase 제거
canvas
.getObjects()
.filter((obj) => obj.name === 'wallLine' || obj.name === 'roofBase')
.forEach((line) => {
canvas.remove(line)
})
const polygon = addPolygonByLines(outerLines, { name: 'wallLine' }) const polygon = addPolygonByLines(outerLines, { name: 'wallLine' })
polygon.lines = [...outerLines] polygon.lines = [...outerLines]
@ -242,7 +259,7 @@ export function useRoofShapeSetting() {
canvas?.renderAll() canvas?.renderAll()
roof.drawHelpLine() roof.drawHelpLine()
fn && fn(false) setShowRoofShapeSettingModal(false)
} }
const initLineSetting = () => { const initLineSetting = () => {
@ -387,7 +404,8 @@ export function useRoofShapeSetting() {
// 벽 // 벽
attributes = { attributes = {
type: LINE_TYPE.WALLLINE.WALL, type: LINE_TYPE.WALLLINE.WALL,
offset: hasSleeve === '0' ? 0 : sleeveOffset / 10, width: hasSleeve === '0' ? 0 : sleeveOffset / 10,
sleeve: hasSleeve === '1',
} }
break break
} }
@ -415,7 +433,7 @@ export function useRoofShapeSetting() {
// 한쪽흐름 // 한쪽흐름
attributes = { attributes = {
type: LINE_TYPE.WALLLINE.SHED, type: LINE_TYPE.WALLLINE.SHED,
offset: shedWidth / 10, width: shedWidth / 10,
} }
break break
} }

View File

@ -4,12 +4,16 @@ import { useEffect, useRef, useState } from 'react'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { useEvent } from '@/hooks/useEvent' import { useEvent } from '@/hooks/useEvent'
import { useLine } from '@/hooks/useLine' import { useLine } from '@/hooks/useLine'
import { useSwal } from '@/hooks/useSwal'
export function useWallLineOffsetSetting() { // 외벽선 편집 및 오프셋
export function useWallLineOffsetSetting(setShowWallLineOffsetSettingModal) {
const canvas = useRecoilValue(canvasState) const canvas = useRecoilValue(canvasState)
const { showLine, addLine } = useLine() const { showLine, addLine } = useLine()
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { swalFire } = useSwal()
const { addCanvasMouseEventListener, initEvent } = useEvent() const { addCanvasMouseEventListener, initEvent } = useEvent()
const wallLineEditRef = useRef(null)
const length1Ref = useRef(null) const length1Ref = useRef(null)
const length2Ref = useRef(null) const length2Ref = useRef(null)
const radioTypeRef = useRef('1') const radioTypeRef = useRef('1')
@ -17,6 +21,25 @@ export function useWallLineOffsetSetting() {
const arrow1Ref = useRef(null) const arrow1Ref = useRef(null)
const arrow2Ref = useRef(null) const arrow2Ref = useRef(null)
const [isLoading, setIsLoading] = useState(false)
const drawLine = (point1, point2, idx, direction = currentWallLineRef.current.direction) => {
const line = addLine([point1.x, point1.y, point2.x, point2.y], {
stroke: 'black',
strokeWidth: 4,
idx: idx,
selectable: true,
name: 'outerLine',
x1: point1.x,
y1: point1.y,
x2: point2.x,
y2: point2.y,
direction: direction,
})
line.attributes = { ...currentWallLineRef.current.attributes }
}
const TYPES = { const TYPES = {
WALL_LINE_EDIT: 'wallLineEdit', WALL_LINE_EDIT: 'wallLineEdit',
OFFSET: 'offset', OFFSET: 'offset',
@ -31,42 +54,128 @@ export function useWallLineOffsetSetting() {
useEffect(() => { useEffect(() => {
const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
if (outerLines.length === 0) {
swalFire({ text: '외벽선이 없습니다.' })
setShowWallLineOffsetSettingModal(false)
return
}
outerLines.forEach((outerLine) => { outerLines.forEach((outerLine) => {
outerLine.set({ selectable: true }) outerLine.set({ selectable: true })
showLine(outerLine) showLine(outerLine)
}) })
const roofs = canvas.getObjects().filter((obj) => obj.name === 'roofBase') //outerLine과 그 text만 남겨둔다.
const exceptObjs = canvas.getObjects().filter((obj) => obj.name !== 'outerLine' && obj.parent?.name !== 'outerLine')
roofs.forEach((roof) => { exceptObjs.forEach((obj) => {
roof.innerLines.forEach((innerLine) => { canvas.remove(obj)
canvas.remove(innerLine)
})
canvas.remove(roof)
}) })
const wallLines = canvas.getObjects().filter((obj) => obj.name === 'wallLine') canvas.setActiveObject(outerLines[0])
wallLines.forEach((wallLine) => { currentWallLineRef.current = outerLines[0]
canvas.remove(wallLine) addCircleByLine(currentWallLineRef.current)
})
addCanvasMouseEventListener('mouse:down', mouseDown) addCanvasMouseEventListener('mouse:down', mouseDown)
setIsLoading(true)
return () => { return () => {
removeOuterLineEditCircle()
canvas.discardActiveObject() canvas.discardActiveObject()
initEvent() initEvent()
} }
}, []) }, [])
useEffect(() => {
if (!isLoading) {
return
}
removeOuterLineEditCircle()
addCanvasMouseEventListener('mouse:down', mouseDown)
if (type === TYPES.WALL_LINE_EDIT) {
addCircleByLine(currentWallLineRef.current)
}
}, [type])
const removeOuterLineEditCircle = () => {
canvas.remove(...canvas.getObjects().filter((obj) => obj.name === 'outerLineEditCircleStart' || obj.name === 'outerLineEditCircleEnd'))
}
const mouseDown = (e) => { const mouseDown = (e) => {
removeOuterLineEditCircle()
if (!e.target || (e.target && e.target.name !== 'outerLine')) { if (!e.target || (e.target && e.target.name !== 'outerLine')) {
currentWallLineRef.current = null
return return
} }
currentWallLineRef.current = e.target currentWallLineRef.current = e.target
console.log(currentWallLineRef.current.idx, currentWallLineRef.current.direction)
if (type === TYPES.WALL_LINE_EDIT) {
addCircleByLine(currentWallLineRef.current)
}
}
const addCircleByLine = (line) => {
const direction = currentWallLineRef.current?.direction
let startPoint
let endPoint
let circle1, circle2
if (direction === 'top' || direction === 'bottom') {
// y1, y2중에 더 작은 값이 startPoint, 더 큰 값이 endPoint
if (line.y1 > line.y2) {
startPoint = { x: line.x2, y: line.y2 }
endPoint = { x: line.x1, y: line.y1 }
} else {
startPoint = { x: line.x1, y: line.y1 }
endPoint = { x: line.x2, y: line.y2 }
}
arrow1Ref.current = 'down'
arrow2Ref.current = 'up'
} else {
// x1, x2중에 더 작은 값이 startPoint, 더 큰 값이 endPoint
if (line.x1 > line.x2) {
startPoint = { x: line.x2, y: line.y2 }
endPoint = { x: line.x1, y: line.y1 }
} else {
startPoint = { x: line.x1, y: line.y1 }
endPoint = { x: line.x2, y: line.y2 }
}
arrow1Ref.current = 'right'
arrow2Ref.current = 'left'
}
circle1 = new fabric.Circle({
radius: 10,
fill: 'red',
top: startPoint.y - 10,
left: startPoint.x - 10,
name: 'outerLineEditCircleStart',
hasControls: false,
hasBorders: false,
lockMovementX: true,
lockMovementY: true,
})
circle2 = new fabric.Circle({
radius: 10,
fill: 'blue',
top: endPoint.y - 10,
left: endPoint.x - 10,
name: 'outerLineEditCircleEnd',
hasControls: false,
hasBorders: false,
lockMovementX: true,
lockMovementY: true,
})
wallLineEditRef.current.setArrow()
canvas.add(circle1)
canvas.add(circle2)
} }
const handleSave = () => { const handleSave = () => {
if (currentWallLineRef.current === null) {
alert('보조선을 먼저 선택하세요')
return
}
switch (type) { switch (type) {
case TYPES.WALL_LINE_EDIT: case TYPES.WALL_LINE_EDIT:
handleWallLineEditSave() handleWallLineEditSave()
@ -78,51 +187,324 @@ export function useWallLineOffsetSetting() {
} }
const handleWallLineEditSave = () => { const handleWallLineEditSave = () => {
const direction = currentWallLineRef.current.direction const startPoint = canvas
let canDirections = direction === 'left' || direction === 'right' ? ['up', 'down'] : ['left', 'right'] .getObjects()
if (radioTypeRef === 1) { .filter((obj) => obj.name === 'outerLineEditCircleStart')
if (!canDirections.includes(arrow1Ref.current)) { .map((obj) => {
alert('방향을 다시 선택하세요') return {
return x: obj.left + obj.radius,
y: obj.top + obj.radius,
}
})[0]
const endPoint = canvas
.getObjects()
.filter((obj) => obj.name === 'outerLineEditCircleEnd')
.map((obj) => {
return {
x: obj.left + obj.radius,
y: obj.top + obj.radius,
}
})[0]
const length = Number(length1Ref.current.value) / 10
const length2 = Number(length2Ref.current.value) / 10
const currentIdx = currentWallLineRef.current.idx
const currentDirection = currentWallLineRef.current.direction
let point1, point2, point3
if (radioTypeRef.current === '1') {
// 1지점 선택시 방향은 down, right만 가능
if (arrow1Ref.current === 'down') {
if (currentDirection === 'top') {
point1 = { x: endPoint.x, y: endPoint.y }
point2 = { x: startPoint.x, y: startPoint.y + length }
point3 = { x: startPoint.x, y: startPoint.y }
} else {
point1 = { x: startPoint.x, y: startPoint.y }
point2 = { x: startPoint.x, y: startPoint.y + length }
point3 = { x: endPoint.x, y: endPoint.y }
}
} else {
if (currentDirection === 'left') {
point1 = { x: endPoint.x, y: endPoint.y }
point2 = { x: startPoint.x + length, y: startPoint.y }
point3 = { x: startPoint.x, y: startPoint.y }
} else {
point1 = { x: startPoint.x, y: startPoint.y }
point2 = { x: startPoint.x + length, y: startPoint.y }
point3 = { x: endPoint.x, y: endPoint.y }
}
} }
} else { } else {
if (!canDirections.includes(arrow2Ref.current)) { // 2지점일 경우 방향은 up, left만 가능
alert('방향을 다시 선택하세요') if (arrow2Ref.current === 'up') {
return if (currentDirection === 'bottom') {
point1 = { x: startPoint.x, y: startPoint.y }
point2 = { x: endPoint.x, y: endPoint.y - length2 }
point3 = { x: endPoint.x, y: endPoint.y }
} else {
point1 = { x: endPoint.x, y: endPoint.y }
point2 = { x: endPoint.x, y: endPoint.y - length2 }
point3 = { x: startPoint.x, y: startPoint.y }
}
} else {
if (currentDirection === 'right') {
point1 = { x: startPoint.x, y: startPoint.y }
point2 = { x: endPoint.x - length2, y: endPoint.y }
point3 = { x: endPoint.x, y: endPoint.y }
} else {
point1 = { x: endPoint.x, y: endPoint.y }
point2 = { x: endPoint.x - length2, y: endPoint.y }
point3 = { x: startPoint.x, y: startPoint.y }
}
} }
} }
rearrangeOuterLine(currentIdx + 1)
drawLine(point1, point2, currentIdx)
drawLine(point2, point3, currentIdx + 1)
removeOuterLineEditCircle()
canvas.remove(currentWallLineRef.current)
currentWallLineRef.current = null
canvas.renderAll()
}
const rearrangeOuterLine = (idxParam) => {
const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
outerLines.forEach((outerLine) => {
if (outerLine.idx >= idxParam) {
outerLine.idx = outerLine.idx + 1
}
})
} }
const handleOffsetSave = () => { const handleOffsetSave = () => {
const direction = currentWallLineRef.current.direction const direction = currentWallLineRef.current.direction
let canDirections = direction === 'left' || direction === 'right' ? ['up', 'down'] : ['left', 'right'] let canDirections = direction === 'left' || direction === 'right' ? ['up', 'down'] : ['left', 'right']
const currentIdx = currentWallLineRef.current.idx
if (!canDirections.includes(arrow1Ref.current)) { if (!canDirections.includes(arrow1Ref.current)) {
alert('방향을 다시 선택하세요') alert('방향을 다시 선택하세요')
return return
} }
const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
const idx = currentWallLineRef.current.idx const idx = currentWallLineRef.current.idx
const prevLine = outerLines.find((line) => (line.idx === idx - 1 < 0 ? outerLines.length - 1 : idx - 1)) const prevIdx = idx - 1 <= 0 ? outerLines.length : idx - 1
const nextIdx = idx + 1 > outerLines.length ? 1 : idx + 1
const currentLine = currentWallLineRef.current const currentLine = currentWallLineRef.current
const nextLine = outerLines.find((line) => (line.idx === idx + 1 > outerLines.length - 1 ? 0 : idx + 1)) const prevLine = outerLines.find((line) => line.idx === prevIdx)
const nextLine = outerLines.find((line) => line.idx === nextIdx)
const length = length1Ref.current.value / 10
const currentLineX = Math.floor(Math.max(currentLine.x1, currentLine.x2))
const currentLineY = Math.floor(Math.max(currentLine.y1, currentLine.y2))
switch (arrow1Ref.current) { switch (arrow1Ref.current) {
case 'up': { case 'up': {
console.log(prevLine, currentLine, nextLine) if (prevLine.direction === currentLine.direction) {
const newX =
currentLine.direction === 'left'
? Math.floor(Math.max(currentLine.x1, currentLine.x2))
: Math.floor(Math.min(currentLine.x1, currentLine.x2))
const newPoint1 = { x: newX, y: currentLineY - length }
const newPoint2 = { x: prevLine.x2, y: prevLine.y2 }
rearrangeOuterLine(currentIdx)
drawLine(newPoint1, newPoint2, currentIdx, 'top')
if (Math.abs(currentLineY - nextLine.y1) < 2) {
nextLine.set({ y1: currentLineY - length })
} else {
nextLine.set({ y2: currentLineY - length })
}
} else if (nextLine.direction === currentLine.direction) {
const newX =
currentLine.direction === 'left'
? Math.floor(Math.min(currentLine.x1, currentLine.x2))
: Math.floor(Math.max(currentLine.x1, currentLine.x2))
const newPoint1 = { x: newX, y: currentLineY - length }
const newPoint2 = { x: nextLine.x1, y: nextLine.y1 }
rearrangeOuterLine(currentIdx + 1)
drawLine(newPoint1, newPoint2, currentIdx + 1, 'top')
if (Math.abs(currentLineY - prevLine.y1) < 2) {
prevLine.set({ y1: currentLineY - length })
} else {
prevLine.set({ y2: currentLineY - length })
}
} else {
if (Math.abs(currentLineY - prevLine.y1) < 2) {
prevLine.set({ y1: prevLine.y1 - length })
} else {
prevLine.set({ y2: prevLine.y2 - length })
}
if (Math.abs(currentLineY - nextLine.y1) < 2) {
nextLine.set({ y1: nextLine.y1 - length })
} else {
nextLine.set({ y2: nextLine.y2 - length })
}
}
currentLine.set({ y1: currentLine.y1 - length, y2: currentLine.y2 - length })
break break
} }
case 'down': { case 'down': {
if (prevLine.direction === currentLine.direction) {
const newX =
currentLine.direction === 'left'
? Math.floor(Math.max(currentLine.x1, currentLine.x2))
: Math.floor(Math.min(currentLine.x1, currentLine.x2))
const newPoint1 = { x: newX, y: currentLineY + length }
const newPoint2 = { x: prevLine.x2, y: prevLine.y2 }
rearrangeOuterLine(currentIdx)
drawLine(newPoint1, newPoint2, currentIdx, 'bottom')
if (Math.abs(currentLineY - nextLine.y1) < 2) {
nextLine.set({ y1: currentLineY + length })
} else {
nextLine.set({ y2: currentLineY + length })
}
} else if (nextLine.direction === currentLine.direction) {
const newX =
currentLine.direction === 'left'
? Math.floor(Math.min(currentLine.x1, currentLine.x2))
: Math.floor(Math.max(currentLine.x1, currentLine.x2))
const newPoint1 = { x: newX, y: currentLineY + length }
const newPoint2 = { x: nextLine.x1, y: nextLine.y1 }
rearrangeOuterLine(currentIdx + 1)
drawLine(newPoint1, newPoint2, currentIdx + 1, 'bottom')
if (Math.abs(currentLineY - prevLine.y1) < 2) {
prevLine.set({ y1: currentLineY + length })
} else {
prevLine.set({ y2: currentLineY + length })
}
} else {
if (Math.abs(currentLineY - prevLine.y1) < 2) {
prevLine.set({ y1: prevLine.y1 + length })
} else {
prevLine.set({ y2: prevLine.y2 + length })
}
if (Math.abs(currentLineY - nextLine.y1) < 2) {
nextLine.set({ y1: nextLine.y1 + length })
} else {
nextLine.set({ y2: nextLine.y2 + length })
}
}
currentLine.set({ y1: currentLine.y1 + length, y2: currentLine.y2 + length })
break break
} }
case 'left': { case 'left': {
if (prevLine.direction === currentLine.direction) {
const newY =
currentLine.direction === 'top'
? Math.floor(Math.max(currentLine.y1, currentLine.y2))
: Math.floor(Math.min(currentLine.y1, currentLine.y2))
const newPoint1 = { x: currentLineX - length, y: newY }
const newPoint2 = { x: prevLine.x2, y: prevLine.y2 }
rearrangeOuterLine(currentIdx)
drawLine(newPoint1, newPoint2, currentIdx, 'left')
if (Math.abs(currentLineX - nextLine.x1) < 2) {
nextLine.set({ x1: currentLineX - length })
} else {
nextLine.set({ x2: currentLineX - length })
}
} else if (nextLine.direction === currentLine.direction) {
const newY =
currentLine.direction === 'top'
? Math.floor(Math.min(currentLine.y1, currentLine.y2))
: Math.floor(Math.max(currentLine.y1, currentLine.y2))
const newPoint1 = { x: currentLineX - length, y: newY }
const newPoint2 = { x: nextLine.x1, y: nextLine.y1 }
rearrangeOuterLine(currentIdx + 1)
drawLine(newPoint1, newPoint2, currentIdx + 1, 'left')
if (Math.abs(currentLineX - prevLine.x1) < 2) {
prevLine.set({ x1: currentLineX - length })
} else {
prevLine.set({ x2: currentLineX - length })
}
} else {
if (Math.abs(currentLineX - prevLine.x1) < 2) {
prevLine.set({ x1: prevLine.x1 - length })
} else {
prevLine.set({ x2: prevLine.x2 - length })
}
if (Math.abs(currentLineX - nextLine.x1) < 2) {
nextLine.set({ x1: nextLine.x1 - length })
} else {
nextLine.set({ x2: nextLine.x2 - length })
}
}
currentLine.set({ x1: currentLine.x1 - length, x2: currentLine.x2 - length })
break break
} }
case 'right': { case 'right': {
if (prevLine.direction === currentLine.direction) {
const newY =
currentLine.direction === 'top'
? Math.floor(Math.max(currentLine.y1, currentLine.y2))
: Math.floor(Math.min(currentLine.y1, currentLine.y2))
const newPoint1 = { x: currentLineX + length, y: newY }
const newPoint2 = { x: prevLine.x2, y: prevLine.y2 }
rearrangeOuterLine(currentIdx)
drawLine(newPoint1, newPoint2, currentIdx, 'right')
if (Math.abs(currentLineX - nextLine.x1) < 2) {
nextLine.set({ x1: currentLineX + length })
} else {
nextLine.set({ x2: currentLineX + length })
}
} else if (nextLine.direction === currentLine.direction) {
const newY =
currentLine.direction === 'top'
? Math.floor(Math.min(currentLine.y1, currentLine.y2))
: Math.floor(Math.max(currentLine.y1, currentLine.y2))
const newPoint1 = { x: currentLineX + length, y: newY }
const newPoint2 = { x: nextLine.x1, y: nextLine.y1 }
rearrangeOuterLine(currentIdx + 1)
drawLine(newPoint1, newPoint2, currentIdx + 1, 'right')
if (Math.abs(currentLineX - prevLine.x1) < 2) {
prevLine.set({ x1: currentLineX + length })
} else {
prevLine.set({ x2: currentLineX + length })
}
} else {
if (Math.abs(currentLineX - prevLine.x1) < 2) {
prevLine.set({ x1: prevLine.x1 + length })
} else {
prevLine.set({ x2: prevLine.x2 + length })
}
if (Math.abs(currentLineX - nextLine.x1) < 2) {
nextLine.set({ x1: nextLine.x1 + length })
} else {
nextLine.set({ x2: nextLine.x2 + length })
}
}
currentLine.set({ x1: currentLine.x1 + length, x2: currentLine.x2 + length })
break break
} }
} }
canvas.renderAll()
} }
return { type, setType, buttonMenu, TYPES, length1Ref, length2Ref, radioTypeRef, currentWallLineRef, arrow1Ref, arrow2Ref, handleSave } return {
type,
setType,
buttonMenu,
TYPES,
length1Ref,
length2Ref,
radioTypeRef,
currentWallLineRef,
arrow1Ref,
arrow2Ref,
handleSave,
wallLineEditRef,
}
} }

View File

@ -0,0 +1,818 @@
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
import {
adsorptionPointAddModeState,
adsorptionPointModeState,
adsorptionRangeState,
canvasState,
dotLineIntervalSelector,
verticalHorizontalModeState,
} from '@/store/canvasAtom'
import { useEvent } from '@/hooks/useEvent'
import { useMouse } from '@/hooks/useMouse'
import { useLine } from '@/hooks/useLine'
import { useTempGrid } from '@/hooks/useTempGrid'
import { useEffect, useRef } from 'react'
import { distanceBetweenPoints, setSurfaceShapePattern } from '@/util/canvas-util'
import { fabric } from 'fabric'
import { calculateAngle, drawDirectionArrow } from '@/util/qpolygon-utils'
import {
placementShapeDrawingAngle1State,
placementShapeDrawingAngle2State,
placementShapeDrawingArrow1State,
placementShapeDrawingArrow2State,
placementShapeDrawingDiagonalState,
placementShapeDrawingFixState,
placementShapeDrawingLength1State,
placementShapeDrawingLength2State,
placementShapeDrawingPointsState,
placementShapeDrawingTypeState,
} from '@/store/placementShapeDrawingAtom'
import { usePolygon } from '@/hooks/usePolygon'
import { POLYGON_TYPE } from '@/common/common'
// 면형상 배치
export function usePlacementShapeDrawing(setShowPlaceShapeDrawingModal) {
const canvas = useRecoilValue(canvasState)
const { addCanvasMouseEventListener, addDocumentEventListener, removeAllMouseEventListeners, removeAllDocumentEventListeners, removeMouseEvent } =
useEvent()
const { getIntersectMousePoint } = useMouse()
const { addLine, removeLine } = useLine()
const { addPolygonByLines } = usePolygon()
const { tempGridMode } = useTempGrid()
const verticalHorizontalMode = useRecoilValue(verticalHorizontalModeState)
const adsorptionPointAddMode = useRecoilValue(adsorptionPointAddModeState)
const adsorptionPointMode = useRecoilValue(adsorptionPointModeState)
const adsorptionRange = useRecoilValue(adsorptionRangeState)
const interval = useRecoilValue(dotLineIntervalSelector) // 가로 세로 간격
const length1Ref = useRef(null)
const length2Ref = useRef(null)
const angle1Ref = useRef(null)
const angle2Ref = useRef(null)
const [length1, setLength1] = useRecoilState(placementShapeDrawingLength1State)
const [length2, setLength2] = useRecoilState(placementShapeDrawingLength2State)
const [arrow1, setArrow1] = useRecoilState(placementShapeDrawingArrow1State)
const [arrow2, setArrow2] = useRecoilState(placementShapeDrawingArrow2State)
const [points, setPoints] = useRecoilState(placementShapeDrawingPointsState)
const [type, setType] = useRecoilState(placementShapeDrawingTypeState)
const [angle1, setAngle1] = useRecoilState(placementShapeDrawingAngle1State)
const [angle2, setAngle2] = useRecoilState(placementShapeDrawingAngle2State)
const [outerLineDiagonalLength, setOuterLineDiagonalLength] = useRecoilState(placementShapeDrawingDiagonalState)
const setOuterLineFix = useSetRecoilState(placementShapeDrawingFixState)
const arrow1Ref = useRef(arrow1)
const arrow2Ref = useRef(arrow2)
const outerLineDiagonalLengthRef = useRef(null)
const isFix = useRef(false)
useEffect(() => {
if (adsorptionPointAddMode || tempGridMode) {
return
}
addCanvasMouseEventListener('mouse:down', mouseDown)
clear()
}, [verticalHorizontalMode, points, adsorptionPointAddMode, adsorptionPointMode, adsorptionRange, interval, tempGridMode])
useEffect(() => {
arrow1Ref.current = arrow1
}, [arrow1])
useEffect(() => {
arrow2Ref.current = arrow2
}, [arrow2])
useEffect(() => {
clear()
addDocumentEventListener('keydown', document, keydown[type])
}, [type])
const clear = () => {
setLength1(0)
setLength2(0)
setArrow1('')
setArrow2('')
setAngle1(0)
setAngle2(0)
setOuterLineDiagonalLength(0)
}
const mouseDown = (e) => {
let pointer = getIntersectMousePoint(e)
if (points.length === 0) {
setPoints((prev) => [...prev, pointer])
} else {
const lastPoint = points[points.length - 1]
let newPoint = { x: pointer.x, y: pointer.y }
const length = distanceBetweenPoints(lastPoint, newPoint)
if (verticalHorizontalMode) {
const vector = {
x: pointer.x - points[points.length - 1].x,
y: pointer.y - points[points.length - 1].y,
}
const slope = Math.abs(vector.y / vector.x) // 기울기 계산
let scaledVector
if (slope >= 1) {
// 기울기가 1 이상이면 x축 방향으로 그림
scaledVector = {
x: 0,
y: vector.y >= 0 ? Number(length) : -Number(length),
}
} else {
// 기울기가 1 미만이면 y축 방향으로 그림
scaledVector = {
x: vector.x >= 0 ? Number(length) : -Number(length),
y: 0,
}
}
const verticalLength = scaledVector.y
const horizontalLength = scaledVector.x
newPoint = {
x: lastPoint.x + horizontalLength,
y: lastPoint.y + verticalLength,
}
}
setPoints((prev) => [...prev, newPoint])
}
}
useEffect(() => {
canvas
?.getObjects()
.filter((obj) => obj.name === 'placementShapeDrawingLine' || obj.name === 'helpGuideLine')
.forEach((obj) => {
removeLine(obj)
})
canvas?.remove(canvas?.getObjects().find((obj) => obj.name === 'placementShapeDrawingStartPoint'))
if (points.length === 0) {
setOuterLineFix(true)
removeAllDocumentEventListeners()
return
}
addDocumentEventListener('keydown', document, keydown[type])
if (points.length === 1) {
const point = new fabric.Circle({
radius: 5,
fill: 'transparent',
stroke: 'red',
left: points[0].x - 5,
top: points[0].y - 5,
selectable: false,
name: 'placementShapeDrawingStartPoint',
})
canvas?.add(point)
} else {
setOuterLineFix(false)
canvas
.getObjects()
.filter((obj) => obj.name === 'placementShapeDrawingPoint')
.forEach((obj) => {
canvas.remove(obj)
})
points.forEach((point, idx) => {
const circle = new fabric.Circle({
left: point.x,
top: point.y,
visible: false,
name: 'placementShapeDrawingPoint',
})
canvas.add(circle)
})
points.forEach((point, idx) => {
if (idx === 0) {
return
}
drawLine(points[idx - 1], point, idx)
})
const lastPoint = points[points.length - 1]
const firstPoint = points[0]
if (isFix.current) {
removeAllMouseEventListeners()
removeAllDocumentEventListeners()
const lines = canvas?.getObjects().filter((obj) => obj.name === 'placementShapeDrawingLine')
const roof = addPolygonByLines(lines, {
stroke: 'black',
strokeWidth: 3,
selectable: true,
name: POLYGON_TYPE.ROOF,
originX: 'center',
originY: 'center',
direction: 'south',
})
setSurfaceShapePattern(roof)
drawDirectionArrow(roof)
lines.forEach((line) => {
removeLine(line)
})
canvas?.renderAll()
setShowPlaceShapeDrawingModal(false)
}
if (points.length < 3) {
return
}
if (Math.abs(lastPoint.x - firstPoint.x) < 1 && Math.abs(lastPoint.y - firstPoint.y) < 1) {
return
}
if (Math.abs(lastPoint.x - firstPoint.x) < 1 || Math.abs(lastPoint.y - firstPoint.y) < 1) {
let isAllRightAngle = true
const firstPoint = points[0]
points.forEach((point, idx) => {
if (idx === 0 || !isAllRightAngle) {
return
}
const angle = calculateAngle(point, firstPoint)
if (angle % 90 !== 0) {
isAllRightAngle = false
}
})
if (isAllRightAngle) {
return
}
const line = addLine([lastPoint.x, lastPoint.y, firstPoint.x, firstPoint.y], {
stroke: 'grey',
strokeWidth: 1,
selectable: false,
name: 'helpGuideLine',
})
} else {
const guideLine1 = addLine([lastPoint.x, lastPoint.y, lastPoint.x, firstPoint.y], {
stroke: 'grey',
strokeWidth: 1,
strokeDashArray: [1, 1, 1],
name: 'helpGuideLine',
})
const guideLine2 = addLine([guideLine1.x2, guideLine1.y2, firstPoint.x, firstPoint.y], {
stroke: 'grey',
strokeWidth: 1,
strokeDashArray: [1, 1, 1],
name: 'helpGuideLine',
})
}
}
}, [points])
const drawLine = (point1, point2, idx) => {
addLine([point1.x, point1.y, point2.x, point2.y], {
stroke: 'black',
strokeWidth: 3,
idx: idx,
selectable: true,
name: 'placementShapeDrawingLine',
x1: point1.x,
y1: point1.y,
x2: point2.x,
y2: point2.y,
})
}
// 직각 완료될 경우 확인
const checkRightAngle = (direction) => {
const activeElem = document.activeElement
const canDirection =
direction === '↓' || direction === '↑'
? arrow1Ref.current === '←' || arrow1Ref.current === '→'
: arrow1Ref.current === '↓' || arrow1Ref.current === '↑'
if (activeElem === length1Ref.current || activeElem === angle1Ref.current) {
setArrow1(direction)
arrow1Ref.current = direction
length2Ref.current.focus()
} else if (activeElem === length2Ref.current || activeElem === angle2Ref.current) {
if (!canDirection) {
return
}
setArrow2(direction)
arrow2Ref.current = direction
}
const length1Num = Number(length1Ref.current.value) / 10
const length2Num = Number(length2Ref.current.value) / 10
if (points.length === 0) {
return
}
if (length1Num === 0 || length2Num === 0 || arrow1Ref.current === '' || arrow2Ref.current === '') {
return
}
if (arrow1Ref.current === '↓' && arrow2Ref.current === '→') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
return [...prev, { x: prev[prev.length - 1].x + length2Num, y: prev[prev.length - 1].y + length1Num }]
})
} else if (arrow1Ref.current === '↓' && arrow2Ref.current === '←') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
return [...prev, { x: prev[prev.length - 1].x - length2Num, y: prev[prev.length - 1].y + length1Num }]
})
} else if (arrow1Ref.current === '↑' && arrow2Ref.current === '→') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
return [...prev, { x: prev[prev.length - 1].x + length2Num, y: prev[prev.length - 1].y - length1Num }]
})
} else if (arrow1Ref.current === '↑' && arrow2Ref.current === '←') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
return [...prev, { x: prev[prev.length - 1].x - length2Num, y: prev[prev.length - 1].y - length1Num }]
})
} else if (arrow1Ref.current === '→' && arrow2Ref.current === '↓') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
return [...prev, { x: prev[prev.length - 1].x + length1Num, y: prev[prev.length - 1].y + length2Num }]
})
} else if (arrow1Ref.current === '→' && arrow2Ref.current === '↑') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
return [...prev, { x: prev[prev.length - 1].x + length1Num, y: prev[prev.length - 1].y - length2Num }]
})
} else if (arrow1Ref.current === '←' && arrow2Ref.current === '↓') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
return [...prev, { x: prev[prev.length - 1].x - length1Num, y: prev[prev.length - 1].y + length2Num }]
})
} else if (arrow1Ref.current === '←' && arrow2Ref.current === '↑') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
return [...prev, { x: prev[prev.length - 1].x - length1Num, y: prev[prev.length - 1].y - length2Num }]
})
}
}
//이구배 완료될 경우 확인 ↓, ↑, ←, →
const checkDoublePitch = (direction) => {
const activeElem = document.activeElement
const canDirection =
direction === '↓' || direction === '↑'
? arrow1Ref.current === '←' || arrow1Ref.current === '→'
: arrow1Ref.current === '↓' || arrow1Ref.current === '↑'
if (activeElem === length1Ref.current || activeElem === angle1Ref.current) {
setArrow1(direction)
arrow1Ref.current = direction
angle2Ref.current.focus()
} else if (activeElem === length2Ref.current || activeElem === angle2Ref.current) {
if (!canDirection) {
return
}
setArrow2(direction)
arrow2Ref.current = direction
}
const angle1Value = angle1Ref.current.value
const angle2Value = angle2Ref.current.value
const length1Value = length1Ref.current.value
const length2Value = length2Ref.current.value
const arrow1Value = arrow1Ref.current
const arrow2Value = arrow2Ref.current
if (angle1Value !== 0 && length1Value !== 0 && angle2Value !== 0 && arrow1Value !== '' && arrow2Value !== '') {
if (arrow1Value === '↓' && arrow2Value === '→') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
return [...prev, { x: prev[prev.length - 1].x + length1Value / 10, y: prev[prev.length - 1].y + length2Value / 10 }]
})
} else if (arrow1Value === '↓' && arrow2Value === '←') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
return [...prev, { x: prev[prev.length - 1].x - length1Value / 10, y: prev[prev.length - 1].y + length2Value / 10 }]
})
} else if (arrow1Value === '↑' && arrow2Value === '→') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
return [...prev, { x: prev[prev.length - 1].x + length1Value / 10, y: prev[prev.length - 1].y - length2Value / 10 }]
})
} else if (arrow1Value === '↑' && arrow2Value === '←') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
return [...prev, { x: prev[prev.length - 1].x - length1Value / 10, y: prev[prev.length - 1].y - length2Value / 10 }]
})
} else if (arrow1Value === '→' && arrow2Value === '↓') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
return [...prev, { x: prev[prev.length - 1].x + length2Value / 10, y: prev[prev.length - 1].y + length1Value / 10 }]
})
} else if (arrow1Value === '→' && arrow2Value === '↑') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
return [...prev, { x: prev[prev.length - 1].x + length2Value / 10, y: prev[prev.length - 1].y - length1Value / 10 }]
})
} else if (arrow1Value === '←' && arrow2Value === '↓') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
return [...prev, { x: prev[prev.length - 1].x - length2Value / 10, y: prev[prev.length - 1].y + length1Value / 10 }]
})
} else if (arrow1Value === '←' && arrow2Value === '↑') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
return [...prev, { x: prev[prev.length - 1].x - length2Value / 10, y: prev[prev.length - 1].y - length1Value / 10 }]
})
}
angle1Ref.current.focus()
}
}
//대각선 완료될 경우 확인
const checkDiagonal = (direction) => {
const activeElem = document.activeElement
const diagonalLength = outerLineDiagonalLengthRef.current.value // 대각선 길이
const length1Value = length1Ref.current.value
if (diagonalLength <= length1Value) {
alert('대각선 길이는 직선 길이보다 길어야 합니다.')
return
}
const canDirection =
direction === '↓' || direction === '↑'
? arrow1Ref.current === '←' || arrow1Ref.current === '→'
: arrow1Ref.current === '↓' || arrow1Ref.current === '↑'
if (activeElem === length1Ref.current) {
setArrow1(direction)
arrow1Ref.current = direction
} else if (activeElem === length2Ref.current || activeElem === angle2Ref.current) {
if (!canDirection) {
return
}
setArrow2(direction)
arrow2Ref.current = direction
}
const arrow1Value = arrow1Ref.current
const arrow2Value = arrow2Ref.current
const getLength2 = () => {
return Math.floor(Math.sqrt(diagonalLength ** 2 - length1Value ** 2))
}
const length2Value = getLength2()
if (diagonalLength !== 0 && length1Value !== 0 && arrow1Value !== '') {
setLength2(getLength2())
length2Ref.current.focus()
}
if (length1Value !== 0 && length2Value !== 0 && arrow1Value !== '' && arrow2Value !== '') {
if (arrow1Value === '↓' && arrow2Value === '→') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
return [...prev, { x: prev[prev.length - 1].x + length2Value / 10, y: prev[prev.length - 1].y + length1Value / 10 }]
})
} else if (arrow1Value === '↓' && arrow2Value === '←') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
return [...prev, { x: prev[prev.length - 1].x - length2Value / 10, y: prev[prev.length - 1].y + length1Value / 10 }]
})
} else if (arrow1Value === '↑' && arrow2Value === '→') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
return [...prev, { x: prev[prev.length - 1].x + length2Value / 10, y: prev[prev.length - 1].y - length1Value / 10 }]
})
} else if (arrow1Value === '↑' && arrow2Value === '←') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
return [...prev, { x: prev[prev.length - 1].x - length2Value / 10, y: prev[prev.length - 1].y - length1Value / 10 }]
})
} else if (arrow1Value === '→' && arrow2Value === '↓') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
return [...prev, { x: prev[prev.length - 1].x + length1Value / 10, y: prev[prev.length - 1].y + length2Value / 10 }]
})
} else if (arrow1Value === '→' && arrow2Value === '↑') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
return [
...prev,
{
x: prev[prev.length - 1].x + length1Value / 10,
y: prev[prev.length - 1].y - length2Value / 10,
},
]
})
}
}
}
const keydown = {
outerLine: (e) => {
if (points.length === 0) {
return
}
// 포커스가 length1에 있지 않으면 length1에 포커스를 줌
const activeElem = document.activeElement
if (activeElem !== length1Ref.current) {
length1Ref.current.focus()
}
const key = e.key
if (!length1Ref.current) {
return
}
const lengthNum = Number(length1Ref.current.value) / 10
if (lengthNum === 0) {
return
}
switch (key) {
case 'Down': // IE/Edge에서 사용되는 값
case 'ArrowDown': {
setArrow1('↓')
setPoints((prev) => {
if (prev.length === 0) {
return []
}
return [...prev, { x: prev[prev.length - 1].x, y: prev[prev.length - 1].y + lengthNum }]
})
break
}
case 'Up': // IE/Edge에서 사용되는 값
case 'ArrowUp':
setArrow1('↑')
setPoints((prev) => {
if (prev.length === 0) {
return []
}
return [...prev, { x: prev[prev.length - 1].x, y: prev[prev.length - 1].y - lengthNum }]
})
break
case 'Left': // IE/Edge에서 사용되는 값
case 'ArrowLeft':
setArrow1('←')
setPoints((prev) => {
if (prev.length === 0) {
return []
}
return [...prev, { x: prev[prev.length - 1].x - lengthNum, y: prev[prev.length - 1].y }]
})
break
case 'Right': // IE/Edge에서 사용되는 값
case 'ArrowRight':
setArrow1('→')
setPoints((prev) => {
if (prev.length === 0) {
return []
}
return [...prev, { x: prev[prev.length - 1].x + lengthNum, y: prev[prev.length - 1].y }]
})
break
}
},
rightAngle: (e) => {
if (points.length === 0) {
return
}
const key = e.key
const activeElem = document.activeElement
if (activeElem !== length1Ref.current && activeElem !== length2Ref.current) {
length1Ref.current.focus()
}
switch (key) {
case 'Down': // IE/Edge에서 사용되는 값
case 'ArrowDown': {
checkRightAngle('↓')
break
}
case 'Up': // IE/Edge에서 사용되는 값
case 'ArrowUp':
checkRightAngle('↑')
break
case 'Left': // IE/Edge에서 사용되는 값
case 'ArrowLeft':
checkRightAngle('←')
break
case 'Right': // IE/Edge에서 사용되는 값
case 'ArrowRight':
checkRightAngle('→')
break
case 'Enter':
break
}
},
doublePitch: (e) => {
if (points.length === 0) {
return
}
const key = e.key
switch (key) {
case 'Down': // IE/Edge에서 사용되는 값
case 'ArrowDown': {
checkDoublePitch('↓')
break
}
case 'Up': // IE/Edge에서 사용되는 값
case 'ArrowUp':
checkDoublePitch('↑')
break
case 'Left': // IE/Edge에서 사용되는 값
case 'ArrowLeft':
checkDoublePitch('←')
break
case 'Right': // IE/Edge에서 사용되는 값
case 'ArrowRight':
checkDoublePitch('→')
break
}
},
angle: (e) => {
if (points.length === 0) {
return
}
const key = e.key
switch (key) {
case 'Enter': {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
const lastPoint = prev[prev.length - 1]
const length = length1Ref.current.value / 10
const angle = angle1Ref.current.value
//lastPoint로부터 angle1만큼의 각도로 length1만큼의 길이를 가지는 선을 그림
const radian = (angle * Math.PI) / 180
const x = lastPoint.x + length * Math.cos(radian)
const y = lastPoint.y - length * Math.sin(radian)
return [...prev, { x, y }]
})
}
}
},
diagonalLine: (e) => {
if (points.length === 0) {
return
}
const key = e.key
switch (key) {
case 'Down': // IE/Edge에서 사용되는 값
case 'ArrowDown': {
checkDiagonal('↓')
break
}
case 'Up': // IE/Edge에서 사용되는 값
case 'ArrowUp':
checkDiagonal('↑')
break
case 'Left': // IE/Edge에서 사용되는 값
case 'ArrowLeft':
checkDiagonal('←')
break
case 'Right': // IE/Edge에서 사용되는 값
case 'ArrowRight':
checkDiagonal('→')
break
}
},
}
/**
* 일변전으로 돌아가기
*/
const handleRollback = () => {
//points의 마지막 요소를 제거
setPoints((prev) => prev.slice(0, prev.length - 1))
}
const handleFix = () => {
if (points.length < 3) {
return
}
let isAllRightAngle = true
const firstPoint = points[0]
points.forEach((point, idx) => {
if (idx === 0 || !isAllRightAngle) {
return
}
const angle = calculateAngle(point, firstPoint)
if (angle % 90 !== 0) {
isAllRightAngle = false
}
})
if (isAllRightAngle) {
alert('부정확한 다각형입니다.')
return
}
setPoints((prev) => {
return [...prev, { x: prev[0].x, y: prev[0].y }]
})
isFix.current = true
}
return {
points,
setPoints,
length1,
setLength1,
length2,
setLength2,
length1Ref,
length2Ref,
arrow1,
setArrow1,
arrow2,
setArrow2,
arrow1Ref,
arrow2Ref,
angle1,
setAngle1,
angle1Ref,
angle2,
setAngle2,
angle2Ref,
outerLineDiagonalLength,
setOuterLineDiagonalLength,
outerLineDiagonalLengthRef,
type,
setType,
handleFix,
handleRollback,
}
}

View File

@ -1,21 +1,21 @@
'use client' 'use client'
import { useEffect, useRef, useState } from 'react' import { useRecoilValue } from 'recoil'
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
import { canvasState } from '@/store/canvasAtom' import { canvasState } from '@/store/canvasAtom'
import { MENU } from '@/common/common' import { MENU, BATCH_TYPE, POLYGON_TYPE } from '@/common/common'
import { getIntersectionPoint } from '@/util/canvas-util' import { getIntersectionPoint, setSurfaceShapePattern } from '@/util/canvas-util'
import { degreesToRadians } from '@turf/turf' import { degreesToRadians } from '@turf/turf'
import { QPolygon } from '@/components/fabric/QPolygon' import { QPolygon } from '@/components/fabric/QPolygon'
import { fabric } from 'fabric'
import { useSwal } from '@/hooks/useSwal' import { useSwal } from '@/hooks/useSwal'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { useEvent } from '@/hooks/useEvent'
export function useSurfaceShapeBatch() { export function useSurfaceShapeBatch() {
const { getMessage } = useMessage() const { getMessage } = useMessage()
const canvas = useRecoilValue(canvasState) const canvas = useRecoilValue(canvasState)
const { swalFire } = useSwal() const { swalFire } = useSwal()
const { addCanvasMouseEventListener, initEvent } = useEvent()
const applySurfaceShape = (surfaceRefs, selectedType, setShowPlacementSurfaceSettingModal) => { const applySurfaceShape = (surfaceRefs, selectedType, setShowPlacementSurfaceSettingModal) => {
let length1, length2, length3, length4, length5 let length1, length2, length3, length4, length5
@ -57,7 +57,7 @@ export function useSurfaceShapeBatch() {
let points = [] let points = []
if (checkSurfaceShape(surfaceId, { length1, length2, length3, length4, length5 })) { if (checkSurfaceShape(surfaceId, { length1, length2, length3, length4, length5 })) {
setShowPlacementSurfaceSettingModal(false) setShowPlacementSurfaceSettingModal(false)
canvas?.on('mouse:move', (e) => { addCanvasMouseEventListener('mouse:move', (e) => {
if (!isDrawing) { if (!isDrawing) {
return return
} }
@ -110,15 +110,13 @@ export function useSurfaceShapeBatch() {
obj.set({ direction: direction }) obj.set({ direction: direction })
obj.set({ originAngle: originAngle }) obj.set({ originAngle: originAngle })
// setCurrentPattern(obj)
canvas?.renderAll() canvas?.renderAll()
}) })
canvas?.on('mouse:down', (e) => { addCanvasMouseEventListener('mouse:down', (e) => {
isDrawing = false isDrawing = false
obj.set('name', MENU.BATCH_CANVAS.SURFACE_SHAPE_BATCH) obj.set('name', POLYGON_TYPE.ROOF)
canvas?.off('mouse:down') initEvent()
canvas.off('mouse:move')
setSurfaceShapePattern(obj) setSurfaceShapePattern(obj)
setShowPlacementSurfaceSettingModal(true) setShowPlacementSurfaceSettingModal(true)
}) })
@ -132,23 +130,23 @@ export function useSurfaceShapeBatch() {
if (surfaceId === 1) { if (surfaceId === 1) {
if (length1 === 0) { if (length1 === 0) {
swalFire({ text: getMessage('surface.shape.validate.size'), icon: 'error' }) swalFire({ text: getMessage('common.canvas.validate.size'), icon: 'error' })
check = false check = false
} }
if (length2 === 0) { if (length2 === 0) {
if (length3 === 0) { if (length3 === 0) {
swalFire({ text: getMessage('surface.shape.validate.size'), icon: 'error' }) swalFire({ text: getMessage('common.canvas.validate.size'), icon: 'error' })
check = false check = false
} }
} }
} else if ([2, 4].includes(surfaceId)) { } else if ([2, 4].includes(surfaceId)) {
if (length1 === 0 || length2 === 0) { if (length1 === 0 || length2 === 0) {
swalFire({ text: getMessage('surface.shape.validate.size'), icon: 'error' }) swalFire({ text: getMessage('common.canvas.validate.size'), icon: 'error' })
check = false check = false
} }
} else if ([3, 5, 6, 15, 18].includes(surfaceId)) { } else if ([3, 5, 6, 15, 18].includes(surfaceId)) {
if (length1 === 0 || length2 === 0 || length3 === 0) { if (length1 === 0 || length2 === 0 || length3 === 0) {
swalFire({ text: getMessage('surface.shape.validate.size'), icon: 'error' }) swalFire({ text: getMessage('common.canvas.validate.size'), icon: 'error' })
check = false check = false
} }
if (surfaceId === 3 && length3 > length1) { if (surfaceId === 3 && length3 > length1) {
@ -174,7 +172,7 @@ export function useSurfaceShapeBatch() {
} }
} else if ([8, 12, 13, 16, 17].includes(surfaceId)) { } else if ([8, 12, 13, 16, 17].includes(surfaceId)) {
if (length1 === 0 || length2 === 0 || length3 === 0 || length4 === 0) { if (length1 === 0 || length2 === 0 || length3 === 0 || length4 === 0) {
swalFire({ text: getMessage('surface.shape.validate.size'), icon: 'error' }) swalFire({ text: getMessage('common.canvas.validate.size'), icon: 'error' })
check = false check = false
} }
@ -202,7 +200,7 @@ export function useSurfaceShapeBatch() {
} }
} else if ([7, 9, 10, 11, 14].includes(surfaceId)) { } else if ([7, 9, 10, 11, 14].includes(surfaceId)) {
if (length1 === 0 || length2 === 0 || length3 === 0 || length4 === 0 || length5 === 0) { if (length1 === 0 || length2 === 0 || length3 === 0 || length4 === 0 || length5 === 0) {
swalFire({ text: getMessage('surface.shape.validate.size'), icon: 'error' }) swalFire({ text: getMessage('common.canvas.validate.size'), icon: 'error' })
check = false check = false
} }
if (surfaceId === 9 || surfaceId === 10 || surfaceId === 11) { if (surfaceId === 9 || surfaceId === 10 || surfaceId === 11) {
@ -566,66 +564,32 @@ export function useSurfaceShapeBatch() {
return points return points
} }
//면형상 선택 클릭시 지붕 패턴 입히기 const deleteAllSurfacesAndObjects = () => {
const setSurfaceShapePattern = (polygon) => { swalFire({
const ratio = window.devicePixelRatio || 1 text: '배치면 내용을 전부 삭제하시겠습니까?',
type: 'confirm',
let width = 265 / 10 confirmFn: () => {
let height = 150 / 10 canvas?.getObjects().forEach((obj) => {
let roofStyle = 2 if (
const inputPatternSize = { width: width, height: height } //임시 사이즈 obj.name === POLYGON_TYPE.ROOF ||
const patternSize = { ...inputPatternSize } // 입력된 값을 뒤집기 위해 obj.name === BATCH_TYPE.OPENING ||
obj.name === BATCH_TYPE.SHADOW ||
if (polygon.direction === 'east' || polygon.direction === 'west') { obj.name === BATCH_TYPE.TRIANGLE_DORMER ||
//세로형이면 width height를 바꿈 obj.name === BATCH_TYPE.PENTAGON_DORMER
;[patternSize.width, patternSize.height] = [inputPatternSize.height, patternSize.width] ) {
} canvas?.remove(obj)
}
// 패턴 소스를 위한 임시 캔버스 생성 })
const patternSourceCanvas = document.createElement('canvas') swalFire({ text: '삭제 완료 되었습니다.' })
patternSourceCanvas.width = polygon.width * ratio },
patternSourceCanvas.height = polygon.height * ratio // denyFn: () => {
const ctx = patternSourceCanvas.getContext('2d') // swalFire({ text: '취소되었습니다.', icon: 'error' })
const offset = roofStyle === 1 ? 0 : patternSize.width / 2 // },
const rows = Math.floor(patternSourceCanvas.height / patternSize.height)
const cols = Math.floor(patternSourceCanvas.width / patternSize.width)
ctx.strokeStyle = 'green'
ctx.lineWidth = 0.4
for (let row = 0; row <= rows; row++) {
const y = row * patternSize.height
ctx.beginPath()
ctx.moveTo(0, y) // 선 시작점
ctx.lineTo(patternSourceCanvas.width, y) // 선 끝점
ctx.stroke()
for (let col = 0; col <= cols; col++) {
const x = col * patternSize.width + (row % 2 === 0 ? 0 : offset)
const yStart = row * patternSize.height
const yEnd = yStart + patternSize.height
ctx.beginPath()
ctx.moveTo(x, yStart) // 선 시작점
ctx.lineTo(x, yEnd) // 선 끝점
ctx.stroke()
}
}
// 패턴 생성
const pattern = new fabric.Pattern({
source: patternSourceCanvas,
repeat: 'repeat',
}) })
polygon.set('fill', null)
polygon.set('fill', pattern)
canvas?.renderAll()
} }
return { return {
applySurfaceShape, applySurfaceShape,
deleteAllSurfacesAndObjects,
} }
} }

View File

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

View File

@ -1,6 +1,6 @@
import { useState } from 'react' import { useState } from 'react'
import { useRecoilState, useRecoilValue } from 'recoil' import { useRecoilState, useRecoilValue } from 'recoil'
import { canvasSizeState, currentObjectState } from '@/store/canvasAtom' import { canvasSizeState, currentObjectState, fontFamilyState, fontSizeState } from '@/store/canvasAtom'
import { QPolygon } from '@/components/fabric/QPolygon' import { QPolygon } from '@/components/fabric/QPolygon'
// 캔버스에 필요한 이벤트 // 캔버스에 필요한 이벤트
@ -8,6 +8,8 @@ export function useCanvasEvent() {
const [canvas, setCanvasForEvent] = useState(null) const [canvas, setCanvasForEvent] = useState(null)
const [currentObject, setCurrentObject] = useRecoilState(currentObjectState) const [currentObject, setCurrentObject] = useRecoilState(currentObjectState)
const canvasSize = useRecoilValue(canvasSizeState) const canvasSize = useRecoilValue(canvasSizeState)
const fontSize = useRecoilValue(fontSizeState)
const fontFamily = useRecoilValue(fontFamilyState)
// 기본적인 이벤트 필요시 추가 // 기본적인 이벤트 필요시 추가
const attachDefaultEventOnCanvas = () => { const attachDefaultEventOnCanvas = () => {
@ -74,9 +76,16 @@ export function useCanvasEvent() {
}) })
} }
if (target.type.toLowerCase().includes('text')) {
target.set({ fontSize })
target.set({ fontFamily })
}
if (target.name === 'lengthText') { if (target.name === 'lengthText') {
const x = target.left const x = target.left
const y = target.top const y = target.top
target.lockMovementX = false
target.lockMovementY = false
// Add a property to store the previous value // Add a property to store the previous value
const previousValue = target.text const previousValue = target.text
target.on('selected', (e) => { target.on('selected', (e) => {

160
src/hooks/useContextMenu.js Normal file
View File

@ -0,0 +1,160 @@
import { useRecoilValue } from 'recoil'
import { currentMenuState } from '@/store/canvasAtom'
import { useEffect, useState } from 'react'
import { MENU } from '@/common/common'
import AuxiliaryMove from '@/components/floor-plan/modal/auxiliary/AuxiliaryMove'
import AuxiliarySize from '@/components/floor-plan/modal/auxiliary/AuxiliarySize'
export function useContextMenu() {
const currentMenu = useRecoilValue(currentMenuState)
const [contextMenu, setContextMenu] = useState([[]])
const [currentContextMenu, setCurrentContextMenu] = useState(null)
useEffect(() => {
switch (currentMenu) {
case MENU.PLAN_DRAWING:
setContextMenu([
[
{
id: 'gridMove',
name: '그리드 이동',
},
{
id: 'gridCopy',
name: '그리드 복사',
},
{
id: 'gridColorEdit',
name: '그리드 색 변경',
},
{
id: 'remove',
name: '삭제',
},
{
id: 'removeAll',
name: '전체 삭제',
},
],
])
break
case MENU.ROOF_COVERING.EXTERIOR_WALL_LINE:
case MENU.ROOF_COVERING.ROOF_SHAPE_SETTINGS:
case MENU.ROOF_COVERING.ROOF_SHAPE_PASSIVITY_SETTINGS:
case MENU.ROOF_COVERING.ROOF_SHAPE_EDITING:
case MENU.ROOF_COVERING.HELP_LINE_DRAWING:
case MENU.ROOF_COVERING.EAVES_KERAVA_EDIT:
case MENU.ROOF_COVERING.MOVEMENT_SHAPE_UPDOWN:
case MENU.ROOF_COVERING.OUTLINE_EDIT_OFFSET:
case MENU.ROOF_COVERING.ROOF_SHAPE_ALLOC:
case MENU.ROOF_COVERING.DEFAULT:
setContextMenu([
[
{
id: 'roofMaterialPlacement',
name: '지붕재 배치',
},
{
id: 'roofMaterialRemove',
name: '지붕재 삭제',
},
{
id: 'roofMaterialRemoveAll',
name: '지붕재 전체 삭제',
},
{
id: 'selectMove',
name: '선택・이동',
},
{
id: 'wallLineRemove',
name: '외벽선 삭제',
},
],
[
{
id: 'sizeEdit',
name: '사이즈 변경',
component: <AuxiliarySize setCurrentContextMenu={setCurrentContextMenu} />,
},
{
id: 'auxiliaryMove',
name: '보조선 이동(M)',
component: <AuxiliaryMove setCurrentContextMenu={setCurrentContextMenu} />,
},
{
id: 'auxiliaryCopy',
name: '보조선 복사(C)',
},
{
id: 'auxiliaryRemove',
name: '보조선 삭제(D)',
},
{
id: 'auxiliaryVerticalBisector',
name: '보조선 수직이등분선',
},
{
id: 'auxiliaryCut',
name: '보조선 절삭',
},
{
id: 'auxiliaryRemoveAll',
name: '보조선 전체 삭제',
},
],
])
break
case MENU.BATCH_CANVAS.SLOPE_SETTING:
case MENU.BATCH_CANVAS.BATCH_DRAWING:
case MENU.BATCH_CANVAS.SURFACE_SHAPE_BATCH:
case MENU.BATCH_CANVAS.OBJECT_BATCH:
case MENU.BATCH_CANVAS.ALL_REMOVE:
case MENU.BATCH_CANVAS.DEFAULT:
setContextMenu([
[
{
id: 'sizeEdit',
name: '사이즈 변경',
},
{
id: 'remove',
name: '삭제(D)',
},
{
id: 'move',
name: '이동(M)',
},
{
id: 'copy',
name: '복사(C)',
},
],
[
{
id: 'roofMaterialEdit',
name: '지붕재 변경',
},
{
id: 'linePropertyEdit',
name: '각 변 속성 변경',
},
{
id: 'flowDirectionEdit',
name: '흐름 방향 변경',
},
],
])
break
default:
setContextMenu([])
break
}
}, [currentMenu])
return {
contextMenu,
currentContextMenu,
setCurrentContextMenu,
}
}

Some files were not shown because too many files have changed in this diff Show More