Merge branch 'dev' into feature/test-jy
# Conflicts: # src/util/qpolygon-utils.js
@ -8,4 +8,7 @@ DATABASE_URL="sqlserver://mssql.devgrr.kr:1433;database=qcast;user=qcast;passwor
|
||||
|
||||
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"
|
||||
@ -6,4 +6,7 @@ DATABASE_URL=""
|
||||
|
||||
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"
|
||||
@ -17,6 +17,7 @@
|
||||
"framer-motion": "^11.2.13",
|
||||
"fs": "^0.0.1-security",
|
||||
"iron-session": "^8.0.2",
|
||||
"js-cookie": "^3.0.5",
|
||||
"mathjs": "^13.0.2",
|
||||
"mssql": "^11.0.1",
|
||||
"next": "14.2.3",
|
||||
@ -43,7 +44,7 @@
|
||||
"prettier": "^3.3.3",
|
||||
"prisma": "^5.18.0",
|
||||
"react-color-palette": "^7.2.2",
|
||||
"react-dropdown-select": "^4.11.3",
|
||||
"react-select": "^5.8.1",
|
||||
"sass": "^1.77.8",
|
||||
"tailwindcss": "^3.4.1"
|
||||
}
|
||||
|
||||
4
public/static/images/canvas/circuit_del.svg
Normal 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 |
@ -1,5 +1,5 @@
|
||||
<svg width="154" height="155" viewBox="0 0 154 155" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_4103_3996)">
|
||||
<svg width="154" height="140" viewBox="0 0 154 140" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_24_15)">
|
||||
<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"/>
|
||||
<circle cx="70" cy="8" r="7.5" stroke="#FF0000"/>
|
||||
@ -9,13 +9,8 @@
|
||||
<path d="M144 134L148.5 138L153 134" stroke="black"/>
|
||||
<rect x="144" 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>
|
||||
<clipPath id="clip0_4103_3996">
|
||||
<clipPath id="clip0_24_15">
|
||||
<rect width="140" height="140" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 716 B |
6
public/static/images/sub/community_clip.svg
Normal 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 |
5
public/static/images/sub/community_pop_close.svg
Normal 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 |
11
public/static/images/sub/community_search.svg
Normal 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="패스 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 |
6
public/static/images/sub/file_down_btn.svg
Normal 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 |
6
public/static/images/sub/grid-btn-excel.svg
Normal 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 |
8
public/static/images/sub/grid-btn-file.svg
Normal 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 |
4
public/static/images/sub/grid_tip.svg
Normal 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 |
12
src/app/SessionProvider.js
Normal 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>
|
||||
}
|
||||
@ -7,10 +7,7 @@ export default async function CommunityArchivePage() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Hero title="자료 다운로드" />
|
||||
<div className="container flex flex-wrap items-center justify-between mx-auto p-4 m-4 border">
|
||||
<Archive />
|
||||
</div>
|
||||
<Archive />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
import Hero from '@/components/Hero'
|
||||
import Faq from '@/components/community/Faq'
|
||||
import { initCheck } from '@/util/session-util'
|
||||
|
||||
@ -7,10 +6,7 @@ export default async function CommunityFaqPage() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Hero title="FAQ" />
|
||||
<div className="container flex flex-wrap items-center justify-between mx-auto p-4 m-4 border">
|
||||
<Faq />
|
||||
</div>
|
||||
<Faq />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
import Hero from '@/components/Hero'
|
||||
import Notice from '@/components/community/Notice'
|
||||
import { initCheck } from '@/util/session-util'
|
||||
|
||||
@ -7,10 +6,7 @@ export default async function CommunityNoticePage() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Hero title="공지사항" />
|
||||
<div className="container flex flex-wrap items-center justify-between mx-auto p-4 m-4 border">
|
||||
<Notice />
|
||||
</div>
|
||||
<Notice />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,19 +1,9 @@
|
||||
'use client'
|
||||
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
|
||||
export default function CompletePage() {
|
||||
const { getMessage } = useMessage()
|
||||
import JoinComplete from '@/components/auth/JoinComplete'
|
||||
|
||||
export default function JoinCompletePage() {
|
||||
return (
|
||||
<>
|
||||
<div className="flex min-h-full flex-1 flex-col justify-center px-6 py-12 lg:px-8">
|
||||
<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')} : {getMessage('join.complete.email')}
|
||||
</div>
|
||||
</div>
|
||||
<JoinComplete />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@ -4,7 +4,6 @@ import { headers } from 'next/headers'
|
||||
import { redirect } from 'next/navigation'
|
||||
import { getSession } from '@/lib/authActions'
|
||||
import RecoilRootWrapper from './RecoilWrapper'
|
||||
import UIProvider from './UIProvider'
|
||||
|
||||
import { ToastContainer } from 'react-toastify'
|
||||
|
||||
@ -14,7 +13,9 @@ import QModal from '@/components/common/modal/QModal'
|
||||
|
||||
import './globals.css'
|
||||
import '../styles/style.scss'
|
||||
import '../styles/contents.scss'
|
||||
import Dimmed from '@/components/ui/Dimmed'
|
||||
import SessionProvider from './SessionProvider'
|
||||
|
||||
// const inter = Inter({ subsets: ['latin'] })
|
||||
|
||||
@ -48,6 +49,8 @@ export default async function RootLayout({ children }) {
|
||||
telNo: session.telNo,
|
||||
fax: session.fax,
|
||||
email: session.email,
|
||||
storeLvl: session.storeLvl,
|
||||
groupId: session.groupId,
|
||||
pwdInitYn: session.pwdInitYn,
|
||||
isLoggedIn: session.isLoggedIn,
|
||||
}
|
||||
@ -61,21 +64,26 @@ export default async function RootLayout({ children }) {
|
||||
<RecoilRootWrapper>
|
||||
<html lang="en">
|
||||
<body>
|
||||
{headerPathname !== '/login' ? (
|
||||
{headerPathname === '/login' || headerPathname === '/join' ? (
|
||||
<QcastProvider>{children}</QcastProvider>
|
||||
) : (
|
||||
<div className="wrap">
|
||||
<Header userSession={sessionProps} />
|
||||
<UIProvider>
|
||||
<div className="content">
|
||||
<Dimmed />
|
||||
<QcastProvider>{children}</QcastProvider>
|
||||
<div className="content">
|
||||
<Dimmed />
|
||||
<QcastProvider>
|
||||
<SessionProvider useSession={sessionProps}>{children}</SessionProvider>
|
||||
</QcastProvider>
|
||||
</div>
|
||||
<footer>
|
||||
<div className="footer-inner">
|
||||
<span>COPYRIGHT©2024 Hanwha Japan All Rights Reserved.</span>
|
||||
</div>
|
||||
</UIProvider>
|
||||
</footer>
|
||||
</div>
|
||||
) : (
|
||||
<QcastProvider>{children}</QcastProvider>
|
||||
)}
|
||||
<ToastContainer />
|
||||
{/* <QModal /> */}
|
||||
<QModal />
|
||||
</body>
|
||||
</html>
|
||||
</RecoilRootWrapper>
|
||||
|
||||
@ -1,10 +1,9 @@
|
||||
import Login from '@/components/auth/Login'
|
||||
import NewLogin from '@/components/auth/NewLogin'
|
||||
|
||||
export default function LoginPage() {
|
||||
return (
|
||||
<>
|
||||
<NewLogin />
|
||||
<Login />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@ -94,10 +94,21 @@ export const LineType = {
|
||||
// 오브젝트 배치 > 개구배치, 그림자배치
|
||||
export const BATCH_TYPE = {
|
||||
OPENING: 'opening',
|
||||
OPENING_TEMP: 'openingTemp',
|
||||
SHADOW: 'shadow',
|
||||
SHADOW_TEMP: 'shadowTemp',
|
||||
TRIANGLE_DORMER: 'triangleDormer',
|
||||
TRIANGLE_DORMER_TEMP: 'triangleDormerTemp',
|
||||
PENTAGON_DORMER: 'pentagonDormer',
|
||||
PENTAGON_DORMER_TEMP: 'pentagonDormerTemp',
|
||||
}
|
||||
// 오브젝트 배치 > 프리입력, 치수입력
|
||||
export const INPUT_TYPE = {
|
||||
FREE: 'free',
|
||||
DIMENSION: 'dimension',
|
||||
}
|
||||
|
||||
export const POLYGON_TYPE = {
|
||||
ROOF: 'roof',
|
||||
TRESTLE: 'trestle',
|
||||
}
|
||||
|
||||
@ -13,6 +13,7 @@ import { stuffSearchState } from '@/store/stuffAtom'
|
||||
import { useForm } from 'react-hook-form'
|
||||
import '@/styles/contents.scss'
|
||||
import ChangePasswordPop from './main/ChangePasswordPop'
|
||||
import { searchState } from '@/store/boardAtom'
|
||||
export default function MainPage() {
|
||||
const [sessionState, setSessionState] = useRecoilState(sessionStore)
|
||||
|
||||
@ -36,6 +37,8 @@ export default function MainPage() {
|
||||
|
||||
const [stuffSearch, setStuffSearch] = useRecoilState(stuffSearchState)
|
||||
|
||||
const [searchForm, setSearchForm] = useRecoilState(searchState)
|
||||
|
||||
useEffect(() => {
|
||||
if (sessionState.pwdInitYn === 'Y') {
|
||||
fetchObjectList()
|
||||
@ -72,14 +75,8 @@ export default function MainPage() {
|
||||
})
|
||||
router.push('/management/stuff')
|
||||
} else {
|
||||
alert('작업중')
|
||||
return
|
||||
|
||||
//FAQ일떄
|
||||
//faq리코일에
|
||||
//searchValue= e.target.value
|
||||
//mainFlag:'Y'
|
||||
// router.push('/community/faq')
|
||||
setSearchForm({ ...searchForm, searchValue: searchTxt, mainFlag: 'Y' })
|
||||
router.push('/community/faq')
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -100,13 +97,8 @@ export default function MainPage() {
|
||||
|
||||
router.push('/management/stuff')
|
||||
} else {
|
||||
alert('작업중')
|
||||
return
|
||||
//FAQ일떄
|
||||
//faq리코일에
|
||||
//searchValue= e.target.value
|
||||
//mainFlag:'Y'
|
||||
// router.push('/community/faq')
|
||||
setSearchForm({ ...searchForm, searchValue: searchTxt, mainFlag: 'Y' })
|
||||
router.push('/community/faq')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -20,6 +20,7 @@ import Image from 'next/image'
|
||||
|
||||
import QInput from './common/input/Qinput'
|
||||
import QSelect from './common/select/QSelect'
|
||||
import QPagination from './common/pagination/QPagination'
|
||||
|
||||
export default function Playground() {
|
||||
const [useCadFile, setUseCadFile] = useRecoilState(useCadFileState)
|
||||
@ -29,7 +30,7 @@ export default function Playground() {
|
||||
const fileRef = useRef(null)
|
||||
const queryRef = useRef(null)
|
||||
const [zoom, setZoom] = useState(20)
|
||||
const { get, promisePost } = useAxios()
|
||||
const { get, promiseGet, promisePost } = useAxios()
|
||||
const testVar = process.env.NEXT_PUBLIC_TEST
|
||||
const converterUrl = process.env.NEXT_PUBLIC_CONVERTER_API_URL
|
||||
const { getMessage } = useMessage()
|
||||
@ -41,6 +42,9 @@ export default function Playground() {
|
||||
const [radioInput, setRadioInput] = useState('')
|
||||
const [checkboxInput, setCheckboxInput] = useState([])
|
||||
const [selectedValue, setSelectedValue] = useState('')
|
||||
|
||||
const [users, setUsers] = useState([])
|
||||
|
||||
useEffect(() => {
|
||||
console.log('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 (
|
||||
<>
|
||||
<div className="container mx-auto p-4 m-4 border">
|
||||
@ -290,6 +308,35 @@ export default function Playground() {
|
||||
Sweetalert - confirm
|
||||
</Button>
|
||||
</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>
|
||||
</>
|
||||
)
|
||||
|
||||
@ -3,7 +3,8 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { Button } from '@nextui-org/react'
|
||||
|
||||
import { get, post } from '@/lib/Axios'
|
||||
import { useAxios } from '@/hooks/useAxios'
|
||||
|
||||
import { useRecoilState } from 'recoil'
|
||||
import { customSettingsState } from '@/store/canvasAtom'
|
||||
import { modalContent, modalState } from '@/store/modalAtom'
|
||||
@ -20,6 +21,8 @@ export default function Settings() {
|
||||
const [open, setOpen] = useRecoilState(modalState)
|
||||
const [contents, setContent] = useRecoilState(modalContent)
|
||||
|
||||
const { get, post } = useAxios()
|
||||
|
||||
const handleSavePopup = () => {
|
||||
console.log('color ', color)
|
||||
}
|
||||
|
||||
@ -1,30 +1,28 @@
|
||||
'use client'
|
||||
|
||||
import { useAxios } from '@/hooks/useAxios'
|
||||
import { redirect } from 'next/navigation'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import Cookies from 'js-cookie'
|
||||
|
||||
export default function Join() {
|
||||
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 = {
|
||||
langCd: 'JA',
|
||||
lastEditUser: formData.get('userId'),
|
||||
storeQcastNm: formData.get('storeQcastNm'),
|
||||
storeQcastNmKana: formData.get('storeQcastNmKana'),
|
||||
postCd: formData.get('postCd'),
|
||||
addr: formData.get('addr'),
|
||||
telNo: formData.get('telNo'),
|
||||
fax: formData.get('fax'),
|
||||
payTermsCd: 'JB02',
|
||||
kamId: 'E1101011',
|
||||
qtCompNm: formData.get('qtCompNm'),
|
||||
qtPostCd: formData.get('qtPostCd'),
|
||||
qtAddr: formData.get('qtAddr'),
|
||||
qtTelNo: formData.get('qtTelNo'),
|
||||
qtFax: formData.get('qtFax'),
|
||||
bizNo: formData.get('bizNo'),
|
||||
userInfo: {
|
||||
userId: formData.get('userId'),
|
||||
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) => {
|
||||
if (res) {
|
||||
if (res.result.resultCode == 'S') {
|
||||
redirect('/join/complete')
|
||||
} else {
|
||||
alert(res.result.resultMsg)
|
||||
await promisePost({ url: '/api/login/v1.0/user/join', data: param })
|
||||
.then((res) => {
|
||||
if (res) {
|
||||
if (res.data.result.resultCode == 'S') {
|
||||
Cookies.set('joinEmail', formData.get('email'), { expires: 1 })
|
||||
router.push('/join/complete')
|
||||
} else {
|
||||
alert(res.data.result.resultMsg)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
.catch((error) => {
|
||||
alert(error.response.data.message)
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex min-h-full flex-1 flex-col justify-center px-6 py-12 lg:px-8">
|
||||
<h1 className="text-center text-4xl font-bold">{getMessage('join.title')}</h1>
|
||||
<form action={joinProcess}>
|
||||
<div className="mt-10">
|
||||
<div>
|
||||
● {getMessage('join.sub1.title')} (*{getMessage('common.require')}) <span>{getMessage('join.sub1.comment')}</span>
|
||||
<div className="center-page-wrap">
|
||||
<div className="center-page-inner">
|
||||
<form onSubmit={joinProcess}>
|
||||
<div className="center-page-tit">{getMessage('join.title')}</div>
|
||||
<div className="sub-table-box signup">
|
||||
<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>
|
||||
<table className="w-full">
|
||||
<colgroup>
|
||||
<col style={{ width: '20%' }} />
|
||||
<col style={{ width: '80%' }} />
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th style={{ border: '1px solid gray', padding: '5px' }}>{getMessage('join.sub1.storeQcastNm')} *</th>
|
||||
<td style={{ border: '1px solid gray', padding: '5px' }}>
|
||||
<input
|
||||
type="text"
|
||||
id="storeQcastNm"
|
||||
name="storeQcastNm"
|
||||
required
|
||||
alt={getMessage('join.sub1.storeQcastNm')}
|
||||
className="block w-full border-0 py-1.5 ring-1 ring-inset ring-gray-300"
|
||||
placeholder={getMessage('join.sub1.storeQcastNm_placeholder')}
|
||||
></input>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th style={{ border: '1px solid gray', padding: '5px' }}>{getMessage('join.sub1.storeQcastNmKana')} *</th>
|
||||
<td style={{ border: '1px solid gray', padding: '5px' }}>
|
||||
<input
|
||||
type="text"
|
||||
id="storeQcastNmKana"
|
||||
name="storeQcastNmKana"
|
||||
required
|
||||
className="block w-full border-0 py-1.5 ring-1 ring-inset ring-gray-300"
|
||||
placeholder={getMessage('join.sub1.storeQcastNmKana_placeholder')}
|
||||
></input>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th style={{ border: '1px solid gray', padding: '5px' }}>
|
||||
{getMessage('join.sub1.postCd')}/{getMessage('join.sub1.addr')} *
|
||||
</th>
|
||||
<td style={{ border: '1px solid gray', padding: '5px' }}>
|
||||
<input
|
||||
type="text"
|
||||
id="postCd"
|
||||
name="postCd"
|
||||
required
|
||||
className="block border-0 py-1.5 ring-1 ring-inset ring-gray-300"
|
||||
placeholder={getMessage('join.sub1.postCd_placeholder')}
|
||||
></input>
|
||||
<input
|
||||
type="text"
|
||||
id="addr"
|
||||
name="addr"
|
||||
required
|
||||
className="block w-full border-0 py-1.5 ring-1 ring-inset ring-gray-300"
|
||||
placeholder={getMessage('join.sub1.addr_placeholder')}
|
||||
></input>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th style={{ border: '1px solid gray', padding: '5px' }}>{getMessage('join.sub1.telNo')} *</th>
|
||||
<td style={{ border: '1px solid gray', padding: '5px' }}>
|
||||
<input
|
||||
type="text"
|
||||
id="telNo"
|
||||
name="telNo"
|
||||
required
|
||||
className="block w-full border-0 py-1.5 ring-1 ring-inset ring-gray-300"
|
||||
placeholder={getMessage('join.sub1.telNo_placeholder')}
|
||||
></input>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th style={{ border: '1px solid gray', padding: '5px' }}>{getMessage('join.sub1.fax')} *</th>
|
||||
<td style={{ border: '1px solid gray', padding: '5px' }}>
|
||||
<input
|
||||
type="text"
|
||||
id="fax"
|
||||
name="fax"
|
||||
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>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div className="mt-5">
|
||||
● {getMessage('join.sub2.title')} (*{getMessage('common.require')})
|
||||
<div className="sub-table-box signup">
|
||||
<div className="table-box-title-wrap">
|
||||
<div className="title-wrap">
|
||||
<h3>
|
||||
{getMessage('join.sub2.title')} <span className="important">(*{getMessage('common.require')})</span>
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div className="common-table">
|
||||
<table>
|
||||
<colgroup>
|
||||
<col style={{ width: '180px' }} />
|
||||
<col />
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>
|
||||
{getMessage('join.sub2.userNm')} <span className="important">*</span>
|
||||
</th>
|
||||
<td>
|
||||
<div className="input-wrap" style={{ width: '200px' }}>
|
||||
<input type="text" id="userNm" name="userNm" className="input-light" required />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{getMessage('join.sub2.userNmKana')}</th>
|
||||
<td>
|
||||
<div className="input-wrap" style={{ width: '200px' }}>
|
||||
<input type="text" id="userNmKana" name="userNmKana" className="input-light" />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
{getMessage('join.sub2.userId')} <span className="important">*</span>
|
||||
</th>
|
||||
<td>
|
||||
<div className="input-wrap" style={{ width: '200px' }}>
|
||||
<input type="text" id="userId" name="userId" className="input-light" required />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
{getMessage('join.sub2.email')} <span className="important">*</span>
|
||||
</th>
|
||||
<td>
|
||||
<div className="input-wrap" style={{ width: '200px' }}>
|
||||
<input type="text" id="email" name="email" className="input-light" required />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
{getMessage('join.sub2.telNo')} <span className="important">*</span>
|
||||
</th>
|
||||
<td>
|
||||
<div className="input-wrap" style={{ width: '200px' }}>
|
||||
<input
|
||||
type="text"
|
||||
id="userTelNo"
|
||||
name="userTelNo"
|
||||
className="input-light"
|
||||
placeholder={getMessage('join.sub2.telNo_placeholder')}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
{getMessage('join.sub2.fax')} <span className="important">*</span>
|
||||
</th>
|
||||
<td>
|
||||
<div className="input-wrap" style={{ width: '200px' }}>
|
||||
<input
|
||||
type="text"
|
||||
id="userFax"
|
||||
name="userFax"
|
||||
className="input-light"
|
||||
placeholder={getMessage('join.sub1.fax_placeholder')}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
</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>
|
||||
<table className="w-full">
|
||||
<colgroup>
|
||||
<col style={{ width: '20%' }} />
|
||||
<col style={{ width: '80%' }} />
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th style={{ border: '1px solid gray', padding: '5px' }}>{getMessage('join.sub2.userNm')} *</th>
|
||||
<td style={{ border: '1px solid gray', padding: '5px' }}>
|
||||
<input
|
||||
type="text"
|
||||
id="userNm"
|
||||
name="userNm"
|
||||
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 className="sign-up-btn-wrap">
|
||||
<button
|
||||
type="button"
|
||||
className="btn-origin grey mr5"
|
||||
onClick={() => {
|
||||
router.push('/login')
|
||||
}}
|
||||
>
|
||||
{getMessage('join.btn.login_page')}
|
||||
</button>
|
||||
<button type="submit" className="btn-origin navy">
|
||||
{getMessage('join.btn.approval_request')}
|
||||
</button>
|
||||
</div>
|
||||
<table className="w-full">
|
||||
<colgroup>
|
||||
<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>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
44
src/components/auth/JoinComplete.jsx
Normal 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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -1,62 +1,80 @@
|
||||
'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 { setSession } from '@/lib/authActions'
|
||||
import { redirect } from 'next/navigation'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
|
||||
import { Button, Switch } from '@nextui-org/react'
|
||||
import { useRecoilState } from 'recoil'
|
||||
import { globalLocaleStore } from '@/store/localeAtom'
|
||||
import { modalContent, modalState } from '@/store/modalAtom'
|
||||
import { sessionStore } from '@/store/commonAtom'
|
||||
import { useRouter } from 'next/navigation'
|
||||
|
||||
import Cookies from 'js-cookie'
|
||||
|
||||
import { useSearchParams } from 'next/navigation'
|
||||
|
||||
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 [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')
|
||||
}
|
||||
const [passwordReset, setPasswordReset] = useState(1)
|
||||
|
||||
setIsSelected(!isSelected)
|
||||
}
|
||||
const { promisePost, promisePatch, post } = useAxios(globalLocaleState)
|
||||
|
||||
// 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)
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
const loginProcess = async (e) => {
|
||||
e.preventDefault()
|
||||
const formData = new FormData(e.target)
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
// 임시 로그인 처리
|
||||
setSession({
|
||||
userId: 'NEW016610',
|
||||
@ -71,9 +89,10 @@ export default function Login() {
|
||||
telNo: '336610',
|
||||
fax: null,
|
||||
email: 't10t@naver.com',
|
||||
pwdInitYn: 'N',
|
||||
pwdInitYn: 'Y',
|
||||
storeLvl: '1',
|
||||
groupId: '60000',
|
||||
})
|
||||
|
||||
setSessionState({
|
||||
userId: 'NEW016610',
|
||||
saleStoreId: null,
|
||||
@ -87,155 +106,230 @@ export default function Login() {
|
||||
telNo: '336610',
|
||||
fax: null,
|
||||
email: 't10t@naver.com',
|
||||
pwdInitYn: 'N',
|
||||
pwdInitYn: 'Y',
|
||||
storeLvl: '1',
|
||||
groupId: '60000',
|
||||
})
|
||||
|
||||
redirect('/')
|
||||
if (chkLoginId) {
|
||||
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 [contents, setContent] = useRecoilState(modalContent)
|
||||
|
||||
const initPasswordProcess = async (formData) => {
|
||||
const initPasswordProcess = async () => {
|
||||
const param = {
|
||||
langCd: currentLocale,
|
||||
lastEditUser: formData.get('checkId'),
|
||||
loginId: formData.get('checkId'),
|
||||
email: formData.get('checkEmail'),
|
||||
loginId: checkId,
|
||||
email: 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)
|
||||
}
|
||||
}
|
||||
await promisePatch({
|
||||
url: '/api/login/v1.0/user/init-password',
|
||||
data: param,
|
||||
})
|
||||
.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 (
|
||||
<div className="flex flex-col align-center">
|
||||
<div className="flex min-h-full flex-1 flex-col justify-center px-6 py-12 lg:px-8">
|
||||
<div className="mt-10 sm:mx-auto sm:w-full sm:max-w-sm">
|
||||
<h1 className="text-center text-4xl font-bold leading-9 tracking-tight text-gray-900">{getMessage('site.name')}</h1>
|
||||
<h2 className="mt-5 text-center text-2xl font-bold leading-9 tracking-tight text-gray-900">{getMessage('site.sub_name')}</h2>
|
||||
</div>
|
||||
<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>
|
||||
|
||||
<div className="mt-5 sm:mx-auto sm:w-full sm:max-w-sm">
|
||||
<form action={loginProcess} className="space-y-6">
|
||||
<div>
|
||||
<label htmlFor="userId" className="block text-sm font-medium leading-6 text-gray-900">
|
||||
ID
|
||||
</label>
|
||||
<div className="mt-2">
|
||||
<input
|
||||
id="userId"
|
||||
name="id"
|
||||
type="text"
|
||||
required
|
||||
// autoComplete="email"
|
||||
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"
|
||||
/>
|
||||
{passwordReset === 1 && (
|
||||
<>
|
||||
<div className="login-input-frame">
|
||||
<form onSubmit={loginProcess} className="space-y-6">
|
||||
<div className="login-frame-tit">
|
||||
<span>{getMessage('site.name')}</span>
|
||||
{getMessage('site.sub_name')}
|
||||
</div>
|
||||
<div className="login-input-wrap">
|
||||
<div className="login-area id">
|
||||
<input
|
||||
type="text"
|
||||
className="login-input"
|
||||
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 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 className="login-copyright">COPYRIGHT©2024 Hanwha Japan All Rights Reserved.</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -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>
|
||||
)
|
||||
}
|
||||
@ -27,7 +27,7 @@ export default function QContextMenu(props) {
|
||||
const handleContextMenu = (e) => {
|
||||
// e.preventDefault() //기존 contextmenu 막고
|
||||
setContextMenu({ visible: true, x: e.pageX, y: e.pageY })
|
||||
console.log(111, canvasProps)
|
||||
// console.log(111, canvasProps)
|
||||
canvasProps?.upperCanvasEl.removeEventListener('contextmenu', handleContextMenu) //한번 노출 후 이벤트 삭제
|
||||
}
|
||||
|
||||
|
||||
43
src/components/common/pagination/QPagination.jsx
Normal 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>
|
||||
)
|
||||
}
|
||||
@ -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() {
|
||||
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 (
|
||||
<>
|
||||
<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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
103
src/components/community/ArchiveTable.jsx
Normal 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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -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() {
|
||||
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 (
|
||||
<>
|
||||
<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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@ -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() {
|
||||
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 (
|
||||
<>
|
||||
<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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
78
src/components/community/Pagination.jsx
Normal 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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
117
src/components/community/Search.jsx
Normal 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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
110
src/components/community/Table.jsx
Normal 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} />}
|
||||
</>
|
||||
)
|
||||
}
|
||||
77
src/components/community/modal/BoardDetailModal.jsx
Normal 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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -75,7 +75,7 @@ export const QLine = fabric.util.createClass(fabric.Line, {
|
||||
const y2 = this.top + this.height * scaleY
|
||||
const dx = x2 - x1
|
||||
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() {
|
||||
@ -150,7 +150,7 @@ export const QLine = fabric.util.createClass(fabric.Line, {
|
||||
|
||||
getLength() {
|
||||
//10배 곱해진 값 return
|
||||
return Number(this.length.toFixed(1) * 10)
|
||||
return Number(this.length.toFixed(0) * 10)
|
||||
},
|
||||
|
||||
setViewLengthText(bool) {
|
||||
|
||||
@ -121,13 +121,13 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
||||
|
||||
this.on('modified', (e) => {
|
||||
this.addLengthText()
|
||||
if (this.arrow) {
|
||||
drawDirectionArrow(this)
|
||||
}
|
||||
// if (this.arrow) {
|
||||
// drawDirectionArrow(this)
|
||||
// }
|
||||
})
|
||||
|
||||
this.on('selected', () => {
|
||||
drawDirectionArrow(this)
|
||||
// drawDirectionArrow(this)
|
||||
Object.keys(this.controls).forEach((controlKey) => {
|
||||
if (controlKey !== 'ml' && controlKey !== 'mr') {
|
||||
this.setControlVisible(controlKey, false)
|
||||
@ -200,14 +200,32 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
||||
const end = points[(i + 1) % points.length]
|
||||
const dx = end.x - start.x
|
||||
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
|
||||
|
||||
// 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,
|
||||
top: midPoint.y,
|
||||
fontSize: this.fontSize,
|
||||
|
||||
@ -1,19 +1,19 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { useEffect, useRef } from 'react'
|
||||
|
||||
import { useCanvas } from '@/hooks/useCanvas'
|
||||
import { useEvent } from '@/hooks/useEvent'
|
||||
import QContextMenu from '@/components/common/context-menu/QContextMenu'
|
||||
import { useContextMenu } from '@/hooks/useContextMenu'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { currentMenuState } from '@/store/canvasAtom'
|
||||
import { MENU } from '@/common/common'
|
||||
import { currentObjectState } from '@/store/canvasAtom'
|
||||
|
||||
export default function CanvasFrame({ plan }) {
|
||||
const canvasRef = useRef(null)
|
||||
const { canvas } = useCanvas('canvas')
|
||||
const currentMenu = useRecoilValue(currentMenuState)
|
||||
const [contextMenu, setContextMenu] = useState([[]])
|
||||
const { contextMenu, currentContextMenu, setCurrentContextMenu } = useContextMenu()
|
||||
|
||||
useEvent()
|
||||
|
||||
const loadCanvas = () => {
|
||||
@ -29,131 +29,9 @@ export default function CanvasFrame({ plan }) {
|
||||
|
||||
useEffect(() => {
|
||||
loadCanvas()
|
||||
}, [plan])
|
||||
}, [plan, canvas])
|
||||
|
||||
useEffect(() => {
|
||||
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])
|
||||
const onClickContextMenu = (index) => {}
|
||||
|
||||
return (
|
||||
<div className="canvas-frame flex justify-center">
|
||||
@ -162,11 +40,12 @@ export default function CanvasFrame({ plan }) {
|
||||
{contextMenu.map((menus, index) => (
|
||||
<ul key={index}>
|
||||
{menus.map((menu) => (
|
||||
<li>{menu.name}</li>
|
||||
<li onClick={(e) => setCurrentContextMenu(menu)}>{menu.name}</li>
|
||||
))}
|
||||
</ul>
|
||||
))}
|
||||
</QContextMenu>
|
||||
{currentContextMenu?.component}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,108 +1,29 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||
import { useContext, useEffect, useState } from 'react'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import CanvasFrame from './CanvasFrame'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { useSwal } from '@/hooks/useSwal'
|
||||
import { usePlan } from '@/hooks/usePlan'
|
||||
import { globalLocaleStore } from '@/store/localeAtom'
|
||||
import { currentCanvasPlanState, initCanvasPlansState, plansState } from '@/store/canvasAtom'
|
||||
import { sessionStore } from '@/store/commonAtom'
|
||||
import { SessionContext } from '@/app/SessionProvider'
|
||||
|
||||
export default function CanvasLayout() {
|
||||
const { session } = useContext(SessionContext)
|
||||
console.log('session >>> ', session)
|
||||
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 globalLocaleState = useRecoilValue(globalLocaleStore)
|
||||
|
||||
const { getMessage } = useMessage()
|
||||
const { swalFire } = useSwal()
|
||||
const { getCanvasByObjectNo, delCanvasById, checkModifiedCanvasPlan, saveCanvas } = 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)
|
||||
}
|
||||
const { plans, loadCanvasPlanData, handleCurrentPlan, handleAddPlan, handleDeletePlan } = usePlan()
|
||||
|
||||
useEffect(() => {
|
||||
getCanvasByObjectNo(sessionState.userId, objectNo).then((res) => {
|
||||
console.log('canvas 목록 ', res)
|
||||
if (res.length > 0) {
|
||||
setInitCanvasPlans(res)
|
||||
setPlans(res)
|
||||
handleCurrentPlan(res.at(-1).id) // last 데이터에 포커싱
|
||||
setPlanNum(res.length)
|
||||
} else {
|
||||
addNewPlan()
|
||||
}
|
||||
})
|
||||
console.log('loadCanvasPlanData 실행, sessionState.userId >>> ', sessionState.userId)
|
||||
loadCanvasPlanData(sessionState.userId, objectNo)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
@ -110,13 +31,17 @@ export default function CanvasLayout() {
|
||||
<div className="canvas-page-list">
|
||||
<div className="canvas-plane-wrap">
|
||||
{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>
|
||||
<i
|
||||
className="close"
|
||||
onClick={(e) =>
|
||||
swalFire({
|
||||
html: getMessage('common.message.confirm.delete') + `</br>${plan.name}`,
|
||||
text: `${plan.name} ` + getMessage('plan.message.confirm.delete'),
|
||||
type: 'confirm',
|
||||
confirmFn: () => {
|
||||
handleDeletePlan(e, plan.id)
|
||||
@ -127,9 +52,11 @@ export default function CanvasLayout() {
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<button className="plane-add" onClick={addNewPlan}>
|
||||
<span></span>
|
||||
</button>
|
||||
{plans.length < 10 && (
|
||||
<button className="plane-add" onClick={() => handleAddPlan(sessionState.userId, objectNo)}>
|
||||
<span></span>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<CanvasFrame plan={plans.find((plan) => plan.isCurrent === true)} />
|
||||
</div>
|
||||
|
||||
@ -19,6 +19,7 @@ import { MENU } from '@/common/common'
|
||||
import KO from '@/locales/ko.json'
|
||||
import JA from '@/locales/ja.json'
|
||||
import { settingModalFirstOptionsState } from '@/store/settingAtom'
|
||||
import { placementShapeDrawingPointsState } from '@/store/placementShapeDrawingAtom'
|
||||
|
||||
const canvasMenus = [
|
||||
{ index: 0, name: 'plan.menu.plan.drawing', icon: 'con00', title: MENU.PLAN_DRAWING },
|
||||
@ -48,6 +49,8 @@ export default function CanvasMenu(props) {
|
||||
setShowWallLineOffsetSettingModal,
|
||||
setShowRoofAllocationSettingModal,
|
||||
setShowBasicSettingModal,
|
||||
setShowCircuitTrestleSettingModal,
|
||||
setShowPropertiesSettingModal,
|
||||
} = props
|
||||
|
||||
const [menuNumber, setMenuNumber] = useState(null)
|
||||
@ -56,7 +59,8 @@ export default function CanvasMenu(props) {
|
||||
const [verticalHorizontalMode, setVerticalHorizontalMode] = useRecoilState(verticalHorizontalModeState)
|
||||
const [appMessageState, setAppMessageState] = useRecoilState(appMessageStore)
|
||||
const setCurrentMenu = useSetRecoilState(currentMenuState)
|
||||
const setPoints = useSetRecoilState(outerLinePointsState)
|
||||
const setOuterLinePoints = useSetRecoilState(outerLinePointsState)
|
||||
const setPlacementPoints = useSetRecoilState(placementShapeDrawingPointsState)
|
||||
const [canvasZoom, setCanvasZoom] = useRecoilState(canvasZoomState)
|
||||
const [currentCanvasPlan, setcurrentCanvasPlan] = useRecoilState(currentCanvasPlanState)
|
||||
|
||||
@ -104,6 +108,8 @@ export default function CanvasMenu(props) {
|
||||
setShowRoofAllocationSettingModal,
|
||||
setShowObjectSettingModal,
|
||||
setShowBasicSettingModal,
|
||||
setShowCircuitTrestleSettingModal,
|
||||
setShowPropertiesSettingModal,
|
||||
type,
|
||||
}
|
||||
|
||||
@ -117,14 +123,14 @@ export default function CanvasMenu(props) {
|
||||
}, [menuNumber, type])
|
||||
|
||||
// 저장버튼(btn08) 클릭 시 호출되는 함수
|
||||
const handleSaveCanvas = () => {
|
||||
swalFire({
|
||||
html: getMessage('common.message.confirm.save') + `</br>${currentCanvasPlan.name}`,
|
||||
type: 'confirm',
|
||||
confirmFn: () => {
|
||||
saveCanvas(sessionState.userId)
|
||||
},
|
||||
})
|
||||
const handleSaveCanvas = async () => {
|
||||
// swalFire({
|
||||
// text: `${currentCanvasPlan.name} ` + getMessage('plan.message.confirm.save'),
|
||||
// type: 'confirm',
|
||||
// confirmFn: async () => {
|
||||
await saveCanvas(sessionState.userId)
|
||||
// },
|
||||
// })
|
||||
}
|
||||
|
||||
const onClickPlacementInitialMenu = () => {
|
||||
@ -136,7 +142,8 @@ export default function CanvasMenu(props) {
|
||||
}
|
||||
|
||||
const handleClear = () => {
|
||||
setPoints([])
|
||||
setOuterLinePoints([])
|
||||
setPlacementPoints([])
|
||||
canvas?.clear()
|
||||
}
|
||||
|
||||
@ -161,7 +168,11 @@ export default function CanvasMenu(props) {
|
||||
<ul className="canvas-menu-list">
|
||||
{canvasMenus.map((menu) => {
|
||||
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>
|
||||
<span className={`menu-icon ${menu.icon}`}></span>
|
||||
{getMessage(menu.name)}
|
||||
@ -193,7 +204,12 @@ export default function CanvasMenu(props) {
|
||||
<button className="btn06"></button>
|
||||
</div>
|
||||
<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>
|
||||
<button className="control-btn plus" onClick={handleZoomClear}></button>
|
||||
</div>
|
||||
|
||||
@ -27,6 +27,7 @@ import RoofShapePassivitySetting from '@/components/floor-plan/modal/roofShape/R
|
||||
import MovementSetting from '@/components/floor-plan/modal/movement/MovementSetting'
|
||||
import RoofAllocationSetting from '@/components/floor-plan/modal/roofAllocation/RoofAllocationSetting'
|
||||
import BasicSetting from '@/components/floor-plan/modal/basic/BasicSetting'
|
||||
import CircuitTrestleSetting from '@/components/floor-plan/modal/circuitTrestle/CircuitTrestleSetting'
|
||||
|
||||
export default function FloorPlan() {
|
||||
const [showCanvasSettingModal, setShowCanvasSettingModal] = useState(false)
|
||||
@ -45,6 +46,7 @@ export default function FloorPlan() {
|
||||
const [showWallLineOffsetSettingModal, setShowWallLineOffsetSettingModal] = useState(false)
|
||||
const [showRoofAllocationSettingModal, setShowRoofAllocationSettingModal] = useState(false)
|
||||
const [showBasicSettingModal, setShowBasicSettingModal] = useState(false)
|
||||
const [showCircuitTrestleSettingModal, setShowCircuitTrestleSettingModal] = useState(false)
|
||||
const globalLocaleState = useRecoilValue(globalLocaleStore)
|
||||
const { get } = useAxios(globalLocaleState)
|
||||
|
||||
@ -84,6 +86,8 @@ export default function FloorPlan() {
|
||||
setShowWallLineOffsetSettingModal,
|
||||
setShowRoofAllocationSettingModal,
|
||||
setShowBasicSettingModal,
|
||||
setShowCircuitTrestleSettingModal,
|
||||
setShowPropertiesSettingModal,
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
@ -158,6 +162,7 @@ export default function FloorPlan() {
|
||||
{showObjectSettingModal && <ObjectSetting setShowObjectSettingModal={setShowObjectSettingModal} />}
|
||||
{showPlacementSurfaceSettingModal && <PlacementSurfaceSetting setShowPlacementSurfaceSettingModal={setShowPlacementSurfaceSettingModal} />}
|
||||
{showBasicSettingModal && <BasicSetting setShowBasicSettingModal={setShowBasicSettingModal} />}
|
||||
{showCircuitTrestleSettingModal && <CircuitTrestleSetting setShowCircuitTrestleSettingModal={setShowCircuitTrestleSettingModal} />}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
|
||||
@ -5,7 +5,7 @@ import { useEffect, useState } from 'react'
|
||||
import { MENU } from '@/common/common'
|
||||
import { currentMenuState } from '@/store/canvasAtom'
|
||||
import { useSetRecoilState } from 'recoil'
|
||||
|
||||
import { useSurfaceShapeBatch } from '@/hooks/surface/useSurfaceShapeBatch'
|
||||
export default function MenuDepth01(props) {
|
||||
const {
|
||||
setShowOutlineModal,
|
||||
@ -23,10 +23,15 @@ export default function MenuDepth01(props) {
|
||||
setShowRoofAllocationSettingModal,
|
||||
setShowObjectSettingModal,
|
||||
setShowBasicSettingModal,
|
||||
setShowCircuitTrestleSettingModal,
|
||||
setShowPropertiesSettingModal,
|
||||
} = props
|
||||
const { getMessage } = useMessage()
|
||||
const [activeMenu, setActiveMenu] = useState()
|
||||
const setCurrentMenu = useSetRecoilState(currentMenuState)
|
||||
|
||||
const { deleteAllSurfacesAndObjects } = useSurfaceShapeBatch()
|
||||
|
||||
const onClickMenu = ({ id, menu, name }) => {
|
||||
setActiveMenu(menu)
|
||||
setShowOutlineModal(menu === MENU.ROOF_COVERING.EXTERIOR_WALL_LINE)
|
||||
@ -42,6 +47,8 @@ export default function MenuDepth01(props) {
|
||||
setShowWallLineOffsetSettingModal(id === 6)
|
||||
setShowRoofAllocationSettingModal(id === 7)
|
||||
setShowPlaceShapeDrawingModal(false)
|
||||
setShowPropertiesSettingModal(false)
|
||||
setShowCircuitTrestleSettingModal(false)
|
||||
}
|
||||
|
||||
if (type === 'surface') {
|
||||
@ -53,11 +60,17 @@ export default function MenuDepth01(props) {
|
||||
setShowMovementModal(false)
|
||||
setShowWallLineOffsetSettingModal(false)
|
||||
setShowRoofAllocationSettingModal(false)
|
||||
|
||||
setShowPropertiesSettingModal(false)
|
||||
setShowCircuitTrestleSettingModal(false)
|
||||
setShowSlopeSettingModal(id === 0)
|
||||
setShowPlaceShapeDrawingModal(id === 1)
|
||||
setShowPlacementSurfaceSettingModal(id === 2)
|
||||
setShowObjectSettingModal(id === 3)
|
||||
|
||||
//배치면 전체 삭제
|
||||
if (id === 4) {
|
||||
deleteAllSurfacesAndObjects()
|
||||
}
|
||||
}
|
||||
|
||||
if (type === 'module') {
|
||||
@ -69,7 +82,9 @@ export default function MenuDepth01(props) {
|
||||
setShowMovementModal(false)
|
||||
setShowWallLineOffsetSettingModal(false)
|
||||
setShowRoofAllocationSettingModal(false)
|
||||
setShowPropertiesSettingModal(false)
|
||||
setShowBasicSettingModal(id === 0)
|
||||
setShowCircuitTrestleSettingModal(id === 1)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -49,7 +49,7 @@ export default function AuxiliaryDrawing({ setShowAuxiliaryModal }) {
|
||||
handleFix,
|
||||
buttonAct,
|
||||
setButtonAct,
|
||||
} = useAuxiliaryDrawing()
|
||||
} = useAuxiliaryDrawing(setShowAuxiliaryModal)
|
||||
|
||||
const outerLineProps = {
|
||||
length1,
|
||||
|
||||
49
src/components/floor-plan/modal/auxiliary/AuxiliaryMove.jsx
Normal 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>
|
||||
)
|
||||
}
|
||||
61
src/components/floor-plan/modal/auxiliary/AuxiliarySize.jsx
Normal 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>
|
||||
)
|
||||
}
|
||||
@ -1,14 +1,18 @@
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import WithDraggable from '@/components/common/draggable/withDraggable'
|
||||
import WithDraggable from '@/components/common/draggable/WithDraggable'
|
||||
import { useState } from 'react'
|
||||
import Orientation from '@/components/floor-plan/modal/basic/step/Orientation'
|
||||
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 { useRecoilState } from 'recoil'
|
||||
import { canvasSettingState } from '@/store/canvasAtom'
|
||||
|
||||
export default function BasicSetting({ setShowBasicSettingModal }) {
|
||||
const { getMessage } = useMessage()
|
||||
const [tabNum, setTabNum] = useState(1)
|
||||
|
||||
const [canvasSetting, setCanvasSetting] = useRecoilState(canvasSettingState)
|
||||
return (
|
||||
<WithDraggable isShow={true} pos={{ x: 50, y: -950 }}>
|
||||
<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>
|
||||
{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">
|
||||
{tabNum !== 1 && (
|
||||
@ -36,7 +45,7 @@ export default function BasicSetting({ setShowBasicSettingModal }) {
|
||||
{getMessage('modal.module.basic.setting.prev')}
|
||||
</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 && (
|
||||
<button className="btn-frame modal" onClick={() => setTabNum(tabNum + 1)}>
|
||||
Next
|
||||
|
||||
@ -74,12 +74,15 @@ export default function Module({}) {
|
||||
</tr>
|
||||
</>
|
||||
))}
|
||||
<tr>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></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>
|
||||
|
||||
@ -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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -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>
|
||||
)
|
||||
}
|
||||
@ -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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -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">屋内PCS(HQJP-KA-5シリーズ)</label>
|
||||
</div>
|
||||
<div className="d-check-box pop sel">
|
||||
<input type="checkbox" id="ch02" />
|
||||
<label htmlFor="ch02">屋外マルチPCS(HQJP-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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
414
src/components/floor-plan/modal/circuitTrestle/step/StepUp.jsx
Normal 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>*/}
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -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>
|
||||
)
|
||||
}
|
||||
@ -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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -10,7 +10,7 @@ import { useEavesGableEdit } from '@/hooks/roofcover/useEavesGableEdit'
|
||||
export default function EavesGableEdit({ setShowEavesGableEditModal }) {
|
||||
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 = {
|
||||
pitchRef,
|
||||
offsetRef,
|
||||
|
||||
@ -1,6 +1,14 @@
|
||||
'use client'
|
||||
|
||||
import { useState, useRef, useEffect } from 'react'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
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 { useState } from 'react'
|
||||
import OpenSpace from '@/components/floor-plan/modal/object/type/OpenSpace'
|
||||
import Shadow from '@/components/floor-plan/modal/object/type/Shadow'
|
||||
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 }) {
|
||||
const { getMessage } = useMessage()
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
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 = [
|
||||
{ id: 1, name: getMessage('modal.object.setting.type.open.space.placement') },
|
||||
{ id: 2, name: getMessage('modal.object.setting.type.shadow.placement') },
|
||||
@ -34,13 +91,20 @@ export default function ObjectSetting({ setShowObjectSettingModal }) {
|
||||
</div>
|
||||
<div className="properties-setting-wrap outer">
|
||||
<div className="setting-tit">{getMessage('setting')}</div>
|
||||
{buttonAct === 1 && <OpenSpace />}
|
||||
{buttonAct === 2 && <Shadow />}
|
||||
{buttonAct === 3 && <TriangleDormer />}
|
||||
{buttonAct === 4 && <PentagonDormer />}
|
||||
{buttonAct === 1 && <OpenSpace ref={objectPlacement} />}
|
||||
{buttonAct === 2 && <Shadow ref={objectPlacement} />}
|
||||
{buttonAct === 3 && <TriangleDormer ref={dormerPlacement} />}
|
||||
{buttonAct === 4 && <PentagonDormer ref={dormerPlacement} />}
|
||||
</div>
|
||||
<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>
|
||||
|
||||
@ -1,18 +1,45 @@
|
||||
import { forwardRef, useState, useEffect } from 'react'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { INPUT_TYPE } from '@/common/common'
|
||||
|
||||
export default function OpenSpace() {
|
||||
const OpenSpace = forwardRef((props, refs) => {
|
||||
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 (
|
||||
<div className="discrimination-box mb10">
|
||||
<div className="mb-box">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-box">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
@ -24,7 +51,13 @@ export default function OpenSpace() {
|
||||
<div className="eaves-keraba-td">
|
||||
<div className="outline-form">
|
||||
<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>
|
||||
<span className="thin">mm</span>
|
||||
</div>
|
||||
@ -35,7 +68,13 @@ export default function OpenSpace() {
|
||||
<div className="eaves-keraba-td">
|
||||
<div className="outline-form">
|
||||
<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>
|
||||
<span className="thin">mm</span>
|
||||
</div>
|
||||
@ -45,9 +84,11 @@ export default function OpenSpace() {
|
||||
</div>
|
||||
</div>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
export default OpenSpace
|
||||
|
||||
@ -1,18 +1,45 @@
|
||||
import { forwardRef, useState, useEffect } from 'react'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { INPUT_TYPE } from '@/common/common'
|
||||
|
||||
export default function Shadow() {
|
||||
const Shadow = forwardRef((props, refs) => {
|
||||
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 (
|
||||
<div className="discrimination-box mb10">
|
||||
<div className="mb-box">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-box">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
@ -24,7 +51,13 @@ export default function Shadow() {
|
||||
<div className="eaves-keraba-td">
|
||||
<div className="outline-form">
|
||||
<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>
|
||||
<span className="thin">mm</span>
|
||||
</div>
|
||||
@ -35,7 +68,13 @@ export default function Shadow() {
|
||||
<div className="eaves-keraba-td">
|
||||
<div className="outline-form">
|
||||
<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>
|
||||
<span className="thin">mm</span>
|
||||
</div>
|
||||
@ -46,4 +85,6 @@ export default function Shadow() {
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
export default Shadow
|
||||
|
||||
@ -1,8 +1,18 @@
|
||||
import Image from 'next/image'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { forwardRef, useState } from 'react'
|
||||
|
||||
export default function TriangleDormer() {
|
||||
const TriangleDormer = forwardRef((props, refs) => {
|
||||
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 (
|
||||
<>
|
||||
<div className="discrimination-box mb10">
|
||||
@ -18,7 +28,7 @@ export default function TriangleDormer() {
|
||||
<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={2000} />
|
||||
<input type="text" className="input-origin block" placeholder={0} ref={refs.heightRef} defaultValue={1500} />
|
||||
</div>
|
||||
<span className="thin">mm</span>
|
||||
</div>
|
||||
@ -29,18 +39,7 @@ export default function TriangleDormer() {
|
||||
<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={1000} />
|
||||
</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} />
|
||||
<input type="text" className="input-origin block" placeholder={0} ref={refs.offsetRef} />
|
||||
</div>
|
||||
<span className="thin">mm</span>
|
||||
</div>
|
||||
@ -51,7 +50,7 @@ export default function TriangleDormer() {
|
||||
<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={4} />
|
||||
<input type="text" className="input-origin block" placeholder={0} ref={refs.pitchRef} defaultValue={4} />
|
||||
</div>
|
||||
<span className="thin">寸</span>
|
||||
</div>
|
||||
@ -69,13 +68,15 @@ export default function TriangleDormer() {
|
||||
<span className="right">{getMessage('commons.east')}</span>
|
||||
<span className="bottom">{getMessage('commons.south')}</span>
|
||||
<span className="left">{getMessage('commons.west')}</span>
|
||||
<button className="plane-btn up"></button>
|
||||
<button className="plane-btn right"></button>
|
||||
<button className="plane-btn down act"></button>
|
||||
<button className="plane-btn left"></button>
|
||||
<button className={`plane-btn up ${direction === 'up' ? ' act' : ''}`} value="up" onClick={getDirection}></button>
|
||||
<button className={`plane-btn right ${direction === 'right' ? ' act' : ''}`} value="right" onClick={getDirection}></button>
|
||||
<button className={`plane-btn down ${direction === 'down' ? ' act' : ''}`} value="down" onClick={getDirection}></button>
|
||||
<button className={`plane-btn left ${direction === 'left' ? ' act' : ''}`} value="left" onClick={getDirection}></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
export default TriangleDormer
|
||||
|
||||
@ -6,7 +6,7 @@ export default function PropertiesSetting(props) {
|
||||
const { getMessage } = useMessage()
|
||||
const { setShowPropertiesSettingModal } = props
|
||||
|
||||
const { handleSetEaves, handleSetGable, handleRollback, handleFix, closeModal } = usePropertiesSetting()
|
||||
const { handleSetEaves, handleSetGable, handleRollback, handleFix, closeModal } = usePropertiesSetting(setShowPropertiesSettingModal)
|
||||
|
||||
return (
|
||||
<WithDraggable isShow={true} pos={{ x: 50, y: -950 }}>
|
||||
|
||||
@ -39,7 +39,7 @@ export default function WallLineSetting(props) {
|
||||
outerLineDiagonalLengthRef,
|
||||
handleRollback,
|
||||
handleFix,
|
||||
} = useOuterLineWall()
|
||||
} = useOuterLineWall(setShowOutlineModal)
|
||||
|
||||
const outerLineProps = {
|
||||
length1,
|
||||
@ -171,7 +171,7 @@ export default function WallLineSetting(props) {
|
||||
<button
|
||||
className="btn-frame modal act"
|
||||
onClick={() => {
|
||||
handleFix(setShowOutlineModal)
|
||||
handleFix()
|
||||
setShowPropertiesSettingModal(true)
|
||||
}}
|
||||
>
|
||||
|
||||
@ -8,6 +8,7 @@ import Diagonal from '@/components/floor-plan/modal/lineTypes/Diagonal'
|
||||
import { useOuterLineWall } from '@/hooks/roofcover/useOuterLineWall'
|
||||
import { OUTER_LINE_TYPE } from '@/store/outerLineAtom'
|
||||
import OuterLineWall from '@/components/floor-plan/modal/lineTypes/OuterLineWall'
|
||||
import { usePlacementShapeDrawing } from '@/hooks/surface/usePlacementShapeDrawing'
|
||||
|
||||
export default function PlacementShapeDrawing({ setShowPlaceShapeDrawingModal }) {
|
||||
const { getMessage } = useMessage()
|
||||
@ -45,7 +46,7 @@ export default function PlacementShapeDrawing({ setShowPlaceShapeDrawingModal })
|
||||
outerLineDiagonalLengthRef,
|
||||
handleRollback,
|
||||
handleFix,
|
||||
} = useOuterLineWall()
|
||||
} = usePlacementShapeDrawing(setShowPlaceShapeDrawingModal)
|
||||
|
||||
const outerLineProps = {
|
||||
length1,
|
||||
@ -129,8 +130,12 @@ export default function PlacementShapeDrawing({ setShowPlaceShapeDrawingModal })
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<div className="modal-btn-wrap">
|
||||
{types.map((type) => (
|
||||
<button className={`btn-frame modal ${buttonAct === type.id ? 'act' : ''}`} onClick={() => onClickButton(type)}>
|
||||
{types.map((type, idx) => (
|
||||
<button
|
||||
key={`placement-${idx}`}
|
||||
className={`btn-frame modal ${buttonAct === type.id ? 'act' : ''}`}
|
||||
onClick={() => onClickButton(type)}
|
||||
>
|
||||
{type.name}
|
||||
</button>
|
||||
))}
|
||||
@ -145,8 +150,12 @@ export default function PlacementShapeDrawing({ setShowPlaceShapeDrawingModal })
|
||||
</div>
|
||||
|
||||
<div className="grid-btn-wrap">
|
||||
<button className="btn-frame modal mr5">{getMessage('modal.cover.outline.rollback')}</button>
|
||||
<button className="btn-frame modal act">{getMessage('modal.cover.outline.fix')}</button>
|
||||
<button className="btn-frame modal mr5" onClick={handleRollback}>
|
||||
{getMessage('modal.cover.outline.rollback')}
|
||||
</button>
|
||||
<button className="btn-frame modal act" onClick={handleFix}>
|
||||
{getMessage('modal.cover.outline.fix')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -4,17 +4,29 @@ import { useEffect, useState, useRef } from 'react'
|
||||
import Image from 'next/image'
|
||||
import PlacementSurface from '@/components/floor-plan/modal/placementSurface/PlacementSurface'
|
||||
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 }) {
|
||||
const { getMessage } = useMessage()
|
||||
|
||||
const [selectedType, setSelectedType] = useState()
|
||||
const [rotate, setRotate] = useState(0)
|
||||
const [xInversion, setXInversion] = useState(false)
|
||||
const [yInversion, setYInversion] = useState(false)
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
|
||||
const { applySurfaceShape } = useSurfaceShapeBatch()
|
||||
|
||||
const surfaceShapePolygons = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
|
||||
|
||||
//오브젝트 배치에서 넘어오면 면형상 선택 가능
|
||||
useEffect(() => {
|
||||
surfaceShapePolygons.forEach((obj) => {
|
||||
obj.set({ selectable: true })
|
||||
})
|
||||
}, [])
|
||||
|
||||
const surfaceRefs = {
|
||||
length1: useRef(null),
|
||||
length2: useRef(null),
|
||||
|
||||
@ -2,76 +2,12 @@ import { useMessage } from '@/hooks/useMessage'
|
||||
import WithDraggable from '@/components/common/draggable/WithDraggable'
|
||||
import { useState } from 'react'
|
||||
import QSelectBox from '@/components/common/select/QSelectBox'
|
||||
import { useRoofAllocationSetting } from '@/hooks/roofcover/useRoofAllocationSetting'
|
||||
|
||||
export default function RoofAllocationSetting({ setShowRoofAllocationSettingModal }) {
|
||||
const { getMessage } = useMessage()
|
||||
const [selectedRoofMaterial, setSelectedRoofMaterial] = useState(null)
|
||||
const [values, setValues] = useState([
|
||||
{
|
||||
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))
|
||||
}
|
||||
const { handleSave, onAddRoofMaterial, onDeleteRoofMaterial, values, roofMaterials, selectedRoofMaterial, setSelectedRoofMaterial } =
|
||||
useRoofAllocationSetting(setShowRoofAllocationSettingModal)
|
||||
|
||||
return (
|
||||
<WithDraggable isShow={true} pos={{ x: 50, y: -950 }}>
|
||||
@ -103,7 +39,7 @@ export default function RoofAllocationSetting({ setShowRoofAllocationSettingModa
|
||||
{values.map((value, index) => (
|
||||
<div className="grid-option-box" key={index}>
|
||||
<div className="d-check-radio pop no-text">
|
||||
<input type="radio" name="radio01" id="ra01" />
|
||||
<input type="radio" name="radio01" />
|
||||
<label htmlFor="ra01"></label>
|
||||
</div>
|
||||
<div className="grid-option-block-form">
|
||||
@ -213,7 +149,9 @@ export default function RoofAllocationSetting({ setShowRoofAllocationSettingModa
|
||||
))}
|
||||
</div>
|
||||
<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>
|
||||
|
||||
@ -1,18 +1,29 @@
|
||||
import { useState } from 'react'
|
||||
import WithDraggable from '@/components/common/draggable/WithDraggable'
|
||||
import Eaves from '@/components/floor-plan/modal/roofShape/passivity/Eaves'
|
||||
import Gable from '@/components/floor-plan/modal/roofShape/passivity/Gable'
|
||||
import Shed from '@/components/floor-plan/modal/roofShape/passivity/Shed'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { useRoofShapePassivitySetting } from '@/hooks/roofcover/useRoofShapePassivitySetting'
|
||||
|
||||
export default function RoofShapePassivitySetting({ setShowRoofShapePassivitySettingModal }) {
|
||||
const { handleSave, handleConfirm, handleRollback, buttons, type, setType, TYPES, offsetRef, pitchRef } =
|
||||
useRoofShapePassivitySetting(setShowRoofShapePassivitySettingModal)
|
||||
const { getMessage } = useMessage()
|
||||
const [buttonAct, setButtonAct] = useState(1)
|
||||
const buttons = [
|
||||
{ id: 1, name: getMessage('eaves') },
|
||||
{ id: 2, name: getMessage('gable') },
|
||||
{ id: 3, name: getMessage('windage') },
|
||||
]
|
||||
|
||||
const eavesProps = {
|
||||
offsetRef,
|
||||
pitchRef,
|
||||
}
|
||||
|
||||
const gableProps = {
|
||||
offsetRef,
|
||||
pitchRef,
|
||||
}
|
||||
|
||||
const shedProps = {
|
||||
offsetRef,
|
||||
}
|
||||
|
||||
return (
|
||||
<WithDraggable isShow={true} pos={{ x: 50, y: -950 }}>
|
||||
<div className={`modal-pop-wrap xxm`}>
|
||||
@ -25,7 +36,7 @@ export default function RoofShapePassivitySetting({ setShowRoofShapePassivitySet
|
||||
<div className="modal-body">
|
||||
<div className="modal-btn-wrap">
|
||||
{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>
|
||||
))}
|
||||
@ -33,17 +44,23 @@ export default function RoofShapePassivitySetting({ setShowRoofShapePassivitySet
|
||||
<div className="modal-bottom-border-bx">
|
||||
<div className="setting-tit">{getMessage('setting')}</div>
|
||||
<div className="discrimination-box">
|
||||
{buttonAct === 1 && <Eaves />}
|
||||
{buttonAct === 2 && <Gable />}
|
||||
{buttonAct === 3 && <Shed />}
|
||||
{type === TYPES.EAVES && <Eaves {...eavesProps} />}
|
||||
{type === TYPES.GABLE && <Gable {...gableProps} />}
|
||||
{type === TYPES.SHED && <Shed {...shedProps} />}
|
||||
</div>
|
||||
<div className="grid-btn-wrap">
|
||||
<button className="btn-frame sub-tab mr5">{getMessage('common.setting.rollback')}</button>
|
||||
<button className="btn-frame sub-tab act">{getMessage('apply')}</button>
|
||||
<button className="btn-frame sub-tab mr5" onClick={handleRollback}>
|
||||
{getMessage('common.setting.rollback')}
|
||||
</button>
|
||||
<button className="btn-frame sub-tab act" onClick={handleConfirm}>
|
||||
{getMessage('apply')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<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>
|
||||
|
||||
@ -37,7 +37,7 @@ export default function RoofShapeSetting({ setShowRoofShapeSettingModal }) {
|
||||
buttonMenu,
|
||||
handleConfirm,
|
||||
handleRollBack,
|
||||
} = useRoofShapeSetting()
|
||||
} = useRoofShapeSetting(setShowRoofShapeSettingModal)
|
||||
|
||||
const ridgeProps = { pitch, setPitch, eavesOffset, setEavesOffset }
|
||||
const patternProps = { pitch, setPitch, eavesOffset, setEavesOffset, gableOffset, setGableOffset }
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
|
||||
export default function Eaves() {
|
||||
export default function Eaves({ offsetRef, pitchRef }) {
|
||||
const { getMessage } = useMessage()
|
||||
return (
|
||||
<>
|
||||
@ -9,7 +9,7 @@ export default function Eaves() {
|
||||
{getMessage('slope')}
|
||||
</span>
|
||||
<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>
|
||||
<span className="thin">寸</span>
|
||||
</div>
|
||||
@ -18,7 +18,7 @@ export default function Eaves() {
|
||||
{getMessage('eaves.offset')}
|
||||
</span>
|
||||
<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>
|
||||
<span className="thin">mm</span>
|
||||
</div>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
|
||||
export default function Gable() {
|
||||
export default function Gable({ offsetRef, pitchRef }) {
|
||||
const { getMessage } = useMessage()
|
||||
return (
|
||||
<>
|
||||
@ -9,7 +9,7 @@ export default function Gable() {
|
||||
{getMessage('slope')}
|
||||
</span>
|
||||
<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>
|
||||
<span className="thin">寸</span>
|
||||
</div>
|
||||
@ -18,7 +18,7 @@ export default function Gable() {
|
||||
{getMessage('gable.offset')}
|
||||
</span>
|
||||
<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>
|
||||
<span className="thin">mm</span>
|
||||
</div>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
|
||||
export default function Shed() {
|
||||
export default function Shed({ offsetRef }) {
|
||||
const { getMessage } = useMessage()
|
||||
return (
|
||||
<>
|
||||
@ -9,7 +9,7 @@ export default function Shed() {
|
||||
{getMessage('shed.width')}
|
||||
</span>
|
||||
<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>
|
||||
<span className="thin">mm</span>
|
||||
</div>
|
||||
|
||||
@ -7,9 +7,20 @@ import { useWallLineOffsetSetting } from '@/hooks/roofcover/useWallLineOffsetSet
|
||||
|
||||
export default function WallLineOffsetSetting({ setShowWallLineOffsetSettingModal }) {
|
||||
const { getMessage } = useMessage()
|
||||
const { type, setType, buttonMenu, currentWallLineRef, TYPES, radioTypeRef, arrow1Ref, arrow2Ref, length1Ref, length2Ref, handleSave } =
|
||||
useWallLineOffsetSetting()
|
||||
const [buttonAct, setButtonAct] = useState(1)
|
||||
const {
|
||||
type,
|
||||
setType,
|
||||
buttonMenu,
|
||||
currentWallLineRef,
|
||||
TYPES,
|
||||
radioTypeRef,
|
||||
arrow1Ref,
|
||||
arrow2Ref,
|
||||
length1Ref,
|
||||
length2Ref,
|
||||
handleSave,
|
||||
wallLineEditRef,
|
||||
} = useWallLineOffsetSetting(setShowWallLineOffsetSettingModal)
|
||||
|
||||
const wallLineProps = {
|
||||
length1Ref,
|
||||
@ -45,7 +56,7 @@ export default function WallLineOffsetSetting({ setShowWallLineOffsetSettingModa
|
||||
</div>
|
||||
<div className="properties-setting-wrap outer">
|
||||
<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} />}
|
||||
</div>
|
||||
<div className="grid-btn-wrap">
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { forwardRef, useEffect, useImperativeHandle, useState } from 'react'
|
||||
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 { addDocumentEventListener, initEvent } = useEvent()
|
||||
const [type, setType] = useState(1)
|
||||
@ -10,76 +10,25 @@ export default function WallLine({ length1Ref, length2Ref, arrow1Ref, arrow2Ref,
|
||||
const [arrow2, setArrow2] = useState('up')
|
||||
|
||||
useEffect(() => {
|
||||
addDocumentEventListener('keydown', document, keyDown)
|
||||
|
||||
return () => {
|
||||
initEvent()
|
||||
}
|
||||
}, [])
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
setArrow,
|
||||
}))
|
||||
|
||||
const setArrow = () => {
|
||||
setArrow1(arrow1Ref.current)
|
||||
setArrow2(arrow2Ref.current)
|
||||
}
|
||||
|
||||
const onChange = (e) => {
|
||||
setType(Number(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 (
|
||||
<>
|
||||
<div className="outline-wrap">
|
||||
@ -107,10 +56,10 @@ export default function WallLine({ length1Ref, length2Ref, arrow1Ref, arrow2Ref,
|
||||
<div className="eaves-keraba-td">
|
||||
<div className="outline-form">
|
||||
<div className="grid-direction">
|
||||
<button className={`direction up ${arrow1 === 'up' ? 'act' : ''} `} onClick={() => handleBtnClick('ArrowUp')}></button>
|
||||
<button className={`direction down ${arrow1 === 'down' ? 'act' : ''} `} onClick={() => handleBtnClick('ArrowDown')}></button>
|
||||
<button className={`direction left ${arrow1 === 'left' ? 'act' : ''} `} onClick={() => handleBtnClick('ArrowLeft')}></button>
|
||||
<button className={`direction right ${arrow1 === 'right' ? 'act' : ''} `} onClick={() => handleBtnClick('ArrowRight')}></button>
|
||||
<button className={`direction up ${arrow1 === 'up' ? 'act' : ''} `}></button>
|
||||
<button className={`direction down ${arrow1 === 'down' ? 'act' : ''} `}></button>
|
||||
<button className={`direction left ${arrow1 === 'left' ? 'act' : ''} `}></button>
|
||||
<button className={`direction right ${arrow1 === 'right' ? 'act' : ''} `}></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -141,10 +90,10 @@ export default function WallLine({ length1Ref, length2Ref, arrow1Ref, arrow2Ref,
|
||||
<div className="eaves-keraba-td">
|
||||
<div className="outline-form">
|
||||
<div className="grid-direction">
|
||||
<button className={`direction up ${arrow2 === 'up' ? 'act' : ''} `} onClick={() => handleBtnClick('ArrowUp')}></button>
|
||||
<button className={`direction down ${arrow2 === 'down' ? 'act' : ''} `} onClick={() => handleBtnClick('ArrowDown')}></button>
|
||||
<button className={`direction left ${arrow2 === 'left' ? 'act' : ''} `} onClick={() => handleBtnClick('ArrowLeft')}></button>
|
||||
<button className={`direction right ${arrow2 === 'right' ? 'act' : ''} `} onClick={() => handleBtnClick('ArrowRight')}></button>
|
||||
<button className={`direction up ${arrow2 === 'up' ? 'act' : ''} `}></button>
|
||||
<button className={`direction down ${arrow2 === 'down' ? 'act' : ''} `}></button>
|
||||
<button className={`direction left ${arrow2 === 'left' ? 'act' : ''} `}></button>
|
||||
<button className={`direction right ${arrow2 === 'right' ? 'act' : ''} `}></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -155,4 +104,4 @@ export default function WallLine({ length1Ref, length2Ref, arrow1Ref, arrow2Ref,
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
@ -12,6 +12,10 @@ import { logout } from '@/lib/authActions'
|
||||
|
||||
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) => {
|
||||
const listWrap = e.target.closest(target)
|
||||
const ListItem = Array.from(listWrap.childNodes)
|
||||
@ -28,6 +32,8 @@ export const ToggleonMouse = (e, act, target) => {
|
||||
}
|
||||
|
||||
export default function Header(props) {
|
||||
const [userInfoModal, setUserInfoModal] = useState(false)
|
||||
|
||||
const { userSession } = props
|
||||
const [sessionState, setSessionState] = useRecoilState(sessionStore)
|
||||
const { getMessage } = useMessage()
|
||||
@ -40,12 +46,42 @@ export default function Header(props) {
|
||||
const dimmedState = useRecoilValue(dimmedStore)
|
||||
const isDimmed = dimmedState ? 'opacity-50 bg-black' : ''
|
||||
|
||||
const SelectOptions = [
|
||||
{ id: 0, name: 'オンライン保証シ', link: '' },
|
||||
{ id: 1, name: 'ステム', link: '' },
|
||||
{ id: 2, name: 'TEST1', link: 'https://www.weather.go.kr/w/index.do' },
|
||||
{ id: 3, name: 'TEST2', link: 'https://www.google.com' },
|
||||
]
|
||||
// Link 이동 자동 로그인
|
||||
const [globalLocaleState, setGlbalLocaleState] = useRecoilState(globalLocaleStore)
|
||||
const { promisePost } = useAxios(globalLocaleState)
|
||||
|
||||
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 = [
|
||||
{ id: 0, name: 'header.menus.home', url: '/', children: [] },
|
||||
{
|
||||
@ -96,7 +132,7 @@ export default function Header(props) {
|
||||
onMouseLeave={(e) => ToggleonMouse(e, 'remove', 'nav > ul')}
|
||||
>
|
||||
{menu.children.length === 0 ? (
|
||||
<Link key={`${menu.id}`} href={menu.url}>
|
||||
<Link key={`${menu.id}`} href={menu.url} scroll={false}>
|
||||
{getMessage(menu.name)}
|
||||
</Link>
|
||||
) : (
|
||||
@ -111,7 +147,9 @@ export default function Header(props) {
|
||||
onMouseEnter={(e) => ToggleonMouse(e, 'add', '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>
|
||||
)
|
||||
})}
|
||||
@ -137,9 +175,15 @@ export default function Header(props) {
|
||||
</div>
|
||||
<div className="header-left">
|
||||
<div className="profile-box">
|
||||
<Link href="/roof2">
|
||||
<Link
|
||||
href="#"
|
||||
onClick={() => {
|
||||
setUserInfoModal(true)
|
||||
}}
|
||||
>
|
||||
<button className="profile">{userSession.userNm}</button>
|
||||
</Link>
|
||||
{userInfoModal && <UserInfoModal userId={sessionState.userId} userInfoModal={userInfoModal} setUserInfoModal={setUserInfoModal} />}
|
||||
</div>
|
||||
<div className="sign-out-box">
|
||||
<button className="sign-out" onClick={() => logout()}>
|
||||
@ -147,7 +191,7 @@ export default function Header(props) {
|
||||
</button>
|
||||
</div>
|
||||
<div className="select-box">
|
||||
<QSelectBox title={'Q.ORDER'} options={SelectOptions} onChange={onChangeSelect} />
|
||||
<QSelectBox options={SelectOptions} onChange={onChangeSelect} />
|
||||
</div>
|
||||
<div className="btn-wrap">
|
||||
<button className="btn-frame small dark" onClick={() => navPage()}>
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
import React from 'react'
|
||||
import React, { useState } from 'react'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { useForm } from 'react-hook-form'
|
||||
import { sessionStore } from '@/store/commonAtom'
|
||||
import { useRecoilValue, useRecoilState } from 'recoil'
|
||||
import { useAxios } from '@/hooks/useAxios'
|
||||
import { globalLocaleStore } from '@/store/localeAtom'
|
||||
import { useRouter } from 'next/navigation'
|
||||
|
||||
export default function ChangePasswordPop() {
|
||||
const globalLocaleState = useRecoilValue(globalLocaleStore)
|
||||
@ -12,7 +13,7 @@ export default function ChangePasswordPop() {
|
||||
const { patch } = useAxios(globalLocaleState)
|
||||
const { getMessage } = useMessage()
|
||||
const [sessionState, setSessionState] = useRecoilState(sessionStore)
|
||||
|
||||
const router = useRouter()
|
||||
const formInitValue = {
|
||||
password1: '',
|
||||
password2: '',
|
||||
@ -74,6 +75,8 @@ export default function ChangePasswordPop() {
|
||||
if (res.result.resultCode === 'S') {
|
||||
alert(getMessage('main.popup.login.success'))
|
||||
setSessionState({ ...sessionState, pwdInitYn: 'Y' })
|
||||
//메인으로 이동
|
||||
router.push('/')
|
||||
} else {
|
||||
alert(res.result.resultMsg)
|
||||
}
|
||||
@ -154,7 +157,6 @@ export default function ChangePasswordPop() {
|
||||
className="btn-origin grey"
|
||||
onClick={() => {
|
||||
router.push('/login')
|
||||
setOpen(false)
|
||||
}}
|
||||
>
|
||||
{getMessage('main.popup.login.btn2')}
|
||||
|
||||
@ -15,6 +15,7 @@ import { convertNumberToPriceDecimal } from '@/util/common-utils'
|
||||
import { appMessageStore, globalLocaleStore } from '@/store/localeAtom'
|
||||
import KO from '@/locales/ko.json'
|
||||
import JA from '@/locales/ja.json'
|
||||
import QPagination from '../common/pagination/QPagination'
|
||||
import '@/styles/grid.scss'
|
||||
import { sessionStore } from '@/store/commonAtom'
|
||||
|
||||
@ -24,9 +25,9 @@ export default function Stuff() {
|
||||
const stuffSearchParams = useRecoilValue(stuffSearchState)
|
||||
const [stuffSearch, setStuffSearch] = useRecoilState(stuffSearchState)
|
||||
const { getMessage } = useMessage()
|
||||
const [curPage, setCurPage] = useState(1) //현재 페이지 번호
|
||||
const [defaultSize, setDefaultSize] = useState(100) //페이지 당 게시물 수
|
||||
const [gridCount, setGridCount] = useState(0) //총 갯수
|
||||
const [pageNo, setPageNo] = useState(1) //현재 페이지 번호
|
||||
const [pageSize, setPageSize] = useState(100) //페이지 당 게시물 수
|
||||
const [totalCount, setTotalCount] = useState(0) //총 갯수
|
||||
const [defaultSortType, setDefaultSortType] = useState('R')
|
||||
|
||||
const globalLocaleState = useRecoilValue(globalLocaleStore)
|
||||
@ -53,10 +54,8 @@ export default function Stuff() {
|
||||
const onDoubleClick = (e) => {
|
||||
let objectNo = e.target.innerText
|
||||
if (objectNo.substring(0, 1) === 'R') {
|
||||
console.log('진짜')
|
||||
router.push(`${pathname}/detail?objectNo=${objectNo.toString()}`)
|
||||
} else {
|
||||
console.log('임시')
|
||||
router.push(`${pathname}/tempdetail?objectNo=${objectNo.toString()}`)
|
||||
}
|
||||
}
|
||||
@ -64,28 +63,16 @@ export default function Stuff() {
|
||||
const [gridProps, setGridProps] = useState({
|
||||
gridData: [],
|
||||
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: [
|
||||
{
|
||||
field: 'lastEditDatetime',
|
||||
minWidth: 200,
|
||||
headerName: getMessage('stuff.gridHeader.lastEditDatetime'),
|
||||
headerCheckboxSelection: true,
|
||||
headerCheckboxSelectionCurrentPageOnly: true, //페이징시 현재 페이지만 체크되도록
|
||||
checkboxSelection: true,
|
||||
showDisabledCheckboxes: true,
|
||||
// .centered {
|
||||
// .ag-header-cell-label {
|
||||
// justify-content: center !important;
|
||||
// }
|
||||
// }
|
||||
cellStyle: { textAlign: 'center' },
|
||||
//suppressMovable: true, //헤더 못움직이게
|
||||
// width : 100
|
||||
// minWidth : 100
|
||||
// maxWidth : 100
|
||||
valueFormatter: function (params) {
|
||||
if (params.value) {
|
||||
return dayjs(params?.value).format('YYYY.MM.DD HH:mm:ss')
|
||||
@ -96,8 +83,8 @@ export default function Stuff() {
|
||||
},
|
||||
{
|
||||
field: 'objectNo',
|
||||
minWidth: 230,
|
||||
headerName: getMessage('stuff.gridHeader.objectNo'),
|
||||
// headerClass: 'centered', //_test.scss에 추가 테스트
|
||||
cellRenderer: function (params) {
|
||||
if (params.data.objectNo) {
|
||||
return (
|
||||
@ -131,7 +118,7 @@ export default function Stuff() {
|
||||
headerName: getMessage('stuff.gridHeader.saleStoreId'),
|
||||
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: 'dispCompanyName', headerName: getMessage('stuff.gridHeader.dispCompanyName'), 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') {
|
||||
return
|
||||
} else {
|
||||
console.log(' 상세이동::::::::', event.data)
|
||||
//T 면 임시 R은 진짜
|
||||
if (event.data.objectNo) {
|
||||
if (event.data.objectNo.substring(0, 1) === 'R') {
|
||||
console.log('진짜:::::::::')
|
||||
router.push(`${pathname}/detail?objectNo=${event.data.objectNo.toString()}`)
|
||||
} else {
|
||||
console.log('임시:::::::::::::::::')
|
||||
router.push(`${pathname}/tempdetail?objectNo=${event.data.objectNo.toString()}`)
|
||||
}
|
||||
}
|
||||
@ -242,56 +226,42 @@ export default function Stuff() {
|
||||
// 진입시 그리드 데이터 조회
|
||||
useEffect(() => {
|
||||
if (isObjectNotEmpty(sessionState)) {
|
||||
//물건 메뉴 눌러서 최초 진입 sessionState
|
||||
if (stuffSearchParams?.code === 'S') {
|
||||
const params = {
|
||||
schObjectNo: '',
|
||||
schAddress: '',
|
||||
schObjectName: '',
|
||||
schSaleStoreName: '',
|
||||
schReceiveUser: '',
|
||||
schDispCompanyName: '',
|
||||
schDateType: 'U',
|
||||
schObjectNo: stuffSearchParams?.schObjectNo,
|
||||
schAddress: stuffSearchParams?.schAddress,
|
||||
schObjectName: stuffSearchParams?.schObjectName,
|
||||
schSaleStoreName: stuffSearchParams?.schSaleStoreName,
|
||||
schReceiveUser: stuffSearchParams?.schReceiveUser,
|
||||
schDispCompanyName: stuffSearchParams?.schDispCompanyName,
|
||||
schDateType: stuffSearchParams.schDateType,
|
||||
schFromDt: dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD'),
|
||||
schToDt: dayjs(new Date()).format('YYYY-MM-DD'),
|
||||
startRow: (curPage - 1) * defaultSize + 1,
|
||||
endRow: curPage * defaultSize,
|
||||
startRow: (pageNo - 1) * pageSize + 1,
|
||||
endRow: pageNo * pageSize,
|
||||
schSelSaleStoreId: '',
|
||||
schSortType: 'R',
|
||||
schSortType: stuffSearchParams.schSortType,
|
||||
}
|
||||
|
||||
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)}`
|
||||
const apiUrl = `/api/object/list?saleStoreId=T01&${queryStringFormatter(params)}`
|
||||
|
||||
await get({
|
||||
url: apiUrl,
|
||||
}).then((res) => {
|
||||
if (!isEmptyArray(res)) {
|
||||
setGridProps({ ...gridProps, gridData: res, count: res[0].totCnt })
|
||||
setGridCount(res[0].totCnt)
|
||||
setTotalCount(res[0].totCnt)
|
||||
}
|
||||
})
|
||||
}
|
||||
fetchData()
|
||||
} else {
|
||||
//메인화면에서 진입
|
||||
const params = {
|
||||
schObjectNo: '',
|
||||
schObjectNo: stuffSearchParams.schObjectNo,
|
||||
schAddress: '',
|
||||
schObjectName: '',
|
||||
schSaleStoreName: '',
|
||||
@ -300,30 +270,13 @@ export default function Stuff() {
|
||||
schDateType: 'U',
|
||||
schFromDt: dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD'),
|
||||
schToDt: dayjs(new Date()).format('YYYY-MM-DD'),
|
||||
startRow: (curPage - 1) * defaultSize + 1,
|
||||
endRow: curPage * defaultSize,
|
||||
startRow: (pageNo - 1) * pageSize + 1,
|
||||
endRow: pageNo * pageSize,
|
||||
schSelSaleStoreId: '',
|
||||
schSortType: 'R',
|
||||
}
|
||||
|
||||
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)}`
|
||||
|
||||
await get({
|
||||
@ -331,34 +284,34 @@ export default function Stuff() {
|
||||
}).then((res) => {
|
||||
if (!isEmptyArray(res)) {
|
||||
setGridProps({ ...gridProps, gridData: res, count: res[0].totCnt })
|
||||
setGridCount(res[0].totCnt)
|
||||
setTotalCount(res[0].totCnt)
|
||||
}
|
||||
})
|
||||
}
|
||||
fetchData()
|
||||
}
|
||||
}
|
||||
}, [sessionState])
|
||||
}, [pageNo, sessionState])
|
||||
|
||||
useEffect(() => {
|
||||
if (stuffSearchParams?.code === 'E') {
|
||||
//console.log('조회누름::::::::', stuffSearchParams)
|
||||
stuffSearchParams.startRow = (curPage - 1) * defaultSize + 1
|
||||
stuffSearchParams.endRow = curPage * defaultSize
|
||||
//console.log('조회누름::::::::', stuffSearchParams, sessionState)
|
||||
stuffSearchParams.startRow = 1
|
||||
stuffSearchParams.endRow = 1 * pageSize
|
||||
stuffSearchParams.schSortType = defaultSortType
|
||||
|
||||
setPageNo(1)
|
||||
|
||||
async function fetchData() {
|
||||
// console.log('조회누름 세션정보:::::::::::::', sessionState)
|
||||
// const apiUrl = `/api/object/list?saleStoreId=201TES01&${queryStringFormatter(stuffSearchParams)}`
|
||||
// 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)}`
|
||||
await get({ url: apiUrl }).then((res) => {
|
||||
// console.log('검색조건 변경 조회 API결과:::::::', res)
|
||||
if (!isEmptyArray(res)) {
|
||||
setGridProps({ ...gridProps, gridData: res, count: res[0].totCnt })
|
||||
setGridCount(res[0].totCnt)
|
||||
setTotalCount(res[0].totCnt)
|
||||
} else {
|
||||
setGridProps({ ...gridProps, gridData: [], count: 0 })
|
||||
setGridCount(0)
|
||||
setTotalCount(0)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -368,53 +321,58 @@ export default function Stuff() {
|
||||
|
||||
//페이지 갯수 변경 이벤트
|
||||
const onChangePerPage = (e) => {
|
||||
let startRow = (curPage - 1) * e.target.value + 1
|
||||
let startRow = (1 - 1) * e.target.value + 1
|
||||
stuffSearchParams.startRow = startRow
|
||||
stuffSearchParams.endRow = curPage * e.target.value
|
||||
setDefaultSize(e.target.value)
|
||||
stuffSearchParams.endRow = 1 * e.target.value
|
||||
setPageSize(e.target.value)
|
||||
setStuffSearch({
|
||||
...stuffSearch,
|
||||
code: 'S',
|
||||
startRow: startRow,
|
||||
endRow: curPage * e.target.value,
|
||||
endRow: 1 * e.target.value,
|
||||
})
|
||||
// console.log('페이지 갯수 변경 때 셋팅된 검색조건:::', stuffSearchParams)
|
||||
// console.log('페이지 갯수 변경 때 sessionState:::', sessionState)
|
||||
// const apiUrl = `/api/object/list?saleStoreId=201TES01&${queryStringFormatter(stuffSearchParams)}`
|
||||
// const apiUrl = `/api/object/list?saleStoreId=X167&${queryStringFormatter(stuffSearchParams)}`
|
||||
const apiUrl = `/api/object/list?saleStoreId=${sessionState?.storeId}&${queryStringFormatter(stuffSearchParams)}`
|
||||
|
||||
setPageNo(1)
|
||||
// const apiUrl = `/api/object/list?saleStoreId=${sessionState?.storeId}&${queryStringFormatter(stuffSearchParams)}`
|
||||
const apiUrl = `/api/object/list?saleStoreId=T01&${queryStringFormatter(stuffSearchParams)}`
|
||||
get({ url: apiUrl }).then((res) => {
|
||||
if (!isEmptyArray(res)) {
|
||||
setGridProps({ ...gridProps, gridData: res, count: res[0].totCnt })
|
||||
setGridCount(res[0].totCnt)
|
||||
setTotalCount(res[0].totCnt)
|
||||
} else {
|
||||
setGridProps({ ...gridProps, gridData: [], count: 0 })
|
||||
setGridCount(0)
|
||||
setTotalCount(0)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
//최근 등록일 수정일 정렬 이벤트
|
||||
const onChangeSortType = (e) => {
|
||||
let startRow = (1 - 1) * pageSize + 1
|
||||
stuffSearchParams.startRow = startRow
|
||||
stuffSearchParams.endRow = 1 * pageSize
|
||||
|
||||
stuffSearchParams.schSortType = e.target.value
|
||||
// console.log('셋팅된 검색조건:::', stuffSearchParams)
|
||||
setDefaultSortType(e.target.value)
|
||||
setStuffSearch({
|
||||
...stuffSearch,
|
||||
code: 'S',
|
||||
startRow: startRow,
|
||||
endRow: 1 * pageSize,
|
||||
schSortType: e.target.value,
|
||||
})
|
||||
// console.log('정렬 변경시 세션정보::::::::::::', sessionState)
|
||||
// const apiUrl = `/api/object/list?saleStoreId=201TES01&${queryStringFormatter(stuffSearchParams)}`
|
||||
// const apiUrl = `/api/object/list?saleStoreId=X167&${queryStringFormatter(stuffSearchParams)}`
|
||||
const apiUrl = `/api/object/list?saleStoreId=${sessionState?.storeId}&${queryStringFormatter(stuffSearchParams)}`
|
||||
|
||||
setPageNo(1)
|
||||
|
||||
const apiUrl = `/api/object/list?saleStoreId=T01&${queryStringFormatter(stuffSearchParams)}`
|
||||
// const apiUrl = `/api/object/list?saleStoreId=${sessionState?.storeId}&${queryStringFormatter(stuffSearchParams)}`
|
||||
get({ url: apiUrl }).then((res) => {
|
||||
if (!isEmptyArray(res)) {
|
||||
setGridProps({ ...gridProps, gridData: res, count: res[0].totCnt })
|
||||
setGridCount(res[0].totCnt)
|
||||
setTotalCount(res[0].totCnt)
|
||||
} else {
|
||||
setGridProps({ ...gridProps, gridData: [], count: 0 })
|
||||
setGridCount(0)
|
||||
setTotalCount(0)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -427,20 +385,34 @@ export default function Stuff() {
|
||||
}
|
||||
}, [globalLocaleState])
|
||||
|
||||
// 페이징 현재페이지 변경
|
||||
const handleChangePage = (page) => {
|
||||
stuffSearchParams.code = 'S'
|
||||
|
||||
setStuffSearch({
|
||||
...stuffSearch,
|
||||
code: 'S',
|
||||
startRow: (page - 1) * pageSize + 1,
|
||||
endRow: page * pageSize,
|
||||
})
|
||||
|
||||
setPageNo(page)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* 퍼블시작 */}
|
||||
<div className="sub-table-box">
|
||||
<div className="table-box-title-wrap">
|
||||
<div className="title-wrap">
|
||||
<h3>물건목록</h3>
|
||||
<h3>{getMessage('stuff.search.grid.title')}</h3>
|
||||
<ul className="info-wrap">
|
||||
<li>
|
||||
전체
|
||||
<span>{convertNumberToPriceDecimal(gridCount)}</span>
|
||||
{getMessage('stuff.search.grid.all')}
|
||||
<span>{convertNumberToPriceDecimal(totalCount)}</span>
|
||||
</li>
|
||||
<li>
|
||||
선택
|
||||
{getMessage('stuff.search.grid.selected')}
|
||||
<span className="red">{convertNumberToPriceDecimal(selectedRowDataCount)}</span>
|
||||
</li>
|
||||
</ul>
|
||||
@ -448,8 +420,8 @@ export default function Stuff() {
|
||||
<div className="left-unit-box">
|
||||
<div className="select-box mr5" style={{ width: '110px' }}>
|
||||
<select className="select-light black" name="" id="" onChange={onChangeSortType}>
|
||||
<option value="R">최근 등록일</option>
|
||||
<option value="U">최근 수정일</option>
|
||||
<option value="R">{getMessage('stuff.search.grid.schSortTypeR')}</option>
|
||||
<option value="U">{getMessage('stuff.search.grid.schSortTypeU')}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="select-box" style={{ width: '80px' }}>
|
||||
@ -464,7 +436,9 @@ export default function Stuff() {
|
||||
<div className="grid-table-wrap">
|
||||
<div className="q-grid">
|
||||
<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>
|
||||
|
||||
@ -1,23 +1,30 @@
|
||||
'use client'
|
||||
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import { useRouter, useSearchParams } from 'next/navigation'
|
||||
import React, { useState, useEffect, useRef } from 'react'
|
||||
import { useRouter, useSearchParams, usePathname } from 'next/navigation'
|
||||
import { Button } from '@nextui-org/react'
|
||||
import Select from 'react-dropdown-select'
|
||||
import Select from 'react-select'
|
||||
import Link from 'next/link'
|
||||
import { useAxios } from '@/hooks/useAxios'
|
||||
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 { useForm } from 'react-hook-form'
|
||||
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() {
|
||||
const sessionState = useRecoilValue(sessionStore)
|
||||
|
||||
const router = useRouter()
|
||||
const pathname = usePathname()
|
||||
const searchParams = useSearchParams()
|
||||
const { getMessage } = useMessage()
|
||||
const globalLocaleState = useRecoilValue(globalLocaleStore)
|
||||
|
||||
const { get, post, del } = useAxios(globalLocaleState)
|
||||
const ref = useRef()
|
||||
const { get, post, del, promisePost } = useAxios(globalLocaleState)
|
||||
//form
|
||||
const formInitValue = {
|
||||
// 물건번호 T...(임시) R...(진짜)
|
||||
@ -26,16 +33,17 @@ export default function StuffDetail() {
|
||||
objectName: '', //물건명
|
||||
objectNameOmit: '', //경칭선택
|
||||
objectNameKana: '', //물건명 후리가나
|
||||
saleStoreId: '', //판매점ID
|
||||
saleStoreName: '', //판매점명
|
||||
otherSaleStoreId: '',
|
||||
otherSaleStoreName: '',
|
||||
saleStoreLevel: '', //1차점스토어레벨
|
||||
saleStoreId: '', //1차점판매점ID
|
||||
saleStoreName: '', //1차점판매점명
|
||||
otherSaleStoreId: '', //1차점 외 판매점ID
|
||||
otherSaleStoreName: '', //1차점 외 판매점명
|
||||
otherSaleStoreLevel: '', //1차점 외 스토어레벨
|
||||
zipNo: '', //우편번호
|
||||
prefId: '', //도도부현
|
||||
prefName: '',
|
||||
address: '', //주소
|
||||
areaId: '', //발전량시뮬레이션지역id
|
||||
// areaName: '', //발전량시뮬레이션지역명
|
||||
windSpeed: '', //기준풍속
|
||||
verticalSnowCover: '', //수직적설량NEW
|
||||
coldRegionFlg: false, //한랭지대책시행(true : 1 / false : 0)
|
||||
@ -56,20 +64,25 @@ export default function StuffDetail() {
|
||||
const [prefValue, setPrefValue] = useState('')
|
||||
const [saleStoreList, setSaleStoreList] = useState([]) // 판매점 리스트
|
||||
const [otherSaleStoreList, setOtherSaleStoreList] = useState([])
|
||||
const [originOtherSaleStoreList, setOriginOtherSaleStoreList] = useState([])
|
||||
|
||||
const [areaIdList, setAreaIdList] = useState([]) //발전시뮬레이션 리스트
|
||||
|
||||
const [windSpeedList, setWindSpeedList] = useState([]) //기준풍속 리스트
|
||||
// const [windSpeedList, setWindSpeedList] = useState([]) //기준풍속 리스트 팝업으로이동
|
||||
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 [editMode, setEditMode] = useState('NEW')
|
||||
const [detailData, setDetailData] = useState({})
|
||||
|
||||
useEffect(() => {
|
||||
// console.log('objectNo::', objectNo)
|
||||
|
||||
if (objectNo) {
|
||||
//console.log('수정화면')
|
||||
console.log('수정화면')
|
||||
setEditMode('EDIT')
|
||||
|
||||
if (objectNo.substring(0, 1) === 'R') {
|
||||
@ -77,33 +90,9 @@ export default function StuffDetail() {
|
||||
setIsFormValid(true)
|
||||
}
|
||||
get({ url: `/api/object/${objectNo}/detail` }).then((res) => {
|
||||
// console.log('물건번호로 상세 API 호출')
|
||||
console.log('물건번호로 상세 API 호출')
|
||||
if (res != null) {
|
||||
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 {
|
||||
@ -115,60 +104,121 @@ export default function StuffDetail() {
|
||||
setPrefCodeList(res)
|
||||
}
|
||||
})
|
||||
// 판매점목록 API /api/object/saleStore/판매점코드/list - 판매점 목록 조회
|
||||
|
||||
// 임시 1차점 판매점코드 saleStoreId=201TES01
|
||||
// T01
|
||||
//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)) {
|
||||
// console.log('판매점 결과:::::', res)
|
||||
const firstList = res.filter((row) => row.saleStoreLevel === '1')
|
||||
const otherList = res.filter((row) => row.saleStoreLevel !== '1')
|
||||
// console.log('first:::::', firstList)
|
||||
// console.log('otherList:::::', otherList)
|
||||
//1차점 셀렉트박스
|
||||
setSaleStoreList(firstList)
|
||||
//1차 판매점 자동완성 값 셋팅
|
||||
form.setValue('saleStoreId', firstList[0].saleStoreId)
|
||||
//1차 판매점 번호 셋팅
|
||||
form.setValue('saleStoreName', firstList[0].saleStoreId)
|
||||
|
||||
//1차점 아닌 판매점 셀렉트박스
|
||||
setOriginOtherSaleStoreList(otherList)
|
||||
setOtherSaleStoreList(otherList)
|
||||
form.setValue('otherSaleStoreId', otherList[0].saleStoreId)
|
||||
form.setValue('otherSaleStoreName', otherList[0].saleStoreId)
|
||||
}
|
||||
})
|
||||
}
|
||||
}, [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차점 변경 이벤트
|
||||
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('saleStoreName', '')
|
||||
} else {
|
||||
const name = saleStoreList.find((obj) => obj.saleStoreId === key.target.value).saleStoreName
|
||||
form.setValue('saleStoreId', key.target.value)
|
||||
form.setValue('saleStoreName', name)
|
||||
form.setValue('saleStoreLevel', '')
|
||||
form.setValue('otherSaleStoreId', '')
|
||||
form.setValue('otherSaleStoreName', '')
|
||||
form.setValue('otherSaleStoreLevel', '')
|
||||
setOtherSaleStoreList(originOtherSaleStoreList)
|
||||
//1차점 지웠을때 2차점 자동완성 초기화
|
||||
handleClear()
|
||||
}
|
||||
}
|
||||
|
||||
//2차점 변경 이벤트
|
||||
const onSelectionChange2 = (key) => {
|
||||
const name = otherSaleStoreList.find((obj) => obj.saleStoreId === key.target.value).saleStoreName
|
||||
form.setValue('otherSaleStoreId', key.target.value)
|
||||
form.setValue('otherSaleStoreNm', name)
|
||||
}
|
||||
// 우편번호 숫자만 체크
|
||||
const _zipNo = watch('zipNo')
|
||||
useEffect(() => {
|
||||
if (_zipNo !== '' && _zipNo.length === 7 && !_zipNo.match(/\D/g)) {
|
||||
setButtonValid(true)
|
||||
if (isObjectNotEmpty(key)) {
|
||||
form.setValue('otherSaleStoreId', key.saleStoreId)
|
||||
form.setValue('otherSaleStoreName', key.saleStoreName)
|
||||
form.setValue('otherSaleStoreLevel', key.saleStoreLevel)
|
||||
} 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: '', //담당자
|
||||
@ -192,7 +242,7 @@ export default function StuffDetail() {
|
||||
const _objectName = watch('objectName')
|
||||
const _objectNameOmit = watch('objectNameOmit')
|
||||
const _saleStoreId = watch('saleStoreId')
|
||||
const _otherSaleStoreId = watch('otherSaleStoreId')
|
||||
const _saleStoreLevel = watch('saleStoreLevel')
|
||||
const _prefId = watch('prefId')
|
||||
const _address = watch('address')
|
||||
const _areaId = watch('areaId') //new
|
||||
@ -201,68 +251,56 @@ export default function StuffDetail() {
|
||||
const _installHeight = watch('installHeight')
|
||||
|
||||
useEffect(() => {
|
||||
// console.log('mode:::::', editMode)
|
||||
if (editMode === 'NEW') {
|
||||
const formData = form.getValues()
|
||||
// console.log('폼::::::::::::', formData)
|
||||
// console.log('임시저장폼::::::::::::', formData)
|
||||
let errors = {}
|
||||
if (!_dispCompanyName || _dispCompanyName.trim().length === 0) {
|
||||
if (!formData.dispCompanyName || formData.dispCompanyName.trim().length === 0) {
|
||||
errors.dispCompanyName = true
|
||||
}
|
||||
if (!_objectName || _objectName.trim().length === 0) {
|
||||
if (!formData.objectName || formData.objectName.trim().length === 0) {
|
||||
errors.objectName = true
|
||||
}
|
||||
if (!_objectNameOmit) {
|
||||
if (!formData.objectNameOmit) {
|
||||
errors.objectNameOmit = true
|
||||
}
|
||||
if (!_saleStoreId) {
|
||||
if (!formData.saleStoreId) {
|
||||
errors.saleStoreId = true
|
||||
}
|
||||
|
||||
// if (!_otherSaleStoreId) {
|
||||
// errors.otherSaleStoreId = true
|
||||
// }
|
||||
|
||||
if (!_zipNo || _zipNo.length != 7) {
|
||||
errors.zipCode = true
|
||||
}
|
||||
|
||||
if (!_prefId) {
|
||||
if (!formData.prefId) {
|
||||
errors.prefId = true
|
||||
}
|
||||
|
||||
if (!_address.trim().length === 0) {
|
||||
errors.address = true
|
||||
}
|
||||
|
||||
if (!_areaId) {
|
||||
if (!formData.areaId) {
|
||||
errors.areaId = true
|
||||
}
|
||||
|
||||
if (!_windSpeed) {
|
||||
if (!formData.windSpeed) {
|
||||
errors.windSpeed = true
|
||||
}
|
||||
|
||||
if (!_verticalSnowCover) {
|
||||
if (!formData.verticalSnowCover) {
|
||||
errors.verticalSnowCover = true
|
||||
}
|
||||
|
||||
if (!_installHeight) {
|
||||
if (!formData.installHeight) {
|
||||
errors.installHeight = true
|
||||
}
|
||||
|
||||
// console.log('errors::', errors)
|
||||
// console.log('임시저장용::', errors)
|
||||
setIsFormValid(Object.keys(errors).length === 0)
|
||||
} else {
|
||||
// console.log('상세일때 폼체크')
|
||||
console.log('상세일때 폼체크')
|
||||
}
|
||||
}, [
|
||||
_dispCompanyName,
|
||||
_objectName,
|
||||
_objectNameOmit,
|
||||
_saleStoreId,
|
||||
_saleStoreLevel,
|
||||
// _otherSaleStoreId,
|
||||
_zipNo,
|
||||
// _otherSaleStoreLevel,
|
||||
_prefId,
|
||||
_address,
|
||||
_areaId,
|
||||
@ -271,52 +309,33 @@ export default function StuffDetail() {
|
||||
_installHeight,
|
||||
])
|
||||
|
||||
// 주소검색 API
|
||||
const onSearchPostNumber = () => {
|
||||
const params = {
|
||||
zipcode: _zipNo,
|
||||
}
|
||||
// 주소검색 팝업오픈
|
||||
const onSearchPostNumberPopOpen = () => {
|
||||
setShowAddressButtonValid(true)
|
||||
}
|
||||
|
||||
get({ url: `https://zipcloud.ibsnet.co.jp/api/search?${queryStringFormatter(params)}` }).then((res) => {
|
||||
//7830060
|
||||
//9302226
|
||||
//0790177 3개짜리
|
||||
if (res.status === 200) {
|
||||
if (res.results != null) {
|
||||
console.log('주소검색::', res.results)
|
||||
// console.log('prefcode::', res.results[0].prefcode)
|
||||
// console.log('address::', res.results[0].address2 + res.results[0].address3)
|
||||
setPrefValue(res.results[0].prefcode)
|
||||
form.setValue('prefId', res.results[0].prefcode)
|
||||
form.setValue('prefName', res.results[0].address1)
|
||||
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)
|
||||
}
|
||||
})
|
||||
//설계의뢰 팝업 오픈
|
||||
const onSearchDesignRequestPopOpen = () => {
|
||||
setShowDesignRequestButtonValid(true)
|
||||
}
|
||||
|
||||
// 풍속선택 팝업 오픈
|
||||
const onSearchWindSpeedPopOpen = () => {
|
||||
const prefName = form.watch('prefName')
|
||||
if (prefName === '') {
|
||||
alert(getMessage('stuff.windSelectPopup.error.message1'))
|
||||
} else {
|
||||
setShowWindSpeedButtonValid(true)
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (prefValue !== '') {
|
||||
// 발전량시뮬레이션 지역 목록
|
||||
// /api/object/prefecture/도도부현코드/list
|
||||
get({ url: `/api/object/prefecture/${prefValue}/list` }).then((res) => {
|
||||
if (!isEmptyArray(res)) {
|
||||
// console.log('발전량 시뮬레이션::::::::', res)
|
||||
form.setValue('areaId', res[0].prefId)
|
||||
form.setValue('areaId', res[0].areaId)
|
||||
form.setValue('areaName', res[0].prefName)
|
||||
setAreaIdList(res)
|
||||
}
|
||||
@ -329,20 +348,18 @@ export default function StuffDetail() {
|
||||
form.setValue('areaId', e.target.value)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!isEmptyArray(areaIdList)) {
|
||||
//도도부현넘기는지 발전량시뮬레이션지역 넘기는지 ->도도부현넘기기
|
||||
console.log('prefName::', form.watch('prefName'))
|
||||
let _prefName = form.watch('prefName')
|
||||
//http://localhost:8080/api/object/windSpeed/兵庫県/list
|
||||
get({ url: `/api/object/windSpeed/${_prefName}/list` }).then((res) => {
|
||||
if (!isEmptyArray(res)) {
|
||||
// console.log('기준풍속결과:::::::::', res)
|
||||
setWindSpeedList(res)
|
||||
}
|
||||
})
|
||||
}
|
||||
}, [areaIdList])
|
||||
// useEffect(() => {
|
||||
// if (!isEmptyArray(areaIdList)) {
|
||||
// let _prefName = form.watch('prefName')
|
||||
// // console.log('기준풍속 가져오는 API', _prefName)
|
||||
// get({ url: `/api/object/windSpeed/${_prefName}/list` }).then((res) => {
|
||||
// // console.log('res::', res)
|
||||
// if (!isEmptyArray(res)) {
|
||||
// setWindSpeedList(res)
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
// }, [areaIdList])
|
||||
|
||||
//필수값 다 입력했을때
|
||||
const onValid = (data) => {
|
||||
@ -378,10 +395,11 @@ export default function StuffDetail() {
|
||||
// 임시저장
|
||||
const onTempSave = async () => {
|
||||
const formData = form.getValues()
|
||||
|
||||
// console.log('formData::', formData)
|
||||
const params = {
|
||||
saleStoreId: formData.saleStoreId,
|
||||
saleStoreName: formData.saleStoreName,
|
||||
saleStoreId: formData.otherSaleStoreId ? formData.otherSaleStoreId : formData.saleStoreId,
|
||||
saleStoreName: formData.otherSaleStoreName ? formData.otherSaleStoreName : formData.saleStoreName,
|
||||
saleStoreLevel: formData.otherSaleStoreLevel ? formData.otherSaleStoreLevel : formData.saleStoreLevel,
|
||||
objectStatusId: formData.objectStatusId,
|
||||
objectName: formData.objectName,
|
||||
objectNameOmit: formData.objectNameOmit,
|
||||
@ -403,10 +421,17 @@ export default function StuffDetail() {
|
||||
workNo: null,
|
||||
workName: null,
|
||||
}
|
||||
console.log('임시저장params::', params)
|
||||
return
|
||||
await post({ url: '/api/object/save-object', data: params }).then((res) => {
|
||||
console.log('res::::::', res)
|
||||
//1차점 or 2차점 안고르고 임시저장하면
|
||||
if (params.saleStoreId == '') {
|
||||
params.saleStoreId = sessionState.storeId
|
||||
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'
|
||||
|
||||
del({ url: `/api/object/${testobj}` }).then((res) => {
|
||||
// console.log('삭제 결과:::', res)
|
||||
console.log('삭제 결과:::', res)
|
||||
router.push('/management/stuff')
|
||||
})
|
||||
}
|
||||
@ -431,7 +456,8 @@ export default function StuffDetail() {
|
||||
<form onSubmit={handleSubmit(onValid)}>
|
||||
<div className="sub-table-box">
|
||||
<div className="promise-gudie">
|
||||
<span className="important">*</span> 필수 입력항목
|
||||
<span className="important">*</span>
|
||||
{getMessage('stuff.detail.required')}
|
||||
</div>
|
||||
<div className="infomation-table">
|
||||
<table>
|
||||
@ -440,9 +466,22 @@ export default function StuffDetail() {
|
||||
<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>
|
||||
담당자 <span className="important">*</span>
|
||||
{getMessage('stuff.detail.dispCompanyName')} <span className="important">*</span>
|
||||
</th>
|
||||
<td>
|
||||
<div className="input-wrap" style={{ width: '500px' }}>
|
||||
@ -452,24 +491,24 @@ export default function StuffDetail() {
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
물건구분/물건명 <span className="important">*</span>
|
||||
{getMessage('stuff.detail.objectStatusId')} <span className="important">*</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">신축</label>
|
||||
<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="objectStatus0">기축</label>
|
||||
<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="">경칭선택</option>
|
||||
<option value="11">경칭11</option>
|
||||
<option value="22">경칭22</option>
|
||||
<option value="33">경칭33</option>
|
||||
@ -479,7 +518,7 @@ export default function StuffDetail() {
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>물건명 후리가나</th>
|
||||
<th>{getMessage('stuff.detail.objectNameKana')}</th>
|
||||
<td>
|
||||
<div className="input-wrap" style={{ width: '789px' }}>
|
||||
<input type="text" className="input-light" {...form.register('objectNameKana')} />
|
||||
@ -490,26 +529,27 @@ export default function StuffDetail() {
|
||||
<th>
|
||||
<div className="flx-box">
|
||||
<div className="title">
|
||||
1차 판매점명 / ID
|
||||
{getMessage('stuff.detail.saleStoreId')}
|
||||
<span className="important">*</span>
|
||||
</div>
|
||||
<div className="tooltips"></div>
|
||||
<div className="tooltips">
|
||||
<span>{getMessage('stuff.detail.tooltip.saleStoreId')}</span>
|
||||
</div>
|
||||
</div>
|
||||
</th>
|
||||
<td>
|
||||
<div className="flx-box">
|
||||
<div className="select-wrap mr5" style={{ width: '567px' }}>
|
||||
{saleStoreList?.length > 0 && (
|
||||
<select className="select-light" onChange={onSelectionChange} value={form.watch('saleStoreId')}>
|
||||
{saleStoreList.map((row) => {
|
||||
return (
|
||||
<option key={row.saleStoreLevel} value={row.saleStoreId}>
|
||||
{row.saleStoreName}
|
||||
</option>
|
||||
)
|
||||
})}
|
||||
</select>
|
||||
)}
|
||||
<Select
|
||||
className="react-select-custom"
|
||||
classNamePrefix="custom"
|
||||
placeholder="Select"
|
||||
options={saleStoreList}
|
||||
onChange={onSelectionChange}
|
||||
getOptionLabel={(x) => x.saleStoreName}
|
||||
getOptionValue={(x) => x.saleStoreId}
|
||||
isClearable={true}
|
||||
/>
|
||||
</div>
|
||||
<div className="input-wrap" style={{ width: '216px' }}>
|
||||
<input type="text" className="input-light" value={form.watch('saleStoreId')} {...form.register('saleStoreId')} readOnly />
|
||||
@ -520,24 +560,27 @@ export default function StuffDetail() {
|
||||
<tr>
|
||||
<th>
|
||||
<div className="flx-box">
|
||||
<div className="title">2차 판매점명 / ID</div>
|
||||
<div className="tooltips"></div>
|
||||
<div className="title">{getMessage('stuff.detail.otherSaleStoreId')}</div>
|
||||
<div className="tooltips">
|
||||
<span>{getMessage('stuff.detail.tooltip.saleStoreId')}</span>
|
||||
</div>
|
||||
</div>
|
||||
</th>
|
||||
<td>
|
||||
<div className="flx-box">
|
||||
<div className="select-wrap mr5" style={{ width: '567px' }}>
|
||||
{otherSaleStoreList?.length > 0 && (
|
||||
<select className="select-light" onChange={onSelectionChange2} value={form.watch('otherSaleStoreId')}>
|
||||
{otherSaleStoreList.map((row) => {
|
||||
return (
|
||||
<option key={row.saleStoreId} value={row.saleStoreId} text={row.saleStoreName}>
|
||||
{row.saleStoreName}
|
||||
</option>
|
||||
)
|
||||
})}
|
||||
</select>
|
||||
)}
|
||||
<Select
|
||||
className="react-select-custom"
|
||||
classNamePrefix="custom"
|
||||
placeholder="Select"
|
||||
ref={ref}
|
||||
options={otherSaleStoreList}
|
||||
onChange={onSelectionChange2}
|
||||
getOptionLabel={(x) => x.saleStoreName}
|
||||
getOptionValue={(x) => x.saleStoreId}
|
||||
isDisabled={form.watch('saleStoreId') !== '' ? false : true}
|
||||
isClearable={true}
|
||||
/>
|
||||
</div>
|
||||
<div className="input-wrap" style={{ width: '216px' }}>
|
||||
<input
|
||||
@ -553,48 +596,37 @@ export default function StuffDetail() {
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
우편번호 <span className="important">*</span>
|
||||
{getMessage('stuff.detail.zipNo')} <span className="important">*</span>
|
||||
</th>
|
||||
<td>
|
||||
<div className="flx-box">
|
||||
<div className="input-wrap mr5" style={{ width: '200px' }}>
|
||||
<input
|
||||
type="text"
|
||||
className="input-light"
|
||||
maxLength={7}
|
||||
{...form.register('zipNo', {
|
||||
minLength: { value: 7, message: '7자리만가능' },
|
||||
pattern: { value: /^[0-9]*$/g, message: '숫자만 입력' },
|
||||
})}
|
||||
/>
|
||||
<input type="text" className="input-light" disabled value={form.watch('zipNo')} />
|
||||
</div>
|
||||
<Button className="btn-origin grey" isDisabled={!buttonValid} onClick={onSearchPostNumber}>
|
||||
주소검색
|
||||
<Button className="btn-origin grey" onClick={onSearchPostNumberPopOpen}>
|
||||
{getMessage('stuff.detail.btn.addressPop')}
|
||||
</Button>
|
||||
<div className="guide">*우편번호 7자리를 입력한 후, 주소검색 버튼을 클릭해 주십시오</div>
|
||||
<div className="guide">{getMessage('stuff.detail.btn.addressPop.guide')}</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
도도부현 / 주소 <span className="important">*</span>
|
||||
{getMessage('stuff.detail.prefId')}
|
||||
<span className="important">*</span>
|
||||
</th>
|
||||
<td>
|
||||
<div className="flx-box">
|
||||
<div className="input-wrap mr5" style={{ width: '200px' }}>
|
||||
<input type="text" className="input-light" value={form.watch('prefName')} {...form.register('prefName')} readOnly />
|
||||
{/* {prefCodeList?.length > 0 && (
|
||||
<Select className="max-w-xs" selectedKeys={prefValue} isDisabled {...form.register('prefId')}>
|
||||
{prefCodeList.map((row) => {
|
||||
return <SelectItem key={row.prefId}>{row.prefName}</SelectItem>
|
||||
})}
|
||||
</Select>
|
||||
)} */}
|
||||
{/* {prefCodeList?.length > 0 && (
|
||||
<select>
|
||||
<option value=""></option>
|
||||
<div className="select-wrap" style={{ width: '200px' }}>
|
||||
{prefCodeList?.length > 0 && (
|
||||
<select className="select-light" name="prefName" {...register('prefId')} disabled>
|
||||
{prefCodeList.map((row) => (
|
||||
<option key={row.prefId} value={row.prefId}>
|
||||
{row.prefName}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
)} */}
|
||||
)}
|
||||
</div>
|
||||
<div className="input-wrap mr5" style={{ width: '580px' }}>
|
||||
<input type="text" className="input-light" value={form.watch('address')} {...form.register('address')} />
|
||||
@ -604,17 +636,15 @@ export default function StuffDetail() {
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
발전량시뮬레이션지역 <span className="important">*</span>
|
||||
{getMessage('stuff.detail.areaId')} <span className="important">*</span>
|
||||
</th>
|
||||
<td>
|
||||
<div className="select-wrap" style={{ width: '200px' }}>
|
||||
<select
|
||||
className="select-light"
|
||||
name="areaId"
|
||||
onChange={(e) => {
|
||||
form.setValue('areaId', e.target.value)
|
||||
}}
|
||||
disabled={areaIdList?.length > 0 ? false : true}
|
||||
onChange={handleAreaIdOnChange}
|
||||
>
|
||||
{areaIdList.map((row) => {
|
||||
return (
|
||||
@ -629,11 +659,14 @@ export default function StuffDetail() {
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
기준풍속 <span className="important">*</span>
|
||||
{getMessage('stuff.detail.windSpeed')} <span className="important">*</span>
|
||||
</th>
|
||||
<td>
|
||||
<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')}>
|
||||
{windSpeedList.map((row) => {
|
||||
return (
|
||||
@ -643,15 +676,17 @@ export default function StuffDetail() {
|
||||
)
|
||||
})}
|
||||
</select>
|
||||
</div>
|
||||
<span>m/s이하</span>
|
||||
<button className="btn-origin grey mr5">풍속선택</button>
|
||||
</div> */}
|
||||
<span className="mr10">{getMessage('stuff.detail.windSpeedSpan')}</span>
|
||||
<button className="btn-origin grey" onClick={onSearchWindSpeedPopOpen}>
|
||||
{getMessage('stuff.detail.btn.windSpeedPop')}
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
수직적설량 <span className="important">*</span>
|
||||
{getMessage('stuff.detail.verticalSnowCover')} <span className="important">*</span>
|
||||
</th>
|
||||
<td>
|
||||
<div className="flx-box">
|
||||
@ -665,14 +700,14 @@ export default function StuffDetail() {
|
||||
<span className="mr10">cm</span>
|
||||
<div className="d-check-box light">
|
||||
<input type="checkbox" id="coldRegionFlg" {...form.register('coldRegionFlg')} />
|
||||
<label htmlFor="coldRegionFlg">한랭지대책시행</label>
|
||||
<label htmlFor="coldRegionFlg">{getMessage('stuff.detail.coldRegionFlg')}</label>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
면조도구분 <span className="important">*</span>
|
||||
{getMessage('stuff.detail.surfaceType')} <span className="important">*</span>
|
||||
</th>
|
||||
<td>
|
||||
<div className="flx-box">
|
||||
@ -686,7 +721,7 @@ export default function StuffDetail() {
|
||||
</div>
|
||||
<div className="d-check-box light mr5">
|
||||
<input type="checkbox" id="saltAreaFlg" {...form.register('saltAreaFlg')} />
|
||||
<label htmlFor="saltAreaFlg">염해지역용아이템사용</label>
|
||||
<label htmlFor="saltAreaFlg">{getMessage('stuff.detail.saltAreaFlg')}</label>
|
||||
</div>
|
||||
<div className="tooltips"></div>
|
||||
</div>
|
||||
@ -694,7 +729,7 @@ export default function StuffDetail() {
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
설치높이 <span className="important">*</span>
|
||||
{getMessage('stuff.detail.installHeight')} <span className="important">*</span>
|
||||
</th>
|
||||
<td>
|
||||
<div className="flx-box">
|
||||
@ -711,25 +746,25 @@ export default function StuffDetail() {
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>계약조건</th>
|
||||
<th>{getMessage('stuff.detail.conType')}</th>
|
||||
<td>
|
||||
<div className="flx-box">
|
||||
<div className="d-check-radio light mr10">
|
||||
<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 className="d-check-radio light mr10">
|
||||
<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>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>메모</th>
|
||||
<th>{getMessage('stuff.detail.remarks')}</th>
|
||||
<td>
|
||||
<div className="input-wrap">
|
||||
<input type="text" className="input-light" />
|
||||
<input type="text" className="input-light" {...form.register('remarks')} />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@ -756,34 +791,152 @@ export default function StuffDetail() {
|
||||
</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' ? (
|
||||
<>
|
||||
<Link href="/management/stuff">
|
||||
<button type="button" className="btn-origin grey">
|
||||
<button type="button" className="btn-origin grey mr5">
|
||||
R상세:물건목록
|
||||
</button>
|
||||
</Link>
|
||||
<button type="submit" className="btn-origin navy mr5">
|
||||
R상세:저장
|
||||
</button>
|
||||
<button type="submit" className="btn-origin navy mr5" onClick={onDelete}>
|
||||
<button type="submit" className="btn-origin navy" onClick={onDelete}>
|
||||
R상세:물건삭제
|
||||
</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">
|
||||
<button type="button" className="btn-origin grey">
|
||||
T상세:물건목록
|
||||
</button>
|
||||
</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} />
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@ -38,9 +38,8 @@ export default function StuffQGrid(props) {
|
||||
flex: 1,
|
||||
sortable: false,
|
||||
suppressMovable: true,
|
||||
resizable: false,
|
||||
resizable: true,
|
||||
suppressSizeToFit: false,
|
||||
headerClass: 'centered', //_test.scss에 추가 테스트
|
||||
}
|
||||
}, [])
|
||||
|
||||
@ -67,24 +66,7 @@ export default function StuffQGrid(props) {
|
||||
|
||||
//더블클릭
|
||||
const onCellDoubleClicked = useCallback((event) => {
|
||||
// if (event.column.colId === 'company') {
|
||||
// return
|
||||
// } else {
|
||||
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
|
||||
@ -92,11 +74,17 @@ export default function StuffQGrid(props) {
|
||||
gridData ? setRowData(gridData) : ''
|
||||
}, [gridData])
|
||||
|
||||
// 임시는 row색깔 구분
|
||||
const getRowClass = (row) => {
|
||||
if (row.data.objectNo.substring(0, 1) === 'T') {
|
||||
return 'important_row'
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="ag-theme-quartz" style={{ height: 500 }}>
|
||||
<AgGridReact
|
||||
ref={gridRef}
|
||||
onGridReady={onGridReady}
|
||||
rowBuffer={rowBuffer}
|
||||
rowData={rowData}
|
||||
columnDefs={colDefs}
|
||||
@ -107,10 +95,9 @@ export default function StuffQGrid(props) {
|
||||
onSelectionChanged={onSelectionChanged}
|
||||
onCellDoubleClicked={onCellDoubleClicked}
|
||||
pagination={isPageable}
|
||||
//paginationPageSize={paginationPageSize}
|
||||
//paginationPageSizeSelector={paginationPageSizeSelector}
|
||||
autoSizeStrategy={autoSizeStrategy}
|
||||
overlayNoRowsTemplate={'<span className="ag-overlay-loading-center">물건 목록이 없습니다.</span>'}
|
||||
getRowClass={getRowClass}
|
||||
autoSizeAllColumns={true}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -4,7 +4,8 @@ import React, { useEffect, useRef, useState } from 'react'
|
||||
import { useAxios } from '@/hooks/useAxios'
|
||||
import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil'
|
||||
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 JA from '@/locales/ja.json'
|
||||
import { stuffSearchState } from '@/store/stuffAtom'
|
||||
@ -42,7 +43,6 @@ export default function StuffSearchCondition() {
|
||||
const resetStuffRecoil = useResetRecoilState(stuffSearchState)
|
||||
const [stuffSearch, setStuffSearch] = useRecoilState(stuffSearchState)
|
||||
const [objectNo, setObjectNo] = useState('') //물건번호
|
||||
// const [saleStoreId, setSaleStoreId] = useState('') //판매대리점ID 세션에서 가져와서세팅
|
||||
const [address, setAddress] = useState('') //물건주소
|
||||
const [objectName, setobjectName] = useState('') //물건명
|
||||
const [saleStoreName, setSaleStoreName] = useState('') //판매대리점명
|
||||
@ -74,13 +74,16 @@ export default function StuffSearchCondition() {
|
||||
startRow: stuffSearch?.startRow ? stuffSearch.startRow : 1,
|
||||
endRow: stuffSearch?.endRow ? stuffSearch.endRow : 100,
|
||||
schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'R',
|
||||
selObject: {
|
||||
label: stuffSearch.selObject.label,
|
||||
value: stuffSearch.selObject.value,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
//초기화
|
||||
const resetRecoil = () => {
|
||||
setObjectNo('')
|
||||
//setSaleStoreId('') //세션정보
|
||||
setAddress('')
|
||||
setobjectName('')
|
||||
setSaleStoreName('')
|
||||
@ -96,12 +99,14 @@ export default function StuffSearchCondition() {
|
||||
|
||||
useEffect(() => {
|
||||
if (isObjectNotEmpty(sessionState)) {
|
||||
// console.log('판매대리점 리스트 가져오기 위한 세션정보::::::::', sessionState)
|
||||
// storeId가 T01 이거나 1차점일때만 판매대리점 선택 활성화
|
||||
// get({ url: `/api/object/saleStore/201TES01/list` }).then((res) => {
|
||||
get({ url: `/api/object/saleStore/${sessionState?.storeId}/list` }).then((res) => {
|
||||
if (!isEmptyArray(res)) {
|
||||
// console.log('판매점 결과:::::', res)
|
||||
res.map((row) => {
|
||||
row.value = row.saleStoreId
|
||||
row.label = row.saleStoreName
|
||||
})
|
||||
setSchSelSaleStoreList(res)
|
||||
}
|
||||
})
|
||||
@ -110,18 +115,21 @@ export default function StuffSearchCondition() {
|
||||
|
||||
//초기화 눌렀을 때 자동완성도..
|
||||
const handleClear = () => {
|
||||
// console.log('ref::', ref.current.state.values)
|
||||
if (ref.current.state.dropDown) {
|
||||
ref.current.methods.dropDown()
|
||||
} else {
|
||||
ref.current.state.values = []
|
||||
if (ref.current) {
|
||||
ref.current.clearValue()
|
||||
}
|
||||
}
|
||||
|
||||
//판매대리점 자동완성 변경
|
||||
const onSelectionChange = (key) => {
|
||||
if (!isEmptyArray(key)) {
|
||||
setSchSelSaleStoreId(key[0].saleStoreId)
|
||||
setStuffSearch({ ...stuffSearch, code: 'S', schSelSaleStoreId: key[0].saleStoreId })
|
||||
if (isObjectNotEmpty(key)) {
|
||||
setSchSelSaleStoreId(key.saleStoreId)
|
||||
setStuffSearch({
|
||||
...stuffSearch,
|
||||
code: 'S',
|
||||
schSelSaleStoreId: key.saleStoreId,
|
||||
selObject: { value: key.saleStoreId, label: key.saleStoreName },
|
||||
})
|
||||
} else {
|
||||
setSchSelSaleStoreId('')
|
||||
setStuffSearch({ ...stuffSearch, schSelSaleStoreId: '' })
|
||||
@ -147,19 +155,19 @@ export default function StuffSearchCondition() {
|
||||
<div className="sub-table-box">
|
||||
<div className="table-box-title-wrap">
|
||||
<div className="title-wrap">
|
||||
<h3>물건현황</h3>
|
||||
<h3>{getMessage('stuff.search.title')}</h3>
|
||||
</div>
|
||||
<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">
|
||||
물건신규등록
|
||||
{getMessage('stuff.search.btn1')}
|
||||
</button>
|
||||
</Link>
|
||||
<button type="button" className="btn-origin navy mr5" onClick={onSubmit}>
|
||||
조회
|
||||
{getMessage('stuff.search.btn2')}
|
||||
</button>
|
||||
<button type="button" className="btn-origin grey" onClick={resetRecoil}>
|
||||
초기화
|
||||
{getMessage('stuff.search.btn3')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -175,13 +183,12 @@ export default function StuffSearchCondition() {
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>물건번호</th>
|
||||
<th>{getMessage('stuff.search.schObjectNo')}</th>
|
||||
<td>
|
||||
<div className="input-wrap">
|
||||
<input
|
||||
type="text"
|
||||
className="input-light"
|
||||
placeholder="물건번호 입력"
|
||||
value={stuffSearch?.code === 'E' || stuffSearch?.code === 'M' ? stuffSearch.schObjectNo : objectNo}
|
||||
onChange={(e) => {
|
||||
setObjectNo(e.target.value)
|
||||
@ -190,29 +197,26 @@ export default function StuffSearchCondition() {
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
<th>판매대리점명</th>
|
||||
<th>{getMessage('stuff.search.schSaleStoreName')}</th>
|
||||
<td>
|
||||
<div className="input-wrap">
|
||||
<input
|
||||
type="text"
|
||||
className="input-light"
|
||||
placeholder="판매대리점명 입력"
|
||||
value={stuffSearch?.schSaleStoreName ? stuffSearch.schSaleStoreName : saleStoreName}
|
||||
onChange={(e) => {
|
||||
//setSaleStoreId(e.target.value)
|
||||
setSaleStoreName(e.target.value)
|
||||
setStuffSearch({ ...stuffSearch, code: 'S', schSaleStoreName: e.target.value })
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
<th>물건주소</th>
|
||||
<th>{getMessage('stuff.search.schAddress')}</th>
|
||||
<td>
|
||||
<div className="input-wrap">
|
||||
<input
|
||||
type="text"
|
||||
className="input-light"
|
||||
placeholder="물건주소 입력"
|
||||
value={stuffSearch?.schAddress ? stuffSearch.schAddress : address}
|
||||
onChange={(e) => {
|
||||
setAddress(e.target.value)
|
||||
@ -223,13 +227,12 @@ export default function StuffSearchCondition() {
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>물건명</th>
|
||||
<th>{getMessage('stuff.search.schObjectName')}</th>
|
||||
<td>
|
||||
<div className="input-wrap">
|
||||
<input
|
||||
type="text"
|
||||
className="input-light"
|
||||
placeholder="물건명 입력"
|
||||
value={stuffSearch?.schObjectName ? stuffSearch.schObjectName : objectName}
|
||||
onChange={(e) => {
|
||||
setobjectName(e.target.value)
|
||||
@ -238,13 +241,12 @@ export default function StuffSearchCondition() {
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
<th>견적처</th>
|
||||
<th>{getMessage('stuff.search.schDispCompanyName')}</th>
|
||||
<td>
|
||||
<div className="input-wrap">
|
||||
<input
|
||||
type="text"
|
||||
className="input-light"
|
||||
placeholder="견적처 입력"
|
||||
value={stuffSearch?.schDispCompanyName ? stuffSearch.schDispCompanyName : dispCompanyName}
|
||||
onChange={(e) => {
|
||||
setDispCompanyName(e.target.value)
|
||||
@ -253,31 +255,34 @@ export default function StuffSearchCondition() {
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
<th>판매대리점 선택</th>
|
||||
<th>{getMessage('stuff.search.schSelSaleStoreId')}</th>
|
||||
<td>
|
||||
{schSelSaleStoreList?.length > 0 && (
|
||||
<Select
|
||||
options={schSelSaleStoreList}
|
||||
value={stuffSearch?.schSelSaleStoreId ? stuffSearch.schSelSaleStoreId : schSelSaleStoreId}
|
||||
labelField="saleStoreName"
|
||||
valueField="saleStoreName"
|
||||
onChange={onSelectionChange}
|
||||
clearable={true}
|
||||
onClearAll={handleClear}
|
||||
ref={ref}
|
||||
disabled={sessionState?.storeLvl === '1' ? false : true}
|
||||
></Select>
|
||||
)}
|
||||
<div className="select-wrap">
|
||||
{schSelSaleStoreList?.length > 0 && (
|
||||
<Select
|
||||
className="react-select-custom"
|
||||
classNamePrefix="custom"
|
||||
placeholder="Select"
|
||||
ref={ref}
|
||||
options={schSelSaleStoreList}
|
||||
onChange={onSelectionChange}
|
||||
getOptionLabel={(x) => x.saleStoreName}
|
||||
getOptionValue={(x) => x.saleStoreId}
|
||||
defaultValue={stuffSearch?.selObject?.value ? stuffSearch?.selObject : null}
|
||||
isDisabled={sessionState?.storeLvl === '1' ? false : true}
|
||||
isClearable={true}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>담당자</th>
|
||||
<th>{getMessage('stuff.search.schReceiveUser')}</th>
|
||||
<td>
|
||||
<div className="input-wrap">
|
||||
<input
|
||||
type="text"
|
||||
className="input-light"
|
||||
placeholder="담당자 입력"
|
||||
value={stuffSearch?.schReceiveUser ? stuffSearch.schReceiveUser : receiveUser}
|
||||
onChange={(e) => {
|
||||
setReceiveUser(e.target.value)
|
||||
@ -286,7 +291,7 @@ export default function StuffSearchCondition() {
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
<th>기간검색</th>
|
||||
<th>{getMessage('stuff.search.period')}</th>
|
||||
<td colSpan={3}>
|
||||
<div className="form-flex-wrap">
|
||||
<div className="radio-wrap mr10">
|
||||
@ -302,7 +307,7 @@ export default function StuffSearchCondition() {
|
||||
setStuffSearch({ ...stuffSearch, code: 'S', schDateType: e.target.value })
|
||||
}}
|
||||
/>
|
||||
<label htmlFor="radio_u">갱신일</label>
|
||||
<label htmlFor="radio_u">{getMessage('stuff.search.schDateTypeU')}</label>
|
||||
</div>
|
||||
<div className="d-check-radio light">
|
||||
<input
|
||||
@ -316,7 +321,7 @@ export default function StuffSearchCondition() {
|
||||
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 className="date-picker-wrap">
|
||||
|
||||
165
src/components/management/popup/FindAddressPop.jsx
Normal 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>
|
||||
)
|
||||
}
|
||||
62
src/components/management/popup/FindAddressPopQGrid.jsx
Normal 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>
|
||||
)
|
||||
}
|
||||
449
src/components/management/popup/PlanRequestPop.jsx
Normal 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>
|
||||
)
|
||||
}
|
||||
62
src/components/management/popup/PlanRequestPopQGrid.jsx
Normal 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>
|
||||
)
|
||||
}
|
||||
115
src/components/management/popup/WindSelectPop.jsx
Normal 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>
|
||||
)
|
||||
}
|
||||
287
src/components/myInfo/UserInfoModal.jsx
Normal 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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
415
src/hooks/object/useObjectBatch.js
Normal 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,
|
||||
}
|
||||
}
|
||||
@ -23,31 +23,23 @@ import {
|
||||
outerLineLength2State,
|
||||
outerLineTypeState,
|
||||
} 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 { 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 {
|
||||
addCanvasMouseEventListener,
|
||||
addDocumentEventListener,
|
||||
removeAllMouseEventListeners,
|
||||
removeAllDocumentEventListeners,
|
||||
removeMouseEvent,
|
||||
removeMouseLine,
|
||||
initEvent,
|
||||
} = useEvent()
|
||||
const { addCanvasMouseEventListener, addDocumentEventListener, removeMouseLine, initEvent } = useEvent()
|
||||
const { getIntersectMousePoint } = useMouse()
|
||||
const { addLine, removeLine } = useLine()
|
||||
const { tempGridMode } = useTempGrid()
|
||||
const { swalFire } = useSwal()
|
||||
const { getAdsorptionPoints } = useAdsorptionPoint()
|
||||
|
||||
const verticalHorizontalMode = useRecoilValue(verticalHorizontalModeState)
|
||||
const adsorptionPointAddMode = useRecoilValue(adsorptionPointAddModeState)
|
||||
const adsorptionPointMode = useRecoilValue(adsorptionPointModeState)
|
||||
const adsorptionRange = useRecoilValue(adsorptionRangeState)
|
||||
const interval = useRecoilValue(dotLineIntervalSelector) // 가로 세로 간격
|
||||
|
||||
const [buttonAct, setButtonAct] = useState(1)
|
||||
|
||||
@ -85,12 +77,16 @@ export function useAuxiliaryDrawing() {
|
||||
|
||||
useEffect(() => {
|
||||
typeRef.current = type
|
||||
clear()
|
||||
addDocumentEventListener('keydown', document, keydown[type])
|
||||
}, [type])
|
||||
|
||||
useEffect(() => {
|
||||
// innerLines가 있을경우 삭제
|
||||
const roofs = canvas?.getObjects().filter((obj) => obj.name === 'roofBase')
|
||||
if (roofs.length === 0) {
|
||||
swalFire({ text: '지붕형상이 없습니다.' })
|
||||
setShowAuxiliaryModal(false)
|
||||
return
|
||||
}
|
||||
|
||||
@ -101,7 +97,7 @@ export function useAuxiliaryDrawing() {
|
||||
addCanvasMouseEventListener('mouse:move', mouseMove)
|
||||
addCanvasMouseEventListener('mouse:down', mouseDown)
|
||||
addDocumentEventListener('contextmenu', document, cutAuxiliary)
|
||||
addDocumentEventListener('keydown', document, keydown)
|
||||
addDocumentEventListener('keydown', document, keydown[type])
|
||||
|
||||
return () => {
|
||||
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 = () => {
|
||||
addCanvasMouseEventListener('mouse:move', mouseMove)
|
||||
setLength1(0)
|
||||
setLength2(0)
|
||||
|
||||
@ -130,7 +122,6 @@ export function useAuxiliaryDrawing() {
|
||||
|
||||
const keydown = {
|
||||
outerLine: (e) => {
|
||||
console.log(123)
|
||||
if (mousePointerArr.current.length === 0) {
|
||||
return
|
||||
}
|
||||
@ -467,7 +458,9 @@ export function useAuxiliaryDrawing() {
|
||||
}
|
||||
|
||||
const mouseDown = (e) => {
|
||||
canvas.renderAll()
|
||||
const pointer = getIntersectMousePoint(e)
|
||||
console.log(pointer)
|
||||
|
||||
mousePointerArr.current.push(pointer)
|
||||
if (mousePointerArr.current.length === 2) {
|
||||
@ -498,7 +491,6 @@ export function useAuxiliaryDrawing() {
|
||||
|
||||
auxiliaryLines.forEach((line1) => {
|
||||
auxiliaryLines.forEach((line2) => {
|
||||
const lines = [line1, line2]
|
||||
if (line1 === line2) {
|
||||
return
|
||||
}
|
||||
@ -546,59 +538,66 @@ export function useAuxiliaryDrawing() {
|
||||
|
||||
// 보조선 절삭
|
||||
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) {
|
||||
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((line2) => {
|
||||
const lines = [line1, line2]
|
||||
allLines.forEach((line2) => {
|
||||
if (line1 === line2) {
|
||||
return
|
||||
}
|
||||
|
||||
const intersectionPoint = calculateIntersection(line1, line2)
|
||||
if (!intersectionPoint || intersectionPoints.current.some((point) => point.x === intersectionPoint.x && point.y === intersectionPoint.y)) {
|
||||
if (!intersectionPoint) {
|
||||
return
|
||||
}
|
||||
roofAdsorptionPoints.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) {
|
||||
return
|
||||
}
|
||||
//historyLine에서 기존 line을 제거한다.
|
||||
lineHistory.current = lineHistory.current.filter((history) => history !== line)
|
||||
const distance1 = distanceBetweenPoints({ x: line1.x1, y: line1.y1 }, intersectionPoint)
|
||||
const distance2 = distanceBetweenPoints({ x: line1.x2, y: line1.y2 }, intersectionPoint)
|
||||
|
||||
let newLine
|
||||
if (distance1 === 0 || distance2 === 0) {
|
||||
return
|
||||
}
|
||||
//historyLine에서 기존 line을 제거한다.
|
||||
lineHistory.current = lineHistory.current.filter((history) => history !== line1)
|
||||
|
||||
if (distance1 >= distance2) {
|
||||
newLine = addLine([line.x1, line.y1, intersectionPoint.x, intersectionPoint.y], {
|
||||
stroke: 'black',
|
||||
strokeWidth: 1,
|
||||
selectable: false,
|
||||
name: 'auxiliaryLine',
|
||||
isFixed: true,
|
||||
intersectionPoint,
|
||||
})
|
||||
} else {
|
||||
newLine = addLine([line.x2, line.y2, intersectionPoint.x, intersectionPoint.y], {
|
||||
stroke: 'black',
|
||||
strokeWidth: 1,
|
||||
selectable: false,
|
||||
name: 'auxiliaryLine',
|
||||
isFixed: true,
|
||||
intersectionPoint,
|
||||
})
|
||||
}
|
||||
lineHistory.current.push(newLine)
|
||||
removeLine(line)
|
||||
})
|
||||
let newLine
|
||||
|
||||
if (distance1 >= distance2) {
|
||||
newLine = addLine([line1.x1, line1.y1, intersectionPoint.x, intersectionPoint.y], {
|
||||
stroke: 'black',
|
||||
strokeWidth: 1,
|
||||
selectable: false,
|
||||
name: 'auxiliaryLine',
|
||||
isFixed: true,
|
||||
intersectionPoint,
|
||||
})
|
||||
} else {
|
||||
newLine = addLine([line1.x2, line1.y2, intersectionPoint.x, intersectionPoint.y], {
|
||||
stroke: 'black',
|
||||
strokeWidth: 1,
|
||||
selectable: false,
|
||||
name: 'auxiliaryLine',
|
||||
isFixed: true,
|
||||
intersectionPoint,
|
||||
})
|
||||
}
|
||||
lineHistory.current.push(newLine)
|
||||
removeLine(line1)
|
||||
})
|
||||
})
|
||||
addCanvasMouseEventListener('mouse:move', mouseMove)
|
||||
@ -624,11 +623,31 @@ export function useAuxiliaryDrawing() {
|
||||
addCanvasMouseEventListener('mouse:move', mouseMove)
|
||||
}
|
||||
|
||||
const handleFix = (fn) => {
|
||||
const handleFix = () => {
|
||||
if (!confirm('지붕선 완료하시겠습니까?')) {
|
||||
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 {
|
||||
|
||||
@ -6,8 +6,11 @@ import { useEvent } from '@/hooks/useEvent'
|
||||
import { LINE_TYPE } from '@/common/common'
|
||||
import { useLine } from '@/hooks/useLine'
|
||||
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 { getMessage } = useMessage()
|
||||
const { addCanvasMouseEventListener, initEvent } = useEvent()
|
||||
@ -20,6 +23,7 @@ export function useEavesGableEdit() {
|
||||
const [type, setType] = useState(TYPES.EAVES)
|
||||
const typeRef = useRef(TYPES.EAVES)
|
||||
const { removeLine } = useLine()
|
||||
const { swalFire } = useSwal()
|
||||
|
||||
const { drawRoofPolygon } = useMode()
|
||||
|
||||
@ -27,6 +31,7 @@ export function useEavesGableEdit() {
|
||||
const offsetRef = useRef(null)
|
||||
const widthRef = useRef(null)
|
||||
const radioTypeRef = useRef('1') // 각 페이지에서 사용하는 radio type
|
||||
const outerLineFix = useRecoilValue(outerLineFixState)
|
||||
|
||||
const buttonMenu = [
|
||||
{ id: 1, name: getMessage('eaves'), type: TYPES.EAVES },
|
||||
@ -35,6 +40,14 @@ export function useEavesGableEdit() {
|
||||
{ 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(() => {
|
||||
const wallLines = canvas.getObjects().filter((obj) => obj.name === 'wallLine')
|
||||
wallLines.forEach((wallLine) => {
|
||||
|
||||
@ -29,8 +29,10 @@ import {
|
||||
} from '@/store/outerLineAtom'
|
||||
import { calculateAngle } from '@/util/qpolygon-utils'
|
||||
import { fabric } from 'fabric'
|
||||
import { QLine } from '@/components/fabric/QLine'
|
||||
|
||||
export function useOuterLineWall() {
|
||||
//외벽선 그리기
|
||||
export function useOuterLineWall(setShowOutlineModal) {
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
const { addCanvasMouseEventListener, addDocumentEventListener, removeAllMouseEventListeners, removeAllDocumentEventListeners, removeMouseEvent } =
|
||||
useEvent()
|
||||
@ -67,8 +69,6 @@ export function useOuterLineWall() {
|
||||
|
||||
const isFix = useRef(false)
|
||||
|
||||
const closeModalFn = useRef(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (adsorptionPointAddMode || tempGridMode) {
|
||||
return
|
||||
@ -208,18 +208,19 @@ export function useOuterLineWall() {
|
||||
removeAllMouseEventListeners()
|
||||
removeAllDocumentEventListeners()
|
||||
canvas?.renderAll()
|
||||
closeModalFn.current(false)
|
||||
setOuterLineFix(true)
|
||||
setShowOutlineModal(false)
|
||||
}
|
||||
|
||||
if (points.length < 3) {
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
const firstPoint = points[0]
|
||||
@ -238,38 +239,27 @@ export function useOuterLineWall() {
|
||||
if (isAllRightAngle) {
|
||||
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',
|
||||
strokeWidth: 1,
|
||||
selectable: false,
|
||||
name: 'helpGuideLine',
|
||||
})
|
||||
|
||||
canvas?.add(line)
|
||||
addLineText(line)
|
||||
} 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',
|
||||
strokeWidth: 1,
|
||||
strokeDashArray: [1, 1, 1],
|
||||
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',
|
||||
strokeWidth: 1,
|
||||
strokeDashArray: [1, 1, 1],
|
||||
name: 'helpGuideLine',
|
||||
})
|
||||
if (guideLine1.length > 0) {
|
||||
canvas?.add(guideLine1)
|
||||
addLineText(guideLine1)
|
||||
}
|
||||
|
||||
canvas?.add(guideLine2)
|
||||
|
||||
addLineText(guideLine2)
|
||||
}*/
|
||||
}
|
||||
}
|
||||
}, [points])
|
||||
|
||||
@ -748,7 +738,7 @@ export function useOuterLineWall() {
|
||||
setPoints((prev) => prev.slice(0, prev.length - 1))
|
||||
}
|
||||
|
||||
const handleFix = (fn) => {
|
||||
const handleFix = () => {
|
||||
if (points.length < 3) {
|
||||
return
|
||||
}
|
||||
@ -778,7 +768,6 @@ export function useOuterLineWall() {
|
||||
})
|
||||
|
||||
isFix.current = true
|
||||
closeModalFn.current = fn
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@ -7,7 +7,8 @@ import { usePolygon } from '@/hooks/usePolygon'
|
||||
import { useLine } from '@/hooks/useLine'
|
||||
import { outerLinePointsState } from '@/store/outerLineAtom'
|
||||
|
||||
export function usePropertiesSetting() {
|
||||
// 외벽선 속성 설정
|
||||
export function usePropertiesSetting(setShowPropertiesSettingModal) {
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
|
||||
const currentObject = useRecoilValue(currentObjectState)
|
||||
@ -19,17 +20,7 @@ export function usePropertiesSetting() {
|
||||
const { removeLine, hideLine } = useLine()
|
||||
|
||||
useEffect(() => {
|
||||
if (!currentObject) {
|
||||
return
|
||||
}
|
||||
if (currentObject.name !== 'outerLine') {
|
||||
return
|
||||
}
|
||||
|
||||
const type = currentObject.attributes?.type
|
||||
|
||||
const lines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
|
||||
|
||||
lines.forEach((line) => {
|
||||
const lineType = line.attributes?.type
|
||||
if (!lineType) {
|
||||
@ -39,6 +30,14 @@ export function usePropertiesSetting() {
|
||||
})
|
||||
}
|
||||
})
|
||||
if (!currentObject) {
|
||||
return
|
||||
}
|
||||
if (currentObject.name !== 'outerLine') {
|
||||
return
|
||||
}
|
||||
|
||||
const type = currentObject.attributes?.type
|
||||
|
||||
if (!type) {
|
||||
currentObject.set({
|
||||
@ -46,6 +45,8 @@ export function usePropertiesSetting() {
|
||||
strokeWidth: 4,
|
||||
})
|
||||
}
|
||||
|
||||
canvas.renderAll()
|
||||
}, [currentObject])
|
||||
|
||||
const history = useRef([])
|
||||
@ -106,6 +107,7 @@ export function usePropertiesSetting() {
|
||||
}
|
||||
const lastLine = history.current.pop()
|
||||
|
||||
delete lastLine.attributes
|
||||
lastLine.set({
|
||||
stroke: '#000000',
|
||||
strokeWidth: 4,
|
||||
@ -131,7 +133,7 @@ export function usePropertiesSetting() {
|
||||
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]
|
||||
|
||||
@ -159,7 +161,7 @@ export function usePropertiesSetting() {
|
||||
|
||||
canvas.renderAll()
|
||||
setPoints([])
|
||||
fn(false)
|
||||
setShowPropertiesSettingModal(false)
|
||||
}
|
||||
|
||||
return { handleSetEaves, handleSetGable, handleRollback, handleFix, closeModal }
|
||||
|
||||
143
src/hooks/roofcover/useRoofAllocationSetting.js
Normal 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,
|
||||
}
|
||||
}
|
||||
220
src/hooks/roofcover/useRoofShapePassivitySetting.js
Normal 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 }
|
||||
}
|
||||
@ -6,10 +6,14 @@ import { LINE_TYPE, MENU } from '@/common/common'
|
||||
import { usePolygon } from '@/hooks/usePolygon'
|
||||
import { useMode } from '@/hooks/useMode'
|
||||
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 [buttonAct, setButtonAct] = useState(1)
|
||||
const { swalFire } = useSwal()
|
||||
const { getMessage } = useMessage()
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
const { addPolygonByLines } = usePolygon()
|
||||
@ -27,9 +31,18 @@ export function useRoofShapeSetting() {
|
||||
const { hideLine, showLine } = useLine()
|
||||
|
||||
const setCurrentMenu = useSetRecoilState(currentMenuState)
|
||||
const outerLineFix = useRecoilValue(outerLineFixState)
|
||||
|
||||
const history = useRef([])
|
||||
|
||||
useEffect(() => {
|
||||
const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
|
||||
if (!outerLineFix || outerLines.length === 0) {
|
||||
swalFire({ text: '외벽선이 없습니다.' })
|
||||
setShowRoofShapeSettingModal(false)
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (shapeNum !== 4) {
|
||||
return
|
||||
@ -98,11 +111,7 @@ export function useRoofShapeSetting() {
|
||||
{ id: 6, name: getMessage('shed') },
|
||||
]
|
||||
|
||||
/**
|
||||
*
|
||||
* @param fn 모달 닫기 위한 함수
|
||||
*/
|
||||
const handleSave = (fn) => {
|
||||
const handleSave = () => {
|
||||
//기존 wallLine 삭제
|
||||
|
||||
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' })
|
||||
polygon.lines = [...outerLines]
|
||||
|
||||
@ -242,7 +259,7 @@ export function useRoofShapeSetting() {
|
||||
|
||||
canvas?.renderAll()
|
||||
roof.drawHelpLine()
|
||||
fn && fn(false)
|
||||
setShowRoofShapeSettingModal(false)
|
||||
}
|
||||
|
||||
const initLineSetting = () => {
|
||||
@ -387,7 +404,8 @@ export function useRoofShapeSetting() {
|
||||
// 벽
|
||||
attributes = {
|
||||
type: LINE_TYPE.WALLLINE.WALL,
|
||||
offset: hasSleeve === '0' ? 0 : sleeveOffset / 10,
|
||||
width: hasSleeve === '0' ? 0 : sleeveOffset / 10,
|
||||
sleeve: hasSleeve === '1',
|
||||
}
|
||||
break
|
||||
}
|
||||
@ -415,7 +433,7 @@ export function useRoofShapeSetting() {
|
||||
// 한쪽흐름
|
||||
attributes = {
|
||||
type: LINE_TYPE.WALLLINE.SHED,
|
||||
offset: shedWidth / 10,
|
||||
width: shedWidth / 10,
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
@ -4,12 +4,16 @@ import { useEffect, useRef, useState } from 'react'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { useEvent } from '@/hooks/useEvent'
|
||||
import { useLine } from '@/hooks/useLine'
|
||||
import { useSwal } from '@/hooks/useSwal'
|
||||
|
||||
export function useWallLineOffsetSetting() {
|
||||
// 외벽선 편집 및 오프셋
|
||||
export function useWallLineOffsetSetting(setShowWallLineOffsetSettingModal) {
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
const { showLine, addLine } = useLine()
|
||||
const { getMessage } = useMessage()
|
||||
const { swalFire } = useSwal()
|
||||
const { addCanvasMouseEventListener, initEvent } = useEvent()
|
||||
const wallLineEditRef = useRef(null)
|
||||
const length1Ref = useRef(null)
|
||||
const length2Ref = useRef(null)
|
||||
const radioTypeRef = useRef('1')
|
||||
@ -17,6 +21,25 @@ export function useWallLineOffsetSetting() {
|
||||
const arrow1Ref = 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 = {
|
||||
WALL_LINE_EDIT: 'wallLineEdit',
|
||||
OFFSET: 'offset',
|
||||
@ -31,42 +54,128 @@ export function useWallLineOffsetSetting() {
|
||||
|
||||
useEffect(() => {
|
||||
const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
|
||||
if (outerLines.length === 0) {
|
||||
swalFire({ text: '외벽선이 없습니다.' })
|
||||
setShowWallLineOffsetSettingModal(false)
|
||||
return
|
||||
}
|
||||
outerLines.forEach((outerLine) => {
|
||||
outerLine.set({ selectable: true })
|
||||
showLine(outerLine)
|
||||
})
|
||||
|
||||
const roofs = canvas.getObjects().filter((obj) => obj.name === 'roofBase')
|
||||
|
||||
roofs.forEach((roof) => {
|
||||
roof.innerLines.forEach((innerLine) => {
|
||||
canvas.remove(innerLine)
|
||||
})
|
||||
canvas.remove(roof)
|
||||
//outerLine과 그 text만 남겨둔다.
|
||||
const exceptObjs = canvas.getObjects().filter((obj) => obj.name !== 'outerLine' && obj.parent?.name !== 'outerLine')
|
||||
exceptObjs.forEach((obj) => {
|
||||
canvas.remove(obj)
|
||||
})
|
||||
|
||||
const wallLines = canvas.getObjects().filter((obj) => obj.name === 'wallLine')
|
||||
wallLines.forEach((wallLine) => {
|
||||
canvas.remove(wallLine)
|
||||
})
|
||||
canvas.setActiveObject(outerLines[0])
|
||||
currentWallLineRef.current = outerLines[0]
|
||||
addCircleByLine(currentWallLineRef.current)
|
||||
|
||||
addCanvasMouseEventListener('mouse:down', mouseDown)
|
||||
|
||||
setIsLoading(true)
|
||||
return () => {
|
||||
removeOuterLineEditCircle()
|
||||
canvas.discardActiveObject()
|
||||
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) => {
|
||||
removeOuterLineEditCircle()
|
||||
if (!e.target || (e.target && e.target.name !== 'outerLine')) {
|
||||
currentWallLineRef.current = null
|
||||
return
|
||||
}
|
||||
|
||||
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 = () => {
|
||||
if (currentWallLineRef.current === null) {
|
||||
alert('보조선을 먼저 선택하세요')
|
||||
return
|
||||
}
|
||||
switch (type) {
|
||||
case TYPES.WALL_LINE_EDIT:
|
||||
handleWallLineEditSave()
|
||||
@ -78,51 +187,324 @@ export function useWallLineOffsetSetting() {
|
||||
}
|
||||
|
||||
const handleWallLineEditSave = () => {
|
||||
const direction = currentWallLineRef.current.direction
|
||||
let canDirections = direction === 'left' || direction === 'right' ? ['up', 'down'] : ['left', 'right']
|
||||
if (radioTypeRef === 1) {
|
||||
if (!canDirections.includes(arrow1Ref.current)) {
|
||||
alert('방향을 다시 선택하세요')
|
||||
return
|
||||
const startPoint = canvas
|
||||
.getObjects()
|
||||
.filter((obj) => obj.name === 'outerLineEditCircleStart')
|
||||
.map((obj) => {
|
||||
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 {
|
||||
if (!canDirections.includes(arrow2Ref.current)) {
|
||||
alert('방향을 다시 선택하세요')
|
||||
return
|
||||
// 2지점일 경우 방향은 up, left만 가능
|
||||
if (arrow2Ref.current === 'up') {
|
||||
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 direction = currentWallLineRef.current.direction
|
||||
let canDirections = direction === 'left' || direction === 'right' ? ['up', 'down'] : ['left', 'right']
|
||||
|
||||
const currentIdx = currentWallLineRef.current.idx
|
||||
if (!canDirections.includes(arrow1Ref.current)) {
|
||||
alert('방향을 다시 선택하세요')
|
||||
return
|
||||
}
|
||||
|
||||
const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
|
||||
|
||||
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 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) {
|
||||
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
|
||||
}
|
||||
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
|
||||
}
|
||||
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
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
818
src/hooks/surface/usePlacementShapeDrawing.js
Normal 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,
|
||||
}
|
||||
}
|
||||
@ -1,21 +1,21 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { canvasState } from '@/store/canvasAtom'
|
||||
import { MENU } from '@/common/common'
|
||||
import { getIntersectionPoint } from '@/util/canvas-util'
|
||||
import { MENU, BATCH_TYPE, POLYGON_TYPE } from '@/common/common'
|
||||
import { getIntersectionPoint, setSurfaceShapePattern } from '@/util/canvas-util'
|
||||
import { degreesToRadians } from '@turf/turf'
|
||||
import { QPolygon } from '@/components/fabric/QPolygon'
|
||||
import { fabric } from 'fabric'
|
||||
import { useSwal } from '@/hooks/useSwal'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { useEvent } from '@/hooks/useEvent'
|
||||
|
||||
export function useSurfaceShapeBatch() {
|
||||
const { getMessage } = useMessage()
|
||||
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
const { swalFire } = useSwal()
|
||||
const { addCanvasMouseEventListener, initEvent } = useEvent()
|
||||
|
||||
const applySurfaceShape = (surfaceRefs, selectedType, setShowPlacementSurfaceSettingModal) => {
|
||||
let length1, length2, length3, length4, length5
|
||||
@ -57,7 +57,7 @@ export function useSurfaceShapeBatch() {
|
||||
let points = []
|
||||
if (checkSurfaceShape(surfaceId, { length1, length2, length3, length4, length5 })) {
|
||||
setShowPlacementSurfaceSettingModal(false)
|
||||
canvas?.on('mouse:move', (e) => {
|
||||
addCanvasMouseEventListener('mouse:move', (e) => {
|
||||
if (!isDrawing) {
|
||||
return
|
||||
}
|
||||
@ -110,15 +110,13 @@ export function useSurfaceShapeBatch() {
|
||||
obj.set({ direction: direction })
|
||||
obj.set({ originAngle: originAngle })
|
||||
|
||||
// setCurrentPattern(obj)
|
||||
canvas?.renderAll()
|
||||
})
|
||||
|
||||
canvas?.on('mouse:down', (e) => {
|
||||
addCanvasMouseEventListener('mouse:down', (e) => {
|
||||
isDrawing = false
|
||||
obj.set('name', MENU.BATCH_CANVAS.SURFACE_SHAPE_BATCH)
|
||||
canvas?.off('mouse:down')
|
||||
canvas.off('mouse:move')
|
||||
obj.set('name', POLYGON_TYPE.ROOF)
|
||||
initEvent()
|
||||
setSurfaceShapePattern(obj)
|
||||
setShowPlacementSurfaceSettingModal(true)
|
||||
})
|
||||
@ -132,23 +130,23 @@ export function useSurfaceShapeBatch() {
|
||||
|
||||
if (surfaceId === 1) {
|
||||
if (length1 === 0) {
|
||||
swalFire({ text: getMessage('surface.shape.validate.size'), icon: 'error' })
|
||||
swalFire({ text: getMessage('common.canvas.validate.size'), icon: 'error' })
|
||||
check = false
|
||||
}
|
||||
if (length2 === 0) {
|
||||
if (length3 === 0) {
|
||||
swalFire({ text: getMessage('surface.shape.validate.size'), icon: 'error' })
|
||||
swalFire({ text: getMessage('common.canvas.validate.size'), icon: 'error' })
|
||||
check = false
|
||||
}
|
||||
}
|
||||
} else if ([2, 4].includes(surfaceId)) {
|
||||
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
|
||||
}
|
||||
} else if ([3, 5, 6, 15, 18].includes(surfaceId)) {
|
||||
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
|
||||
}
|
||||
if (surfaceId === 3 && length3 > length1) {
|
||||
@ -174,7 +172,7 @@ export function useSurfaceShapeBatch() {
|
||||
}
|
||||
} else if ([8, 12, 13, 16, 17].includes(surfaceId)) {
|
||||
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
|
||||
}
|
||||
|
||||
@ -202,7 +200,7 @@ export function useSurfaceShapeBatch() {
|
||||
}
|
||||
} else if ([7, 9, 10, 11, 14].includes(surfaceId)) {
|
||||
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
|
||||
}
|
||||
if (surfaceId === 9 || surfaceId === 10 || surfaceId === 11) {
|
||||
@ -566,66 +564,32 @@ export function useSurfaceShapeBatch() {
|
||||
return points
|
||||
}
|
||||
|
||||
//면형상 선택 클릭시 지붕 패턴 입히기
|
||||
const setSurfaceShapePattern = (polygon) => {
|
||||
const ratio = window.devicePixelRatio || 1
|
||||
|
||||
let width = 265 / 10
|
||||
let height = 150 / 10
|
||||
let roofStyle = 2
|
||||
const inputPatternSize = { width: width, height: height } //임시 사이즈
|
||||
const patternSize = { ...inputPatternSize } // 입력된 값을 뒤집기 위해
|
||||
|
||||
if (polygon.direction === 'east' || polygon.direction === 'west') {
|
||||
//세로형이면 width height를 바꿈
|
||||
;[patternSize.width, patternSize.height] = [inputPatternSize.height, patternSize.width]
|
||||
}
|
||||
|
||||
// 패턴 소스를 위한 임시 캔버스 생성
|
||||
const patternSourceCanvas = document.createElement('canvas')
|
||||
patternSourceCanvas.width = polygon.width * ratio
|
||||
patternSourceCanvas.height = polygon.height * ratio
|
||||
const ctx = patternSourceCanvas.getContext('2d')
|
||||
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',
|
||||
const deleteAllSurfacesAndObjects = () => {
|
||||
swalFire({
|
||||
text: '배치면 내용을 전부 삭제하시겠습니까?',
|
||||
type: 'confirm',
|
||||
confirmFn: () => {
|
||||
canvas?.getObjects().forEach((obj) => {
|
||||
if (
|
||||
obj.name === POLYGON_TYPE.ROOF ||
|
||||
obj.name === BATCH_TYPE.OPENING ||
|
||||
obj.name === BATCH_TYPE.SHADOW ||
|
||||
obj.name === BATCH_TYPE.TRIANGLE_DORMER ||
|
||||
obj.name === BATCH_TYPE.PENTAGON_DORMER
|
||||
) {
|
||||
canvas?.remove(obj)
|
||||
}
|
||||
})
|
||||
swalFire({ text: '삭제 완료 되었습니다.' })
|
||||
},
|
||||
// denyFn: () => {
|
||||
// swalFire({ text: '취소되었습니다.', icon: 'error' })
|
||||
// },
|
||||
})
|
||||
|
||||
polygon.set('fill', null)
|
||||
polygon.set('fill', pattern)
|
||||
canvas?.renderAll()
|
||||
}
|
||||
|
||||
return {
|
||||
applySurfaceShape,
|
||||
deleteAllSurfacesAndObjects,
|
||||
}
|
||||
}
|
||||
|
||||
@ -40,56 +40,60 @@ export function useAxios(lang = '') {
|
||||
// response 추가 로직
|
||||
axios.interceptors.request.use(undefined, (error) => {})
|
||||
|
||||
const get = async ({ url }) => {
|
||||
const get = async ({ url, option = {} }) => {
|
||||
return await getInstances(url)
|
||||
.get(url)
|
||||
.get(url, option)
|
||||
.then((res) => res.data)
|
||||
.catch(console.error)
|
||||
}
|
||||
|
||||
const promiseGet = async ({ url }) => {
|
||||
return await getInstances(url).get(url)
|
||||
const promiseGet = async ({ url, option = {} }) => {
|
||||
return await getInstances(url).get(url, option)
|
||||
}
|
||||
|
||||
const post = async ({ url, data }) => {
|
||||
const post = async ({ url, data, option = {} }) => {
|
||||
return await getInstances(url)
|
||||
.post(url, data)
|
||||
.post(url, data, option)
|
||||
.then((res) => res.data)
|
||||
.catch(console.error)
|
||||
}
|
||||
|
||||
const promisePost = async ({ url, data }) => {
|
||||
return await getInstances(url).post(url, data)
|
||||
const promisePost = async ({ url, data, option = {} }) => {
|
||||
return await getInstances(url).post(url, data, option)
|
||||
}
|
||||
|
||||
const put = async ({ url, data }) => {
|
||||
const put = async ({ url, data, option = {} }) => {
|
||||
return await getInstances(url)
|
||||
.put(url, data)
|
||||
.put(url, data, option)
|
||||
.then((res) => res.data)
|
||||
.catch(console.error)
|
||||
}
|
||||
|
||||
const promisePut = async ({ url, data }) => {
|
||||
return await getInstances(url).put(url, data)
|
||||
const promisePut = async ({ url, data, option = {} }) => {
|
||||
return await getInstances(url).put(url, data, option)
|
||||
}
|
||||
|
||||
const patch = async ({ url, data }) => {
|
||||
const patch = async ({ url, data, option = {} }) => {
|
||||
return await getInstances(url)
|
||||
.patch(url, data)
|
||||
.patch(url, data, option)
|
||||
.then((res) => res.data)
|
||||
.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)
|
||||
.delete(url)
|
||||
.delete(url, option)
|
||||
.then((res) => res.data)
|
||||
.catch(console.error)
|
||||
}
|
||||
|
||||
const promiseDel = async ({ url }) => {
|
||||
return await getInstances(url).delete(url)
|
||||
const promiseDel = async ({ url, option = {} }) => {
|
||||
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 }
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { useState } from 'react'
|
||||
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'
|
||||
|
||||
// 캔버스에 필요한 이벤트
|
||||
@ -8,6 +8,8 @@ export function useCanvasEvent() {
|
||||
const [canvas, setCanvasForEvent] = useState(null)
|
||||
const [currentObject, setCurrentObject] = useRecoilState(currentObjectState)
|
||||
const canvasSize = useRecoilValue(canvasSizeState)
|
||||
const fontSize = useRecoilValue(fontSizeState)
|
||||
const fontFamily = useRecoilValue(fontFamilyState)
|
||||
|
||||
// 기본적인 이벤트 필요시 추가
|
||||
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') {
|
||||
const x = target.left
|
||||
const y = target.top
|
||||
target.lockMovementX = false
|
||||
target.lockMovementY = false
|
||||
// Add a property to store the previous value
|
||||
const previousValue = target.text
|
||||
target.on('selected', (e) => {
|
||||
|
||||
160
src/hooks/useContextMenu.js
Normal 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,
|
||||
}
|
||||
}
|
||||