diff --git a/.env.development b/.env.development index bb0e9585..f843c86b 100644 --- a/.env.development +++ b/.env.development @@ -4,5 +4,5 @@ SESSION_SECRET="i3iHH1yp2/2SpQSIySQ4bpyc4g0D+zCF9FAn5xUG0+Y=" 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" \ No newline at end of file +NEXT_PUBLIC_Q_ORDER_AUTO_LOGIN_URL="http://q-order-qa.q-cells.jp:8120/eos/login/autoLogin" +NEXT_PUBLIC_Q_MUSUBI_AUTO_LOGIN_URL="http://q-musubi-qa.q-cells.jp:8120/qm/login/autoLogin" \ No newline at end of file diff --git a/.env.production b/.env.production index bb0e9585..c4c7918d 100644 --- a/.env.production +++ b/.env.production @@ -4,5 +4,5 @@ SESSION_SECRET="i3iHH1yp2/2SpQSIySQ4bpyc4g0D+zCF9FAn5xUG0+Y=" 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" \ No newline at end of file +NEXT_PUBLIC_Q_ORDER_AUTO_LOGIN_URL="https://q-order.q-cells.jp/eos/login/autoLogin" +NEXT_PUBLIC_Q_MUSUBI_AUTO_LOGIN_URL="https://q-musubi.q-cells.jp/qm/login/autoLogin" \ No newline at end of file diff --git a/package.json b/package.json index d7214b51..4dfef6dd 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,6 @@ "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" diff --git a/src/app/QcastProvider.js b/src/app/QcastProvider.js index 3ae0993d..84a5638e 100644 --- a/src/app/QcastProvider.js +++ b/src/app/QcastProvider.js @@ -1,27 +1,24 @@ 'use client' -import { useEffect } from 'react' -import { useRecoilState, useRecoilValue } from 'recoil' -import { appMessageStore, globalLocaleStore } from '@/store/localeAtom' import { ErrorBoundary } from 'next/dist/client/components/error-boundary' import ServerError from './error' import '@/styles/common.scss' -import KO from '@/locales/ko.json' -import JA from '@/locales/ja.json' +// import KO from '@/locales/ko.json' +// import JA from '@/locales/ja.json' export const QcastProvider = ({ children }) => { - const globalLocale = useRecoilValue(globalLocaleStore) - const [appMessageState, setAppMessageState] = useRecoilState(appMessageStore) + // const globalLocale = useRecoilValue(globalLocaleStore) + // const [appMessageState, setAppMessageState] = useRecoilState(appMessageStore) - useEffect(() => { - if (globalLocale === 'ko') { - setAppMessageState(KO) - } else { - setAppMessageState(JA) - } - }, [globalLocale]) + // useEffect(() => { + // if (globalLocale === 'ko') { + // setAppMessageState(KO) + // } else { + // setAppMessageState(JA) + // } + // }, [globalLocale]) return ( <> diff --git a/src/app/SessionProvider.js b/src/app/SessionProvider.js new file mode 100644 index 00000000..d8f3caa9 --- /dev/null +++ b/src/app/SessionProvider.js @@ -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 {children} +} diff --git a/src/app/layout.js b/src/app/layout.js index 0e9a797c..424c74ef 100644 --- a/src/app/layout.js +++ b/src/app/layout.js @@ -15,6 +15,8 @@ import './globals.css' import '../styles/style.scss' import '../styles/contents.scss' import Dimmed from '@/components/ui/Dimmed' +import SessionProvider from './SessionProvider' +import LocaleSwitch from '@/components/LocaleSwitch' import PopupManager from '@/components/common/popupManager/PopupManager' // const inter = Inter({ subsets: ['latin'] }) @@ -71,11 +73,14 @@ export default async function RootLayout({ children }) {
- {children} + + {children} +
diff --git a/src/common/common.js b/src/common/common.js index ebb394d3..cf9788be 100644 --- a/src/common/common.js +++ b/src/common/common.js @@ -66,8 +66,13 @@ export const LINE_TYPE = { }, SUBLINE: { /** - * + * 추녀 / 마루 / 박공 / 지붕골 / 박공단 */ + HIP: 'hip', + RIDGE: 'ridge', + GABLE: 'gable', + VALLEY: 'valley', + VERGE: 'verge', }, } diff --git a/src/components/LocaleSwitch.jsx b/src/components/LocaleSwitch.jsx new file mode 100644 index 00000000..291f2043 --- /dev/null +++ b/src/components/LocaleSwitch.jsx @@ -0,0 +1,23 @@ +'use client' + +import { globalLocaleStore } from '@/store/localeAtom' +import { useRecoilState } from 'recoil' + +export default function LocaleSwitch() { + const [globalLocale, setGlobalLocale] = useRecoilState(globalLocaleStore) + + return ( + { + if (globalLocale === 'ko') { + setGlobalLocale('ja') + } else { + setGlobalLocale('ko') + } + }} + > + {globalLocale.toUpperCase()} + + ) +} diff --git a/src/components/Playground.jsx b/src/components/Playground.jsx index 6393158d..3bdb7ccb 100644 --- a/src/components/Playground.jsx +++ b/src/components/Playground.jsx @@ -30,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() @@ -43,6 +43,8 @@ export default function Playground() { const [checkboxInput, setCheckboxInput] = useState([]) const [selectedValue, setSelectedValue] = useState('') + const [users, setUsers] = useState([]) + useEffect(() => { console.log('textInput:', textInput) }, [textInput]) @@ -142,6 +144,10 @@ export default function Playground() { }, } + useEffect(() => { + console.log('users:', users) + }, [users]) + return ( <>
@@ -305,6 +311,32 @@ export default function Playground() {
+
+ +
+
+ +
) diff --git a/src/components/Settings.jsx b/src/components/Settings.jsx index 43288dbe..51680711 100644 --- a/src/components/Settings.jsx +++ b/src/components/Settings.jsx @@ -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) } diff --git a/src/components/auth/AutoLogin.jsx b/src/components/auth/AutoLogin.jsx new file mode 100644 index 00000000..0d539cbb --- /dev/null +++ b/src/components/auth/AutoLogin.jsx @@ -0,0 +1,21 @@ +'use client' + +import { useMessage } from '@/hooks/useMessage' + +export default function AutoLoginPage() { + const { getMessage } = useMessage() + + return ( +
+
+ {getMessage('site.name')} + {getMessage('site.sub_name')} +
+
+
+ {getMessage('login.auto.page.text')} +
+
+
+ ) +} diff --git a/src/components/auth/Login.jsx b/src/components/auth/Login.jsx index 28416284..d75d9b6e 100644 --- a/src/components/auth/Login.jsx +++ b/src/components/auth/Login.jsx @@ -15,6 +15,8 @@ import Cookies from 'js-cookie' import { useSearchParams } from 'next/navigation' +import AutoLogin from './AutoLogin' + export default function Login() { // 자동 로그인 const initParams = useSearchParams() @@ -180,8 +182,7 @@ export default function Login() { react - - {passwordReset === 1 && ( + {!autoLoginParam && passwordReset === 1 && ( <>
@@ -266,7 +267,7 @@ export default function Login() {
)} - {passwordReset === 2 && ( + {!autoLoginParam && passwordReset === 2 && ( <>
@@ -328,6 +329,7 @@ export default function Login() {
)} + {autoLoginParam && }
COPYRIGHT©2024 Hanwha Japan All Rights Reserved.
diff --git a/src/components/common/context-menu/QContextMenu.jsx b/src/components/common/context-menu/QContextMenu.jsx index a86e40af..8e3e2314 100644 --- a/src/components/common/context-menu/QContextMenu.jsx +++ b/src/components/common/context-menu/QContextMenu.jsx @@ -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) //한번 노출 후 이벤트 삭제 } diff --git a/src/components/community/Archive.jsx b/src/components/community/Archive.jsx index 768bc500..55de76f1 100644 --- a/src/components/community/Archive.jsx +++ b/src/components/community/Archive.jsx @@ -61,7 +61,7 @@ export default function Archive() {
- +
diff --git a/src/components/community/ArchiveTable.jsx b/src/components/community/ArchiveTable.jsx index ef6b303b..371ae454 100644 --- a/src/components/community/ArchiveTable.jsx +++ b/src/components/community/ArchiveTable.jsx @@ -45,7 +45,7 @@ export default function ArchiveTable({ clsCode }) { } fetchData() - }, [search.searchValue]) + }, [search.searchValue, search.searchFlag]) // 상세 파일 목록 조회 const handleDetailFileListDown = async (noticeNo) => { @@ -74,30 +74,34 @@ export default function ArchiveTable({ clsCode }) { return ( <> -
- {boardList?.map((board) => ( -
-
-
- {/* 번호 */} - {board.rowNumber} + {boardList.length > 0 ? ( +
+ {boardList?.map((board) => ( +
+
+
+ {/* 번호 */} + {board.rowNumber} +
+
+ {/* 제목 */} + {board.title} +
+
+ {/* 등록일 */} + {getMessage('board.sub.updDt')} : {board.uptDt ? board.uptDt : board.regDt} +
-
- {/* 제목 */} - {board.title} -
-
- {/* 등록일 */} - {getMessage('board.sub.updDt')} : {board.uptDt ? board.uptDt : board.regDt} +
+ {/* 첨부파일 */} +
-
- {/* 첨부파일 */} - -
-
- ))} -
+ ))} +
+ ) : ( +
{getMessage('common.message.no.data')}
+ )} ) } diff --git a/src/components/fabric/QLine.js b/src/components/fabric/QLine.js index ae3c6c62..616143fe 100644 --- a/src/components/fabric/QLine.js +++ b/src/components/fabric/QLine.js @@ -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) { diff --git a/src/components/fabric/QPolygon.js b/src/components/fabric/QPolygon.js index 0ac7a519..ad08e884 100644 --- a/src/components/fabric/QPolygon.js +++ b/src/components/fabric/QPolygon.js @@ -199,32 +199,16 @@ 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 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 - } + midPoint = new fabric.Point((start.x + end.x) / 2, (start.y + end.y) / 2) 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, diff --git a/src/components/floor-plan/CanvasFrame.jsx b/src/components/floor-plan/CanvasFrame.jsx index 9b17b1f0..d3cc9684 100644 --- a/src/components/floor-plan/CanvasFrame.jsx +++ b/src/components/floor-plan/CanvasFrame.jsx @@ -22,9 +22,6 @@ export default function CanvasFrame({ plan }) { const currentObject = useRecoilValue(currentObjectState) - useEffect(() => { - console.log(currentObject) - }, [currentObject]) useEvent() const loadCanvas = () => { @@ -46,7 +43,10 @@ export default function CanvasFrame({ plan }) { return (
- +
+ +
+ {contextMenu.map((menus, index) => (
    @@ -62,7 +62,7 @@ export default function CanvasFrame({ plan }) { > {menu.name} - ))} + ))}
))}
diff --git a/src/components/floor-plan/CanvasLayout.jsx b/src/components/floor-plan/CanvasLayout.jsx index 7138907c..d10b103c 100644 --- a/src/components/floor-plan/CanvasLayout.jsx +++ b/src/components/floor-plan/CanvasLayout.jsx @@ -1,140 +1,44 @@ '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() { +export default function CanvasLayout(props) { + const { menuNumber } = props + const { session } = useContext(SessionContext) 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 { getMessage } = useMessage() const { swalFire } = useSwal() - const { getCanvasByObjectNo, delCanvasById, checkModifiedCanvasPlan, saveCanvas, currentCanvasData } = usePlan() - - const handleCurrentPlan = (newCurrentId) => { - // console.log('currentPlan newCurrentId: ', newCurrentId) - if (!currentCanvasPlan || currentCanvasPlan.id !== newCurrentId) { - if (currentCanvasPlan?.id && checkModifiedCanvasPlan()) { - swalFire({ - html: getMessage('common.message.confirm.save') + `
${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 addEmptyPlan = () => { - setPlans([...plans, { id: planNum, name: `Plan ${planNum + 1}`, objectNo: `${objectNo}` }]) - handleCurrentPlan(planNum) - setPlanNum(planNum + 1) - } - - const addCopyPlan = () => { - const copyPlan = { - id: planNum, - name: `Plan ${planNum + 1}`, - objectNo: `${objectNo}`, - userId: sessionState.userId, - canvasStatus: currentCanvasData(), - } - setPlans((plans) => [...plans, copyPlan]) - handleCurrentPlan(planNum) - setPlanNum(planNum + 1) - } + const { plans, loadCanvasPlanData, handleCurrentPlan, handleAddPlan, handleDeletePlan } = usePlan() useEffect(() => { - if (!currentCanvasPlan) { - 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 { - addEmptyPlan() - } - }) - } + loadCanvasPlanData(session.userId, objectNo) }, []) return (
-
+
{plans.map((plan) => (
{plans.length < 10 && ( - )} diff --git a/src/components/floor-plan/CanvasMenu.jsx b/src/components/floor-plan/CanvasMenu.jsx index 14d8ee14..1836f4df 100644 --- a/src/components/floor-plan/CanvasMenu.jsx +++ b/src/components/floor-plan/CanvasMenu.jsx @@ -36,6 +36,8 @@ const canvasMenus = [ export default function CanvasMenu(props) { const { + menuNumber, + setMenuNumber, setShowCanvasSettingModal, showOutlineModal, setShowOutlineModal, @@ -57,13 +59,13 @@ export default function CanvasMenu(props) { } = props const { addPopup, closePopup } = usePopup() - const [menuNumber, setMenuNumber] = useState(null) const [type, setType] = useState('') 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) const { handleZoomClear } = useCanvasEvent() @@ -127,14 +129,14 @@ export default function CanvasMenu(props) { }, [menuNumber, type]) // 저장버튼(btn08) 클릭 시 호출되는 함수 - const handleSaveCanvas = () => { - swalFire({ - html: getMessage('common.message.confirm.save') + `
${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 = () => { @@ -146,7 +148,8 @@ export default function CanvasMenu(props) { } const handleClear = () => { - setPoints([]) + setOuterLinePoints([]) + setPlacementPoints([]) canvas?.clear() } // diff --git a/src/components/floor-plan/FloorPlan.jsx b/src/components/floor-plan/FloorPlan.jsx index 6f798f0a..b115ca48 100644 --- a/src/components/floor-plan/FloorPlan.jsx +++ b/src/components/floor-plan/FloorPlan.jsx @@ -52,6 +52,8 @@ export default function FloorPlan() { const [settingModalSecondOptions, setSettingModalSecondOptions] = useRecoilState(settingModalSecondOptionsState) const [objectNo, setObjectNo] = useState('test123240912001') // 이후 삭제 필요 + const [menuNumber, setMenuNumber] = useState(null) + const [showDotLineGridModal, setShowDotLineGridModal] = useState(false) const [showGridCopyModal, setShowGridCopyModal] = useState(false) const [showGridMoveModal, setShowGridMoveModal] = useState(false) const [color, setColor] = useRecoilState(gridColorState) @@ -63,6 +65,8 @@ export default function FloorPlan() { } const modalProps = { + menuNumber, + setMenuNumber, setShowCanvasSettingModal, showOutlineModal, setShowOutlineModal, diff --git a/src/components/floor-plan/modal/Slope.jsx b/src/components/floor-plan/modal/Slope.jsx index 0ae32ab6..ba9ede2a 100644 --- a/src/components/floor-plan/modal/Slope.jsx +++ b/src/components/floor-plan/modal/Slope.jsx @@ -1,8 +1,13 @@ import { useMessage } from '@/hooks/useMessage' import WithDraggable from '@/components/common/draggable/WithDraggable' +import { globalPitchState } from '@/store/canvasAtom' +import { useRecoilState } from 'recoil' +import { useRef } from 'react' export default function Slope({ setShowSlopeSettingModal }) { const { getMessage } = useMessage() + const [globalPitch, setGlobalPitch] = useRecoilState(globalPitchState) + const inputRef = useRef() return (
@@ -19,13 +24,21 @@ export default function Slope({ setShowSlopeSettingModal }) { {getMessage('slope')}
- +
{getMessage('size.angle')}
- +
diff --git a/src/components/floor-plan/modal/grid/DotLineGrid.jsx b/src/components/floor-plan/modal/grid/DotLineGrid.jsx index 4d839906..ed9ffa21 100644 --- a/src/components/floor-plan/modal/grid/DotLineGrid.jsx +++ b/src/components/floor-plan/modal/grid/DotLineGrid.jsx @@ -7,7 +7,7 @@ import { useRecoilState, useRecoilValue, useResetRecoilState, useSetRecoilState import { onlyNumberInputChange } from '@/util/input-utils' import { fabric } from 'fabric' import { gridColorState } from '@/store/gridAtom' -import { settingModalGridOptionsState } from '@/store/settingAtom' +import { gridDisplaySelector, settingModalGridOptionsState } from '@/store/settingAtom' import { useAxios } from '@/hooks/useAxios' import { useSwal } from '@/hooks/useSwal' import { usePopup } from '@/hooks/usePopup' @@ -25,6 +25,7 @@ export default function DotLineGrid(props) { const setSettingModalGridOptions = useSetRecoilState(settingModalGridOptionsState) const gridColor = useRecoilValue(gridColorState) const canvas = useRecoilValue(canvasState) + const isGridDisplay = useRecoilValue(gridDisplaySelector) const [dotLineGridSetting, setDotLineGridSettingState] = useRecoilState(dotLineGridSettingState) const resetDotLineGridSetting = useResetRecoilState(dotLineGridSettingState) @@ -181,6 +182,7 @@ export default function DotLineGrid(props) { fill: pattern, selectable: false, name: 'dotGrid', + visible: isGridDisplay, }, ) @@ -211,6 +213,7 @@ export default function DotLineGrid(props) { strokeDashArray: [5, 2], opacity: 0.3, direction: 'horizontal', + visible: isGridDisplay, }, ) canvas.add(horizontalLine) @@ -237,6 +240,7 @@ export default function DotLineGrid(props) { strokeDashArray: [5, 2], opacity: 0.3, direction: 'vertical', + visible: isGridDisplay, }, ) canvas.add(verticalLine) diff --git a/src/components/floor-plan/modal/object/ObjectSetting.jsx b/src/components/floor-plan/modal/object/ObjectSetting.jsx index d5fe934e..764535e3 100644 --- a/src/components/floor-plan/modal/object/ObjectSetting.jsx +++ b/src/components/floor-plan/modal/object/ObjectSetting.jsx @@ -46,6 +46,7 @@ export default function ObjectSetting({ setShowObjectSettingModal }) { heightRef: useRef(null), pitchRef: useRef(null), offsetRef: useRef(null), + offsetWidthRef: useRef(null), directionRef: useRef(null), } diff --git a/src/components/floor-plan/modal/object/type/PentagonDormer.jsx b/src/components/floor-plan/modal/object/type/PentagonDormer.jsx index e2aeea15..73f9c5d8 100644 --- a/src/components/floor-plan/modal/object/type/PentagonDormer.jsx +++ b/src/components/floor-plan/modal/object/type/PentagonDormer.jsx @@ -1,8 +1,17 @@ import Image from 'next/image' import { useMessage } from '@/hooks/useMessage' +import { forwardRef, useState } from 'react' -export default function PentagonDormer() { +const PentagonDormer = 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 ( <>
@@ -18,7 +27,7 @@ export default function PentagonDormer() {
- +
mm
@@ -29,7 +38,7 @@ export default function PentagonDormer() {
- +
mm
@@ -40,18 +49,18 @@ export default function PentagonDormer() {
- +
mm
-
{getMessage('modal.object.setting.offset.depth')}
+
{getMessage('modal.object.setting.offset.width')}
- +
mm
@@ -62,7 +71,7 @@ export default function PentagonDormer() {
- +
@@ -80,13 +89,15 @@ export default function PentagonDormer() { {getMessage('commons.east')} {getMessage('commons.south')} {getMessage('commons.west')} - - - - + + + +
) -} +}) + +export default PentagonDormer diff --git a/src/components/floor-plan/modal/object/type/TriangleDormer.jsx b/src/components/floor-plan/modal/object/type/TriangleDormer.jsx index 9ab7712d..f2a8a1f0 100644 --- a/src/components/floor-plan/modal/object/type/TriangleDormer.jsx +++ b/src/components/floor-plan/modal/object/type/TriangleDormer.jsx @@ -5,7 +5,6 @@ import { forwardRef, useState } from 'react' const TriangleDormer = forwardRef((props, refs) => { const { getMessage } = useMessage() const [direction, setDirection] = useState('down') - refs.directionRef.current = direction const getDirection = (e) => { diff --git a/src/components/floor-plan/modal/setting01/FirstOption.jsx b/src/components/floor-plan/modal/setting01/FirstOption.jsx index c4e75ea4..848596ba 100644 --- a/src/components/floor-plan/modal/setting01/FirstOption.jsx +++ b/src/components/floor-plan/modal/setting01/FirstOption.jsx @@ -1,14 +1,14 @@ import { useRecoilState } from 'recoil' -import { settingModalFirstOptionsState, settingModalSecondOptionsState } from '@/store/settingAtom' +import { settingModalSecondOptionsState } from '@/store/settingAtom' import { useMessage } from '@/hooks/useMessage' import React, { useEffect, useState } from 'react' import { useAxios } from '@/hooks/useAxios' import { useSwal } from '@/hooks/useSwal' -import { adsorptionPointAddModeState } from '@/store/canvasAtom' +import { useFirstOption } from '@/hooks/option/useFirstOption' export default function FirstOption() { const [objectNo, setObjectNo] = useState('test123240912001') // 이후 삭제 필요 - const [settingModalFirstOptions, setSettingModalFirstOptions] = useRecoilState(settingModalFirstOptionsState) + const { settingModalFirstOptions, setSettingModalFirstOptions } = useFirstOption() const [settingModalSecondOptions, setSettingModalSecondOptions] = useRecoilState(settingModalSecondOptionsState) const { option1, option2, dimensionDisplay } = settingModalFirstOptions const { option3, option4 } = settingModalSecondOptions diff --git a/src/components/main/MainContents.jsx b/src/components/main/MainContents.jsx index ba9a8d77..6dcca943 100644 --- a/src/components/main/MainContents.jsx +++ b/src/components/main/MainContents.jsx @@ -34,7 +34,6 @@ export default function MainContents({ objectList, businessCharger, businessChar startRow: 1, endRow: 1, } - // const noticeApiUrl = `api/board/list?schNoticeTpCd=QC&schNoticeClsCd=NOTICE&schTitle=&startRow=1&endRow=1` const noticeApiUrl = `api/board/list?${queryStringFormatter(param)}` const res = await get({ url: noticeApiUrl }) //console.log('공지res::', res) @@ -57,7 +56,6 @@ export default function MainContents({ objectList, businessCharger, businessChar startRow: 1, endRow: 3, } - // const faqApiUrl = `api/board/list?schNoticeTpCd=QC&schNoticeClsCd=FAQ&schTitle=&startRow=1&endRow=1` const faqApiUrl = `api/board/list?${queryStringFormatter(param)}` const res = await get({ url: faqApiUrl }) //console.log('FAQres::', res) diff --git a/src/components/management/Stuff.jsx b/src/components/management/Stuff.jsx index 9a0a73c5..58cd7bff 100644 --- a/src/components/management/Stuff.jsx +++ b/src/components/management/Stuff.jsx @@ -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) @@ -62,13 +63,10 @@ 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, //페이징시 현재 페이지만 체크되도록 @@ -228,56 +226,40 @@ 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)}` - 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: '', @@ -286,30 +268,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({ @@ -317,34 +282,33 @@ 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 + 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=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) } }) } @@ -354,53 +318,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)}` + + 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) } }) } //최근 등록일 수정일 정렬 이벤트 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)}` + + 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) } }) } @@ -413,20 +382,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 ( <> {/* 퍼블시작 */}
-

물건목록

+

{getMessage('stuff.search.grid.title')}

  • - 전체 - {convertNumberToPriceDecimal(gridCount)} + {getMessage('stuff.search.grid.all')} + {convertNumberToPriceDecimal(totalCount)}
  • - 선택 + {getMessage('stuff.search.grid.selected')} {convertNumberToPriceDecimal(selectedRowDataCount)}
@@ -434,8 +417,8 @@ export default function Stuff() {
@@ -450,7 +433,9 @@ export default function Stuff() {
-
페이징 컴포넌트예정
+
+ +
diff --git a/src/components/management/StuffDetail.jsx b/src/components/management/StuffDetail.jsx index 7167fc63..6800a5e8 100644 --- a/src/components/management/StuffDetail.jsx +++ b/src/components/management/StuffDetail.jsx @@ -16,6 +16,8 @@ import FindAddressPop from './popup/FindAddressPop' import PlanRequestPop from './popup/PlanRequestPop' import WindSelectPop from './popup/WindSelectPop' export default function StuffDetail() { + const [selOptions, setSelOptions] = useState('') + const sessionState = useRecoilValue(sessionStore) const router = useRouter() @@ -79,7 +81,7 @@ export default function StuffDetail() { const [detailData, setDetailData] = useState({}) useEffect(() => { - console.log('objectNo::', objectNo) + // console.log('objectNo::', objectNo) if (objectNo) { console.log('수정화면') @@ -100,33 +102,34 @@ export default function StuffDetail() { // 도도부현API get({ url: '/api/object/prefecture/list' }).then((res) => { if (!isEmptyArray(res)) { - // console.log('신규화면 도도부현API 결과:::', res) setPrefCodeList(res) } }) // 임시 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) => { + //1차점 : X167 T01 + // get({ url: `/api/object/saleStore/TEMP02/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) + setSelOptions(sessionState?.storeId) + form.setValue('saleStoreId', sessionState?.storeId) + form.setValue('saleStoreLevel', sessionState?.storeLvl) + //1차점 아닌 판매점 셀렉트박스 setOriginOtherSaleStoreList(otherList) setOtherSaleStoreList(otherList) } }) } - }, [objectNo]) + }, [objectNo, sessionState]) useEffect(() => { if (isObjectNotEmpty(detailData)) { - console.log('상세데이타:::::::', detailData) - // 도도부현API get({ url: '/api/object/prefecture/list' }).then((res) => { if (!isEmptyArray(res)) { @@ -151,6 +154,8 @@ export default function StuffDetail() { setOtherSaleStoreList(otherList) } }) + + console.log('상세데이타::세팅:::::', detailData) } }, [detailData]) @@ -161,12 +166,14 @@ export default function StuffDetail() { form.setValue('saleStoreId', key.saleStoreId) form.setValue('saleStoreName', key.saleStoreName) form.setValue('saleStoreLevel', key.saleStoreLevel) + setSelOptions(key.saleStoreId) //선택한 1차점 정보로 2차점 list 추리기 //長府工産株式会社 大阪支社 let newOtherSaleStoreList = originOtherSaleStoreList.filter((row) => row.firstAgentId === key.saleStoreId) setOtherSaleStoreList(newOtherSaleStoreList) } else { //X누름 + setSelOptions('') form.setValue('saleStoreId', '') form.setValue('saleStoreName', '') form.setValue('saleStoreLevel', '') @@ -209,8 +216,17 @@ export default function StuffDetail() { form.setValue('zipNo', info.zipNo) } - //팝업에서 넘어온 설계의뢰 정보 - const setPlanReqInfo = (info) => {} + //팝업에서 넘어온 설계의뢰 정보로 바꾸기 + const setPlanReqInfo = (info) => { + console.log('팝업에서 넘어온 설계의뢰 정보::: ', info) + //building : 신축 기축 + //planReqName : 물건명 + //zipNo : 우편번호 + //도도부현 :address1 주소 : address2 미세팅 + //기준풍속 팝업열려면 setPrefValue(info.prefId) 필요 + //기준풍속 : + // form.setValue('dispCompanyName', info.planReqName) + } //팝업에서 넘어온 바람정보 const setWindSppedInfo = (info) => { @@ -286,7 +302,7 @@ export default function StuffDetail() { } // console.log('임시저장용::', errors) - setIsFormValid(Object.keys(errors).length === 0) + setIsFormValid(Object.keys(errors).length === 0 ? true : false) } else { console.log('상세일때 폼체크') } @@ -331,9 +347,8 @@ export default function StuffDetail() { // 발전량시뮬레이션 지역 목록 get({ url: `/api/object/prefecture/${prefValue}/list` }).then((res) => { if (!isEmptyArray(res)) { - // console.log('발전량 시뮬레이션::::::::', res) - form.setValue('areaId', res[0].areaId) - form.setValue('areaName', res[0].prefName) + // form.setValue('areaId', res[0].areaId) + // form.setValue('areaName', res[0].prefName) setAreaIdList(res) } }) @@ -342,28 +357,18 @@ export default function StuffDetail() { // 발전량 시뮬레이션 변경 const handleAreaIdOnChange = (e) => { - form.setValue('areaId', e.target.value) + form.setValue('areaId', e.areaId) + form.setValue('areaName', e.prefName) } - // 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) => { + const onValid = (data, e) => { + const formData = form.getValues() + console.log('필수값 통과:::', data, formData) + // console.log('필수값 formData:::', formData) // 수정모드일때는 PUT // console.log('필수값 다 있고 저장') // console.log('data::::::', data) - const formData = form.getValues() // console.log('formData::::', formData) // const _dispCompanyName = watch('dispCompanyName') // const _objectStatusId = watch('objectStatusId') @@ -391,6 +396,8 @@ export default function StuffDetail() { // 임시저장 const onTempSave = async () => { + console.log('임시저장:::::') + return const formData = form.getValues() // console.log('formData::', formData) const params = { @@ -441,7 +448,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') }) } @@ -470,9 +477,9 @@ export default function StuffDetail() {
- +
@@ -536,13 +543,21 @@ export default function StuffDetail() {
-
+
{prefCodeList?.length > 0 && ( - + // + 0 ? false : true} @@ -643,8 +678,25 @@ export default function StuffDetail() { {row.prefName} ) + })} + + */} + + isDisabled={areaIdList.length > 0 ? false : true} + />
@@ -669,9 +721,9 @@ export default function StuffDetail() {
*/} {getMessage('stuff.detail.windSpeedSpan')} - +
@@ -764,13 +816,13 @@ export default function StuffDetail() {
{!isFormValid ? ( - + ) : ( - + )} +
@@ -813,7 +865,7 @@ export default function StuffDetail() {
- {/* */} +
@@ -865,7 +917,7 @@ export default function StuffDetail() {
-
+
{/* { - setObjectNo(e.target.value) - setStuffSearch({ ...stuffSearch, code: 'S', schObjectNo: e.target.value }) + setObjectNo(objectNoRef.current.value) }} + onKeyUp={handleByOnKeyUp} />
@@ -204,12 +237,11 @@ export default function StuffSearchCondition() {
{ - setSaleStoreName(e.target.value) - setStuffSearch({ ...stuffSearch, code: 'S', schSaleStoreName: e.target.value }) + setSaleStoreName(saleStoreNameRef.current.value) }} />
@@ -219,12 +251,11 @@ export default function StuffSearchCondition() {
{ - setAddress(e.target.value) - setStuffSearch({ ...stuffSearch, code: 'S', schAddress: e.target.value }) + setAddress(addressRef.current.value) }} />
@@ -236,12 +267,11 @@ export default function StuffSearchCondition() {
{ - setobjectName(e.target.value) - setStuffSearch({ ...stuffSearch, code: 'S', schObjectName: e.target.value }) + setobjectName(objectNameRef.current.value) }} />
@@ -251,30 +281,50 @@ export default function StuffSearchCondition() {
{ - setDispCompanyName(e.target.value) - setStuffSearch({ ...stuffSearch, code: 'S', schDispCompanyName: e.target.value }) + setDispCompanyName(dispCompanyNameRef.current.value) }} />
{getMessage('stuff.search.schSelSaleStoreId')} - {schSelSaleStoreList?.length > 0 && ( - x.saleStoreName} + getOptionValue={(x) => x.saleStoreId} + value={schSelSaleStoreList.filter(function (option) { + if (stuffSearch?.code === 'S' && schSelSaleStoreId === '') { + return false + } else if (stuffSearch?.code === 'S' && schSelSaleStoreId !== '') { + return option.saleStoreId === schSelSaleStoreId + } else if (stuffSearch?.code === 'E' && schSelSaleStoreId !== '') { + return option.saleStoreId === schSelSaleStoreId + } else { + if (stuffSearch?.schSelSaleStoreId !== '') { + return option.saleStoreId === stuffSearch.schSelSaleStoreId + } else { + return false + } + } + })} + isDisabled={sessionState?.storeLvl === '1' ? false : true} + isClearable={true} + /> + )} +
@@ -284,11 +334,10 @@ export default function StuffSearchCondition() { { - setReceiveUser(e.target.value) - setStuffSearch({ ...stuffSearch, code: 'S', schReceiveUser: e.target.value }) + setReceiveUser(receiveUserRef.current.value) }} />
@@ -323,7 +372,7 @@ export default function StuffSearchCondition() { setStuffSearch({ ...stuffSearch, code: 'S', schDateType: e.target.value }) }} /> - +
diff --git a/src/components/management/popup/FindAddressPop.jsx b/src/components/management/popup/FindAddressPop.jsx index b3702ded..0c480a52 100644 --- a/src/components/management/popup/FindAddressPop.jsx +++ b/src/components/management/popup/FindAddressPop.jsx @@ -79,7 +79,6 @@ export default function FindAddressPop(props) { } // 주소적용 클릭 const applyAddress = () => { - // console.log('주소적용 클릭:::::::::', prefId, address1, address2, address3, zipNo) if (prefId == null) { alert(getMessage('stuff.addressPopup.error.message2')) } else { diff --git a/src/components/management/popup/PlanRequestPop.jsx b/src/components/management/popup/PlanRequestPop.jsx index 224519d0..ab2a836c 100644 --- a/src/components/management/popup/PlanRequestPop.jsx +++ b/src/components/management/popup/PlanRequestPop.jsx @@ -1,6 +1,5 @@ import React, { useState, useRef, useEffect } 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 { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil' @@ -12,14 +11,24 @@ 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 sessionState = useRecoilValue(sessionStore) + const [pageNo, setPageNo] = useState(1) //현재 페이지 번호 + const [pageSize, setPageSize] = useState(20) //페이지 당 게시물 개수 + const [totalCount, setTotalCount] = useState(0) //총 갯수 const globalLocaleState = useRecoilValue(globalLocaleStore) - const { get } = useAxios(globalLocaleState) + 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')) @@ -34,7 +43,6 @@ export default function PlanRequestPop(props) { setStartDate: setEndDate, } - const ref = useRef() const resetPlanReqRecoil = useResetRecoilState(planReqSearchState) const [planReqSearch, setPlanReqSearch] = useRecoilState(planReqSearchState) @@ -47,14 +55,41 @@ export default function PlanRequestPop(props) { const [schDateGbn, setSchDateGbn] = useState('S') //기간구분코드(S/R) //초기화 - const resetRecoil = () => {} + 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.state.dropDown) { - ref.current.methods.dropDown() + 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 { - ref.current.state.values = [] + //X누름 + setSchPlanStatCd('') + setPlanReqSearch({ ...planReqSearch, schPlanStatCd: '' }) } } @@ -63,6 +98,60 @@ export default function PlanRequestPop(props) { setEndDate(planReqSearch?.schEndDt ? planReqSearch.schEndDt : dayjs(new Date()).format('YYYY-MM-DD')) }, [planReqSearch]) + // 조회 + const onSubmit = (page, type) => { + const params = { + // saleStoreId: 'T100', + // saleStoreLevel: '1', + saleStoreId: props?.otherSaleStoreId ? props.otherSaleStoreId : props.saleStoreId, + saleStoreLevel: props?.otherSaleStoreLevel ? props.otherSaleStoreLevel : props.saleStoreLevel, + 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) + } + // console.log(params) + + 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, @@ -117,6 +206,45 @@ export default function PlanRequestPop(props) { ], }) + //설계의뢰 그리드에서 선택한 설계의로 정보 + 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 (
@@ -132,8 +260,17 @@ export default function PlanRequestPop(props) {

{getMessage('stuff.planReqPopup.popTitle')}

- - + +
@@ -224,12 +361,18 @@ export default function PlanRequestPop(props) { {getMessage('stuff.planReqPopup.search.planStatName')}
- + +
@@ -158,7 +158,7 @@ export default function UserInfoModal({ userId, userInfoModal, setUserInfoModal {getMessage('myinfo.info.nameKana')}
- +
@@ -166,7 +166,7 @@ export default function UserInfoModal({ userId, userInfoModal, setUserInfoModal {getMessage('myinfo.info.name')}
- +
@@ -235,7 +235,7 @@ export default function UserInfoModal({ userId, userInfoModal, setUserInfoModal {getMessage('myinfo.info.category')}
- +
@@ -243,7 +243,7 @@ export default function UserInfoModal({ userId, userInfoModal, setUserInfoModal {getMessage('myinfo.info.tel')}
- +
@@ -251,7 +251,7 @@ export default function UserInfoModal({ userId, userInfoModal, setUserInfoModal {getMessage('myinfo.info.fax')}
- +
@@ -259,7 +259,7 @@ export default function UserInfoModal({ userId, userInfoModal, setUserInfoModal {getMessage('myinfo.info.mail')}
- +
diff --git a/src/hooks/object/useObjectBatch.js b/src/hooks/object/useObjectBatch.js index 7229f470..9ad8af5d 100644 --- a/src/hooks/object/useObjectBatch.js +++ b/src/hooks/object/useObjectBatch.js @@ -4,14 +4,7 @@ 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 { polygonToTurfPolygon, rectToPolygon, triangleToPolygon, pointsToTurfPolygon, setSurfaceShapePattern } from '@/util/canvas-util' import { useSwal } from '@/hooks/useSwal' import * as turf from '@turf/turf' import { QPolygon } from '@/components/fabric/QPolygon' @@ -211,11 +204,13 @@ export function useObjectBatch() { 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 width = dormerPlacement.widthRef.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 + const offsetWidthRef = dormerPlacement.offsetWidthRef.current.value === '' ? 0 : parseInt(dormerPlacement.offsetWidthRef.current.value) / 10 + const directionRef = dormerPlacement.directionRef.current - let dormer, dormerOffset, isDown, selectedSurface + let dormer, dormerOffset, isDown, selectedSurface, pentagonPoints, pentagonOffsetPoints console.log('dormerPlacement', dormerPlacement) @@ -229,8 +224,6 @@ export function useObjectBatch() { 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 @@ -305,16 +298,16 @@ export function useObjectBatch() { addCanvasMouseEventListener('mouse:up', (e) => { if (dormer) { - // const trianglePolygon = pointsToTurfPolygon(triangleToPolygon(dormer)) - // const selectedSurfacePolygon = polygonToTurfPolygon(selectedSurface) + const trianglePolygon = pointsToTurfPolygon(triangleToPolygon(dormer)) + const selectedSurfacePolygon = polygonToTurfPolygon(selectedSurface) - // //지붕 밖으로 그렸을때 - // if (!turf.booleanWithin(trianglePolygon, selectedSurfacePolygon)) { - // swalFire({ text: '개구를 배치할 수 없습니다.', icon: 'error' }) - // //일단 지워 - // deleteTempObjects() - // return - // } + //지붕 밖으로 그렸을때 + if (!turf.booleanWithin(trianglePolygon, selectedSurfacePolygon)) { + swalFire({ text: '개구를 배치할 수 없습니다.', icon: 'error' }) + //일단 지워 + deleteTempObjects() + return + } //각도 추가 let originAngle = 0 //기본 남쪽 @@ -387,6 +380,183 @@ export function useObjectBatch() { drawDirectionArrow(leftTriangle) drawDirectionArrow(rightTriangle) + isDown = false + initEvent() + } + }) + } else if (buttonAct === 4) { + const heightLength = height - (width / 2) * (pitch * 0.25) + //(동의길이 깊이)+출폭(깊이)-[(입력한 폭값)/2+출폭(폭)]*(0.25*입력한 寸) + const heightOffsetLength = height + offsetRef - (width / 2 + offsetWidthRef) * (pitch * 0.25) + + 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 + } + + pentagonPoints = [ + { x: pointer.x, y: pointer.y }, + { x: pointer.x - width / 2, y: pointer.y + (height - heightLength) }, + { x: pointer.x - width / 2, y: pointer.y + height }, + { x: pointer.x + width / 2, y: pointer.y + height }, + { x: pointer.x + width / 2, y: pointer.y + (height - heightLength) }, + ] + + pentagonOffsetPoints = [ + { x: pointer.x, y: pointer.y }, + { x: pointer.x - width / 2 - offsetWidthRef, y: pointer.y + height + offsetRef - heightOffsetLength }, + { x: pointer.x - width / 2 - offsetWidthRef, y: pointer.y + height + offsetRef }, + { x: pointer.x + width / 2 + offsetWidthRef, y: pointer.y + height + offsetRef }, + { x: pointer.x + width / 2 + offsetWidthRef, y: pointer.y + height + offsetRef - heightOffsetLength }, + ] + + dormer = new QPolygon(pentagonPoints, { + fill: 'white', + stroke: 'red', + strokeDashArray: [5, 5], + strokeWidth: 1, + 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 || offsetWidthRef > 0) { + dormerOffset = new QPolygon(pentagonOffsetPoints, { + fill: 'gray', + stroke: 'red', + strokeDashArray: [5, 5], + strokeWidth: 1, + 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' + } + + const offsetMode = offsetRef > 0 || offsetWidthRef > 0 ? 'offset' : 'normal' + let splitedPentagon = + offsetRef > 0 || offsetWidthRef > 0 + ? splitDormerPentagon(dormerOffset, directionRef, offsetMode) + : splitDormerPentagon(dormer, directionRef, offsetMode) + canvas?.remove(offsetRef > 0 || offsetWidthRef > 0 ? dormerOffset : dormer) + + if (offsetRef > 0) + dormer.set({ + name: dormerName, + stroke: 'black', + strokeWidth: 1, + strokeDashArray: [0], + }) //오프셋이 있을땐 같이 도머로 만든다 + + const leftPentagon = new QPolygon(splitedPentagon[0], { + fill: 'transparent', + stroke: 'red', + 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 rightPentagon = new QPolygon(splitedPentagon[1], { + fill: 'transparent', + stroke: 'red', + 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(leftPentagon) + canvas?.add(rightPentagon) + + //패턴 + setSurfaceShapePattern(leftPentagon) + setSurfaceShapePattern(rightPentagon) + //방향 + drawDirectionArrow(leftPentagon) + drawDirectionArrow(rightPentagon) + isDown = false initEvent() } @@ -408,8 +578,140 @@ export function useObjectBatch() { initEvent() //이벤트 초기화 } + const splitDormerTriangle = (triangle, direction) => { + const halfWidth = triangle.width / 2 + + let leftPoints = [] + let rightPoints = [] + + if (direction === 'down') { + leftPoints = [ + { x: triangle.left, y: triangle.top }, + { x: triangle.left - halfWidth, y: triangle.top + triangle.height }, + { x: triangle.left, y: triangle.top + triangle.height }, + ] + + rightPoints = [ + { x: triangle.left, y: triangle.top }, + { x: triangle.left, y: triangle.top + triangle.height }, + { x: triangle.left + halfWidth, y: triangle.top + triangle.height }, + ] + } else if (direction === 'up') { + leftPoints = [ + { x: triangle.left, y: triangle.top }, + { x: triangle.left - halfWidth, y: triangle.top - triangle.height }, + { x: triangle.left, y: triangle.top - triangle.height }, + ] + + rightPoints = [ + { x: triangle.left, y: triangle.top }, + { x: triangle.left, y: triangle.top - triangle.height }, + { x: triangle.left + halfWidth, y: triangle.top - triangle.height }, + ] + } else if (direction === 'left') { + leftPoints = [ + { x: triangle.left, y: triangle.top }, + { x: triangle.left - triangle.height, y: triangle.top - halfWidth }, + { x: triangle.left - triangle.height, y: triangle.top }, + ] + + rightPoints = [ + { x: triangle.left, y: triangle.top }, + { x: triangle.left - triangle.height, y: triangle.top }, + { x: triangle.left - triangle.height, y: triangle.top + halfWidth }, + ] + } else if (direction === 'right') { + leftPoints = [ + { x: triangle.left, y: triangle.top }, + { x: triangle.left + triangle.height, y: triangle.top }, + { x: triangle.left + triangle.height, y: triangle.top + triangle.height }, + ] + + rightPoints = [ + { x: triangle.left, y: triangle.top }, + { x: triangle.left + triangle.height, y: triangle.top }, + { x: triangle.left + triangle.height, y: triangle.top - triangle.height }, + ] + } + + return [leftPoints, rightPoints] + } + + const splitDormerPentagon = (pentagon, direction, offsetMode) => { + const points = pentagon.points + + console.log(pentagon.points) + + let leftPoints = [] + let rightPoints = [] + + if (direction === 'down') { + leftPoints = [ + { x: points[0].x, y: points[0].y }, + { x: points[1].x, y: points[1].y }, + { x: points[2].x, y: points[2].y }, + { x: points[0].x, y: points[3].y }, + ] + + rightPoints = [ + { x: points[0].x, y: points[0].y }, + { x: points[0].x, y: points[2].y }, + { x: points[3].x, y: points[3].y }, + { x: points[4].x, y: points[4].y }, + ] + } else if (direction === 'up') { + leftPoints = [ + { x: points[0].x, y: points[0].y }, + { x: points[1].x, y: points[0].y - (points[1].y - points[0].y) }, + { x: points[2].x, y: points[0].y - (points[2].y - points[0].y) }, + { x: points[0].x, y: points[0].y - (points[2].y - points[0].y) }, + ] + + rightPoints = [ + { x: points[0].x, y: points[0].y }, + { x: points[3].x, y: points[0].y - (points[1].y - points[0].y) }, + { x: points[3].x, y: points[0].y - (points[2].y - points[0].y) }, + { x: points[0].x, y: points[0].y - (points[2].y - points[0].y) }, + ] + } else if (direction === 'left') { + leftPoints = [ + { x: points[0].x, y: points[0].y }, + { x: points[0].x - (points[1].y - points[0].y), y: points[0].y - (points[0].x - points[1].x) }, + { x: points[0].x - (points[1].y - points[0].y) - (points[2].y - points[1].y), y: points[0].y - (points[0].x - points[1].x) }, + { x: points[0].x - (points[1].y - points[0].y) - (points[2].y - points[1].y), y: points[0].y }, + ] + + rightPoints = [ + { x: points[0].x, y: points[0].y }, + { x: points[0].x - (points[1].y - points[0].y), y: points[0].y + (points[0].x - points[1].x) }, + { x: points[0].x - (points[1].y - points[0].y) - (points[2].y - points[1].y), y: points[0].y + (points[0].x - points[1].x) }, + { x: points[0].x - (points[1].y - points[0].y) - (points[2].y - points[1].y), y: points[0].y }, + ] + } else if (direction === 'right') { + leftPoints = [ + { x: points[0].x, y: points[0].y }, + { x: points[0].x + (points[1].y - points[0].y), y: points[0].y + (points[0].x - points[1].x) }, + { x: points[0].x + (points[1].y - points[0].y) + (points[2].y - points[1].y), y: points[0].y + (points[0].x - points[1].x) }, + { x: points[0].x + (points[1].y - points[0].y) + (points[2].y - points[1].y), y: points[0].y }, + ] + + rightPoints = [ + { x: points[0].x, y: points[0].y }, + { x: points[0].x + (points[1].y - points[0].y), y: points[0].y - (points[0].x - points[1].x) }, + { x: points[0].x + (points[1].y - points[0].y) + (points[2].y - points[1].y), y: points[0].y - (points[0].x - points[1].x) }, + { x: points[0].x + (points[1].y - points[0].y) + (points[2].y - points[1].y), y: points[0].y }, + ] + } + + console.log(leftPoints, rightPoints) + + return [leftPoints, rightPoints] + } + return { applyOpeningAndShadow, applyDormers, + splitDormerTriangle, + splitDormerPentagon, } } diff --git a/src/hooks/option/useFirstOption.js b/src/hooks/option/useFirstOption.js new file mode 100644 index 00000000..4010b69e --- /dev/null +++ b/src/hooks/option/useFirstOption.js @@ -0,0 +1,23 @@ +import { useRecoilState, useRecoilValue } from 'recoil' +import { canvasState } from '@/store/canvasAtom' +import { useEffect } from 'react' +import { settingModalFirstOptionsState } from '@/store/settingAtom' + +export function useFirstOption() { + const canvas = useRecoilValue(canvasState) + + const [settingModalFirstOptions, setSettingModalFirstOptions] = useRecoilState(settingModalFirstOptionsState) + + useEffect(() => { + const option1 = settingModalFirstOptions.option1 + + canvas + .getObjects() + .filter((obj) => obj.name === '') + .forEach((obj) => { + obj.set({ visible: !obj.visible }) + }) + }, [settingModalFirstOptions]) + + return { settingModalFirstOptions, setSettingModalFirstOptions } +} diff --git a/src/hooks/roofcover/useAuxiliaryDrawing.js b/src/hooks/roofcover/useAuxiliaryDrawing.js index cb33f407..e5e79740 100644 --- a/src/hooks/roofcover/useAuxiliaryDrawing.js +++ b/src/hooks/roofcover/useAuxiliaryDrawing.js @@ -23,10 +23,11 @@ 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(setShowAuxiliaryModal) { @@ -76,6 +77,8 @@ export function useAuxiliaryDrawing(setShowAuxiliaryModal) { useEffect(() => { typeRef.current = type + clear() + addDocumentEventListener('keydown', document, keydown[type]) }, [type]) useEffect(() => { @@ -103,12 +106,8 @@ export function useAuxiliaryDrawing(setShowAuxiliaryModal) { } }, []) - useEffect(() => { - clear() - addDocumentEventListener('keydown', document, keydown[type]) - }, [type]) - const clear = () => { + addCanvasMouseEventListener('mouse:move', mouseMove) setLength1(0) setLength2(0) @@ -459,8 +458,9 @@ export function useAuxiliaryDrawing(setShowAuxiliaryModal) { } const mouseDown = (e) => { - addCanvasMouseEventListener('mouse:move', mouseMove) + canvas.renderAll() const pointer = getIntersectMousePoint(e) + console.log(pointer) mousePointerArr.current.push(pointer) if (mousePointerArr.current.length === 2) { @@ -538,59 +538,66 @@ export function useAuxiliaryDrawing(setShowAuxiliaryModal) { // 보조선 절삭 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) @@ -600,10 +607,15 @@ export function useAuxiliaryDrawing(setShowAuxiliaryModal) { * 일변전으로 돌아가기 */ const handleRollback = () => { - const lastLine = lineHistory.current.pop() - mousePointerArr.current = [] - canvas.remove(...canvas.getObjects().filter((obj) => obj.name === 'innerPoint')) + const innerPoint = canvas.getObjects().find((obj) => obj.name === 'innerPoint') + if (innerPoint) { + mousePointerArr.current = [] + canvas.remove(innerPoint) + canvas.renderAll() + return + } + const lastLine = lineHistory.current.pop() if (lastLine) { roofAdsorptionPoints.current = roofAdsorptionPoints.current.filter( (point) => point.x !== lastLine.intersectionPoint?.x && point.y !== lastLine.intersectionPoint?.y, @@ -621,10 +633,25 @@ export function useAuxiliaryDrawing(setShowAuxiliaryModal) { return } - const roofBases = canvas.getObjects().find((obj) => obj.name === 'roofBase') + const roofBases = canvas.getObjects().filter((obj) => obj.name === 'roofBase') const innerLines = [...lineHistory.current] - roofBases.innerLines = [...innerLines] + 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) { + line.attributes = { ...line.attributes, roofId: roofBase.id } + return true + } + }) + + roofBase.innerLines = [...roofInnerLines] + }) setShowAuxiliaryModal(false) } diff --git a/src/hooks/roofcover/useOuterLineWall.js b/src/hooks/roofcover/useOuterLineWall.js index 07dab81e..4b4987af 100644 --- a/src/hooks/roofcover/useOuterLineWall.js +++ b/src/hooks/roofcover/useOuterLineWall.js @@ -29,12 +29,20 @@ import { } from '@/store/outerLineAtom' import { calculateAngle } from '@/util/qpolygon-utils' import { fabric } from 'fabric' +import { QLine } from '@/components/fabric/QLine' +import { outlineDisplaySelector } from '@/store/settingAtom' //외벽선 그리기 export function useOuterLineWall(setShowOutlineModal) { const canvas = useRecoilValue(canvasState) - const { addCanvasMouseEventListener, addDocumentEventListener, removeAllMouseEventListeners, removeAllDocumentEventListeners, removeMouseEvent } = - useEvent() + const { + initEvent, + addCanvasMouseEventListener, + addDocumentEventListener, + removeAllMouseEventListeners, + removeAllDocumentEventListeners, + removeMouseEvent, + } = useEvent() const { getIntersectMousePoint } = useMouse() const { addLine, removeLine } = useLine() const { tempGridMode } = useTempGrid() @@ -46,6 +54,8 @@ export function useOuterLineWall(setShowOutlineModal) { const adsorptionRange = useRecoilValue(adsorptionRangeState) const interval = useRecoilValue(dotLineIntervalSelector) // 가로 세로 간격 + const isOutlineDisplay = useRecoilValue(outlineDisplaySelector) + const length1Ref = useRef(null) const length2Ref = useRef(null) const angle1Ref = useRef(null) @@ -75,6 +85,9 @@ export function useOuterLineWall(setShowOutlineModal) { addCanvasMouseEventListener('mouse:down', mouseDown) clear() + return () => { + initEvent() + } }, [verticalHorizontalMode, points, adsorptionPointAddMode, adsorptionPointMode, adsorptionRange, interval, tempGridMode]) useEffect(() => { @@ -215,11 +228,11 @@ export function useOuterLineWall(setShowOutlineModal) { 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 +251,30 @@ export function useOuterLineWall(setShowOutlineModal) { 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', + visible: isOutlineDisplay, }) - - 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', + visible: isOutlineDisplay, }) - 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', + visible: isOutlineDisplay, }) - if (guideLine1.length > 0) { - canvas?.add(guideLine1) - addLineText(guideLine1) - } - - canvas?.add(guideLine2) - - addLineText(guideLine2) - }*/ + } } }, [points]) @@ -284,6 +289,7 @@ export function useOuterLineWall(setShowOutlineModal) { y1: point1.y, x2: point2.x, y2: point2.y, + visible: isOutlineDisplay, }) } diff --git a/src/hooks/roofcover/useRoofAllocationSetting.js b/src/hooks/roofcover/useRoofAllocationSetting.js index 6533fe79..bc55c921 100644 --- a/src/hooks/roofcover/useRoofAllocationSetting.js +++ b/src/hooks/roofcover/useRoofAllocationSetting.js @@ -95,7 +95,11 @@ export function useRoofAllocationSetting(setShowRoofAllocationSettingModal) { const roofBases = canvas.getObjects().filter((obj) => obj.name === 'roofBase') const wallLines = canvas.getObjects().filter((obj) => obj.name === 'wallLine') roofBases.forEach((roofBase) => { - splitPolygonWithLines(roofBase) + try { + splitPolygonWithLines(roofBase) + } catch (e) { + return + } roofBase.innerLines.forEach((line) => { canvas.remove(line) diff --git a/src/hooks/roofcover/useRoofShapePassivitySetting.js b/src/hooks/roofcover/useRoofShapePassivitySetting.js index 217e71bc..f7b8b9fd 100644 --- a/src/hooks/roofcover/useRoofShapePassivitySetting.js +++ b/src/hooks/roofcover/useRoofShapePassivitySetting.js @@ -33,6 +33,11 @@ export function useRoofShapePassivitySetting(setShowRoofShapePassivitySettingMod 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 }, @@ -46,15 +51,20 @@ export function useRoofShapePassivitySetting(setShowRoofShapePassivitySettingMod 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) + + 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 @@ -66,10 +76,11 @@ export function useRoofShapePassivitySetting(setShowRoofShapePassivitySettingMod canvas?.renderAll() return () => { + handleLineToPolygon() canvas?.discardActiveObject() initEvent() } - }, []) + }, [isLoading]) useEffect(() => { const lines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') @@ -166,6 +177,14 @@ export function useRoofShapePassivitySetting(setShowRoofShapePassivitySettingMod } 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) => { @@ -176,13 +195,28 @@ export function useRoofShapePassivitySetting(setShowRoofShapePassivitySettingMod hideLine(line) }) - const wall = addPolygonByLines(lines, { name: 'wallLine', fill: 'transparent', stroke: 'black' }) + 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 (isFix.current) { + // 완료 한 경우에는 지붕까지 그려줌 + const roof = drawRoofPolygon(wall) + } - const roof = drawRoofPolygon(wall) canvas.renderAll() - setShowRoofShapePassivitySettingModal(false) } + return { handleSave, handleConfirm, buttons, type, setType, TYPES, offsetRef, pitchRef, handleRollback } } diff --git a/src/hooks/roofcover/useRoofShapeSetting.js b/src/hooks/roofcover/useRoofShapeSetting.js index 1722e287..016cd365 100644 --- a/src/hooks/roofcover/useRoofShapeSetting.js +++ b/src/hooks/roofcover/useRoofShapeSetting.js @@ -244,8 +244,13 @@ export function useRoofShapeSetting(setShowRoofShapeSettingModal) { } } - // 기존 wallLine 제거 - canvas?.remove(canvas.getObjects().filter((obj) => obj.name === 'wallLine')) + // 기존 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] diff --git a/src/hooks/roofcover/useWallLineOffsetSetting.js b/src/hooks/roofcover/useWallLineOffsetSetting.js index 6f65b58e..b84e9ef4 100644 --- a/src/hooks/roofcover/useWallLineOffsetSetting.js +++ b/src/hooks/roofcover/useWallLineOffsetSetting.js @@ -1,4 +1,4 @@ -import { canvasState } from '@/store/canvasAtom' +import { canvasState, currentObjectState } from '@/store/canvasAtom' import { useRecoilValue } from 'recoil' import { useEffect, useRef, useState } from 'react' import { useMessage } from '@/hooks/useMessage' @@ -21,6 +21,8 @@ export function useWallLineOffsetSetting(setShowWallLineOffsetSettingModal) { const arrow1Ref = useRef(null) const arrow2Ref = useRef(null) + const currentObject = useRecoilValue(currentObjectState) + const [isLoading, setIsLoading] = useState(false) const drawLine = (point1, point2, idx, direction = currentWallLineRef.current.direction) => { @@ -87,6 +89,7 @@ export function useWallLineOffsetSetting(setShowWallLineOffsetSettingModal) { if (!isLoading) { return } + canvas?.discardActiveObject() removeOuterLineEditCircle() addCanvasMouseEventListener('mouse:down', mouseDown) if (type === TYPES.WALL_LINE_EDIT) { @@ -94,6 +97,20 @@ export function useWallLineOffsetSetting(setShowWallLineOffsetSettingModal) { } }, [type]) + useEffect(() => { + canvas + .getObjects() + .filter((obj) => obj.name === 'outerLine') + .forEach((line) => { + line.set({ stroke: 'black' }) + }) + + if (currentObject?.name === 'outerLine') { + currentObject.set({ stroke: '#EA10AC' }) + canvas.renderAll() + } + }, [currentObject]) + const removeOuterLineEditCircle = () => { canvas.remove(...canvas.getObjects().filter((obj) => obj.name === 'outerLineEditCircleStart' || obj.name === 'outerLineEditCircleEnd')) } @@ -106,7 +123,6 @@ export function useWallLineOffsetSetting(setShowWallLineOffsetSettingModal) { } currentWallLineRef.current = e.target - console.log(currentWallLineRef.current.idx, currentWallLineRef.current.direction) if (type === TYPES.WALL_LINE_EDIT) { addCircleByLine(currentWallLineRef.current) } diff --git a/src/hooks/surface/usePlacementShapeDrawing.js b/src/hooks/surface/usePlacementShapeDrawing.js index 4df37618..123e991d 100644 --- a/src/hooks/surface/usePlacementShapeDrawing.js +++ b/src/hooks/surface/usePlacementShapeDrawing.js @@ -232,11 +232,11 @@ export function usePlacementShapeDrawing(setShowPlaceShapeDrawingModal) { 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] @@ -255,38 +255,27 @@ export function usePlacementShapeDrawing(setShowPlaceShapeDrawingModal) { 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]) diff --git a/src/hooks/useAdsorptionPoint.js b/src/hooks/useAdsorptionPoint.js index 49d49714..396db98c 100644 --- a/src/hooks/useAdsorptionPoint.js +++ b/src/hooks/useAdsorptionPoint.js @@ -2,13 +2,14 @@ import { useRecoilState, useRecoilValue } from 'recoil' import { adsorptionPointAddModeState, adsorptionPointModeState, adsorptionRangeState, canvasState } from '@/store/canvasAtom' import { fabric } from 'fabric' import { useMouse } from '@/hooks/useMouse' +import { gridDisplaySelector } from '@/store/settingAtom' export function useAdsorptionPoint() { const canvas = useRecoilValue(canvasState) const [adsorptionPointAddMode, setAdsorptionPointAddMode] = useRecoilState(adsorptionPointAddModeState) const [adsorptionPointMode, setAdsorptionPointMode] = useRecoilState(adsorptionPointModeState) const [adsorptionRange, setAdsorptionRange] = useRecoilState(adsorptionRangeState) - + const isGridDisplay = useRecoilValue(gridDisplaySelector) const { getIntersectMousePoint } = useMouse() const getAdsorptionPoints = () => { @@ -28,6 +29,7 @@ export function useAdsorptionPoint() { y: pointer.y, selectable: true, name: 'adsorptionPoint', + visible: isGridDisplay, }) canvas.add(adsorptionPoint) diff --git a/src/hooks/useAxios.js b/src/hooks/useAxios.js index c125c28a..f2e8add9 100644 --- a/src/hooks/useAxios.js +++ b/src/hooks/useAxios.js @@ -40,59 +40,59 @@ 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 promisePatch = async ({ url, data }) => { - return await getInstances(url).patch(url, data) + const promisePatch = async ({ url, data, option = {} }) => { + return await getInstances(url).patch(url, data, option) } - const del = async ({ url }) => { + 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, promisePatch, del, promiseDel } diff --git a/src/hooks/useEvent.js b/src/hooks/useEvent.js index 639b61b3..a84dea38 100644 --- a/src/hooks/useEvent.js +++ b/src/hooks/useEvent.js @@ -2,10 +2,11 @@ import { useEffect, useRef } from 'react' import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil' import { canvasState, canvasZoomState, currentMenuState, textModeState } from '@/store/canvasAtom' import { fabric } from 'fabric' -import { calculateDistance, distanceBetweenPoints, findClosestPoint } from '@/util/canvas-util' +import { calculateDistance, calculateIntersection, distanceBetweenPoints, findClosestPoint, polygonToTurfPolygon } from '@/util/canvas-util' import { useAdsorptionPoint } from '@/hooks/useAdsorptionPoint' import { useDotLineGrid } from '@/hooks/useDotLineGrid' import { useTempGrid } from '@/hooks/useTempGrid' +import { gridDisplaySelector } from '@/store/settingAtom' export function useEvent() { const canvas = useRecoilValue(canvasState) @@ -43,7 +44,6 @@ export function useEvent() { //default Event 추가 addCanvasMouseEventListener('mouse:move', defaultMouseMoveEvent) addCanvasMouseEventListener('mouse:out', defaultMouseOutEvent) - addDocumentEventListener('keydown', document, defaultKeyboardEvent) addDocumentEventListener('contextmenu', document, defaultContextMenuEvent) if (adsorptionPointAddMode) { addCanvasMouseEventListener('mouse:down', adsorptionPointAddModeStateEvent) @@ -152,6 +152,17 @@ export function useEvent() { } } + try { + const helpGuideLines = canvas.getObjects().filter((obj) => obj.name === 'helpGuideLine') + if (helpGuideLines.length === 2) { + const guideIntersectionPoint = calculateIntersection(helpGuideLines[0], helpGuideLines[1]) + + if (guideIntersectionPoint && distanceBetweenPoints(guideIntersectionPoint, pointer) <= adsorptionRange) { + arrivalPoint = guideIntersectionPoint + } + } + } catch (e) {} + const horizontalLine = new fabric.Line([-1 * canvas.width, arrivalPoint.y, 2 * canvas.width, arrivalPoint.y], { stroke: 'red', strokeWidth: 1, diff --git a/src/hooks/useLine.js b/src/hooks/useLine.js index f2941e33..a313d127 100644 --- a/src/hooks/useLine.js +++ b/src/hooks/useLine.js @@ -14,7 +14,7 @@ export const useLine = () => { fontFamily: fontFamily, }) - if (line.length === 0) { + if (line.length < 1) { return null } diff --git a/src/hooks/useMessage.js b/src/hooks/useMessage.js index f336f0b7..3a2fc316 100644 --- a/src/hooks/useMessage.js +++ b/src/hooks/useMessage.js @@ -1,14 +1,17 @@ +import { useEffect } from 'react' import { useRecoilValue } from 'recoil' -import { appMessageStore } from '@/store/localeAtom' +import { appMessageStore, globalLocaleStore } from '@/store/localeAtom' -// import KO from '@/locales/ko.json' -// import JA from '@/locales/ja.json' +import KO from '@/locales/ko.json' +import JA from '@/locales/ja.json' const SESSION_STORAGE_MESSAGE_KEY = 'QCAST_MESSAGE_STORAGE' export const useMessage = () => { // const globalLocale = useRecoilValue(globalLocaleState) - const appMessageState = useRecoilValue(appMessageStore) + // const appMessageState = useRecoilValue(appMessageStore) + const globalLocale = useRecoilValue(globalLocaleStore) + const appMessageState = globalLocale === 'ko' ? KO : JA const getMessage = (key, args = []) => { // if (sessionStorage.getItem(SESSION_STORAGE_MESSAGE_KEY) === null) { diff --git a/src/hooks/usePagination.js b/src/hooks/usePagination.js index 4fc3a959..7d76d293 100644 --- a/src/hooks/usePagination.js +++ b/src/hooks/usePagination.js @@ -1,4 +1,4 @@ -import { useState } from 'react' +import { useEffect, useState } from 'react' /** * 페이지네이션 훅 @@ -14,6 +14,10 @@ const usePagination = ({ pageNo = 1, pageSize = 10, pagePerBlock = 10, totalCoun setCurrentPage(page) } + useEffect(() => { + setCurrentPage(pageNo) + }, [pageNo]) + const pageGroup = Math.floor((currentPage - 1) / pagePerBlock) + 1 const totalPages = Math.ceil(totalCount / pageSize) const pages = Array.from({ length: totalPages }, (_, i) => i + 1) diff --git a/src/hooks/usePlan.js b/src/hooks/usePlan.js index 6d74cd93..b1273d82 100644 --- a/src/hooks/usePlan.js +++ b/src/hooks/usePlan.js @@ -1,14 +1,19 @@ +import { useEffect, useState } from 'react' import { useRecoilState } from 'recoil' +import { v4 as uuidv4 } from 'uuid' import { canvasState, currentCanvasPlanState, initCanvasPlansState, plansState } from '@/store/canvasAtom' import { useAxios } from '@/hooks/useAxios' import { useMessage } from '@/hooks/useMessage' import { useSwal } from '@/hooks/useSwal' export function usePlan() { + const [planNum, setPlanNum] = useState(0) + const [canvas, setCanvas] = useRecoilState(canvasState) const [currentCanvasPlan, setCurrentCanvasPlan] = useRecoilState(currentCanvasPlanState) - const [initCanvasPlans, setInitCanvasPlans] = useRecoilState(initCanvasPlansState) - const [plans, setPlans] = useRecoilState(plansState) + const [initCanvasPlans, setInitCanvasPlans] = useRecoilState(initCanvasPlansState) // DB에 저장된 plan + const [plans, setPlans] = useRecoilState(plansState) // 전체 plan (DB에 저장된 plan + 저장 안된 새로운 plan) + const { swalFire } = useSwal() const { getMessage } = useMessage() const { get, promisePost, promisePut, promiseDel } = useAxios() @@ -72,6 +77,9 @@ export function usePlan() { // }, 1000) } + /** + * 현재 캔버스에 그려진 데이터를 추출 + */ const currentCanvasData = () => { removeMouseLines() return addCanvas() @@ -81,8 +89,7 @@ export function usePlan() { * 실시간 캔버스 상태와 DB에 저장된 캔버스 상태를 비교하여 수정 여부를 판단 */ const checkModifiedCanvasPlan = () => { - removeMouseLines() - const canvasStatus = addCanvas() + const canvasStatus = currentCanvasData() const initPlanData = initCanvasPlans.find((plan) => plan.id === currentCanvasPlan.id) if (!initPlanData) { @@ -90,15 +97,10 @@ export function usePlan() { return JSON.parse(canvasStatus).objects.length > 0 } else { // 저장된 캔버스 - if (canvasStatus === initPlanData.canvasStatus) { - return false - } else { - // 각각 object들의 id 목록을 추출하여 비교 - const canvasObjsIds = getObjectIds(JSON.parse(canvasStatus).objects) - const dbObjsIds = getObjectIds(JSON.parse(initPlanData.canvasStatus).objects) - - return canvasObjsIds.length !== dbObjsIds.length || !canvasObjsIds.every((id, index) => id === dbObjsIds[index]) - } + // 각각 object들의 id 목록을 추출하여 비교 + const canvasObjsIds = getObjectIds(JSON.parse(canvasStatus).objects) + const dbObjsIds = getObjectIds(JSON.parse(initPlanData.canvasStatus).objects) + return canvasObjsIds.length !== dbObjsIds.length || !canvasObjsIds.every((id, index) => id === dbObjsIds[index]) } } const getObjectIds = (objects) => { @@ -112,9 +114,6 @@ export function usePlan() { * DB에 저장된 데이터를 canvas에서 사용할 수 있도록 포맷화 */ const dbToCanvasFormat = (cs) => { - // return JSON.stringify(cs.replace(/##/g, '"')) //.replace(/\\/g, ''))//.slice(1, -1)) - // JSON.stringify()를 사용하면 "가 \"로 바뀐다. 따라서, JSON.stringify를 제거 - // canvasToDbFormat()에서 \\의 상황을 없앴으므로 replace(/\\/g, '')를 제거 return cs.replace(/##/g, '"') } @@ -122,92 +121,24 @@ export function usePlan() { * canvas의 데이터를 DB에 저장할 수 있도록 포맷화 */ const canvasToDbFormat = (cs) => { - // return JSON.stringify(cs).replace(/"/g, '##') - // addCanvas()에서 JSON.stringify()를 거쳐서 나오는데, 또 감싸버려서 \가 \\로 된다. 따라서, JSON.stringify를 제거 return cs.replace(/"/g, '##') } /** - * 페이지 내 캔버스를 저장하는 함수 - * - * 1. 신규 저장 : POST - * param(body) : userId, objectNo, canvasStatus - * 2. 수정 저장 : PUT - * param(body) : id, canvasStatus + * 페이지 내 캔버스를 저장 */ const saveCanvas = async (userId) => { - removeMouseLines() - const canvasStatus = addCanvas() - - if (initCanvasPlans.some((plan) => plan.id === currentCanvasPlan.id)) { - // canvas 수정 - const planData = { - id: currentCanvasPlan.id, - canvasStatus: canvasToDbFormat(canvasStatus), - } - - return await promisePut({ url: '/api/canvas-management/canvas-statuses', data: planData }) - .then((res) => { - swalFire({ text: getMessage('common.message.save') }) - // console.log('[PUT] canvas-statuses res :::::::: %o', res) - setInitCanvasPlans((initCanvasPlans) => - initCanvasPlans.map((plan) => (plan.id === currentCanvasPlan.id ? { ...plan, canvasStatus: canvasStatus } : plan)), - ) - setPlans((plans) => plans.map((plan) => (plan.id === currentCanvasPlan.id ? { ...plan, canvasStatus: canvasStatus } : plan))) - }) - .catch((error) => { - swalFire({ text: error.message, icon: 'error' }) - // console.error('[PUT] canvas-statuses error :::::::: %o', error) - }) - } else { - // canvas 신규 등록 - const planData = { - userId: userId, - imageName: 'image_name', // api 필수항목이여서 임시로 넣음, 이후 삭제 필요 - objectNo: currentCanvasPlan.objectNo, - canvasStatus: canvasToDbFormat(canvasStatus), - } - - return await promisePost({ url: '/api/canvas-management/canvas-statuses', data: planData }) - .then((res) => { - swalFire({ text: getMessage('common.message.save') }) - // console.log('[POST] canvas-statuses response :::::::: %o', res) - setInitCanvasPlans((initCanvasPlans) => [ - ...initCanvasPlans, - { - id: res.data, - name: currentCanvasPlan.objectNo + '-' + res.data, - userId: userId, - canvasStatus: canvasStatus, - isNew: currentCanvasPlan.id, - }, - ]) - setPlans((plans) => - plans.map((plan) => - plan.id === currentCanvasPlan.id - ? { - ...plan, - id: res.data, - name: currentCanvasPlan.objectNo + '-' + res.data, - userId: userId, - canvasStatus: canvasStatus, - isNew: currentCanvasPlan.id, - } - : plan, - ), - ) - }) - .catch((error) => { - swalFire({ text: error.message, icon: 'error' }) - // console.error('[POST] canvas-statuses res error :::::::: %o', error) - }) - } + const canvasStatus = currentCanvasData() + initCanvasPlans.some((plan) => plan.id === currentCanvasPlan.id) + ? await putCanvasStatus(canvasStatus) + : await postCanvasStatus(userId, canvasStatus) } /** - * objectNo에 해당하는 canvas 목록을 조회하는 함수 + * objectNo에 해당하는 canvas 목록을 조회 */ const getCanvasByObjectNo = async (userId, objectNo) => { + // console.log(`[GET] objectNo: ${objectNo} / userId: ${userId}`) return get({ url: `/api/canvas-management/canvas-statuses/by-object/${objectNo}/${userId}` }).then((res) => res.map((item) => ({ id: item.id, @@ -220,27 +151,193 @@ export function usePlan() { } /** - * id에 해당하는 canvas 데이터를 삭제하는 함수 + * canvas 데이터를 추가 + */ + const postCanvasStatus = async (userId, canvasStatus) => { + const planData = { + userId: userId, + imageName: 'image_name', // api 필수항목이여서 임시로 넣음, 이후 삭제 필요 + objectNo: currentCanvasPlan.objectNo, + canvasStatus: canvasToDbFormat(canvasStatus), + } + await promisePost({ url: '/api/canvas-management/canvas-statuses', data: planData }) + .then((res) => { + swalFire({ text: getMessage('plan.message.save') }) + setInitCanvasPlans((initCanvasPlans) => [...initCanvasPlans, { id: res.data, canvasStatus: canvasStatus }]) + setPlans((plans) => + plans.map((plan) => + plan.id === currentCanvasPlan.id + ? { + ...plan, + id: res.data, + name: currentCanvasPlan.objectNo + '-' + res.data, + canvasStatus: canvasStatus, + } + : plan, + ), + ) + }) + .catch((error) => { + swalFire({ text: error.message, icon: 'error' }) + }) + } + + /** + * id에 해당하는 canvas 데이터를 수정 + */ + const putCanvasStatus = async (canvasStatus) => { + const planData = { + id: currentCanvasPlan.id, + canvasStatus: canvasToDbFormat(canvasStatus), + } + await promisePut({ url: '/api/canvas-management/canvas-statuses', data: planData }) + .then((res) => { + swalFire({ text: getMessage('plan.message.save') }) + setInitCanvasPlans((initCanvasPlans) => + initCanvasPlans.map((plan) => (plan.id === currentCanvasPlan.id ? { ...plan, canvasStatus: canvasStatus } : plan)), + ) + setPlans((plans) => plans.map((plan) => (plan.id === currentCanvasPlan.id ? { ...plan, canvasStatus: canvasStatus } : plan))) + }) + .catch((error) => { + swalFire({ text: error.message, icon: 'error' }) + }) + } + + /** + * id에 해당하는 canvas 데이터를 삭제 */ const delCanvasById = (id) => { return promiseDel({ url: `/api/canvas-management/canvas-statuses/by-id/${id}` }) } /** - * objectNo에 해당하는 canvas 데이터들을 삭제하는 함수 + * objectNo에 해당하는 canvas 데이터들을 삭제 */ const delCanvasByObjectNo = (objectNo) => { return promiseDel({ url: `/api/canvas-management/canvas-statuses/by-object/${objectNo}` }) } + /** + * plan 이동 + * 현재 plan의 작업상태를 확인, 저장 후 이동 + */ + const handleCurrentPlan = (userId, newCurrentId) => { + if (!currentCanvasPlan || currentCanvasPlan.id !== newCurrentId) { + if (currentCanvasPlan?.id && checkModifiedCanvasPlan()) { + swalFire({ + text: `${currentCanvasPlan.name} ` + getMessage('plan.message.confirm.save'), + type: 'confirm', + confirmFn: async () => { + await saveCanvas(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]) + + /** + * 새로운 plan 생성 + * 현재 plan의 데이터가 있을 경우 복제 여부를 확인 + */ + const handleAddPlan = (userId, objectNo) => { + JSON.parse(currentCanvasData()).objects.length > 0 + ? swalFire({ + text: `${currentCanvasPlan.name} ` + getMessage('plan.message.confirm.copy'), + type: 'confirm', + confirmFn: () => { + addPlan(userId, objectNo, currentCanvasData()) + }, + denyFn: () => { + addPlan(userId, objectNo) + }, + }) + : addPlan(userId, objectNo) + } + const addPlan = (userId, objectNo, canvasStatus = '') => { + const id = uuidv4() + const newPlan = { + id: id, + name: `Plan ${planNum + 1}`, + objectNo: objectNo, + userId: userId, + canvasStatus: canvasStatus, + } + setPlans([...plans, newPlan]) + handleCurrentPlan(userId, id) + setPlanNum(planNum + 1) + } + + /** + * plan 삭제 + */ + const handleDeletePlan = (e, id) => { + e.stopPropagation() // 이벤트 버블링 방지 + + if (initCanvasPlans.some((plan) => plan.id === id)) { + delCanvasById(id) + .then((res) => { + swalFire({ text: getMessage('plan.message.delete') }) + setInitCanvasPlans((initCanvasPlans) => initCanvasPlans.filter((plan) => plan.id !== id)) + setPlans((plans) => plans.filter((plan) => plan.id !== id)) + }) + .catch((error) => { + swalFire({ text: error.message, icon: 'error' }) + }) + } else { + setPlans((plans) => plans.filter((plan) => plan.id !== id)) + swalFire({ text: getMessage('plan.message.delete') }) + } + + // 삭제 후 last 데이터에 포커싱 + const lastPlan = plans.filter((plan) => plan.id !== id).at(-1) + if (!lastPlan) { + setPlanNum(0) + setCurrentCanvasPlan(null) + } else if (id !== lastPlan.id) { + updateCurrentPlan(lastPlan.id) + } + } + + /** + * plan 조회 + */ + const loadCanvasPlanData = (userId, objectNo) => { + getCanvasByObjectNo(userId, objectNo).then((res) => { + // console.log('canvas 목록 ', res) + if (res.length > 0) { + setInitCanvasPlans(res) + setPlans(res) + updateCurrentPlan(res.at(-1).id) // last 데이터에 포커싱 + setPlanNum(res.length) + } else { + addPlan(userId, objectNo) + } + }) + } + return { canvas, - removeMouseLines, - currentCanvasData, + plans, saveCanvas, - addCanvas, - checkModifiedCanvasPlan, - getCanvasByObjectNo, - delCanvasById, + handleCurrentPlan, + handleAddPlan, + handleDeletePlan, + loadCanvasPlanData, } } diff --git a/src/hooks/useTempGrid.js b/src/hooks/useTempGrid.js index 8d7bac42..30847a2f 100644 --- a/src/hooks/useTempGrid.js +++ b/src/hooks/useTempGrid.js @@ -1,11 +1,13 @@ import { canvasState, tempGridModeState } from '@/store/canvasAtom' import { useRecoilState, useRecoilValue } from 'recoil' import { gridColorState } from '@/store/gridAtom' +import { gridDisplaySelector } from '@/store/settingAtom' export function useTempGrid() { const canvas = useRecoilValue(canvasState) const gridColor = useRecoilValue(gridColorState) const [tempGridMode, setTempGridMode] = useRecoilState(tempGridModeState) + const isGridDisplay = useRecoilValue(gridDisplaySelector) const tempGridModeStateLeftClickEvent = (e) => { //임의 그리드 모드일 경우 let pointer = canvas.getPointer(e.e) @@ -22,6 +24,7 @@ export function useTempGrid() { strokeDashArray: [5, 2], opacity: 0.3, direction: 'vertical', + visible: isGridDisplay, name: 'tempGrid', }) @@ -48,6 +51,7 @@ export function useTempGrid() { strokeDashArray: [5, 2], opacity: 0.3, name: 'tempGrid', + visible: isGridDisplay, direction: 'horizontal', }) diff --git a/src/locales/client.js b/src/locales/client.js deleted file mode 100644 index c4679862..00000000 --- a/src/locales/client.js +++ /dev/null @@ -1,18 +0,0 @@ -'use client' - -import { createI18nClient } from 'next-international/client' - -export const { useI18n, useScopedI18n, I18nProviderClient, useChangeLocale, defineLocale, useCurrentLocale } = createI18nClient( - { - ko: () => import('./ko'), - ja: () => import('./ja'), - }, - { - // Uncomment to set base path - // basePath: '/base', - // Uncomment to use custom segment name - // segmentName: 'locale', - // Uncomment to set fallback locale - // fallbackLocale: en, - }, -) diff --git a/src/locales/ja.js b/src/locales/ja.js deleted file mode 100644 index 5ae1b171..00000000 --- a/src/locales/ja.js +++ /dev/null @@ -1,90 +0,0 @@ -console.log('Loaded JA') - -export default { - hello: 'こんにちは', - welcome: 'こんにちは {name}!', - locale: '現在のロケールは {locale} です。', - common: { - require: '필수', - }, - site: { - name: 'Q.CAST III', - sub_name: '태양광 발전 시스템 도면관리 사이트', - }, - login: { - login: 'Login', - init_password: { - btn: '비밀번호 초기화', - title: '비밀번호 초기화', - sub_title: '비밀번호를 초기화할 아이디와 이메일 주소를 입력해 주세요.', - }, - }, - join: { - title: 'Q.CAST3 로그인ID 발행 신청', - sub1: { - title: '판매대리점 정보', - comment: '※ 등록되는 리셀러의 회사 이름을 입력하십시오. (2차점은 「○○판매주식회사(2차점:××설비주식회사)」로 기입해 주세요.)', - storeQcastNm: '판매대리점명', - storeQcastNm_placeholder: '株式会社エネルギア・ソリューション・アンド・サービス(2次店:山口住機販売有限会社)', - storeQcastNmKana: '판매대리점명 후리가나', - storeQcastNmKana_placeholder: 'カブシキガイシャエネルギア・ソリューション・アン', - postCd: '우편번호', - postCd_placeholder: '숫자 7자리', - addr: '주소', - addr_placeholder: '전각50자이내', - telNo: '전화번호', - telNo_placeholder: '00-0000-0000', - fax: 'FAX 번호', - fax_placeholder: '00-0000-0000', - }, - sub2: { - title: '담당자 정보', - userNm: '담당자명', - userNmKana: '담당자명 후리가나', - userId: '신청 ID', - email: '이메일 주소', - telNo: '전화번호', - telNo_placeholder: '00-0000-0000', - fax: 'FAX 번호', - fax_placeholder: '00-0000-0000', - category: '부서명', - }, - sub3: { - title: '견적서 제출용 회사정보', - qtCompNm: '회사명', - qtPostCd: '우편번호', - qtPostCd_placeholder: '숫자 7자리', - qtAddr: '주소', - qtAddr_placeholder: '전각50자이내', - qtEmail: '이메일 주소', - qtTelNo: '전화번호', - qtTelNo_placeholder: '00-0000-0000', - qtFax: 'FAX 번호', - qtFax_placeholder: '00-0000-0000', - }, - btn: { - approval_request: 'ID 승인요청', - }, - complete: { - title: 'Q.CAST3 로그인ID 발행신청 완료', - contents: '※ 신청한 ID가 승인되면, 담당자 정보에 입력한 이메일 주소로 로그인 관련 안내 메일이 전송됩니다.', - email_comment: '담당자 이메일 주소', - email: 'test@naver.com', - }, - }, - stuff: { - gridHeader: { - lastEditDatetime: '갱신일시', - objectNo: '물건번호', - planTotCnt: '플랜 수', - objectName: '물건명', - saleStoreId: '대리점ID', - saleStoreName: '대리점명', - address: '물건주소', - dispCompanyName: '견적처', - receiveUser: '담당자', - specDate: '사양확인', - createDatetime: '등록일', - }, - }, -} diff --git a/src/locales/ja.json b/src/locales/ja.json index 92c592c8..b5d24361 100644 --- a/src/locales/ja.json +++ b/src/locales/ja.json @@ -253,6 +253,11 @@ "modal.placement.surface.setting.diagonal.length": "斜めの長さ", "modal.color.picker.title": "色の設定", "modal.color.picker.default.color": "基本色", + "plan.message.confirm.save": "PLAN을 저장하시겠습니까?", + "plan.message.confirm.copy": "PLAN을 복사하시겠습니까?", + "plan.message.confirm.delete": "PLAN을 삭제하시겠습니까?", + "plan.message.save": "저장되었습니다.", + "plan.message.delete": "삭제되었습니다.", "setting": "設定", "common.message.no.data": "No data", "common.message.no.dataDown": "ダウンロードするデータがありません", @@ -341,7 +346,7 @@ "common.message.writeToConfirm": "作成解除を実行しますか?", "common.message.password.init.success": "パスワード [{0}] に初期化されました。", "common.message.no.edit.save": "この文書は変更できません。", - "common.require": "필수", + "common.require": "必須", "commons.west": "立つ", "commons.east": "ドン", "commons.south": "立つ", @@ -386,6 +391,7 @@ "myinfo.message.save": "パスワードが変更されました。", "myinfo.message.password.error": "パスワードが間違っています。", "login": "ログイン", + "login.auto.page.text": "自動ログイン中です。", "login.id.save": "ID保存", "login.id.placeholder": "IDを入力してください。", "login.password.placeholder": "パスワードを入力してください。", @@ -503,6 +509,7 @@ "stuff.planReqPopup.search.period": "期間検索", "stuff.planReqPopup.search.schDateGbnS": "提出日", "stuff.planReqPopup.search.schDateGbnR": "受付日", + "stuff.planReqPopup.error.message1": "設計依頼を選択してください。", "stuff.search.title": "物件状況", "stuff.search.btn1": "物件登録", "stuff.search.btn2": "照会", @@ -517,6 +524,11 @@ "stuff.search.period": "期間検索", "stuff.search.schDateTypeU": "更新日", "stuff.search.schDateTypeR": "登録日", + "stuff.search.grid.title": "商品リスト", + "stuff.search.grid.all": "全体", + "stuff.search.grid.selected": "選択", + "stuff.search.grid.schSortTypeR": "最近の登録日", + "stuff.search.grid.schSortTypeU": "最近の更新日", "stuff.windSelectPopup.title": "風速選択", "stuff.windSelectPopup.table.selected": "選択", "stuff.windSelectPopup.table.windspeed": "風速", diff --git a/src/locales/ko.js b/src/locales/ko.js deleted file mode 100644 index c01b913c..00000000 --- a/src/locales/ko.js +++ /dev/null @@ -1,91 +0,0 @@ -console.log('Loaded KO') - -export default { - hello: '안녕', - welcome: '안녕 {name}!', - locale: '현재 로케일은 {locale}입니다.', - common: { - require: '필수', - }, - site: { - name: 'Q.CAST III', - sub_name: '태양광 발전 시스템 도면관리 사이트', - }, - login: { - login: '로그인', - init_password: { - btn: '비밀번호 초기화', - title: '비밀번호 초기화', - sub_title: '비밀번호를 초기화할 아이디와 이메일 주소를 입력해 주세요.', - complete_message: '비밀번호가 초기화 되었습니다. 초기화된 비밀번호는 아이디와 같습니다.', - }, - }, - join: { - title: 'Q.CAST3 로그인ID 발행 신청', - sub1: { - title: '판매대리점 정보', - comment: '※ 등록되는 리셀러의 회사 이름을 입력하십시오. (2차점은 「○○판매주식회사(2차점:××설비주식회사)」로 기입해 주세요.)', - storeQcastNm: '판매대리점명', - storeQcastNm_placeholder: '주식회사 에너지 기어 솔루션 앤 서비스 (2차점: 야마구치 주기 판매 유한회사)', - storeQcastNmKana: '판매대리점명 후리가나', - storeQcastNmKana_placeholder: '주식회사 에너지 기어 솔루션', - postCd: '우편번호', - postCd_placeholder: '숫자 7자리', - addr: '주소', - addr_placeholder: '전각50자이내', - telNo: '전화번호', - telNo_placeholder: '00-0000-0000', - fax: 'FAX 번호', - fax_placeholder: '00-0000-0000', - }, - sub2: { - title: '담당자 정보', - userNm: '담당자명', - userNmKana: '담당자명 후리가나', - userId: '신청 ID', - email: '이메일 주소', - telNo: '전화번호', - telNo_placeholder: '00-0000-0000', - fax: 'FAX 번호', - fax_placeholder: '00-0000-0000', - category: '부서명', - }, - sub3: { - title: '견적서 제출용 회사정보', - qtCompNm: '회사명', - qtPostCd: '우편번호', - qtPostCd_placeholder: '숫자 7자리', - qtAddr: '주소', - qtAddr_placeholder: '전각50자이내', - qtEmail: '이메일 주소', - qtTelNo: '전화번호', - qtTelNo_placeholder: '00-0000-0000', - qtFax: 'FAX 번호', - qtFax_placeholder: '00-0000-0000', - }, - btn: { - approval_request: 'ID 승인요청', - }, - complete: { - title: 'Q.CAST3 로그인ID 발행신청 완료', - contents: '※ 신청한 ID가 승인되면, 담당자 정보에 입력한 이메일 주소로 로그인 관련 안내 메일이 전송됩니다.', - email_comment: '담당자 이메일 주소', - email: 'test@naver.com', - }, - }, - stuff: { - gridHeader: { - lastEditDatetime: '갱신일시', - objectNo: '물건번호', - planTotCnt: '플랜 수', - objectName: '물건명', - saleStoreId: '대리점ID', - saleStoreName: '대리점명', - address: '물건주소', - dispCompanyName: '견적처', - receiveUser: '담당자', - specDate: '사양확인', - createDatetime: '등록일', - }, - }, -} diff --git a/src/locales/ko.json b/src/locales/ko.json index bcc618fb..d46633ea 100644 --- a/src/locales/ko.json +++ b/src/locales/ko.json @@ -258,6 +258,11 @@ "modal.placement.surface.setting.diagonal.length": "대각선 길이", "modal.color.picker.title": "색 설정", "modal.color.picker.default.color": "기본색상", + "plan.message.confirm.save": "PLAN을 저장하시겠습니까?", + "plan.message.confirm.copy": "PLAN을 복사하시겠습니까?", + "plan.message.confirm.delete": "PLAN을 삭제하시겠습니까?", + "plan.message.save": "저장되었습니다.", + "plan.message.delete": "삭제되었습니다.", "setting": "설정", "common.message.no.data": "No data", "common.message.no.dataDown": "No data to download", @@ -391,6 +396,7 @@ "myinfo.message.save": "비밀번호가 변경되었습니다.", "myinfo.message.password.error": "비밀번호가 틀렸습니다.", "login": "로그인", + "login.auto.page.text": "자동로그인 중 입니다.", "login.id.save": "ID Save", "login.id.placeholder": "아이디를 입력해주세요.", "login.password.placeholder": "비밀번호를 입력해주세요.", @@ -508,6 +514,7 @@ "stuff.planReqPopup.search.period": "기간검색", "stuff.planReqPopup.search.schDateGbnS": "제출일", "stuff.planReqPopup.search.schDateGbnR": "접수일", + "stuff.planReqPopup.error.message1": "설계의뢰를 선택해주세요.", "stuff.search.title": "물건현황", "stuff.search.btn1": "신규등록", "stuff.search.btn2": "조회", @@ -522,6 +529,11 @@ "stuff.search.period": "기간검색", "stuff.search.schDateTypeU": "갱신일", "stuff.search.schDateTypeR": "등록일", + "stuff.search.grid.title": "물건목록", + "stuff.search.grid.all": "전체", + "stuff.search.grid.selected": "선택", + "stuff.search.grid.schSortTypeR": "최근 등록일", + "stuff.search.grid.schSortTypeU": "최근 갱신일", "stuff.windSelectPopup.title": "풍속선택", "stuff.windSelectPopup.table.selected": "선택", "stuff.windSelectPopup.table.windspeed": "풍속", diff --git a/src/locales/server.js b/src/locales/server.js deleted file mode 100644 index 7bfe43f0..00000000 --- a/src/locales/server.js +++ /dev/null @@ -1,14 +0,0 @@ -import { createI18nServer } from 'next-international/server' - -export const { getI18n, getScopedI18n, getCurrentLocale, getStaticParams } = createI18nServer( - { - ko: () => import('./ko'), - ja: () => import('./ja'), - }, - { - // Uncomment to use custom segment name - // segmentName: 'locale', - // Uncomment to set fallback locale - // fallbackLocale: en, - }, -) diff --git a/src/store/canvasAtom.js b/src/store/canvasAtom.js index 7806b8f2..f3a81661 100644 --- a/src/store/canvasAtom.js +++ b/src/store/canvasAtom.js @@ -289,3 +289,8 @@ export const canGridOptionSeletor = selector({ return points.length === 0 || outerLineFix }, }) + +export const globalPitchState = atom({ + key: 'globalPitch', + default: 4, +}) diff --git a/src/store/planReqAtom.js b/src/store/planReqAtom.js index 717d506e..fddf1374 100644 --- a/src/store/planReqAtom.js +++ b/src/store/planReqAtom.js @@ -16,7 +16,7 @@ export const planReqSearchState = atom({ schStartDt: dayjs(new Date()).add(-3, 'month').format('YYYY-MM-DD'), //시작일 schEndDt: dayjs(new Date()).format('YYYY-MM-DD'), //종료일 startRow: 1, - endRow: 100, + endRow: 20, }, dangerouslyAllowMutability: true, }) diff --git a/src/store/settingAtom.js b/src/store/settingAtom.js index 6ec454be..8560c700 100644 --- a/src/store/settingAtom.js +++ b/src/store/settingAtom.js @@ -15,12 +15,12 @@ export const settingModalFirstOptionsState = atom({ { id: 9, column: 'totalDisplay', name: 'modal.canvas.setting.first.option.total', selected: false }, ], dimensionDisplay: [ - { id: 1, column: 'corridorDimension', name: 'modal.canvas.setting.first.option.corridor.dimension', selected: false }, + { id: 1, column: 'corridorDimension', name: 'modal.canvas.setting.first.option.corridor.dimension', selected: true }, { id: 2, column: 'realDimension', name: 'modal.canvas.setting.first.option.real.dimension', selected: false }, { id: 3, column: 'noneDimension', name: 'modal.canvas.setting.first.option.none.dimension', selected: false }, ], option2: [ - { id: 1, column: 'onlyBorder', name: 'modal.canvas.setting.first.option.border', selected: false }, + { id: 1, column: 'onlyBorder', name: 'modal.canvas.setting.first.option.border', selected: true }, { id: 2, column: 'lineHatch', name: 'modal.canvas.setting.first.option.line', selected: false }, { id: 3, column: 'allPainted', name: 'modal.canvas.setting.first.option.all', selected: false }, ], @@ -57,3 +57,102 @@ export const settingModalGridOptionsState = atom({ ], dangerouslyAllowMutability: true, }) + +// 디스플레이 설정 - 할당 표시 +export const allocDisplaySelector = selector({ + key: 'allocDisplaySelector', + get: ({ get }) => { + const settingModalFirstOptions = get(settingModalFirstOptionsState) + return settingModalFirstOptions.option1.find((option) => option.column === 'allocDisplay').selected + }, +}) + +// 디스플레이 설정 - 외벽선 표시 +export const outlineDisplaySelector = selector({ + key: 'outlineDisplaySelector', + get: ({ get }) => { + const settingModalFirstOptions = get(settingModalFirstOptionsState) + return settingModalFirstOptions.option1.find((option) => option.column === 'outlineDisplay').selected + }, +}) + +// 디스플레이 설정 - 그리드 표시 +export const gridDisplaySelector = selector({ + key: 'gridDisplaySelector', + get: ({ get }) => { + const settingModalFirstOptions = get(settingModalFirstOptionsState) + return settingModalFirstOptions.option1.find((option) => option.column === 'gridDisplay').selected + }, +}) + +// 디스플레이 설정 - 지붕선 표시 +export const roofLineDisplaySelector = selector({ + key: 'lineDisplaySelector', + get: ({ get }) => { + const settingModalFirstOptions = get(settingModalFirstOptionsState) + return settingModalFirstOptions.option1.find((option) => option.column === 'lineDisplay').selected + }, +}) + +// 디스플레이 설정 - 문자 표시 +export const wordDisplaySelector = selector({ + key: 'wordDisplaySelector', + get: ({ get }) => { + const settingModalFirstOptions = get(settingModalFirstOptionsState) + return settingModalFirstOptions.option1.find((option) => option.column === 'wordDisplay').selected + }, +}) + +// 디스플레이 설정 - 회로번호 표시 +export const circuitNumDisplaySelector = selector({ + key: 'circuitNumDisplaySelector', + get: ({ get }) => { + const settingModalFirstOptions = get(settingModalFirstOptionsState) + return settingModalFirstOptions.option1.find((option) => option.column === 'circuitNumDisplay').selected + }, +}) + +// 디스플레이 설정 - 흐름 방향 표시 +export const flowDisplaySelector = selector({ + key: 'flowDisplaySelector', + get: ({ get }) => { + const settingModalFirstOptions = get(settingModalFirstOptionsState) + return settingModalFirstOptions.option1.find((option) => option.column === 'flowDisplay').selected + }, +}) + +// 디스플레이 설정 - 가대 표시 +export const trestleDisplaySelector = selector({ + key: 'trestleDisplaySelector', + get: ({ get }) => { + const settingModalFirstOptions = get(settingModalFirstOptionsState) + return settingModalFirstOptions.option1.find((option) => option.column === 'trestleDisplay').selected + }, +}) + +// 디스플레이 설정 - 집계표 표시 +export const totalDisplaySelector = selector({ + key: 'totalDisplaySelector', + get: ({ get }) => { + const settingModalFirstOptions = get(settingModalFirstOptionsState) + return settingModalFirstOptions.option1.find((option) => option.column === 'totalDisplay').selected + }, +}) + +// 디스플레이 설정 - 치수 표시 +export const corridorDimensionSelector = selector({ + key: 'corridorDimensionSelector', + get: ({ get }) => { + const settingModalFirstOptions = get(settingModalFirstOptionsState) + return settingModalFirstOptions.dimensionDisplay.find((option) => option.selected) + }, +}) + +// 디스플레이 설정 - 화면 표시 +export const realDimensionSelector = selector({ + key: 'realDimensionSelector', + get: ({ get }) => { + const settingModalFirstOptions = get(settingModalFirstOptionsState) + return settingModalFirstOptions.dimensionDisplay.find((option) => option.selected) + }, +}) diff --git a/src/store/stuffAtom.js b/src/store/stuffAtom.js index ceb5def0..c47321ae 100644 --- a/src/store/stuffAtom.js +++ b/src/store/stuffAtom.js @@ -18,10 +18,6 @@ export const stuffSearchState = atom({ startRow: 1, endRow: 100, schSortType: 'R', //정렬조건 (R:최근등록일 U:최근수정일) - selObject: { - value: '', - label: '', - }, }, dangerouslyAllowMutability: true, }) diff --git a/src/styles/_modal.scss b/src/styles/_modal.scss index 3901ba0f..5a8167eb 100644 --- a/src/styles/_modal.scss +++ b/src/styles/_modal.scss @@ -1629,7 +1629,7 @@ $alert-color: #101010; min-height: 80px; background-color: #fff; } - + } // 치수선 설정 @@ -1655,24 +1655,33 @@ $alert-color: #101010; .form-box{ width: 100%; background-color: #fff; - padding: 10px 0 20px; + padding: 10px 15px 20px; .line-form{ position: relative; - width: 102px; - height: 40px; + display: flex; + flex-direction: column; + justify-content: flex-end; + min-width: 102px; + min-height: 40px; margin: 0 auto; - border-left: 1px dashed #101010; - border-right: 1px dashed #101010; - .line-font-box{ + + &::before{ + content: ''; position: absolute; - bottom: -3px; + bottom: 0px; left: 0; width: 100%; - text-align: center; + height: 40px; + border-left: 1px dashed #101010; + border-right: 1px dashed #101010; + } + .line-font-box{ .font{ display: block; - padding-bottom: 6px; + padding-bottom: 15px; color: #101010; + text-align: center; + line-height: 1; } .line{ position: relative; @@ -1687,7 +1696,7 @@ $alert-color: #101010; transform: translateY(-50%) rotate(45deg); left: 1px; width: 9px; - height: 9px; + height:+ 9px; border: 1px solid; border-color: inherit; border-top: none; diff --git a/src/styles/_submodal.scss b/src/styles/_submodal.scss index 5b18f74a..e5fae9fb 100644 --- a/src/styles/_submodal.scss +++ b/src/styles/_submodal.scss @@ -228,11 +228,19 @@ margin-bottom: 20px; } .design-request-grid{ - .design-request-grid-tit{ - font-size: 13px; - font-weight: 600; - color: #101010; - margin-bottom: 15px; + .design-request-count{ + display: flex; + align-items: center; + margin-bottom: 10px; + .design-request-grid-tit{ + font-size: 13px; + font-weight: 600; + color: #101010; + } + .select-wrap{ + margin-left: auto; + width: 80px; + } } } diff --git a/src/util/board-utils.js b/src/util/board-utils.js index a129c4d4..c41ba22a 100644 --- a/src/util/board-utils.js +++ b/src/util/board-utils.js @@ -8,8 +8,10 @@ export const handleFileDown = async (file) => { const params = new URLSearchParams({ encodeFileNo: file.encodeFileNo, }) + const options = { responseType: 'blob' } const apiUrl = `${url}?${params.toString()}` - await promiseGet({ url: apiUrl, responseType: 'blob' }) + + await promiseGet({ url: apiUrl, option: options }) .then((resultData) => { if (resultData) { const blob = new Blob([resultData.data], { type: resultData.headers['content-type'] || 'application/octet-stream' }) @@ -25,7 +27,7 @@ export const handleFileDown = async (file) => { } }) .catch((error) => { - alert(error.response.data.message) + alert('File does not exist.') }) } diff --git a/src/util/canvas-util.js b/src/util/canvas-util.js index 17c30f07..20b6ec66 100644 --- a/src/util/canvas-util.js +++ b/src/util/canvas-util.js @@ -744,67 +744,6 @@ export const polygonToTurfPolygon = (polygon) => { ) } -export const splitDormerTriangle = (triangle, direction) => { - const halfWidth = triangle.width / 2 - - let leftPoints = [] - let rightPoints = [] - let leftPointOffset = [] - let rightPointOffset = [] - - if (direction === 'down') { - leftPoints = [ - { x: triangle.left, y: triangle.top }, - { x: triangle.left - halfWidth, y: triangle.top + triangle.height }, - { x: triangle.left, y: triangle.top + triangle.height }, - ] - - rightPoints = [ - { x: triangle.left, y: triangle.top }, - { x: triangle.left, y: triangle.top + triangle.height }, - { x: triangle.left + halfWidth, y: triangle.top + triangle.height }, - ] - } else if (direction === 'up') { - leftPoints = [ - { x: triangle.left, y: triangle.top }, - { x: triangle.left - halfWidth, y: triangle.top - triangle.height }, - { x: triangle.left, y: triangle.top - triangle.height }, - ] - - rightPoints = [ - { x: triangle.left, y: triangle.top }, - { x: triangle.left, y: triangle.top - triangle.height }, - { x: triangle.left + halfWidth, y: triangle.top - triangle.height }, - ] - } else if (direction === 'left') { - leftPoints = [ - { x: triangle.left, y: triangle.top }, - { x: triangle.left - triangle.height, y: triangle.top - halfWidth }, - { x: triangle.left - triangle.height, y: triangle.top }, - ] - - rightPoints = [ - { x: triangle.left, y: triangle.top }, - { x: triangle.left - triangle.height, y: triangle.top }, - { x: triangle.left - triangle.height, y: triangle.top + halfWidth }, - ] - } else if (direction === 'right') { - leftPoints = [ - { x: triangle.left, y: triangle.top }, - { x: triangle.left + triangle.height, y: triangle.top }, - { x: triangle.left + triangle.height, y: triangle.top + triangle.height }, - ] - - rightPoints = [ - { x: triangle.left, y: triangle.top }, - { x: triangle.left + triangle.height, y: triangle.top }, - { x: triangle.left + triangle.height, y: triangle.top - triangle.height }, - ] - } - - return [leftPoints, rightPoints] -} - export const triangleToPolygon = (triangle) => { const points = [] const halfWidth = triangle.width / 2 @@ -853,7 +792,7 @@ export function setSurfaceShapePattern(polygon) { patternSourceCanvas.width = polygon.width * ratio patternSourceCanvas.height = polygon.height * ratio const ctx = patternSourceCanvas.getContext('2d') - const offset = roofStyle === 1 ? 0 : patternSize.width / 2 + let offset = roofStyle === 1 ? 0 : patternSize.width / 2 const rows = Math.floor(patternSourceCanvas.height / patternSize.height) const cols = Math.floor(patternSourceCanvas.width / patternSize.width) @@ -861,23 +800,46 @@ export function setSurfaceShapePattern(polygon) { 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() - + if (polygon.direction === 'east' || polygon.direction === 'west') { + offset = roofStyle === 1 ? 0 : patternSize.height / 2 for (let col = 0; col <= cols; col++) { - const x = col * patternSize.width + (row % 2 === 0 ? 0 : offset) - const yStart = row * patternSize.height - const yEnd = yStart + patternSize.height - + const x = col * patternSize.width + const yStart = 0 + const yEnd = patternSourceCanvas.height ctx.beginPath() ctx.moveTo(x, yStart) // 선 시작점 ctx.lineTo(x, yEnd) // 선 끝점 ctx.stroke() + + for (let row = 0; row <= rows; row++) { + const y = row * patternSize.height + (col % 2 === 0 ? 0 : offset) + const xStart = col * patternSize.width + const xEnd = xStart + patternSize.width + ctx.beginPath() + ctx.moveTo(xStart, y) // 선 시작점 + ctx.lineTo(xEnd, y) // 선 끝점 + ctx.stroke() + } + } + } else { + for (let row = 0; row <= rows; row++) { + const y = row * patternSize.height + + ctx.beginPath() + ctx.moveTo(0, y) // 선 시작점 + ctx.lineTo(patternSourceCanvas.width, y) // 선 끝점 + ctx.stroke() + + 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() + } } } diff --git a/yarn.lock b/yarn.lock index 1472f5ac..c8c53b57 100644 --- a/yarn.lock +++ b/yarn.lock @@ -273,7 +273,7 @@ resolved "https://registry.npmjs.org/@bedrock-layout/use-stateful-ref/-/use-stateful-ref-1.4.1.tgz" integrity sha512-4eKO2KdQEXcR5LI4QcxqlJykJUDQJWDeWYAukIn6sRQYoabcfI5kDl61PUi6FR6o8VFgQ8IEP7HleKqWlSe8SQ== -"@emotion/babel-plugin@^11.11.0", "@emotion/babel-plugin@^11.12.0": +"@emotion/babel-plugin@^11.12.0": version "11.12.0" resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.12.0.tgz#7b43debb250c313101b3f885eba634f1d723fcc2" integrity sha512-y2WQb+oP8Jqvvclh8Q55gLUyb7UFvgv7eJfsj7td5TToBrIUtPay2kMrZi4xjq9qw2vD0ZR5fSho0yqoFgX7Rw== @@ -290,7 +290,7 @@ source-map "^0.5.7" stylis "4.2.0" -"@emotion/cache@^11.11.0", "@emotion/cache@^11.13.0", "@emotion/cache@^11.4.0": +"@emotion/cache@^11.13.0", "@emotion/cache@^11.4.0": version "11.13.1" resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.13.1.tgz#fecfc54d51810beebf05bf2a161271a1a91895d7" integrity sha512-iqouYkuEblRcXmylXIwwOodiEK5Ifl7JcX7o6V4jI3iW4mLXX3dmt5xwBtIkJiQEXFAI+pC8X0i67yiPkH9Ucw== @@ -306,32 +306,11 @@ resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.9.2.tgz#ff9221b9f58b4dfe61e619a7788734bd63f6898b" integrity sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g== -"@emotion/is-prop-valid@^1.2.1": - version "1.3.1" - resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-1.3.1.tgz#8d5cf1132f836d7adbe42cf0b49df7816fc88240" - integrity sha512-/ACwoqx7XQi9knQs/G0qKvv5teDMhD7bXYns9N/wM8ah8iNb8jZ2uNO0YOgiq2o2poIvVtJS2YALasQuMSQ7Kw== - dependencies: - "@emotion/memoize" "^0.9.0" - "@emotion/memoize@^0.9.0": version "0.9.0" resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.9.0.tgz#745969d649977776b43fc7648c556aaa462b4102" integrity sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ== -"@emotion/react@11.11.0": - version "11.11.0" - resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.11.0.tgz#408196b7ef8729d8ad08fc061b03b046d1460e02" - integrity sha512-ZSK3ZJsNkwfjT3JpDAWJZlrGD81Z3ytNDsxw1LKq1o+xkmO5pnWfr6gmCC8gHEFf3nSSX/09YrG67jybNPxSUw== - dependencies: - "@babel/runtime" "^7.18.3" - "@emotion/babel-plugin" "^11.11.0" - "@emotion/cache" "^11.11.0" - "@emotion/serialize" "^1.1.2" - "@emotion/use-insertion-effect-with-fallbacks" "^1.0.1" - "@emotion/utils" "^1.2.1" - "@emotion/weak-memoize" "^0.3.1" - hoist-non-react-statics "^3.3.1" - "@emotion/react@^11.8.1": version "11.13.3" resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.13.3.tgz#a69d0de2a23f5b48e0acf210416638010e4bd2e4" @@ -346,7 +325,7 @@ "@emotion/weak-memoize" "^0.4.0" hoist-non-react-statics "^3.3.1" -"@emotion/serialize@^1.1.2", "@emotion/serialize@^1.2.0", "@emotion/serialize@^1.3.1": +"@emotion/serialize@^1.2.0", "@emotion/serialize@^1.3.1": version "1.3.2" resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.3.2.tgz#e1c1a2e90708d5d85d81ccaee2dfeb3cc0cccf7a" integrity sha512-grVnMvVPK9yUVE6rkKfAJlYZgo0cu3l9iMC77V7DW6E1DUIrU68pSEXRmFZFOFB1QFo57TncmOcvcbMDWsL4yA== @@ -362,38 +341,21 @@ resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.4.0.tgz#c9299c34d248bc26e82563735f78953d2efca83c" integrity sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg== -"@emotion/styled@11.11.0": - version "11.11.0" - resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-11.11.0.tgz#26b75e1b5a1b7a629d7c0a8b708fbf5a9cdce346" - integrity sha512-hM5Nnvu9P3midq5aaXj4I+lnSfNi7Pmd4EWk1fOZ3pxookaQTNew6bp4JaCBYM4HVFZF9g7UjJmsUmC2JlxOng== - dependencies: - "@babel/runtime" "^7.18.3" - "@emotion/babel-plugin" "^11.11.0" - "@emotion/is-prop-valid" "^1.2.1" - "@emotion/serialize" "^1.1.2" - "@emotion/use-insertion-effect-with-fallbacks" "^1.0.1" - "@emotion/utils" "^1.2.1" - "@emotion/unitless@^0.10.0": version "0.10.0" resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.10.0.tgz#2af2f7c7e5150f497bdabd848ce7b218a27cf745" integrity sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg== -"@emotion/use-insertion-effect-with-fallbacks@^1.0.1", "@emotion/use-insertion-effect-with-fallbacks@^1.1.0": +"@emotion/use-insertion-effect-with-fallbacks@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.1.0.tgz#1a818a0b2c481efba0cf34e5ab1e0cb2dcb9dfaf" integrity sha512-+wBOcIV5snwGgI2ya3u99D7/FJquOIniQT1IKyDsBmEgwvpxMNeS65Oib7OnE2d2aY+3BU4OiH+0Wchf8yk3Hw== -"@emotion/utils@^1.2.1", "@emotion/utils@^1.4.0", "@emotion/utils@^1.4.1": +"@emotion/utils@^1.4.0", "@emotion/utils@^1.4.1": version "1.4.1" resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.4.1.tgz#b3adbb43de12ee2149541c4f1337d2eb7774f0ad" integrity sha512-BymCXzCG3r72VKJxaYVwOXATqXIZ85cuvg0YOUDxMGNrKc1DJRZk8MgV5wyXRyEayIMd4FuXJIUgTBXvDNW5cA== -"@emotion/weak-memoize@^0.3.1": - version "0.3.1" - resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz#d0fce5d07b0620caa282b5131c297bb60f9d87e6" - integrity sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww== - "@emotion/weak-memoize@^0.4.0": version "0.4.0" resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz#5e13fac887f08c44f76b0ccaf3370eb00fec9bb6" @@ -5955,14 +5917,6 @@ react-draggable@^4.4.6: clsx "^1.1.1" prop-types "^15.8.1" -react-dropdown-select@^4.11.3: - version "4.11.3" - resolved "https://registry.yarnpkg.com/react-dropdown-select/-/react-dropdown-select-4.11.3.tgz#b23b8906f3bedc9d6a1a2125af936b34d4057158" - integrity sha512-/mOGSqqhmKsxxrmotLM+qn1Ss3nxGN6QnYusyQ7f0wizsWrc7ZmbcZhGRtwkJwpL6JYDQVTn19EYxJU1XfXrDA== - dependencies: - "@emotion/react" "11.11.0" - "@emotion/styled" "11.11.0" - react-hook-form@^7.53.0: version "7.53.0" resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.53.0.tgz#3cf70951bf41fa95207b34486203ebefbd3a05ab"