diff --git a/docs/dictionary.txt b/docs/dictionary.txt index 268cde7a..94bfdb49 100644 --- a/docs/dictionary.txt +++ b/docs/dictionary.txt @@ -28,3 +28,4 @@ Allpainted : allPainted 치수선: dimensionLine 복도치수: planeSize 실제치수: actualSize +모듈설치면: moduleSetupSurface \ No newline at end of file diff --git a/public/static/images/canvas/side_icon10.svg b/public/static/images/canvas/side_icon10.svg new file mode 100644 index 00000000..d7eba9ec --- /dev/null +++ b/public/static/images/canvas/side_icon10.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/app/management/ManagementProvider.js b/src/app/management/ManagementProvider.js new file mode 100644 index 00000000..197b30c0 --- /dev/null +++ b/src/app/management/ManagementProvider.js @@ -0,0 +1,20 @@ +'ues client' + +import { createContext, useEffect, useState } from 'react' + +export const ManagementContext = createContext({ + managementState: {}, + setManagementState: () => {}, +}) + +const ManagementProvider = ({ children }) => { + const [managementState, setManagementState] = useState({}) + + useEffect(() => { + console.log('🚀 ~ managementState:', managementState) + }, [managementState]) + + return {children} +} + +export default ManagementProvider diff --git a/src/app/management/layout.js b/src/app/management/layout.js new file mode 100644 index 00000000..b0e031c7 --- /dev/null +++ b/src/app/management/layout.js @@ -0,0 +1,7 @@ +'use client' + +import ManagementProvider from './ManagementProvider' + +export default function ManagementLayout({ children }) { + return {children} +} diff --git a/src/common/common.js b/src/common/common.js index d0016af1..02a953fe 100644 --- a/src/common/common.js +++ b/src/common/common.js @@ -61,6 +61,8 @@ export const LINE_TYPE = { DEFAULT: 'default', EAVES: 'eaves', GABLE: 'gable', + GABLE_LEFT: 'gableLeft', //케라바 왼쪽 + GABLE_RIGHT: 'gableRight', //케라바 오른쪽 WALL: 'wall', HIPANDGABLE: 'hipAndGable', JERKINHEAD: 'jerkinhead', @@ -74,30 +76,20 @@ export const LINE_TYPE = { HIP: 'hip', RIDGE: 'ridge', GABLE: 'gable', - VALLEY: 'valley', VERGE: 'verge', + ONESIDE_FLOW_RIDGE: 'onesideFlowRidge', //한쪽흐름 용마루 + YOSEMUNE: 'yosemune', //요세무네 + VALLEY: 'valley', //골짜기 + L_ABANDON_VALLEY: 'lAbandonValley', //l의버림계곡 + MANSARD: 'mansard', //맨사드 + WALL_COLLECTION: 'wallCollection', //벽취합 + WALL_COLLECTION_TYPE: 'wallCollectionType', //벽취합(형) + WALL_COLLECTION_FLOW: 'wallCollectionFlow', //벽취합(흐름) + WALL_COLLECTION_FLOW_LEFT: 'wallCollectionFlowLeft', //벽취합(흐름 왼쪽) + WALL_COLLECTION_FLOW_RIGHT: 'wallCollectionFlowRight', //벽취합(흐름 오른쪽) }, } -export const LineType = { - EAVES: 'eaves', // 처마 - RIDGE: 'ridge', // 용마루.... - YOSEMUNE: 'yosemune', //요세무네 - ONESIDE_FLOW_RIDGE: 'onesideFlowRidge', //한쪽흐름 용마루 - WALL_COLLECTION: 'wallCollection', //벽취합 - WALL_COLLECTION_TYPE: 'wallCollectionType', //벽취합(형) - WALL_COLLECTION_FLOW: 'wallCollectionFlow', //벽취합(흐름) - WALL_COLLECTION_FLOW_LEFT: 'wallCollectionFlowLeft', //벽취합(흐름 왼쪽) - WALL_COLLECTION_FLOW_RIGHT: 'wallCollectionFlowRight', //벽취합(흐름 오른쪽) - KERABA: 'keraba', //케라바 - KERABA_LEFT: 'kerabaLeft', //케라바 왼쪽 - KERABA_RIGHT: 'kerabaRight', //케라바 오른쪽 - VALLEY: 'valley', //골짜기 - L_ABANDON_VALLEY: 'lAbandonValley', //l의버림계곡 - MANSARD: 'mansard', //맨사드 - NO_SETTING: 'noSetting', //설정없음 -} - // 오브젝트 배치 > 개구배치, 그림자배치 export const BATCH_TYPE = { OPENING: 'opening', @@ -119,6 +111,7 @@ export const POLYGON_TYPE = { ROOF: 'roof', WALL: 'wall', TRESTLE: 'trestle', + MODULE_SETUP_SURFACE: 'moduleSetupSurface', } export const SAVE_KEY = [ @@ -161,6 +154,13 @@ export const SAVE_KEY = [ 'groupId', 'planeSize', 'actualSize', + 'surfaceId', + 'lines', + 'offset', + 'arrow', + 'surfaceCompass', + 'moduleCompass', + 'isFixed', ] export const OBJECT_PROTOTYPE = [fabric.Line.prototype, fabric.Polygon.prototype, fabric.Triangle.prototype] diff --git a/src/components/Main.jsx b/src/components/Main.jsx index 11046eed..76afea76 100644 --- a/src/components/Main.jsx +++ b/src/components/Main.jsx @@ -1,9 +1,8 @@ 'use client' -import React, { useEffect, useState } from 'react' +import { useEffect, useState, useContext } from 'react' import { useRouter } from 'next/navigation' import { useRecoilState, useRecoilValue } from 'recoil' -import { sessionStore } from '@/store/commonAtom' import { useAxios } from '@/hooks/useAxios' import { globalLocaleStore } from '@/store/localeAtom' import MainContents from './main/MainContents' @@ -12,8 +11,10 @@ import { stuffSearchState } from '@/store/stuffAtom' import '@/styles/contents.scss' import ChangePasswordPop from './main/ChangePasswordPop' import { searchState } from '@/store/boardAtom' +import { SessionContext } from '@/app/SessionProvider' + export default function MainPage() { - const sessionState = useRecoilValue(sessionStore) + const { session } = useContext(SessionContext) const globalLocaleState = useRecoilValue(globalLocaleStore) @@ -33,14 +34,14 @@ export default function MainPage() { const [searchForm, setSearchForm] = useRecoilState(searchState) useEffect(() => { - if (sessionState.pwdInitYn === 'Y') { + if (session.pwdInitYn === 'Y') { fetchObjectList() } - }, [sessionState]) + }, [session]) const fetchObjectList = async () => { try { - const apiUrl = `/api/main-page/object/${sessionState?.storeId}/list` + const apiUrl = `/api/main-page/object/${session?.storeId}/list` await promiseGet({ url: apiUrl, }).then((res) => { @@ -95,7 +96,7 @@ export default function MainPage() { return ( <> - {(sessionState?.pwdInitYn !== 'N' && ( + {(session?.pwdInitYn !== 'N' && ( <>
diff --git a/src/components/auth/Login.jsx b/src/components/auth/Login.jsx index 283d7d99..5418dec7 100644 --- a/src/components/auth/Login.jsx +++ b/src/components/auth/Login.jsx @@ -83,7 +83,7 @@ export default function Login() { /////////////////////////////////////////////////////////// // 임시 로그인 처리 setSession({ - userId: 'NEW016610', + userId: 'NEW0166102', saleStoreId: null, name: null, mail: null, @@ -101,7 +101,7 @@ export default function Login() { custCd: '100000', }) setSessionState({ - userId: 'NEW016610', + userId: 'NEW0166102', saleStoreId: null, name: null, mail: null, diff --git a/src/components/common/font/FontSetting.jsx b/src/components/common/font/FontSetting.jsx index a658871d..b48f3882 100644 --- a/src/components/common/font/FontSetting.jsx +++ b/src/components/common/font/FontSetting.jsx @@ -3,76 +3,69 @@ import QSelectBox from '@/components/common/select/QSelectBox' import { usePopup } from '@/hooks/usePopup' import { useState } from 'react' import { useMessage } from '@/hooks/useMessage' -import { useRecoilState, useRecoilValue } from 'recoil' -import { fontSelector, globalFontAtom } from '@/store/fontAtom' +import { useRecoilValue } from 'recoil' import { contextPopupPositionState } from '@/store/popupAtom' const fonts = [ - { name: 'MS PGothic', value: 'MS PGothic' }, - { name: '@Yu Gothic', value: '@Yu Gothic' }, - { name: 'Yu Gothic', value: 'Yu Gothic' }, - { name: '@Yu Gothic UI', value: '@Yu Gothic UI' }, - { name: 'Yu Gothic UI', value: 'Yu Gothic UI' }, + { id: 1, name: 'MS PGothic', value: 'MS PGothic' }, + { id: 2, name: '@Yu Gothic', value: '@Yu Gothic' }, + { id: 3, name: 'Yu Gothic', value: 'Yu Gothic' }, + { id: 4, name: '@Yu Gothic UI', value: '@Yu Gothic UI' }, + { id: 5, name: 'Yu Gothic UI', value: 'Yu Gothic UI' }, + 3, ] const fontSizes = [ ...Array.from({ length: 4 }).map((_, index) => { - return { name: index + 8, value: index + 8 } + return { id: index + 8, name: index + 8, value: index + 8 } }), ...Array.from({ length: 9 }).map((_, index) => { - return { name: (index + 6) * 2, value: (index + 6) * 2 } + return { id: (index + 6) * 2, name: (index + 6) * 2, value: (index + 6) * 2 } }), - { name: 36, value: 36 }, - { name: 48, value: 48 }, - { name: 72, value: 72 }, + { id: 36, name: 36, value: 36 }, + { id: 48, name: 48, value: 48 }, + { id: 72, name: 72, value: 72 }, ] export default function FontSetting(props) { const contextPopupPosition = useRecoilValue(contextPopupPositionState) - const { id, setIsShow, pos = contextPopupPosition, type, isConfig = false } = props + const { id, setIsShow, pos = contextPopupPosition, type, isConfig = false, onSave, font } = props const { getMessage } = useMessage() const { closePopup } = usePopup() - const [globalFont, setGlobalFont] = useRecoilState(globalFontAtom) - const currentFont = useRecoilValue(fontSelector(type)) - - const [selectedFont, setSelectedFont] = useState(currentFont.fontFamily) - const [selectedFontWeight, setSelectedFontWeight] = useState(currentFont.fontWeight) - const [selectedFontSize, setSelectedFontSize] = useState(currentFont.fontSize) - const [selectedFontColor, setSelectedFontColor] = useState(currentFont.fontColor) + const [selectedFont, setSelectedFont] = useState(font.fontFamily) + const [selectedFontWeight, setSelectedFontWeight] = useState(font.fontWeight) + const [selectedFontSize, setSelectedFontSize] = useState(font.fontSize) + const [selectedFontColor, setSelectedFontColor] = useState(font.fontColor) const fontOptions = [ - { name: getMessage('font.style.normal'), value: 'normal' }, - { name: getMessage('font.style.italic'), value: 'italic' }, + { id: 'normal', name: getMessage('font.style.normal'), value: 'normal' }, + { id: 'italic', name: getMessage('font.style.italic'), value: 'italic' }, { + id: 'bold', name: getMessage('font.style.bold'), value: 'bold', }, - { name: getMessage('font.style.bold.italic'), value: 'boldAndItalic' }, + { id: 'boldAndItalic', name: getMessage('font.style.bold.italic'), value: 'boldAndItalic' }, ] const fontColors = [ - { name: getMessage('color.black'), value: 'black' }, - { name: getMessage('color.red'), value: 'red' }, - { name: getMessage('color.blue'), value: 'blue' }, - { name: getMessage('color.gray'), value: 'gray' }, - { name: getMessage('color.yellow'), value: 'yellow' }, - { name: getMessage('color.green'), value: 'green' }, - { name: getMessage('color.pink'), value: 'pink' }, - { name: getMessage('color.gold'), value: 'gold' }, - { name: getMessage('color.darkblue'), value: 'darkblue' }, + { id: 'black', name: getMessage('color.black'), value: 'black' }, + { id: 'red', name: getMessage('color.red'), value: 'red' }, + { id: 'blue', name: getMessage('color.blue'), value: 'blue' }, + { id: 'gray', name: getMessage('color.gray'), value: 'gray' }, + { id: 'yellow', name: getMessage('color.yellow'), value: 'yellow' }, + { id: 'green', name: getMessage('color.green'), value: 'green' }, + { id: 'pink', name: getMessage('color.pink'), value: 'pink' }, + { id: 'gold', name: getMessage('color.gold'), value: 'gold' }, + { id: 'darkblue', name: getMessage('color.darkblue'), value: 'darkblue' }, ] const handleSaveBtn = () => { - setGlobalFont((prev) => { - return { - ...prev, - [type]: { - fontFamily: selectedFont, - fontSize: selectedFontSize, - fontColor: selectedFontColor, - fontWeight: selectedFontWeight, - }, - } + onSave({ + fontFamily: selectedFont, + fontSize: selectedFontSize, + fontColor: selectedFontColor, + fontWeight: selectedFontWeight, }) if (setIsShow) setIsShow(false) - closePopup(id) + closePopup(id, isConfig) } return ( @@ -125,7 +118,8 @@ export default function FontSetting(props) { style={{ fontFamily: selectedFont?.value ?? '', fontSize: selectedFontSize?.value ?? '12px', - fontWeight: '400', + fontStyle: selectedFontWeight?.value.toLowerCase().includes('italic') ? 'italic' : 'normal', + fontWeight: selectedFontWeight?.value.toLowerCase().includes('bold') ? 'bold' : 'normal', color: selectedFontColor?.value ?? 'black', }} > diff --git a/src/components/common/select/QSelectBox.jsx b/src/components/common/select/QSelectBox.jsx index df745d64..3da9f30b 100644 --- a/src/components/common/select/QSelectBox.jsx +++ b/src/components/common/select/QSelectBox.jsx @@ -18,8 +18,8 @@ export default function QSelectBox({ title = '', options, onChange, value, disab
{} : () => setOpenSelect(!openSelect)}>

{selected}

- -
@@ -696,59 +851,120 @@ export default function Estimate({ params }) {
- {getMessage('estimate.detail.itemTableHeader.col1')} - {getMessage('estimate.detail.itemTableHeader.col2')} - {getMessage('estimate.detail.itemTableHeader.col3')} - {getMessage('estimate.detail.itemTableHeader.col4')} - {getMessage('estimate.detail.itemTableHeader.col5')} - {getMessage('estimate.detail.itemTableHeader.col6')} - {getMessage('estimate.detail.itemTableHeader.col7')} + {getMessage('estimate.detail.itemTableHeader.dispOrder')} + {getMessage('estimate.detail.itemTableHeader.itemId')} + {getMessage('estimate.detail.itemTableHeader.itemNo')} + {getMessage('estimate.detail.itemTableHeader.amount')} + {getMessage('estimate.detail.itemTableHeader.unit')} + {getMessage('estimate.detail.itemTableHeader.salePrice')} + {getMessage('estimate.detail.itemTableHeader.saleTotPrice')} - - -
- - -
- - 100 - -
-
{/* -
- - セット - -
-
- -
-
- -
-
- - 5,561,000 - + {state?.itemList.length > 0 && + state.itemList.map((item, index) => { + return ( + + +
+ onChangeSelect(item.dispOrder)} + checked={selection.has(item.dispOrder) ? true : false} + /> + +
+ + {item?.dispOrder * 100} + +
+
+ { + //onChangeDisplayItem참고 + //itemChangeFlg = 1, partAdd = 0 셋팅 + console.log('수량변경::::::::', e.target.value) + }} + /> +
+ + {item.unit} + +
+
+ { + //onChangeDisplayItem참고 + //itemChangeFlg, partAdd 받아온 그대로 + console.log('단가변경:::::::', e.target.value) + }} + /> +
+ {/*
+ OPEN아이콘 처리 +
*/} +
+ + {convertNumberToPriceDecimal(item?.saleTotPrice)} + + ) + })}
@@ -758,6 +974,13 @@ export default function Estimate({ params }) {
{/* 기본정보끝 */} + {productFeaturesPopupOpen && ( + + )} ) } diff --git a/src/components/estimate/popup/ProductFeaturesPop.jsx b/src/components/estimate/popup/ProductFeaturesPop.jsx new file mode 100644 index 00000000..3940065d --- /dev/null +++ b/src/components/estimate/popup/ProductFeaturesPop.jsx @@ -0,0 +1,67 @@ +'use client' +import { useEffect, useState } from 'react' +import { useMessage } from '@/hooks/useMessage' +export default function ProductFeaturesPop({ specialNoteList, showProductFeatureData, setProductFeaturesPopupOpen }) { + const [showSpecialNoteList, setShowSpecialNoteList] = useState([]) + const { getMessage } = useMessage() + + useEffect(() => { + let pushData = [] + specialNoteList.map((row) => { + let option = showProductFeatureData.split('、') + option.map((row2) => { + if (row.code === row2) { + pushData.push(row) + } + }) + }) + setShowSpecialNoteList(pushData) + }, [specialNoteList]) + + return ( +
+
+
+
+

{getMessage('estimate.detail.productFeaturesPopup.title')}

+ +
+
+
+
+ {showSpecialNoteList.length > 0 && + showSpecialNoteList.map((row) => { + return ( +
+
{row.codeNm}
+
+
+ ) + })} +
+
+
+ +
+
+
+
+
+ ) +} diff --git a/src/components/fabric/QPolygon.js b/src/components/fabric/QPolygon.js index a248412e..c5a08cf1 100644 --- a/src/components/fabric/QPolygon.js +++ b/src/components/fabric/QPolygon.js @@ -131,15 +131,14 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { this.on('selected', () => { Object.keys(this.controls).forEach((controlKey) => { - if (controlKey !== 'ml' && controlKey !== 'mr') { - this.setControlVisible(controlKey, false) - } + this.setControlVisible(controlKey, false) }) + this.set({ hasBorders: false }) }) this.on('removed', () => { // const children = getAllRelatedObjects(this.id, this.canvas) - const children = this.canvas.getObjects().filter((obj) => obj.parentId === this.id && obj.name === 'lengthText') + const children = this.canvas.getObjects().filter((obj) => obj.parentId === this.id) children.forEach((child) => { this.canvas.remove(child) }) @@ -167,6 +166,10 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { }, initLines() { + // if (this.lines.length > 0) { + // return + // } + this.lines = [] this.points.forEach((point, i) => { @@ -189,6 +192,12 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { }) }, + containsPoint: function (point) { + const isInside = this.inPolygon(point) + this.set('selectable', isInside) + return isInside + }, + // 보조선 그리기 drawHelpLine() { // drawHelpLineInHexagon(this, pitch) @@ -263,9 +272,11 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { this.texts = [] points.forEach((start, i) => { const end = points[(i + 1) % points.length] + // planeSize: Math.round(Math.sqrt(Math.pow(newLine.x1 - newLine.x2, 2) + Math.pow(newLine.y1 - newLine.y2, 2))) * 10, + // actualSize: Math.round(Math.sqrt(Math.pow(newLine.x1 - newLine.x2, 2) + Math.pow(newLine.y1 - newLine.y2, 2))) * 10, 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 = Math.round(Math.sqrt(Math.pow(end.x - start.x, 2) + Math.pow(end.y - start.y, 2))) * 10 let midPoint diff --git a/src/components/floor-plan/CanvasFrame.jsx b/src/components/floor-plan/CanvasFrame.jsx index ad10485c..6e812930 100644 --- a/src/components/floor-plan/CanvasFrame.jsx +++ b/src/components/floor-plan/CanvasFrame.jsx @@ -13,14 +13,16 @@ import QContextMenu from '@/components/common/context-menu/QContextMenu' import { useCanvasConfigInitialize } from '@/hooks/common/useCanvasConfigInitialize' import { MENU } from '@/common/common' import PanelBatchStatistics from '@/components/floor-plan/modal/panelBatch/PanelBatchStatistics' +import { totalDisplaySelector } from '@/store/settingAtom' export default function CanvasFrame() { const canvasRef = useRef(null) - const { canvas } = useCanvas('canvas') + const { canvas, handleBackImageLoadToCanvas } = useCanvas('canvas') const { canvasLoadInit, gridInit } = useCanvasConfigInitialize() const currentMenu = useRecoilValue(currentMenuState) const { contextMenu, handleClick } = useContextMenu() - const { selectedPlan, modifiedPlanFlag, checkCanvasObjectEvent, resetModifiedPlans } = usePlan() + const { selectedPlan, modifiedPlanFlag, checkCanvasObjectEvent, resetModifiedPlans, currentCanvasPlan } = usePlan() + const totalDisplay = useRecoilValue(totalDisplaySelector) // 집계표 표시 여부 useEvent() const loadCanvas = () => { @@ -72,7 +74,8 @@ export default function CanvasFrame() { MENU.MODULE_CIRCUIT_SETTING.BASIC_SETTING, MENU.MODULE_CIRCUIT_SETTING.CIRCUIT_TRESTLE_SETTING, MENU.MODULE_CIRCUIT_SETTING.PLAN_ORIENTATION, - ].includes(currentMenu) && } + ].includes(currentMenu) && + totalDisplay && } ) } diff --git a/src/components/floor-plan/CanvasMenu.jsx b/src/components/floor-plan/CanvasMenu.jsx index e544dbec..09758a1e 100644 --- a/src/components/floor-plan/CanvasMenu.jsx +++ b/src/components/floor-plan/CanvasMenu.jsx @@ -235,6 +235,7 @@ export default function CanvasMenu(props) {
+ {/**/} diff --git a/src/components/floor-plan/MenuDepth01.jsx b/src/components/floor-plan/MenuDepth01.jsx index 351fdc97..5b1897b3 100644 --- a/src/components/floor-plan/MenuDepth01.jsx +++ b/src/components/floor-plan/MenuDepth01.jsx @@ -1,7 +1,7 @@ 'use client' import { useMessage } from '@/hooks/useMessage' -import { currentMenuState } from '@/store/canvasAtom' +import { canvasState, currentMenuState } from '@/store/canvasAtom' import { useRecoilState, useRecoilValue } from 'recoil' import { menuTypeState, subMenusState } from '@/store/menuAtom' import useMenu from '@/hooks/common/useMenu' @@ -9,6 +9,7 @@ import { useEffect } from 'react' export default function MenuDepth01() { const type = useRecoilValue(menuTypeState) + const canvas = useRecoilValue(canvasState) const { getMessage } = useMessage() const { handleMenu } = useMenu() const [currentMenu, setCurrentMenu] = useRecoilState(currentMenuState) @@ -24,6 +25,7 @@ export default function MenuDepth01() { useEffect(() => { handleMenu(type) + canvas.discardActiveObject() }, [currentMenu]) return (
diff --git a/src/components/floor-plan/modal/ImgLoad.jsx b/src/components/floor-plan/modal/ImgLoad.jsx new file mode 100644 index 00000000..fa365c84 --- /dev/null +++ b/src/components/floor-plan/modal/ImgLoad.jsx @@ -0,0 +1,89 @@ +import { useMessage } from '@/hooks/useMessage' +import { useRefFiles } from '@/hooks/common/useRefFiles' +import { usePlan } from '@/hooks/usePlan' + +import WithDraggable from '@/components/common/draggable/WithDraggable' + +export default function ImgLoad() { + const { + refImage, + queryRef, + setRefImage, + handleRefFile, + refFileMethod, + setRefFileMethod, + handleRefFileMethod, + mapPositionAddress, + setMapPositionAddress, + handleFileDelete, + handleMapImageDown, + } = useRefFiles() + const { currentCanvasPlan } = usePlan() + const { getMessage } = useMessage() + + return ( + +
+
+

{getMessage('common.input.file')}

+ {/* */} +
+
+
+ サイズ調整と回転 + +
+
+
+
+ handleRefFileMethod(e)} checked={refFileMethod === '1'} /> + +
+
+
+ + handleRefFile(e.target.files[0])} /> +
+
+ {/* + */} + {currentCanvasPlan?.bgImageName === null ? ( + + ) : ( + + )} + {(refImage || currentCanvasPlan?.bgImageName) && } +
+
+
+
+
+ handleRefFileMethod(e)} checked={refFileMethod === '2'} /> + +
+
+ +
+ +
+ {mapPositionAddress && } + {/* */} +
+
+
+
+ +
+
+
+
+ ) +} diff --git a/src/components/floor-plan/modal/basic/BasicSetting.jsx b/src/components/floor-plan/modal/basic/BasicSetting.jsx index 6a7a1d0e..8eda4a09 100644 --- a/src/components/floor-plan/modal/basic/BasicSetting.jsx +++ b/src/components/floor-plan/modal/basic/BasicSetting.jsx @@ -1,20 +1,40 @@ import { useMessage } from '@/hooks/useMessage' import WithDraggable from '@/components/common/draggable/WithDraggable' -import { useState } from 'react' -import Orientation from '@/components/floor-plan/modal/basic/step/Orientation' +import { useEffect, useRef, useState } from 'react' import Module from '@/components/floor-plan/modal/basic/step/Module' import PitchModule from '@/components/floor-plan/modal/basic/step/pitch/PitchModule' import PitchPlacement from '@/components/floor-plan/modal/basic/step/pitch/PitchPlacement' import Placement from '@/components/floor-plan/modal/basic/step/Placement' -import { useRecoilState } from 'recoil' +import { useRecoilValue } from 'recoil' import { canvasSettingState } from '@/store/canvasAtom' import { usePopup } from '@/hooks/usePopup' +import { Orientation } from '@/components/floor-plan/modal/basic/step/Orientation' +import { useModuleBasicSetting } from '@/hooks/module/useModuleBasicSetting' +import { useEvent } from '@/hooks/useEvent' export default function BasicSetting({ id, pos = { x: 50, y: 230 } }) { const { getMessage } = useMessage() const { closePopup } = usePopup() const [tabNum, setTabNum] = useState(1) - const [canvasSetting, setCanvasSetting] = useRecoilState(canvasSettingState) + const canvasSetting = useRecoilValue(canvasSettingState) + const orientationRef = useRef(null) + const { initEvent } = useEvent() + const { makeModuleInstArea, manualModuleSetup, autoModuleSetup } = useModuleBasicSetting() + const handleBtnNextStep = () => { + if (tabNum === 1) { + orientationRef.current.handleNextStep() + } + setTabNum(tabNum + 1) + } + + useEffect(() => { + makeModuleInstArea() //기붕 모듈설치면 생성 + + return () => { + initEvent() //모듈설치면 선택 이벤트 삭제 + } + }, []) + return (
@@ -32,7 +52,7 @@ export default function BasicSetting({ id, pos = { x: 50, y: 230 } }) {
{getMessage('modal.module.basic.setting.module.placement')}
- {tabNum === 1 && } + {tabNum === 1 && } {/*배치면 초기설정 - 입력방법: 복시도 입력 || 실측값 입력*/} {canvasSetting.roofSizeSet && canvasSetting.roofSizeSet != 3 && tabNum === 2 && } {canvasSetting.roofSizeSet && canvasSetting.roofSizeSet != 3 && tabNum === 3 && } @@ -49,14 +69,18 @@ export default function BasicSetting({ id, pos = { x: 50, y: 230 } }) { )} {/*{tabNum !== 3 && }*/} {tabNum !== 3 && ( - )} {tabNum === 3 && ( <> - - + + )}
diff --git a/src/components/floor-plan/modal/basic/step/Orientation.jsx b/src/components/floor-plan/modal/basic/step/Orientation.jsx index 98e10c0c..85ff6b33 100644 --- a/src/components/floor-plan/modal/basic/step/Orientation.jsx +++ b/src/components/floor-plan/modal/basic/step/Orientation.jsx @@ -1,22 +1,21 @@ -import { useState } from 'react' +import { forwardRef, useImperativeHandle, useState } from 'react' import { useMessage } from '@/hooks/useMessage' +import { useOrientation } from '@/hooks/popup/useOrientation' +import { getDegreeInOrientation } from '@/util/canvas-util' -export default function Orientation({ setTabNum }) { +export const Orientation = forwardRef(({ tabNum }, ref) => { const { getMessage } = useMessage() - const [compasDeg, setCompasDeg] = useState(0) + + const { nextStep, compasDeg, setCompasDeg } = useOrientation() + const [hasAnglePassivity, setHasAnglePassivity] = useState(false) - const getDegree = (degree) => { - if (degree % 15 === 0) return degree + useImperativeHandle(ref, () => ({ + handleNextStep, + })) - let value = Math.floor(degree / 15) - const remain = ((degree / 15) % 1).toFixed(5) - - if (remain > 0.4) { - value++ - } - - return value * 15 + const handleNextStep = () => { + nextStep() } return ( @@ -31,7 +30,7 @@ export default function Orientation({ setTabNum }) { {Array.from({ length: 180 / 15 }).map((dot, index) => (
setCompasDeg(15 * (12 + index))} > {index === 0 && 180°} @@ -39,13 +38,17 @@ export default function Orientation({ setTabNum }) {
))} {Array.from({ length: 180 / 15 }).map((dot, index) => ( -
setCompasDeg(15 * index)}> +
setCompasDeg(15 * index)} + > {index === 0 && } {index === 6 && 90°}
))}
-
+
@@ -62,7 +65,11 @@ export default function Orientation({ setTabNum }) { className="input-origin block" value={compasDeg} readOnly={hasAnglePassivity} - onChange={(e) => setCompasDeg(e.target.value !== '' ? Number.parseInt(e.target.value) : 0)} + onChange={(e) => + setCompasDeg( + e.target.value !== '' && parseInt(e.target.value) <= 360 && parseInt(e.target.value) >= 0 ? Number.parseInt(e.target.value) : 0, + ) + } /> ° @@ -72,4 +79,4 @@ export default function Orientation({ setTabNum }) { ) -} +}) diff --git a/src/components/floor-plan/modal/distance/Distance.jsx b/src/components/floor-plan/modal/distance/Distance.jsx index 745ca4f2..ac65c1e5 100644 --- a/src/components/floor-plan/modal/distance/Distance.jsx +++ b/src/components/floor-plan/modal/distance/Distance.jsx @@ -63,7 +63,9 @@ export default function Distance(props) {
- +
diff --git a/src/components/floor-plan/modal/flowDirection/FlowDirectionSetting.jsx b/src/components/floor-plan/modal/flowDirection/FlowDirectionSetting.jsx index 8e54c646..a0573dd1 100644 --- a/src/components/floor-plan/modal/flowDirection/FlowDirectionSetting.jsx +++ b/src/components/floor-plan/modal/flowDirection/FlowDirectionSetting.jsx @@ -5,14 +5,28 @@ import { useRecoilValue } from 'recoil' import { contextPopupPositionState } from '@/store/popupAtom' import { useMessage } from '@/hooks/useMessage' import { usePopup } from '@/hooks/usePopup' - +import { useSurfaceShapeBatch } from '@/hooks/surface/useSurfaceShapeBatch' +import { FLOW_DIRECTION_TYPE, useFlowDirectionSetting } from '@/hooks/popup/useFlowDirectionSetting' +import { canvasState } from '@/store/canvasAtom' export default function FlowDirectionSetting(props) { const contextPopupPosition = useRecoilValue(contextPopupPositionState) const { id, pos = contextPopupPosition, target } = props + const canvas = useRecoilValue(canvasState) const { getMessage } = useMessage() + + useEffect(() => { + return () => { + canvas?.discardActiveObject() + } + }, []) + + const [compasDeg, setCompasDeg] = useState(null) + const [flowDirection, setFlowDirection] = useState(target.direction) const { closePopup } = usePopup() - const [compasDeg, setCompasDeg] = useState(360) + const { changeSurfaceFlowDirection, type, setType } = useFlowDirectionSetting(id) + const orientations = [ + { name: `${getMessage('commons.none')}`, value: 0 }, { name: `${getMessage('commons.south')}`, value: 360 }, { name: `${getMessage('commons.south')}${getMessage('commons.east')}`, value: 315 }, { name: `${getMessage('commons.south')}${getMessage('commons.west')}`, value: 45 }, @@ -23,41 +37,7 @@ export default function FlowDirectionSetting(props) { { name: `${getMessage('commons.north')}`, value: 180 }, ] const [selectedOrientation, setSelectedOrientation] = useState(orientations[0]) - const [type, setType] = useState('0') - useEffect(() => { - if (target?.angle === 0) { - setCompasDeg(360) - } else { - setCompasDeg(target?.angle ?? 360) - } - }, []) - useEffect(() => { - if (type === '0') { - setCompasDeg(selectedOrientation.value) - } - }, [selectedOrientation]) - useEffect(() => { - if (type === '1') { - if ([15, 345, 360].includes(compasDeg)) { - setSelectedOrientation(orientations[0]) - } else if ([30, 45, 60].includes(compasDeg)) { - setSelectedOrientation(orientations[2]) - } else if ([75, 90, 105].includes(compasDeg)) { - setSelectedOrientation(orientations[4]) - } else if ([120, 135, 150].includes(compasDeg)) { - setSelectedOrientation(orientations[6]) - } else if ([165, 180, 195].includes(compasDeg)) { - setSelectedOrientation(orientations[7]) - } else if ([210, 225, 240].includes(compasDeg)) { - setSelectedOrientation(orientations[5]) - } else if ([255, 270, 285].includes(compasDeg)) { - setSelectedOrientation(orientations[3]) - } else if ([300, 315, 330].includes(compasDeg)) { - setSelectedOrientation(orientations[1]) - } - } - }, [compasDeg]) return (
@@ -75,13 +55,13 @@ export default function FlowDirectionSetting(props) {
{getMessage('commons.north')} - + {getMessage('commons.east')} - + {getMessage('commons.south')} - + {getMessage('commons.west')} - +
@@ -90,16 +70,43 @@ export default function FlowDirectionSetting(props) {
{getMessage('modal.shape.flow.direction.setting.orientation.setting.info')}
- setType(e.target.value)} /> + { + setCompasDeg(0) + setType(FLOW_DIRECTION_TYPE.EIGHT_AZIMUTH) + }} + />
- setSelectedOrientation(e)} /> + { + setType(FLOW_DIRECTION_TYPE.EIGHT_AZIMUTH) + setSelectedOrientation(e) + setCompasDeg(e.value) + }} + />
- setType(e.target.value)} /> + { + setType(FLOW_DIRECTION_TYPE.TWENTY_FOUR_AZIMUTH) + }} + />
@@ -110,22 +117,27 @@ export default function FlowDirectionSetting(props) {
setCompasDeg(15 * (12 + index))} - > - {13 - index} -
+ onClick={() => { + setType(FLOW_DIRECTION_TYPE.TWENTY_FOUR_AZIMUTH) + setCompasDeg(15 * (12 + index)) + }} + > ))} {Array.from({ length: 180 / 15 - 1 }).map((dot, index) => (
setCompasDeg(15 * (index + 1))} - > - {24 - index} -
+ onClick={() => { + setType(FLOW_DIRECTION_TYPE.TWENTY_FOUR_AZIMUTH) + setCompasDeg(15 * (index + 1)) + }} + > ))}
-
+
@@ -133,7 +145,9 @@ export default function FlowDirectionSetting(props) {
- +
diff --git a/src/components/floor-plan/modal/lineProperty/LinePropertySetting.jsx b/src/components/floor-plan/modal/lineProperty/LinePropertySetting.jsx index f5425e67..ed84751c 100644 --- a/src/components/floor-plan/modal/lineProperty/LinePropertySetting.jsx +++ b/src/components/floor-plan/modal/lineProperty/LinePropertySetting.jsx @@ -3,38 +3,57 @@ import { useRecoilValue } from 'recoil' import { contextPopupPositionState } from '@/store/popupAtom' import { useMessage } from '@/hooks/useMessage' import { usePopup } from '@/hooks/usePopup' -import { useState } from 'react' +import { useState, useEffect } from 'react' +import { useSurfaceShapeBatch } from '@/hooks/surface/useSurfaceShapeBatch' +import { useEvent } from '@/hooks/useEvent' +import { LINE_TYPE } from '@/common/common' export default function LinePropertySetting(props) { const contextPopupPosition = useRecoilValue(contextPopupPositionState) - const { id, pos = contextPopupPosition } = props + const { id, pos = contextPopupPosition, target } = props const { getMessage } = useMessage() const { closePopup } = usePopup() + const { changeSurfaceLinePropertyEvent, changeSurfaceLineProperty, changeSurfaceLinePropertyReset } = useSurfaceShapeBatch() + const { initEvent } = useEvent() + const properties = [ - { name: getMessage('eaves.line'), value: 'eaves' }, - { name: getMessage('ridge'), value: 'ridge' }, - { name: getMessage('oneside.flow.ridge'), value: 'onesideFlowRidge' }, - { name: getMessage('gable'), value: 'gable' }, - { name: getMessage('gable.left'), value: 'gableLeft' }, - { name: getMessage('gable.right'), value: 'gableRight' }, - { name: getMessage('yosemune'), value: 'yosemune' }, - { name: getMessage('valley'), value: 'valley' }, - { name: getMessage('l.abandon.valley'), value: 'lAbandonValley' }, - { name: getMessage('mansard'), value: 'mansard' }, - { name: getMessage('wall.merge'), value: 'wallCollection' }, - { name: getMessage('wall.merge.type'), value: 'wallCollectionType' }, - { name: getMessage('wall.merge.flow'), value: 'wallCollectionFlow' }, - { name: getMessage('wall.merge.flow.left'), value: 'wallCollectionFlowLeft' }, - { name: getMessage('wall.merge.flow.right'), value: 'wallCollectionFlowRight' }, - { name: getMessage('no.setting'), value: 'noSetting' }, + { name: getMessage('eaves.line'), value: LINE_TYPE.WALLLINE.EAVES }, + { name: getMessage('ridge'), value: LINE_TYPE.SUBLINE.RIDGE }, + { name: getMessage('oneside.flow.ridge'), value: LINE_TYPE.SUBLINE.ONESIDE_FLOW_RIDGE }, + { name: getMessage('gable'), value: LINE_TYPE.WALLLINE.GABLE }, + { name: getMessage('gable.left'), value: LINE_TYPE.WALLLINE.GABLE_LEFT }, + { name: getMessage('gable.right'), value: LINE_TYPE.WALLLINE.GABLE_RIGHT }, + { name: getMessage('yosemune'), value: LINE_TYPE.SUBLINE.YOSEMUNE }, + { name: getMessage('valley'), value: LINE_TYPE.SUBLINE.YOSEMUNE }, + { name: getMessage('l.abandon.valley'), value: LINE_TYPE.SUBLINE.L_ABANDON_VALLEY }, + { name: getMessage('mansard'), value: LINE_TYPE.SUBLINE.MANSARD }, + { name: getMessage('wall.merge'), value: LINE_TYPE.SUBLINE.WALL_COLLECTION }, + { name: getMessage('wall.merge.type'), value: LINE_TYPE.SUBLINE.WALL_COLLECTION_TYPE }, + { name: getMessage('wall.merge.flow'), value: LINE_TYPE.SUBLINE.WALL_COLLECTION_FLOW }, + { name: getMessage('wall.merge.flow.left'), value: LINE_TYPE.SUBLINE.WALL_COLLECTION_FLOW_LEFT }, + { name: getMessage('wall.merge.flow.right'), value: LINE_TYPE.SUBLINE.WALL_COLLECTION_FLOW_RIGHT }, + { name: getMessage('no.setting'), value: LINE_TYPE.WALLLINE.DEFAULT }, ] const [selectedProperty, setSelectedProperty] = useState(null) + + useEffect(() => { + changeSurfaceLinePropertyEvent() + return () => { + initEvent() + } + }, []) + + const handleClosePopup = () => { + closePopup(id) + changeSurfaceLinePropertyReset(target) + } + return (

{getMessage('contextmenu.line.property.edit')}

-
@@ -66,7 +85,9 @@ export default function LinePropertySetting(props) {
- +
diff --git a/src/components/floor-plan/modal/placementShape/PlacementShapeSetting.jsx b/src/components/floor-plan/modal/placementShape/PlacementShapeSetting.jsx index c8dbdbca..6a69aaca 100644 --- a/src/components/floor-plan/modal/placementShape/PlacementShapeSetting.jsx +++ b/src/components/floor-plan/modal/placementShape/PlacementShapeSetting.jsx @@ -8,8 +8,6 @@ import { useMessage } from '@/hooks/useMessage' import { useAxios } from '@/hooks/useAxios' import { useSwal } from '@/hooks/useSwal' import { usePopup } from '@/hooks/usePopup' -import useRefFiles from '@/hooks/common/useRefFiles' -import { usePlan } from '@/hooks/usePlan' import SizeGuide from '@/components/floor-plan/modal/placementShape/SizeGuide' import MaterialGuide from '@/components/floor-plan/modal/placementShape/MaterialGuide' @@ -24,20 +22,6 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, set const [canvasSetting, setCanvasSetting] = useRecoilState(canvasSettingState) const { closePopup } = usePopup() const [basicSetting, setBasicSettings] = useRecoilState(basicSettingState) - const { - refImage, - queryRef, - setRefImage, - handleRefFile, - refFileMethod, - setRefFileMethod, - handleRefFileMethod, - mapPositionAddress, - setMapPositionAddress, - handleFileDelete, - handleMapImageDown, - } = useRefFiles() - const { currentCanvasPlan } = usePlan() const { getMessage } = useMessage() const { get, post } = useAxios() @@ -503,90 +487,6 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, set - - {getMessage('common.input.file')} - -
-
- handleRefFileMethod(e)} - checked={refFileMethod === '1'} - /> - -
-
- handleRefFileMethod(e)} - checked={refFileMethod === '2'} - /> - -
-
- - {/* 파일 불러오기 */} - {refFileMethod === '1' && ( -
-
- - handleRefFile(e.target.files[0])} /> -
-
- {currentCanvasPlan?.bgImageName === null ? ( - - ) : ( - - )} - {(refImage || currentCanvasPlan?.bgImageName) && } -
-
- )} - - {/* 주소 불러오기 */} - {refFileMethod === '2' && ( -
- setMapPositionAddress(e.target.value)} - /> -
- -
- {mapPositionAddress && } - {/* */} -
- )} - {/*
-
- - handleRefFile(e.target.files[0])} /> -
-
- - {refImage && } -
-
*/} - - diff --git a/src/components/floor-plan/modal/setting01/SecondOption.jsx b/src/components/floor-plan/modal/setting01/SecondOption.jsx index 385c7bef..91156ef3 100644 --- a/src/components/floor-plan/modal/setting01/SecondOption.jsx +++ b/src/components/floor-plan/modal/setting01/SecondOption.jsx @@ -1,4 +1,3 @@ -import { useRecoilValue } from 'recoil' import { useMessage } from '@/hooks/useMessage' import React, { useEffect, useState } from 'react' import DimensionLineSetting from '@/components/floor-plan/modal/setting01/dimensionLine/DimensionLineSetting' @@ -6,55 +5,35 @@ import { usePopup } from '@/hooks/usePopup' import { v4 as uuidv4 } from 'uuid' import FontSetting from '@/components/common/font/FontSetting' import PlanSizeSetting from '@/components/floor-plan/modal/setting01/planSize/PlanSizeSetting' -import { dimensionLineSettingsState } from '@/store/commonUtilsAtom' import { useCanvasSetting } from '@/hooks/option/useCanvasSetting' +import { useRecoilState, useRecoilValue } from 'recoil' +import { fontSelector, globalFontAtom } from '@/store/fontAtom' export default function SecondOption() { const { getMessage } = useMessage() - const { addPopup, closePopup, closePopups } = usePopup() + const { addPopup, closePopup } = usePopup() const [showFontSettingModal, setShowFontSettingModal] = useState(false) const [showDimensionLineSettingModal, setShowDimensionLineSettingModal] = useState(false) const [showPlanSizeSettingModal, setShowPlanSizeSettingModal] = useState(false) - const dimensionSettings = useRecoilValue(dimensionLineSettingsState) - + const [globalFont, setGlobalFont] = useRecoilState(globalFontAtom) const [objectNo, setObjectNo] = useState('test123240912001') // 이후 삭제 필요 - const { settingModalFirstOptions, setSettingModalFirstOptions } = useCanvasSetting() const { settingModalSecondOptions, setSettingModalSecondOptions } = useCanvasSetting() const { adsorptionPointMode, setAdsorptionPointMode } = useCanvasSetting() const { fetchSettings, frontSettings, onClickOption } = useCanvasSetting() + const commonFont = useRecoilValue(fontSelector('commonText')) + const flowFont = useRecoilValue(fontSelector('flowText')) + const lengthFont = useRecoilValue(fontSelector('lengthText')) + const circuitNumberTextFont = useRecoilValue(fontSelector('circuitNumberText')) + const [dimensionId, setDimensionId] = useState(uuidv4()) + const [fontId, setFontId] = useState(uuidv4()) + const [planSizeId, setPlanSizeId] = useState(uuidv4()) // 데이터를 최초 한 번만 조회 useEffect(() => { - console.log('SecondOption useEffect 실행') //fetchSettings() }, [objectNo]) - let dimensionId = null - let fontId = null - let planSizeId = null - const [pixel, setPixel] = useState(dimensionSettings.pixel) - const [color, setColor] = useState(dimensionSettings.color) - const [font, setFont] = useState(null) - const [fontSize, setFontSize] = useState(dimensionSettings.fontSize) - const [fontColor, setFontColor] = useState(dimensionSettings.fontColor) - - useEffect(() => { - dimensionId = uuidv4() - fontId = uuidv4() - planSizeId = uuidv4() - }, []) - const dimensionProps = { - color, - setColor, - pixel, - setPixel, - font, - setFont, - fontSize, - setFontSize, - fontColor, - setFontColor, id: dimensionId, isShow: showDimensionLineSettingModal, setIsShow: setShowDimensionLineSettingModal, @@ -62,23 +41,6 @@ export default function SecondOption() { const [horizon, setHorizon] = useState(1600) const [vertical, setVertical] = useState(1600) - - const fontProps = { - id: fontId, - pos: { x: 745, y: 180 }, - setIsShow: setShowFontSettingModal, - isConfig: true, - } - const planSizeProps = { - id: planSizeId, - horizon, - setHorizon, - vertical, - setVertical, - setIsShow: setShowPlanSizeSettingModal, - pos: { x: 1025, y: 180 }, - } - const handlePopup = (type) => { setShowPlanSizeSettingModal(false) setShowFontSettingModal(false) @@ -90,6 +52,7 @@ export default function SecondOption() { setShowDimensionLineSettingModal(false) fontProps.type = 'commonText' fontProps.id = fontId + 1 + fontProps.font = commonFont addPopup(fontId + 1, 2, , true) break } @@ -99,6 +62,7 @@ export default function SecondOption() { setShowFontSettingModal(true) setShowDimensionLineSettingModal(false) fontProps.type = 'flowText' + fontProps.font = flowFont fontProps.id = fontId + 2 addPopup(fontId + 2, 2, , true) break @@ -107,9 +71,9 @@ export default function SecondOption() { case 'font3': { //치수 글꼴변경 setShowFontSettingModal(true) - setShowDimensionLineSettingModal(false) fontProps.type = 'lengthText' + fontProps.font = lengthFont fontProps.id = fontId + 3 addPopup(fontId + 3, 2, , true) break @@ -120,6 +84,7 @@ export default function SecondOption() { setShowFontSettingModal(true) setShowDimensionLineSettingModal(false) fontProps.type = 'circuitNumberText' + fontProps.font = circuitNumberTextFont fontProps.id = fontId addPopup(fontId, 2, , true) break @@ -129,6 +94,12 @@ export default function SecondOption() { //치수선 설정 if (!showDimensionLineSettingModal) { setShowDimensionLineSettingModal(true) + fontProps.font = { + fontFamily: '', + fontColor: '', + fontSize: '', + fontWeight: '', + } addPopup(dimensionId, 2, , true) } else { setShowDimensionLineSettingModal(false) @@ -147,6 +118,38 @@ export default function SecondOption() { } } + const handleFontSave = (font) => { + setGlobalFont((prev) => { + return { + ...prev, + [fontProps.type]: { + fontFamily: font.fontFamily, + fontWeight: font.fontWeight, + fontSize: font.fontSize, + fontColor: font.fontColor, + }, + } + }) + } + + const fontProps = { + id: fontId, + pos: { x: 745, y: 180 }, + setIsShow: setShowFontSettingModal, + onSave: handleFontSave, + isConfig: true, + } + + const planSizeProps = { + id: planSizeId, + horizon, + setHorizon, + vertical, + setVertical, + setIsShow: setShowPlanSizeSettingModal, + pos: { x: 1025, y: 180 }, + } + return ( <>
diff --git a/src/components/floor-plan/modal/setting01/dimensionLine/DimensionLineSetting.jsx b/src/components/floor-plan/modal/setting01/dimensionLine/DimensionLineSetting.jsx index 1aa39e74..65fc2d28 100644 --- a/src/components/floor-plan/modal/setting01/dimensionLine/DimensionLineSetting.jsx +++ b/src/components/floor-plan/modal/setting01/dimensionLine/DimensionLineSetting.jsx @@ -8,68 +8,57 @@ import QSelectBox from '@/components/common/select/QSelectBox' import { useMessage } from '@/hooks/useMessage' import { dimensionLineSettingsState } from '@/store/commonUtilsAtom' import { useRecoilState } from 'recoil' +import { globalFontAtom } from '@/store/fontAtom' -/* - color: 치수선 색 - fontColor: 글꼴 색 - fontSize: 치수선 치수 색 - pixel: 치수선 두깨 -*/ export default function DimensionLineSetting(props) { - const { - color, - setColor, - font, - setFont, - fontColor, - setFontColor, - fontSize, - setFontSize, - pixel, - setPixel, - isShow, - setIsShow, - id, - pos = { x: 985, y: 180 }, - } = props + const { isShow, setIsShow, id, pos = { x: 985, y: 180 } } = props const { addPopup, closePopup, closePopups } = usePopup() const pixels = Array.from({ length: 5 }).map((_, index) => { - return { name: index + 1, value: index + 1 } + return { id: index, name: index + 1, value: index + 1 } }) - const [originColor, setOriginColor] = useState(color) - const [originFont, setOriginFont] = useState(font) - const [originFontColor, setOriginFontColor] = useState(fontColor) - const [originFontSize, setOriginFontSize] = useState(fontSize) - const [originPixel, setOriginPixel] = useState(pixel) + const [dimensionLineSettings, setDimensionLineSettings] = useRecoilState(dimensionLineSettingsState) + const [globalFont, setGlobalFont] = useRecoilState(globalFontAtom) + const [originPixel, setOriginPixel] = useState(dimensionLineSettings.pixel) + const [originColor, setOriginColor] = useState(dimensionLineSettings.color) + const [originFont, setOriginFont] = useState(globalFont.dimensionLineText.fontFamily) + const [originFontColor, setOriginFontColor] = useState(globalFont.dimensionLineText.fontColor) + const [originFontSize, setOriginFontSize] = useState(globalFont.dimensionLineText.fontSize) + const [originFontWeight, setOriginFontWeight] = useState(globalFont.dimensionLineText.fontWeight) const [fontModalId, setFontModalId] = useState(uuidv4()) - const [colorModalId, setColorModalId] = useState(uuidv4()) + const [colorModalId, setColorModalId] = useState(uuidv4()) const [showColorPickerModal, setShowColorPickerModal] = useState(false) const [showFontModal, setShowFontModal] = useState(false) const { getMessage } = useMessage() - const [dimensionLineSettings, setDimensionLineSettings] = useRecoilState(dimensionLineSettingsState) useEffect(() => { - console.log(2, isShow) - if (pixel) { - setOriginPixel(pixels?.filter((data) => data.value === pixel)[0]) + if (originPixel) { + setOriginPixel(pixels?.filter((data) => data.value === originPixel)[0]) } setIsShow(true) }, []) useEffect(() => { - console.log(1, isShow) if (!isShow) { closePopups([fontModalId, colorModalId]) } }, [isShow]) + const handleFontSave = (font) => { + setOriginFont(font.fontFamily) + setOriginFontSize(font.fontSize) + setOriginFontColor(font.fontColor) + setOriginFontWeight(font.fontWeight) + } + const handleColorSave = () => {} + const colorPickerProps = { isShow: showColorPickerModal, setIsShow: setShowColorPickerModal, color: originColor, setColor: setOriginColor, id: colorModalId, + isConfig: true, pos: { x: 495, y: 180, @@ -79,14 +68,13 @@ export default function DimensionLineSetting(props) { const fontProps = { isShow: showFontModal, setIsShow: setShowFontModal, - color: originColor, - setColor: setOriginColor, - font: originFont, - setFont: setOriginFont, - fontColor: 'black', - setFontColor: setOriginFontColor, - fontSize: originFontSize, - setFontSize: setOriginFontSize, + font: { + fontFamily: originFont, + fontSize: originFontSize, + fontColor: originFontColor, + fontWeight: originFontWeight, + }, + onSave: handleFontSave, isConfig: true, id: fontModalId, pos: { @@ -106,6 +94,29 @@ export default function DimensionLineSetting(props) { } } + const onSave = () => { + setGlobalFont((prev) => { + return { + ...prev, + dimensionLineText: { + fontFamily: originFont, + fontWeight: originFontWeight, + fontSize: originFontSize, + fontColor: originFontColor, + }, + } + }) + setDimensionLineSettings((prev) => { + return { + ...prev, + pixel: originPixel?.value, + color: originColor, + } + }) + setIsShow(false) + closePopups([fontModalId, colorModalId, id]) + } + return (
@@ -149,9 +160,10 @@ export default function DimensionLineSetting(props) { className="font" style={{ fontFamily: originFont?.value ?? '', - color: originFontColor?.value ?? 'black', + color: originFontColor.value ?? 'black', fontSize: originFontSize?.value ?? '12px', - fontWeight: '400', + fontStyle: originFontWeight?.value.toLowerCase().includes('italic') ? 'italic' : 'normal', + fontWeight: originFontWeight?.value.toLowerCase().includes('bold') ? 'bold' : 'normal', }} > 9,999 @@ -161,7 +173,7 @@ export default function DimensionLineSetting(props) { style={{ backgroundColor: originColor, borderColor: originColor, - height: originPixel.value, + height: originPixel?.value, }} >
@@ -169,26 +181,7 @@ export default function DimensionLineSetting(props) {
-
diff --git a/src/components/main/MainContents.jsx b/src/components/main/MainContents.jsx index 77df845a..d2b3cc90 100644 --- a/src/components/main/MainContents.jsx +++ b/src/components/main/MainContents.jsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react' +import { useEffect, useState, useContext } from 'react' import ProductItem from './ProductItem' import { useMessage } from '@/hooks/useMessage' import Image from 'next/image' @@ -8,17 +8,16 @@ import { useRecoilValue } from 'recoil' import { useRouter } from 'next/navigation' import { globalLocaleStore } from '@/store/localeAtom' import { queryStringFormatter } from '@/util/common-utils' -import { sessionStore } from '@/store/commonAtom' import MainSkeleton from '../ui/MainSkeleton' +import { SessionContext } from '@/app/SessionProvider' export default function MainContents() { + const { session } = useContext(SessionContext) const { getMessage } = useMessage() const router = useRouter() const globalLocaleState = useRecoilValue(globalLocaleStore) const { promiseGet } = useAxios(globalLocaleState) - const sessionState = useRecoilValue(sessionStore) - //최근 물건 const [objectList, setObjectList] = useState([]) @@ -36,12 +35,12 @@ export default function MainContents() { fetchObjectList() fetchNoticeList() fetchFaqList() - }, [sessionState]) + }, []) //최근 갱신 물건목록 / Sales Contact info 정보 const fetchObjectList = async () => { try { - const apiUrl = `/api/main-page/object/${sessionState?.storeId}/list` + const apiUrl = `/api/main-page/object/${session?.storeId}/list` await promiseGet({ url: apiUrl, }).then((res) => { diff --git a/src/components/management/Stuff.jsx b/src/components/management/Stuff.jsx index fb7ecd76..3dc9a2f4 100644 --- a/src/components/management/Stuff.jsx +++ b/src/components/management/Stuff.jsx @@ -173,8 +173,10 @@ export default function Stuff() { schFromDt: dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD'), schToDt: dayjs(new Date()).format('YYYY-MM-DD'), startRow: (pageNo - 1) * pageSize + 1, - endRow: pageNo * pageSize, - schSelSaleStoreId: stuffSearchParams?.schOtherSelSaleStoreId ? stuffSearchParams.schOtherSelSaleStoreId : stuffSearchParams.schSelSaleStoreId, + // endRow: pageNo * pageSize, + endRow: stuffSearchParams?.endRow, + schSelSaleStoreId: stuffSearchParams?.schSelSaleStoreId ? stuffSearchParams.schSelSaleStoreId : '', + schOtherSelSaleStoreId: stuffSearchParams?.schOtherSelSaleStoreId ? stuffSearchParams.schOtherSelSaleStoreId : '', schSortType: stuffSearchParams.schSortType, } @@ -190,11 +192,7 @@ export default function Stuff() { }) } - //if (session.storeId === 'T01') { fetchData() - //} else if (stuffSearch.schSelSaleStoreId !== '') { - //fetchData() - //} } else if (stuffSearchParams?.code === 'M') { const params = { saleStoreId: session?.storeId, @@ -220,10 +218,6 @@ export default function Stuff() { stuffSearchParams.endRow = 1 * pageSize stuffSearchParams.schSortType = defaultSortType setPageNo(1) - setStuffSearch({ - ...stuffSearch, - code: 'FINISH', - }) async function fetchData() { const apiUrl = `/api/object/list?saleStoreId=${session?.storeId}&${queryStringFormatter(stuffSearchParams)}` @@ -241,6 +235,25 @@ export default function Stuff() { fetchData() } else if (stuffSearchParams?.code === 'C') { resetStuffRecoil() + } else if (stuffSearchParams?.code === 'FINISH') { + stuffSearchParams.startRow = 1 + stuffSearchParams.endRow = 1 * pageSize + stuffSearchParams.schSortType = defaultSortType + setPageNo(1) + async function fetchData() { + const apiUrl = `/api/object/list?saleStoreId=${session?.storeId}&${queryStringFormatter(stuffSearchParams)}` + await get({ url: apiUrl }).then((res) => { + if (!isEmptyArray(res)) { + setGridProps({ ...gridProps, gridData: res, count: res[0].totCnt }) + setTotalCount(res[0].totCnt) + } else { + setGridProps({ ...gridProps, gridData: [], count: 0 }) + setTotalCount(0) + } + }) + } + + fetchData() } }, [stuffSearchParams]) @@ -254,12 +267,15 @@ export default function Stuff() { : stuffSearchParams.schSelSaleStoreId setPageSize(e.target.value) setStuffSearch({ - ...stuffSearchParams, + // ...stuffSearchParams, + ...stuffSearch, + code: 'S', startRow: startRow, endRow: 1 * e.target.value, }) setPageNo(1) + const apiUrl = `/api/object/list?saleStoreId=${session?.storeId}&${queryStringFormatter(stuffSearchParams)}` get({ url: apiUrl }).then((res) => { if (!isEmptyArray(res)) { @@ -345,13 +361,13 @@ export default function Stuff() {
-
- diff --git a/src/components/management/StuffDetail.jsx b/src/components/management/StuffDetail.jsx index 602c6354..ccb943c3 100644 --- a/src/components/management/StuffDetail.jsx +++ b/src/components/management/StuffDetail.jsx @@ -18,6 +18,7 @@ import WindSelectPop from './popup/WindSelectPop' import { useCommonCode } from '@/hooks/common/useCommonCode' import StuffPlanQGrid from './StuffPlanQGrid' import { floorPlanObjectState } from '@/store/floorPlanObjectAtom' +import { ManagementContext } from '@/app/management/ManagementProvider' export default function StuffDetail() { const setFloorPlanObjectNo = useSetRecoilState(floorPlanObjectState) //견적서 화면용 물건번호리코일 @@ -96,7 +97,7 @@ export default function StuffDetail() { const objectNo = searchParams.get('objectNo') //url에서 물건번호 꺼내서 바로 set const [editMode, setEditMode] = useState('NEW') - const [detailData, setDetailData] = useState({}) + const { managementState, setManagementState } = useContext(ManagementContext) const [planGridProps, setPlanGridProps] = useState({ planGridData: [], @@ -289,9 +290,9 @@ export default function StuffDetail() { promiseGet({ url: `/api/object/${objectNo}/detail` }).then((res) => { if (res.status === 200) { if (res.data != null) { - setDetailData(res.data) + setManagementState(res.data) } else { - setDetailData({}) + setManagementState({}) } if (isNotEmptyArray(res.data.planList)) { setPlanGridProps({ ...planGridProps, planGridData: res.data.planList }) @@ -299,7 +300,7 @@ export default function StuffDetail() { setPlanGridProps({ ...planGridProps, planGridData: [] }) } } else { - setDetailData({}) + setManagementState({}) setPlanGridProps({ ...planGridProps, planGridData: [] }) } }) @@ -415,7 +416,7 @@ export default function StuffDetail() { }, [commonCode]) useEffect(() => { - if (isObjectNotEmpty(detailData)) { + if (isObjectNotEmpty(managementState)) { // 도도부현API get({ url: '/api/object/prefecture/list' }).then((res) => { if (!isEmptyArray(res)) { @@ -449,18 +450,17 @@ export default function StuffDetail() { setFavoriteStoreList(favList) setShowSaleStoreList(favList) - if (detailData.firstAgentId != null) { - form.setValue('saleStoreId', detailData.firstAgentId) - setSelOptions(detailData.firstAgentId) + if (managementState.firstAgentId != null) { + form.setValue('saleStoreId', managementState.firstAgentId) + setSelOptions(managementState.firstAgentId) } else { - form.setValue('saleStoreId', detailData.saleStoreId) - setSelOptions(detailData.saleStoreId) + form.setValue('saleStoreId', managementState.saleStoreId) + setSelOptions(managementState.saleStoreId) } //상세데이터의 1차점 아이디로 2차점 목록 조회하기 - let data = detailData?.firstAgentId ? detailData.firstAgentId : detailData.saleStoreId - // url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=0&userId=${session?.userId}` + let data = managementState?.firstAgentId ? managementState.firstAgentId : managementState.saleStoreId url = `/api/object/saleStore/${data}/list?firstFlg=0&userId=${session?.userId}` get({ url: url }).then((res) => { @@ -502,71 +502,74 @@ export default function StuffDetail() { //상세데이터가 1차점이면 1차점에 세팅 //상세데이터가 2차점이면 2차점에 세팅하고 세션으로 1차점 세팅 - if (detailData.saleStoreLevel === '1') { - setSelOptions(detailData.saleStoreId) - form.setValue('saleStoreId', detailData.saleStoreId) - form.setValue('saleStoreLevel', detailData.saleStoreLevel) + if (managementState.saleStoreLevel === '1') { + setSelOptions(managementState.saleStoreId) + form.setValue('saleStoreId', managementState.saleStoreId) + form.setValue('saleStoreLevel', managementState.saleStoreLevel) } else { - setOtherSelOptions(detailData.saleStoreId) - form.setValue('otherSaleStoreId', detailData.saleStoreId) - form.setValue('otherSaleStoreLevel', detailData.saleStoreLevel) + setOtherSelOptions(managementState.saleStoreId) + form.setValue('otherSaleStoreId', managementState.saleStoreId) + form.setValue('otherSaleStoreLevel', managementState.saleStoreLevel) + + form.setValue('saleStoreLevel', '1') } //설계의뢰No. - form.setValue('planReqNo', detailData.planReqNo) + form.setValue('planReqNo', managementState.planReqNo) //담당자 - form.setValue('receiveUser', detailData.receiveUser) + form.setValue('receiveUser', managementState.receiveUser) //물건구분objectStatusId - setSelectObjectStatusId(detailData.objectStatusId) - form.setValue('objectStatusId', detailData.objectStatusId) + setSelectObjectStatusId(managementState.objectStatusId) + form.setValue('objectStatusId', managementState.objectStatusId) //물건명 - form.setValue('objectName', detailData.objectName) + form.setValue('objectName', managementState.objectName) //경칭코드 - setSelHonorificCode(detailData.objectNameOmit) - form.setValue('objectNameOmit', detailData.objectNameOmit) + setSelHonorificCode(managementState.objectNameOmit) + form.setValue('objectNameOmit', managementState.objectNameOmit) //물건명 후리가나 - form.setValue('objectNameKana', detailData.objectNameKana) + form.setValue('objectNameKana', managementState.objectNameKana) //우편번호 - form.setValue('zipNo', detailData.zipNo) + form.setValue('zipNo', managementState.zipNo) //도도부현 / 주소 - setPrefValue(detailData.prefId) - form.setValue('prefId', detailData.prefId) - form.setValue('address', detailData.address) + setPrefValue(managementState.prefId) + form.setValue('prefId', managementState.prefId) + form.setValue('prefName', managementState.prefName) + form.setValue('address', managementState.address) //발전시뮬 - form.setValue('areaId', detailData.areaId) + form.setValue('areaId', managementState.areaId) //기준풍속 - form.setValue('standardWindSpeedId', detailData.standardWindSpeedId) + form.setValue('standardWindSpeedId', managementState.standardWindSpeedId) //수직적설량 - form.setValue('verticalSnowCover', detailData.verticalSnowCover) + form.setValue('verticalSnowCover', managementState.verticalSnowCover) //한랭지대책시행 coldRegionFlg 1이면 true - form.setValue('coldRegionFlg', detailData.coldRegionFlg === '1' ? true : false) + form.setValue('coldRegionFlg', managementState.coldRegionFlg === '1' ? true : false) //면조도구분 surfaceType null로 내려오면 셋팅 안하고 저장할때 필수값 체크하도록 // form.setValue('surfaceType', 'Ⅱ') // form.setValue('surfaceType', 'Ⅲ・Ⅳ') - form.setValue('surfaceType', detailData.surfaceType) + form.setValue('surfaceType', managementState.surfaceType) //염해지역용아이템사용 saltAreaFlg 1이면 true - form.setValue('saltAreaFlg', detailData.saltAreaFlg === '1' ? true : false) + form.setValue('saltAreaFlg', managementState.saltAreaFlg === '1' ? true : false) //설치높이 - form.setValue('installHeight', detailData.installHeight) + form.setValue('installHeight', managementState.installHeight) //계약조건 null로 내려오면 0으로 디폴트셋팅 - if (detailData.conType === null) { + if (managementState.conType === null) { form.setValue('conType', '0') } else { - form.setValue('conType', detailData.conType) + form.setValue('conType', managementState.conType) } //메모 - form.setValue('remarks', detailData.remarks) + form.setValue('remarks', managementState.remarks) }) } - }, [detailData, session]) + }, [managementState]) //경칭선택 변경 이벤트 const onChangeHonorificCode = (key) => { @@ -856,6 +859,7 @@ export default function StuffDetail() { const setPlanReqInfo = (info) => { form.setValue('planReqNo', info.planReqNo) form.setValue('objectStatusId', info.building) + setSelectObjectStatusId(info.building) form.setValue('objectName', info.planReqName) form.setValue('zipNo', info.zipNo) form.setValue('address', info.address2) @@ -872,8 +876,11 @@ export default function StuffDetail() { form.setValue('standardWindSpeedId', `WL_${info.windSpeed}`) form.setValue('verticalSnowCover', info.verticalSnowCover) form.setValue('surfaceType', info.surfaceType) + if (info.surfaceType === 'Ⅱ') { form.setValue('saltAreaFlg', true) + } else { + form.setValue('saltAreaFlg', false) } form.setValue('installHeight', info.installHeight) form.setValue('remarks', info.remarks) @@ -1034,7 +1041,12 @@ export default function StuffDetail() { //설계의뢰 팝업 오픈 const onSearchDesignRequestPopOpen = () => { - setShowDesignRequestButtonValid(true) + const saleStoreId = form.watch('saleStoreId') + if (saleStoreId === '') { + alert(getMessage('stuff.planReqPopup.error.message2')) + } else { + setShowDesignRequestButtonValid(true) + } } // 풍속선택 팝업 오픈 @@ -1177,9 +1189,9 @@ export default function StuffDetail() { return alert(getMessage('stuff.detail.save.valierror2')) } - let detail_sort = Object.keys(detailData) + let detail_sort = Object.keys(managementState) .sort() - .reduce((obj, key) => ((obj[key] = detailData[key]), obj), {}) + .reduce((obj, key) => ((obj[key] = managementState[key]), obj), {}) let params_sort = Object.keys(params) .sort() .reduce((obj, key) => ((obj[key] = params[key]), obj), {}) @@ -1301,7 +1313,7 @@ export default function StuffDetail() { // 물건삭제 const onDelete = () => { - const specificationConfirmDate = detailData.specificationConfirmDate + const specificationConfirmDate = managementState.specificationConfirmDate if (specificationConfirmDate != null) { alert(getMessage('stuff.detail.delete.message1')) } else { @@ -1726,31 +1738,33 @@ export default function StuffDetail() {
-
- { - handleRadioChange(e) - }} - /> - -
-
- { - handleRadioChange(e) - }} - /> - +
+
+ { + handleRadioChange(e) + }} + /> + +
+
+ { + handleRadioChange(e) + }} + /> + +
@@ -1846,8 +1860,7 @@ export default function StuffDetail() {
- {/* {detailData?.tempFlg === '1' && form.watch('planReqNo') ? ( */} - {detailData?.tempFlg === '1' && form.watch('planReqNo') ? ( + {managementState?.tempFlg === '1' && form.watch('planReqNo') ? ( ) : null}
- {/* {detailData?.tempFlg === '1' ? ( */} - {detailData?.tempFlg === '1' ? ( + {managementState?.tempFlg === '1' ? ( <>
{getMessage('stuff.detail.header.specificationConfirmDate')}
-
{headerData.specificationConfirmDate}
+
{managementState?.specificationConfirmDate}
{getMessage('stuff.detail.header.lastEditDatetime')}
- {headerData?.lastEditDatetime ? `${dayjs(headerData.lastEditDatetime).format('YYYY.MM.DD HH:mm:ss')}` : ''}{' '} - {headerData?.lastEditUserName ? `(${headerData.lastEditUserName})` : null} + {managementState?.lastEditDatetime ? `${dayjs(managementState.lastEditDatetime).format('YYYY.MM.DD HH:mm:ss')}` : ''}{' '} + {managementState?.lastEditUserName ? `(${managementState.lastEditUserName})` : null}
{getMessage('stuff.detail.header.createDatetime')}
- {headerData?.createDatetime ? `${dayjs(headerData.lastEditDatetime).format('YYYY.MM.DD')}` : ''}{' '} - {headerData?.createUserName ? `(${headerData.createUserName})` : null} + {managementState?.createDatetime ? `${dayjs(managementState.lastEditDatetime).format('YYYY.MM.DD')}` : ''}{' '} + {managementState?.createUserName ? `(${managementState.createUserName})` : null}
diff --git a/src/components/management/StuffSearchCondition.jsx b/src/components/management/StuffSearchCondition.jsx index 59a4d23c..4a625539 100644 --- a/src/components/management/StuffSearchCondition.jsx +++ b/src/components/management/StuffSearchCondition.jsx @@ -80,7 +80,8 @@ export default function StuffSearchCondition() { schAddress: address ? address : '', schObjectName: objectName ? objectName : '', schDispCompanyName: dispCompanyName ? dispCompanyName : '', - schSelSaleStoreId: stuffSearch?.schOtherSelSaleStoreId ? stuffSearch.schOtherSelSaleStoreId : stuffSearch.schSelSaleStoreId, + schSelSaleStoreId: stuffSearch?.schSelSaleStoreId ? stuffSearch.schSelSaleStoreId : '', + schOtherSelSaleStoreId: stuffSearch?.schOtherSelSaleStoreId ? stuffSearch.schOtherSelSaleStoreId : '', schReceiveUser: receiveUser ? receiveUser : '', schDateType: dateType, schFromDt: dayjs(startDate).format('YYYY-MM-DD'), @@ -90,6 +91,24 @@ export default function StuffSearchCondition() { endRow: stuffSearch?.endRow ? stuffSearch.endRow : 100, schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'R', }) + } else if (stuffSearch.code === 'FINISH') { + setStuffSearch({ + schObjectNo: objectNo, + schSaleStoreName: saleStoreName, + schAddress: address, + schObjectName: objectName, + schDispCompanyName: dispCompanyName, + schSelSaleStoreId: schSelSaleStoreId, + schOtherSelSaleStoreId: otherSaleStoreId, + schReceiveUser: receiveUser, + schDateType: dateType, + schFromDt: dayjs(startDate).format('YYYY-MM-DD'), + schToDt: dayjs(endDate).format('YYYY-MM-DD'), + code: 'E', + startRow: 1, + endRow: 100, + schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'R', + }) } else { setStuffSearch({ schObjectNo: objectNo, @@ -97,7 +116,8 @@ export default function StuffSearchCondition() { schAddress: address, schObjectName: objectName, schDispCompanyName: dispCompanyName, - schSelSaleStoreId: stuffSearch?.schOtherSelSaleStoreId ? stuffSearch.schOtherSelSaleStoreId : stuffSearch.schSelSaleStoreId, + schSelSaleStoreId: stuffSearch?.schSelSaleStoreId, + schOtherSelSaleStoreId: stuffSearch?.schOtherSelSaleStoreId, schReceiveUser: receiveUser, schDateType: dateType, schFromDt: dayjs(startDate).format('YYYY-MM-DD'), @@ -119,7 +139,7 @@ export default function StuffSearchCondition() { objectNameRef.current.value = '' dispCompanyNameRef.current.value = '' receiveUserRef.current.value = '' - + stuffSearch.schDateType = 'U' setObjectNo('') setAddress('') setobjectName('') @@ -131,10 +151,10 @@ export default function StuffSearchCondition() { setEndDate(dayjs(new Date()).format('YYYY-MM-DD')) if (session?.storeId === 'T01') { setSchSelSaleStoreId('') + setOtherSaleStoreId('') handleClear1() //판매대리점선택 자동완성 클리어 resetStuffRecoil() setStuffSearch({ - ...stuffSearch, schSelSaleStoreId: '', schOtherSelSaleStoreId: '', }) @@ -184,30 +204,24 @@ export default function StuffSearchCondition() { setSchSelSaleStoreList(allList) setFavoriteStoreList(favList) setShowSaleStoreList(favList) - // setSchSelSaleStoreId(session?.storeId) - setStuffSearch({ - ...stuffSearch, - code: 'S', - // schSelSaleStoreId: session?.storeId, - }) + if (stuffSearch.schSelSaleStoreId != '') { + setSchSelSaleStoreId(stuffSearch.schSelSaleStoreId) + url = `/api/object/saleStore/${stuffSearch.schSelSaleStoreId}/list?firstFlg=1&userId=${session?.userId}` + get({ url: url }).then((res) => { + if (!isEmptyArray(res)) { + res.map((row) => { + row.value = row.saleStoreId + row.label = row.saleStoreName + }) - //T01일때 2차 판매점 호출하기 디폴트로 1차점을 본인으로 셋팅해서 세션storeId사용 - // 디폴트 셋팅 안하기로 - // url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=0&userId=${session?.userId}` - - // get({ url: url }).then((res) => { - // if (!isEmptyArray(res)) { - // res.map((row) => { - // row.value = row.saleStoreId - // row.label = row.saleStoreName - // }) - - // otherList = res - // setOtherSaleStoreList(otherList) - // } else { - // setOtherSaleStoreList([]) - // } - // }) + otherList = res.filter((row) => row.saleStoreLevel !== '1') + setOtherSaleStoreList(otherList) + setOtherSaleStoreId(stuffSearch.schOtherSelSaleStoreId) + } else { + setOtherSaleStoreList([]) + } + }) + } } else { if (session?.storeLvl === '1') { allList = res @@ -221,11 +235,9 @@ export default function StuffSearchCondition() { setOtherSaleStoreList(otherList) - setStuffSearch({ - ...stuffSearch, - code: 'S', - schSelSaleStoreId: allList[0].saleStoreId, - }) + if (stuffSearch.schOtherSelSaleStoreId != '') { + setOtherSaleStoreId(stuffSearch.schOtherSelSaleStoreId) + } } else { //10X22, 201X112 그냥2차점 //2차점인데 34들고있는애 202X217 @@ -241,7 +253,8 @@ export default function StuffSearchCondition() { setStuffSearch({ ...stuffSearch, code: 'S', - schSelSaleStoreId: otherList[0].saleStoreId, + schSelSaleStoreId: res[0].saleStoreId, + schOtherSelSaleStoreId: otherList[0].saleStoreId, }) } } @@ -267,7 +280,7 @@ export default function StuffSearchCondition() { const onInputChange = (key) => { if (key !== '') { setShowSaleStoreList(schSelSaleStoreList) - setOtherSaleStoreList([]) + // setOtherSaleStoreList([]) } else { setShowSaleStoreList(favoriteStoreList) } @@ -298,8 +311,9 @@ export default function StuffSearchCondition() { }) } else { //X누름 + //화면에선 지우는데 리코일은 조회누르지 않으면 보존 setSchSelSaleStoreId('') - stuffSearch.schSelSaleStoreId = '' + // stuffSearch.schSelSaleStoreId = '' //2차점 판매점목록비우기 setOtherSaleStoreList([]) @@ -311,12 +325,26 @@ export default function StuffSearchCondition() { if (isObjectNotEmpty(key)) { setOtherSaleStoreId(key.saleStoreId) stuffSearch.schOtherSelSaleStoreId = key.saleStoreId + + //2차점 골랐을때 1차점 값 + stuffSearch.schSelSaleStoreId = schSelSaleStoreId } else { //X누르면 검색조건에 1차점으로 셋팅 - setOtherSaleStoreId('') - setSchSelSaleStoreId(schSelSaleStoreId) - stuffSearch.schOtherSelSaleStoreId = '' - stuffSearch.schSelSaleStoreId = schSelSaleStoreId + if (session.storeLvl === '1') { + if (stuffSearch.schOtherSelSaleStoreId === '') { + // 화면에선 지우는데 조회누르기 전이면 리코일은 남김 + // stuffSearch.schSelSaleStoreId = '' + setSchSelSaleStoreId(session.storeId) + } else { + // 화면에선 지우는데 조회누르기 전이면 리코일은 남김 + setOtherSaleStoreId('') + } + } else { + setOtherSaleStoreId('') + setSchSelSaleStoreId(schSelSaleStoreId) + stuffSearch.schOtherSelSaleStoreId = '' + stuffSearch.schSelSaleStoreId = schSelSaleStoreId + } } } @@ -500,7 +528,9 @@ export default function StuffSearchCondition() { } else if (stuffSearch?.code === 'E' && schSelSaleStoreId !== '') { return option.saleStoreId === schSelSaleStoreId } else { - if (stuffSearch?.schSelSaleStoreId !== '') { + if (stuffSearch?.code === 'FINISH') { + return option.saleStoreId === schSelSaleStoreId + } else if (stuffSearch?.schSelSaleStoreId !== '') { return option.saleStoreId === stuffSearch.schSelSaleStoreId } else { return false @@ -531,7 +561,9 @@ export default function StuffSearchCondition() { } else if (stuffSearch?.code === 'E' && schSelSaleStoreId !== '') { return option.saleStoreId === schSelSaleStoreId } else { - if (stuffSearch?.schSelSaleStoreId !== '') { + if (stuffSearch?.code === 'FINISH') { + return option.saleStoreId === schSelSaleStoreId + } else if (stuffSearch?.schSelSaleStoreId !== '') { return option.saleStoreId === stuffSearch.schSelSaleStoreId } else { return false @@ -606,10 +638,11 @@ export default function StuffSearchCondition() { type="radio" name="radio_ptype" id="radio_u" - checked={dateType === 'U' ? true : false} + checked={stuffSearch.schDateType === 'U' ? true : false} value={'U'} onChange={(e) => { setDateType(e.target.value) + stuffSearch.schDateType = e.target.value }} /> @@ -619,10 +652,11 @@ export default function StuffSearchCondition() { type="radio" name="radio_ptype" id="radio_r" - checked={dateType === 'R' ? true : false} + checked={stuffSearch.schDateType === 'R' ? true : false} value={'R'} onChange={(e) => { setDateType(e.target.value) + stuffSearch.schDateType = e.target.value }} /> diff --git a/src/components/management/popup/PlanRequestPop.jsx b/src/components/management/popup/PlanRequestPop.jsx index bbe0f6c9..291549c6 100644 --- a/src/components/management/popup/PlanRequestPop.jsx +++ b/src/components/management/popup/PlanRequestPop.jsx @@ -98,8 +98,8 @@ export default function PlanRequestPop(props) { schPlanReqName: schPlanReqName, schPlanStatCd: schPlanStatCd, schDateGbn: schDateGbn, - schStartDt: dayjs(startDate).format('YYYY-MM-DD'), - schEndDt: dayjs(endDate).format('YYYY-MM-DD'), + schStartDt: startDate ? dayjs(startDate).format('YYYY-MM-DD') : '', + schEndDt: endDate ? dayjs(endDate).format('YYYY-MM-DD') : '', startRow: type === 'S' ? (1 - 1) * pageSize + 1 : (page - 1) * pageSize + 1, endRow: type === 'S' ? 1 * pageSize : page * pageSize, } @@ -226,7 +226,7 @@ export default function PlanRequestPop(props) { ], }) - //설계의뢰 그리드에서 선택한 설계의로 정보 + //설계의뢰 그리드에서 선택한 설계의뢰 정보 const getSelectedRowdata = (data) => { if (isNotEmptyArray(data)) { setPlanReqObject(data[0]) @@ -257,6 +257,16 @@ export default function PlanRequestPop(props) { const handleKeyUp = (e) => { let input = e.target input.value = input.value.replace(/[^0-9]/g, '') + if (e.key === 'Enter') { + onSubmit(pageNo, 'S') + } + } + + // 엔터 이벤트 + const handleByOnKeyUp = (e) => { + if (e.key === 'Enter') { + onSubmit(pageNo, 'S') + } } return ( @@ -324,6 +334,7 @@ export default function PlanRequestPop(props) { onChange={(e) => { setSchTitle(e.target.value) }} + onKeyUp={handleByOnKeyUp} />
@@ -337,6 +348,7 @@ export default function PlanRequestPop(props) { onChange={(e) => { setSchAddress(e.target.value) }} + onKeyUp={handleByOnKeyUp} />
@@ -352,6 +364,7 @@ export default function PlanRequestPop(props) { onChange={(e) => { setSchSaleStoreName(e.target.value) }} + onKeyUp={handleByOnKeyUp} />
@@ -365,6 +378,7 @@ export default function PlanRequestPop(props) { onChange={(e) => { setSchPlanReqName(e.target.value) }} + onKeyUp={handleByOnKeyUp} /> @@ -440,7 +454,7 @@ export default function PlanRequestPop(props) {
Plan List
- diff --git a/src/hooks/common/useCommonUtils.js b/src/hooks/common/useCommonUtils.js index 2902e0c3..594797bb 100644 --- a/src/hooks/common/useCommonUtils.js +++ b/src/hooks/common/useCommonUtils.js @@ -1,16 +1,14 @@ import { useEffect } from 'react' -import { useRecoilValue, useRecoilState } from 'recoil' +import { useRecoilState, useRecoilValue } from 'recoil' import { wordDisplaySelector } from '@/store/settingAtom' import { useEvent } from '@/hooks/useEvent' import { checkLineOrientation, getDistance } from '@/util/canvas-util' -import { dimensionLineSettingsState } from '@/store/commonUtilsAtom' +import { commonUtilsState, dimensionLineSettingsState } from '@/store/commonUtilsAtom' import { fontSelector } from '@/store/fontAtom' import { canvasState } from '@/store/canvasAtom' import { v4 as uuidv4 } from 'uuid' import { usePopup } from '@/hooks/usePopup' import Distance from '@/components/floor-plan/modal/distance/Distance' -import { commonUtilsState } from '@/store/commonUtilsAtom' -import { center, point } from '@turf/turf' export function useCommonUtils() { const canvas = useRecoilValue(canvasState) @@ -39,6 +37,7 @@ export function useCommonUtils() { commonTextKeyEvent() addCanvasMouseEventListener('mouse:down', (event) => { const pointer = canvas?.getPointer(event.e) + textbox = new fabric.Textbox('', { left: pointer.x, top: pointer.y, @@ -49,7 +48,8 @@ export function useCommonUtils() { fill: commonTextFont.fontColor.value, fontFamily: commonTextFont.fontFamily.value, fontSize: commonTextFont.fontSize.value, - fontStyle: commonTextFont.fontWeight.value, + fontStyle: commonTextFont.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal', + fontWeight: commonTextFont.fontWeight.value.toLowerCase().includes('bold') ? 'bold' : 'normal', selectable: true, lockMovementX: true, lockMovementY: true, @@ -262,7 +262,8 @@ export function useCommonUtils() { fill: dimensionLineTextFont.fontColor.value, fontSize: dimensionLineTextFont.fontSize.value, fontFamily: dimensionLineTextFont.fontFamily.value, - fontStyle: dimensionLineTextFont.fontWeight.value, + fontStyle: dimensionLineTextFont.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal', + fontWeight: dimensionLineTextFont.fontWeight.value.toLowerCase().includes('bold') ? 'bold' : 'normal', selectable: true, textAlign: 'center', originX: 'center', @@ -710,7 +711,7 @@ export function useCommonUtils() { canvas?.remove(centerLine, ...extendLine, ...arrows, textObj) const reGroup = new fabric.Group(reGroupObj, { - name: 'dimensionLine', + name: 'dimensionGroup', selectable: true, lineDirection: originLineDirection, groupId: id, diff --git a/src/hooks/common/useFont.js b/src/hooks/common/useFont.js index 595ec74c..e6decd1a 100644 --- a/src/hooks/common/useFont.js +++ b/src/hooks/common/useFont.js @@ -17,8 +17,8 @@ export function useFont() { textObjs.forEach((obj) => { obj.set({ fontFamily: commonText.fontFamily.value, - fontWeight: lengthText.fontWeight.value.toLowerCase().includes('bold') ? 'bold' : 'normal', - fontStyle: lengthText.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal', + fontWeight: commonText.fontWeight.value.toLowerCase().includes('bold') ? 'bold' : 'normal', + fontStyle: commonText.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal', fontSize: commonText.fontSize.value, fill: commonText.fontColor.value, }) @@ -33,8 +33,8 @@ export function useFont() { textObjs.forEach((obj) => { obj.set({ fontFamily: dimensionLineText.fontFamily.value, - fontWeight: lengthText.fontWeight.value.toLowerCase().includes('bold') ? 'bold' : 'normal', - fontStyle: lengthText.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal', + fontWeight: dimensionLineText.fontWeight.value.toLowerCase().includes('bold') ? 'bold' : 'normal', + fontStyle: dimensionLineText.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal', fontSize: dimensionLineText.fontSize.value, fill: dimensionLineText.fontColor.value, }) @@ -49,8 +49,8 @@ export function useFont() { textObjs.forEach((obj) => { obj.set({ fontFamily: flowText.fontFamily.value, - fontWeight: lengthText.fontWeight.value.toLowerCase().includes('bold') ? 'bold' : 'normal', - fontStyle: lengthText.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal', + fontWeight: flowText.fontWeight.value.toLowerCase().includes('bold') ? 'bold' : 'normal', + fontStyle: flowText.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal', fontSize: flowText.fontSize.value, fill: flowText.fontColor.value, }) diff --git a/src/hooks/common/useRefFiles.js b/src/hooks/common/useRefFiles.js index 1b36a109..07680c17 100644 --- a/src/hooks/common/useRefFiles.js +++ b/src/hooks/common/useRefFiles.js @@ -7,7 +7,7 @@ import { convertDwgToPng } from '@/lib/cadAction' import { useAxios } from '../useAxios' import { currentCanvasPlanState } from '@/store/canvasAtom' -export default function useRefFiles() { +export function useRefFiles() { const converterUrl = process.env.NEXT_PUBLIC_CONVERTER_API_URL const [refImage, setRefImage] = useState(null) const [refFileMethod, setRefFileMethod] = useState('1') @@ -54,20 +54,20 @@ export default function useRefFiles() { const res = await get({ url: `http://localhost:3000/api/html2canvas?q=${queryRef.current.value}&fileNm=${uuidv4()}&zoom=20` }) console.log('🚀 ~ handleMapImageDown ~ res:', res) - setCurrentCanvasPlan((prev) => ({ ...prev, bgFileName: res.fileNm, mapPositionAddress: queryRef.current.value })) + setCurrentCanvasPlan((prev) => ({ ...prev, bgImageName: res.fileNm, mapPositionAddress: queryRef.current.value })) } /** * 현재 플랜이 변경되면 플랜 상태 저장 */ - useEffect(() => { - const handleCurrentPlan = async () => { - await promisePut({ url: '/api/canvas-management/canvas-statuses', data: currentCanvasPlan }).then((res) => { - console.log('🚀 ~ awaitpromisePut ~ res:', res) - }) - } - handleCurrentPlan() - }, [currentCanvasPlan]) + // useEffect(() => { + // const handleCurrentPlan = async () => { + // await promisePut({ url: '/api/canvas-management/canvas-statuses', data: currentCanvasPlan }).then((res) => { + // console.log('🚀 ~ awaitpromisePut ~ res:', res) + // }) + // } + // handleCurrentPlan() + // }, [currentCanvasPlan]) /** * RefFile이 캐드 도면 파일일 경우 변환하여 이미지로 저장 diff --git a/src/hooks/floorPlan/estimate/useEstimateController.js b/src/hooks/floorPlan/estimate/useEstimateController.js index 5a0c49e1..4330a208 100644 --- a/src/hooks/floorPlan/estimate/useEstimateController.js +++ b/src/hooks/floorPlan/estimate/useEstimateController.js @@ -19,37 +19,18 @@ const defaultEstimateData = { charger: '', //담당자 objectName: '', //안건명 objectNameOmit: '', //경칭코드 - estimateType: 'YJOD', //주문분류 + estimateType: '', //주문분류 remarks: '', //비고 estimateOption: '', //견적특이사항 - //아이템에 필요없는거 빼기 - itemList: [ - // { - // amount: '', - // fileUploadFlg: '', - // itemChangeFlg: '', - // itemGroup: '', - // itemId: '', //키값?? - // itemName: '', - // itemNo: '', - // moduleFlg: '', - // objectNo: '', - // pkgMaterialFlg: '', - // planNo: '', - // pnowW: '', - // salePrice: '', - // saleTotPrice: '', - // specification: '', - // unit: '', - // }, - ], + itemList: [], fileList: [], fileFlg: '0', //후일 자료 제출 (체크 1 노체크 0) + priceCd: '', } // Helper functions -const updateItemInList = (itemList, itemId, updates) => { - return itemList.map((item) => (item.itemId === itemId ? { ...item, ...updates } : item)) +const updateItemInList = (itemList, dispOrder, updates) => { + return itemList.map((item) => (item.dispOrder === dispOrder ? { ...item, ...updates } : item)) } export const useEstimateController = (planNo) => { @@ -88,34 +69,32 @@ export const useEstimateController = (planNo) => { } } - const updateItem = (itemId, updates) => { + const updateItem = (dispOrder, updates) => { setState({ - itemList: updateItemInList(state.itemList, itemId, updates), + itemList: updateItemInList(state.itemList, dispOrder, updates), }) } const addItem = () => { - const newItemId = Math.max(...state.itemList.map((item) => item.itemId)) + 1 + const newItemDispOrder = Math.max(...state.itemList.map((item) => item.dispOrder)) + 1 setState({ itemList: [ ...state.itemList, { - itemId: newItemId, - amount: '', - fileUploadFlg: '', - itemChangeFlg: '', - itemGroup: '', - itemName: '', + objectNo: objectRecoil.floorPlanObjectNo, + planNo: planNo, + dispOrder: newItemDispOrder.toString(), + itemId: '', //제품번호 itemNo: '', - moduleFlg: '', - objectNo: '', - pkgMaterialFlg: '', - planNo: '', - pnowW: '', - salePrice: '', - saleTotPrice: '', - specification: '', - unit: '', + itemName: '', //형명 + amount: '', //수량 + unitPrice: '0', + unit: '', //단위 + salePrice: '0', //단가 + saleTotPrice: '0', //금액(부가세별도) + itemChangeFlg: '1', //추가시 체인지플래그 1로 + partAdd: '1', //NEW 체인지 플래그 + delFlg: '0', //삭제 플래그 0 삭제하면 1 }, ], }) @@ -125,53 +104,83 @@ export const useEstimateController = (planNo) => { setEstimateData({ ...state, userId: session.userId, sapSalesStoreCd: session.custCd }) }, [state]) + // 첨부파일 다운로드 + const handleEstimateFileDownload = async (originFile) => { + const options = { responseType: 'blob' } + + await promisePost({ url: `/api/file/fileDownload`, data: originFile, option: options }) + .then((resultData) => { + if (resultData) { + const blob = new Blob([resultData.data], { type: resultData.headers['content-type'] || 'application/octet-stream' }) + const fileUrl = window.URL.createObjectURL(blob) + const link = document.createElement('a') + + link.href = fileUrl + link.download = originFile.faileName + document.body.appendChild(link) + link.click() + link.remove() + window.URL.revokeObjectURL(fileUrl) + } + }) + .catch((error) => { + console.log('::FileDownLoad Error::', error) + alert('File does not exist.') + }) + } + //견적서 저장 const handleEstimateSubmit = async () => { //0. 필수체크 let flag = true console.log('::담긴 estimateData:::', estimateData) + // console.log('첨부파일:::::', estimateData.fileList) + //첨부파일을 첨부안했는데 //아이템 fileUploadFlg가1(첨부파일 필수)이 1개라도 있는데 후일 자료 제출(fileFlg) 체크안했으면(0) alert 저장안돼 - if (estimateData.itemList.length > 1) { - estimateData.itemList.map((row) => { - if (row.fileUploadFlg === '1') { - if (estimateData.fileFlg === '0') { - alert(getMessage('estimate.detail.save.requiredMsg')) - flag = false + if (estimateData.fileList.length < 1) { + if (estimateData.itemList.length > 1) { + estimateData.itemList.map((row) => { + if (row.fileUploadFlg === '1') { + if (estimateData.fileFlg === '0') { + alert(getMessage('estimate.detail.save.requiredMsg')) + flag = false + } } - } - }) + }) + } } + if (flag) { //1. 첨부파일 저장 const formData = new FormData() + console.log('첨부파일:!!!', estimateData.fileList) formData.append('file', estimateData.fileList) formData.append('objectNo', estimateData.objectNo) formData.append('planNo', estimateData.planNo) formData.append('category', '10') formData.append('userId', estimateData.userId) - for (const value of formData.values()) { - console.log('formData::', value) - } - await promisePost({ url: '/api/file/fileUpload', data: formData }).then((res) => { - console.log('파일저장결과::::::::::', res) - }) + await post({ url: '/api/file/fileUpload', data: formData }) //2. 상세데이터 저장 - - console.log('상세저장시작!!') return - try { - const result = await promisePost({ - url: ESTIMATE_API_ENDPOINT, - data: estimateData, - }) - alert(getMessage('estimate.detail.save.alertMsg')) - return result - } catch (error) { - console.error('Failed to submit estimate:', error) - throw error - } + await promisePost({ url: `${ESTIMATE_API_ENDPOINT}/save-estimate`, data: estimateData }).then((res) => { + if (res) { + alert(getMessage('estimate.detail.save.alertMsg')) + } + }) + + // try { + // const result = await promisePost({ + // url: ESTIMATE_API_ENDPOINT, + // data: estimateData, + // }) + // alert(getMessage('estimate.detail.save.alertMsg')) + // return result + // } catch (error) { + // console.error('Failed to submit estimate:', error) + // throw error + // } } } @@ -182,5 +191,6 @@ export const useEstimateController = (planNo) => { addItem, handleEstimateSubmit, fetchSetting, + handleEstimateFileDownload, } } diff --git a/src/hooks/module/useModuleBasicSetting.js b/src/hooks/module/useModuleBasicSetting.js new file mode 100644 index 00000000..9ddf86c4 --- /dev/null +++ b/src/hooks/module/useModuleBasicSetting.js @@ -0,0 +1,666 @@ +import { useEffect, useState } from 'react' +import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil' +import { canvasState } from '@/store/canvasAtom' +import { rectToPolygon, setSurfaceShapePattern } from '@/util/canvas-util' +import { roofDisplaySelector } from '@/store/settingAtom' +import offsetPolygon from '@/util/qpolygon-utils' +import { QPolygon } from '@/components/fabric/QPolygon' +import { moduleSetupSurfaceState, moduleIsSetupState } from '@/store/canvasAtom' +import { useEvent } from '@/hooks/useEvent' +import { POLYGON_TYPE, BATCH_TYPE } from '@/common/common' + +import * as turf from '@turf/turf' + +export function useModuleBasicSetting() { + const canvas = useRecoilValue(canvasState) + const roofDisplay = useRecoilValue(roofDisplaySelector) + const [moduleSetupSurface, setModuleSetupSurface] = useRecoilState(moduleSetupSurfaceState) + const [moduleIsSetup, setModuleIsSetup] = useRecoilState(moduleIsSetupState) + const { addTargetMouseEventListener, addCanvasMouseEventListener, initEvent } = useEvent() + let selectedModuleInstSurfaceArray = [] + + const makeModuleInstArea = () => { + //지붕 객체 반환 + const roofs = canvas.getObjects().filter((obj) => obj.name === 'roof') + + if (!roofs) { + return + } + + roofs.forEach((roof) => { + setSurfaceShapePattern(roof, roofDisplay.column, true) //패턴 변경 + const offsetPoints = offsetPolygon(roof.points, -20) //안쪽 offset + //모듈설치영역?? 생성 + const setupSurface = new QPolygon(offsetPoints, { + stroke: 'red', + fill: 'transparent', + strokeDashArray: [10, 4], + strokeWidth: 1, + lockMovementX: true, + lockMovementY: true, + lockRotation: true, + lockScalingX: true, + lockScalingY: true, + selectable: true, + parentId: roof.id, //가대 폴리곤의 임시 인덱스를 넣어줌 + name: POLYGON_TYPE.MODULE_SETUP_SURFACE, + flowDirection: roof.direction, + }) + + canvas.add(setupSurface) + //지붕면 선택 금지 + roof.set({ + selectable: false, + }) + + //모듈설치면 클릭이벤트 + addTargetMouseEventListener('mousedown', setupSurface, function () { + toggleSelection(setupSurface) + }) + }) + } + + //설치 범위 지정 클릭 이벤트 + const toggleSelection = (setupSurface) => { + const isExist = selectedModuleInstSurfaceArray.some((obj) => obj.parentId === setupSurface.parentId) + //최초 선택일때 + if (!isExist) { + //기본 선택이랑 스트로크 굵기가 같으면 선택 안됨으로 봄 + setupSurface.set({ + ...setupSurface, + strokeWidth: 3, + strokeDashArray: [0], + fill: 'transparent', + }) + canvas.discardActiveObject() // 객체의 활성 상태 해제 + //중복으로 들어가는걸 방지하기 위한 코드 + + canvas?.renderAll() + selectedModuleInstSurfaceArray.push(setupSurface) + } else { + //선택후 재선택하면 선택안됨으로 변경 + setupSurface.set({ + ...setupSurface, + fill: 'transparent', + strokeDashArray: [10, 4], + strokeWidth: 1, + }) + canvas.discardActiveObject() // 객체의 활성 상태 해제 + + //폴리곤에 커스텀 인덱스를 가지고 해당 배열 인덱스를 찾아 삭제함 + const removeIndex = setupSurface.parentId + const removeArrayIndex = selectedModuleInstSurfaceArray.findIndex((obj) => obj.parentId === removeIndex) + selectedModuleInstSurfaceArray.splice(removeArrayIndex, 1) + } + + canvas?.renderAll() + setModuleSetupSurface([...selectedModuleInstSurfaceArray]) + } + + /** + * trestle에서 영역을 가져와 mouse:move 이벤트로 해당 영역에 진입했을때 booleanPointInPolygon 로 진입여부를 확인 + * 확인 후 셀을 이동시킴 + */ + const manualModuleSetup = () => { + const moduleSetupSurfaces = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE) //모듈설치면를 가져옴 + const batchObjects = canvas + ?.getObjects() + .filter( + (obj) => + obj.name === BATCH_TYPE.OPENING || + obj.name === BATCH_TYPE.TRIANGLE_DORMER || + obj.name === BATCH_TYPE.PENTAGON_DORMER || + obj.name === BATCH_TYPE.SHADOW, + ) //도머s 객체 + + if (moduleSetupSurfaces.length !== 0) { + let tempModule + let manualDrawModules = moduleIsSetup // 앞에서 자동으로 했을때 추가됨 + let inside = false + let turfPolygon + let flowDirection + let trestlePolygon + addCanvasMouseEventListener('mouse:move', (e) => { + //마우스 이벤트 삭제 후 재추가 + const mousePoint = canvas.getPointer(e.e) + + for (let i = 0; i < moduleSetupSurfaces.length; i++) { + turfPolygon = polygonToTurfPolygon(moduleSetupSurfaces[i]) + trestlePolygon = moduleSetupSurfaces[i] + flowDirection = moduleSetupSurfaces[i].flowDirection //도형의 방향 + let width = flowDirection === 'south' || flowDirection === 'north' ? 172 : 113 + let height = flowDirection === 'south' || flowDirection === 'north' ? 113 : 172 + + const points = [ + { x: mousePoint.x - width / 2, y: mousePoint.y - height / 2 }, + { x: mousePoint.x + width / 2, y: mousePoint.y - height / 2 }, + { x: mousePoint.x + width / 2, y: mousePoint.y + height / 2 }, + { x: mousePoint.x - width / 2, y: mousePoint.y + height / 2 }, + ] + + const turfPoints = coordToTurfPolygon(points) + + if (turf.booleanWithin(turfPoints, turfPolygon)) { + let isDrawing = false + + if (isDrawing) return + canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'tempModule')) //움직일때 일단 지워가면서 움직임 + + tempModule = new fabric.Rect({ + fill: 'white', + stroke: 'black', + strokeWidth: 1, + width: width, + height: height, + left: mousePoint.x - width / 2, + top: mousePoint.y - height / 2, + selectable: false, + lockMovementX: true, + lockMovementY: true, + lockRotation: true, + lockScalingX: true, + lockScalingY: true, + opacity: 0.8, + name: 'tempModule', + parentId: moduleSetupSurfaces[i].parentId, + }) + + canvas?.add(tempModule) //움직여가면서 추가됨 + + /** + * 스냅기능 + */ + let snapDistance = 10 + let cellSnapDistance = 20 + + const trestleLeft = moduleSetupSurfaces[i].left + const trestleTop = moduleSetupSurfaces[i].top + const trestleRight = trestleLeft + moduleSetupSurfaces[i].width * moduleSetupSurfaces[i].scaleX + const trestleBottom = trestleTop + moduleSetupSurfaces[i].height * moduleSetupSurfaces[i].scaleY + const bigCenterY = (trestleTop + trestleTop + moduleSetupSurfaces[i].height) / 2 + + // 작은 폴리곤의 경계 좌표 계산 + const smallLeft = tempModule.left + const smallTop = tempModule.top + const smallRight = smallLeft + tempModule.width * tempModule.scaleX + const smallBottom = smallTop + tempModule.height * tempModule.scaleY + const smallCenterX = smallLeft + (tempModule.width * tempModule.scaleX) / 2 + const smallCenterY = smallTop + (tempModule.height * tempModule.scaleX) / 2 + + /** + * 미리 깔아놓은 셀이 있을때 셀에 흡착됨 + */ + if (manualDrawModules) { + manualDrawModules.forEach((cell) => { + const holdCellLeft = cell.left + const holdCellTop = cell.top + const holdCellRight = holdCellLeft + cell.width * cell.scaleX + const holdCellBottom = holdCellTop + cell.height * cell.scaleY + const holdCellCenterX = holdCellLeft + (cell.width * cell.scaleX) / 2 + const holdCellCenterY = holdCellTop + (cell.height * cell.scaleY) / 2 + + //설치된 셀에 좌측에 스냅 + if (Math.abs(smallRight - holdCellLeft) < snapDistance) { + tempModule.left = holdCellLeft - width - 0.5 + } + + //설치된 셀에 우측에 스냅 + if (Math.abs(smallLeft - holdCellRight) < snapDistance) { + tempModule.left = holdCellRight + 0.5 + } + + //설치된 셀에 위쪽에 스냅 + if (Math.abs(smallBottom - holdCellTop) < snapDistance) { + tempModule.top = holdCellTop - height - 0.5 + } + + //설치된 셀에 밑쪽에 스냅 + if (Math.abs(smallTop - holdCellBottom) < snapDistance) { + tempModule.top = holdCellBottom + 0.5 + } + //가운데 -> 가운데 + if (Math.abs(smallCenterX - holdCellCenterX) < cellSnapDistance) { + tempModule.left = holdCellCenterX - width / 2 + } + //왼쪽 -> 가운데 + if (Math.abs(smallLeft - holdCellCenterX) < cellSnapDistance) { + tempModule.left = holdCellCenterX + } + // 오른쪽 -> 가운데 + if (Math.abs(smallRight - holdCellCenterX) < cellSnapDistance) { + tempModule.left = holdCellCenterX - width + } + //세로 가운데 -> 가운데 + if (Math.abs(smallCenterY - holdCellCenterY) < cellSnapDistance) { + tempModule.top = holdCellCenterY - height / 2 + } + //위쪽 -> 가운데 + if (Math.abs(smallTop - holdCellCenterY) < cellSnapDistance) { + tempModule.top = holdCellCenterY + } + //아랫쪽 -> 가운데 + if (Math.abs(smallBottom - holdCellCenterY) < cellSnapDistance) { + tempModule.top = holdCellCenterY - height + } + }) + } + + // 위쪽 변에 스냅 + if (Math.abs(smallTop - trestleTop) < snapDistance) { + tempModule.top = trestleTop + } + + // 아래쪽 변에 스냅 + if (Math.abs(smallTop + tempModule.height * tempModule.scaleY - (trestleTop + moduleSetupSurfaces[i].height)) < snapDistance) { + tempModule.top = trestleTop + moduleSetupSurfaces[i].height - tempModule.height * tempModule.scaleY + } + + // 왼쪽변에 스냅 + if (Math.abs(smallLeft - trestleLeft) < snapDistance) { + tempModule.left = trestleLeft + } + //오른쪽 변에 스냅 + if (Math.abs(smallRight - trestleRight) < snapDistance) { + tempModule.left = trestleRight - tempModule.width * tempModule.scaleX + } + + if (flowDirection === 'south' || flowDirection === 'north') { + // 모듈왼쪽이 세로중앙선에 붙게 스냅 + if (Math.abs(smallLeft - (trestleLeft + moduleSetupSurfaces[i].width / 2)) < snapDistance) { + tempModule.left = trestleLeft + moduleSetupSurfaces[i].width / 2 + } + + // 모듈이 가운데가 세로중앙선에 붙게 스냅 + if (Math.abs(smallCenterX - (trestleLeft + moduleSetupSurfaces[i].width / 2)) < snapDistance) { + tempModule.left = trestleLeft + moduleSetupSurfaces[i].width / 2 - (tempModule.width * tempModule.scaleX) / 2 + } + + // 모듈오른쪽이 세로중앙선에 붙게 스냅 + if (Math.abs(smallRight - (trestleLeft + moduleSetupSurfaces[i].width / 2)) < snapDistance) { + tempModule.left = trestleLeft + moduleSetupSurfaces[i].width / 2 - tempModule.width * tempModule.scaleX + } + } else { + // 모듈이 가로중앙선에 스냅 + if (Math.abs(smallTop + tempModule.height / 2 - bigCenterY) < snapDistance) { + tempModule.top = bigCenterY - tempModule.height / 2 + } + + if (Math.abs(smallTop - (trestleTop + moduleSetupSurfaces[i].height / 2)) < snapDistance) { + tempModule.top = trestleTop + moduleSetupSurfaces[i].height / 2 + } + // 모듈 밑면이 가로중앙선에 스냅 + if (Math.abs(smallBottom - (trestleTop + moduleSetupSurfaces[i].height / 2)) < snapDistance) { + tempModule.top = trestleTop + moduleSetupSurfaces[i].height / 2 - tempModule.height * tempModule.scaleY + } + } + + tempModule.setCoords() + canvas?.renderAll() + inside = true + break + } else { + inside = false + } + } + + if (!inside) { + canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'tempModule')) + canvas?.renderAll() + } + }) + + addCanvasMouseEventListener('mouse:up', (e) => { + let isIntersection = true + if (!inside) return + if (tempModule) { + const rectPoints = [ + { x: tempModule.left + 0.5, y: tempModule.top + 0.5 }, + { x: tempModule.left + 0.5 + tempModule.width * tempModule.scaleX, y: tempModule.top + 0.5 }, + { + x: tempModule.left + tempModule.width * tempModule.scaleX + 0.5, + y: tempModule.top + tempModule.height * tempModule.scaleY + 0.5, + }, + { x: tempModule.left + 0.5, y: tempModule.top + tempModule.height * tempModule.scaleY + 0.5 }, + ] + + tempModule.set({ points: rectPoints }) + const tempTurfModule = polygonToTurfPolygon(tempModule) + + //도머 객체를 가져옴 + if (batchObjects) { + batchObjects.forEach((object) => { + let dormerTurfPolygon + + if (object.type === 'group') { + //도머는 그룹형태임 + dormerTurfPolygon = batchObjectGroupToTurfPolygon(object) + } else { + //개구, 그림자 + dormerTurfPolygon = polygonToTurfPolygon(rectToPolygon(object)) + } + + const intersection = turf.intersect(turf.featureCollection([dormerTurfPolygon, tempTurfModule])) //겹치는지 확인 + //겹치면 안됨 + if (intersection) { + alert('도머위에 모듈을 올릴 수 없습니다.') + isIntersection = false + } + }) + } + + if (!isIntersection) return + + tempModule.setCoords() //좌표 재정렬 + + if (turf.booleanWithin(tempTurfModule, turfPolygon)) { + //마우스 클릭시 set으로 해당 위치에 셀을 넣음 + const isOverlap = manualDrawModules.some((module) => turf.booleanOverlap(tempTurfModule, polygonToTurfPolygon(module))) //겹치는지 확인 + if (!isOverlap) { + //안겹치면 넣는다 + tempModule.setCoords() + tempModule.set({ name: 'module', fill: '#BFFD9F' }) + manualDrawModules.push(tempModule) //모듈배열에 추가 + //해당 모듈에 프로퍼티로 넣는다 + trestlePolygon.set({ + modules: manualDrawModules, + }) + } else { + alert('셀끼리 겹치면 안되죠?') + } + } else { + alert('나갔죠?!!') + } + } + }) + } + } + + //자동 모듈 설치(그리드 방식) + const autoModuleSetup = () => { + initEvent() + const moduleSetupSurfaces = moduleSetupSurface //선택 설치면 + const notSelectedTrestlePolygons = canvas + ?.getObjects() + .filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE && !moduleSetupSurfaces.includes(obj)) //설치면이 아닌것 + + const batchObjects = canvas + ?.getObjects() + .filter( + (obj) => + obj.name === BATCH_TYPE.OPENING || + obj.name === BATCH_TYPE.TRIANGLE_DORMER || + obj.name === BATCH_TYPE.PENTAGON_DORMER || + obj.name === BATCH_TYPE.SHADOW, + ) //도머s 객체 + + if (moduleSetupSurfaces.length === 0) { + alert('선택된 모듈 설치면이 없습니다.') + return + } + + if (moduleIsSetup.length > 0) { + alert('기존 모듈은 제거됩니다.') + } + + notSelectedTrestlePolygons.forEach((obj) => { + if (obj.modules) { + obj.modules.forEach((module) => { + canvas?.remove(module) + }) + obj.modules = [] + } + }) + + const moduleSetupArray = [] + moduleSetupSurfaces.forEach((moduleSetupSurface, index) => { + moduleSetupSurface.fire('mousedown') + + let maxLengthLine = moduleSetupSurface.lines.reduce((acc, cur) => { + return acc.length > cur.length ? acc : cur + }) + + const turfModuleSetupSurface = polygonToTurfPolygon(moduleSetupSurface) //폴리곤을 turf 객체로 변환 + + const containsBatchObjects = batchObjects.filter((batchObject) => { + let convertBatchObject + + if (batchObject.type === 'group') { + //도머는 그룹형태임 + convertBatchObject = batchObjectGroupToTurfPolygon(batchObject) + } else { + //개구, 그림자 + batchObject.set({ + points: rectToPolygon(batchObject), + }) + canvas?.renderAll() // set된걸 바로 적용하기 위해 + convertBatchObject = polygonToTurfPolygon(batchObject) //rect를 폴리곤으로 변환 -> turf 폴리곤으로 변환 + } + + // 폴리곤 안에 도머 폴리곤이 포함되어있는지 확인해서 반환하는 로직 + return turf.booleanContains(turfModuleSetupSurface, convertBatchObject) || turf.booleanWithin(convertBatchObject, turfModuleSetupSurface) + }) + + let difference = turfModuleSetupSurface //기본 객체(면형상) + + if (containsBatchObjects.length > 0) { + //turf로 도머를 제외시키는 로직 + for (let i = 0; i < containsBatchObjects.length; i++) { + let convertBatchObject + if (containsBatchObjects[i].type === 'group') { + convertBatchObject = batchObjectGroupToTurfPolygon(containsBatchObjects[i]) + } else { + convertBatchObject = polygonToTurfPolygon(containsBatchObjects[i]) + } + + if (i === 0) { + difference = turf.difference(turf.featureCollection([turfModuleSetupSurface, convertBatchObject])) //한 면에 도머가 1개일때 + } else { + if (difference) { + difference = turf.difference(turf.featureCollection([difference, convertBatchObject])) //한면에 도머가 여러개일때 계속 제외시킴 + } + } + } + } + + const bbox = turf.bbox(difference) + + let width = maxLengthLine.flowDirection === 'right' || maxLengthLine.flowDirection === 'left' ? 172.2 : 113.4 + let height = maxLengthLine.flowDirection === 'right' || maxLengthLine.flowDirection === 'left' ? 113.4 : 172.2 + + //배치면때는 방향쪽으로 패널이 넓게 누워져야함 + if (moduleSetupSurface.flowDirection !== undefined) { + width = moduleSetupSurface.flowDirection === 'south' || moduleSetupSurface.flowDirection === 'north' ? 172.2 : 113.4 + height = moduleSetupSurface.flowDirection === 'south' || moduleSetupSurface.flowDirection === 'north' ? 113.4 : 172.2 + } + const cols = Math.floor((bbox[2] - bbox[0]) / width) + const rows = Math.floor((bbox[3] - bbox[1]) / height) + + for (let col = 0; col <= cols; col++) { + for (let row = 0; row <= rows; row++) { + let x = 0, + y = 0, + square = [], + margin = 0 + + if (moduleSetupSurface.flowDirection !== undefined) { + //배치면 처림 방향이 정해져있는 경우 + + if (moduleSetupSurface.flowDirection === 'south' || moduleSetupSurface.flowDirection === 'north') { + //남,북 + margin = (bbox[2] - bbox[0] - cols * width) / 2 //박스 끝에서 박스 시작값을 빼고 width와 계산된 cols를 곱한값을 뺀뒤 나누기 2 하면 가운데 배치됨 + if (moduleSetupSurface.flowDirection === 'south') { + //남쪽 + x = col === 0 ? moduleSetupSurface.left + margin : bbox[0] + col * width + margin //상하 위치 기준이면 좌우 가운데 정렬한다 + y = bbox[3] - row * height + } else { + //북쪽 + x = col === 0 ? moduleSetupSurface.left + margin : bbox[0] + col * width + margin + y = bbox[1] + row * height + } + } else if (moduleSetupSurface.flowDirection === 'east' || moduleSetupSurface.flowDirection === 'west') { + //동쪽 + margin = (bbox[3] - bbox[1] - rows * height) / 2 + if (moduleSetupSurface.flowDirection === 'east') { + x = bbox[2] - col * width + y = rows === 0 ? moduleSetupSurface.top + margin : bbox[1] + row * height + margin //좌우 위치 기준이면 상하 가운데 정렬한다 + } else { + x = bbox[0] + col * width + y = rows === 0 ? moduleSetupSurface.top + margin : bbox[1] + row * height + margin + } + } + } else { + //방향이 없는 경우 ex) 템플릿 + x = bbox[0] + col * width + y = bbox[1] + row * height + } + + square = [ + [x, y], + [x + width, y], + [x + width, y + height], + [x, y + height], + [x, y], + ] + + const squarePolygon = turf.polygon([square]) + const disjointFromTrestle = + turf.booleanContains(turfModuleSetupSurface, squarePolygon) || turf.booleanWithin(squarePolygon, turfModuleSetupSurface) + + if (disjointFromTrestle) { + let turfCoordnates = squarePolygon.geometry.coordinates[0].slice(0, -1) + const points = turfCoordnates.map((coord) => ({ x: coord[0], y: coord[1] })) + + if (containsBatchObjects.length > 0) { + let convertBatchObject + + //도머가 있으면 적용되는 로직 + const isDisjoint = containsBatchObjects.every((batchObject) => { + if (batchObject.type === 'group') { + convertBatchObject = batchObjectGroupToTurfPolygon(batchObject) + } else { + convertBatchObject = polygonToTurfPolygon(batchObject) + } + + return turf.booleanDisjoint(squarePolygon, convertBatchObject) //도머가 여러개일수있으므로 겹치는게 있다면... + }) + + console.log('isDisjoint', isDisjoint) + + if (isDisjoint) { + const tempModule = new QPolygon(points, { + fill: '#BFFD9F', + stroke: 'black', + selectable: true, // 선택 가능하게 설정 + lockMovementX: false, // X 축 이동 잠금 + lockMovementY: false, // Y 축 이동 잠금 + lockRotation: false, // 회전 잠금 + lockScalingX: false, // X 축 크기 조정 잠금 + lockScalingY: false, // Y 축 크기 조정 잠금 + opacity: 0.8, + parentId: moduleSetupSurface.parentId, + }) + canvas?.add(tempModule) + moduleSetupArray.push(tempModule) + } + } else { + //도머가 없을땐 그냥 그림 + const tempModule = new QPolygon(points, { + fill: '#BFFD9F', + stroke: 'black', + selectable: true, // 선택 가능하게 설정 + lockMovementX: true, // X 축 이동 잠금 + lockMovementY: true, // Y 축 이동 잠금 + lockRotation: true, // 회전 잠금 + lockScalingX: true, // X 축 크기 조정 잠금 + lockScalingY: true, // Y 축 크기 조정 잠금 + opacity: 0.8, + parentId: moduleSetupSurface.parentId, + lineCol: col, + lineRow: row, + }) + canvas?.add(tempModule) + moduleSetupArray.push(tempModule) + } + } + } + } + + // let drawRoofCells + // if (maxLengthLine.flowDirection === 'right' || maxLengthLine.flowDirection === 'left') { + // drawRoofCells = trestle.fillCell({ width: 113.4, height: 172.2, padding: 0 }) + // trestle.flowDirection = 'south' + // } else { + // drawRoofCells = trestle.fillCell({ width: 172.2, height: 113.4, padding: 0 }) + // trestle.flowDirection = 'east' + // } + + // drawRoofCells.forEach((cell) => { + // moduleSetupArray.push(cell) + // }) + + /** + * 추후에 가대까지 완료하면 그룹시켜버림 + */ + // const groupCellObj = canvas + // ?.getObjects() + // .filter( + // (obj) => + // obj?.parentId === trestle.parentId || + // obj?.id === trestle.parentId || + // (obj?.name === 'arrow' && obj?.parent.id === trestle.parentId) || + // (obj?.name === 'flowDirectionText' && obj?.parent.parent.id === trestle.parentId), + // ) + + // console.log('groupCellObj', groupCellObj) + + // canvas?.add( + // new fabric.Group(groupCellObj, { + // name: 'cellGroup', + // originX: 'center', + // originY: 'center', + // }), + // ) + + moduleSetupSurface.set({ modules: moduleSetupArray }) + }) + + setModuleIsSetup(moduleSetupArray) + } + + const coordToTurfPolygon = (points) => { + const coordinates = points.map((point) => [point.x, point.y]) + coordinates.push(coordinates[0]) + return turf.polygon([coordinates]) + } + + const polygonToTurfPolygon = (object) => { + let coordinates + coordinates = object.points.map((point) => [point.x, point.y]) + coordinates.push(coordinates[0]) + return turf.polygon( + [coordinates], + {}, + { + parentId: object.parentId, + }, + ) + } + + function batchObjectGroupToTurfPolygon(group) { + const polygons = group.getObjects().filter((obj) => obj.type === 'QPolygon') + let allPoints = [] + + polygons.forEach((obj) => allPoints.push(...obj.get('points'))) + + const points = turf.featureCollection(allPoints.map((point) => turf.point([point.x, point.y]))) + const hull = turf.concave(points, { tolerance: 0.1 }) + + return hull + } + + return { + makeModuleInstArea, + manualModuleSetup, + autoModuleSetup, + } +} diff --git a/src/hooks/option/useCanvasSetting.js b/src/hooks/option/useCanvasSetting.js index 549cc9a4..a585109a 100644 --- a/src/hooks/option/useCanvasSetting.js +++ b/src/hooks/option/useCanvasSetting.js @@ -283,7 +283,7 @@ export function useCanvasSetting() { optionName = ['roof', POLYGON_TYPE.ROOF] break case 'wordDisplay': //문자 표시 - optionName = ['6'] + optionName = ['commonText'] break case 'circuitNumDisplay': //회로번호 표시 optionName = ['7'] @@ -313,6 +313,8 @@ export function useCanvasSetting() { //obj.set({ visible: !obj.visible }) }) + canvas.renderAll() + // console.log( // 'optionName', // optionName, diff --git a/src/hooks/option/useFirstOption.js b/src/hooks/option/useFirstOption.js index c296a942..8757040a 100644 --- a/src/hooks/option/useFirstOption.js +++ b/src/hooks/option/useFirstOption.js @@ -40,7 +40,7 @@ export function useFirstOption() { optionName = ['roof', POLYGON_TYPE.ROOF] break case 'wordDisplay': //문자 표시 - optionName = ['6'] + optionName = ['commonText'] break case 'circuitNumDisplay': //회로번호 표시 optionName = ['7'] diff --git a/src/hooks/popup/useFlowDirectionSetting.js b/src/hooks/popup/useFlowDirectionSetting.js new file mode 100644 index 00000000..9e77aa4a --- /dev/null +++ b/src/hooks/popup/useFlowDirectionSetting.js @@ -0,0 +1,30 @@ +import { canvasState } from '@/store/canvasAtom' +import { useRecoilValue } from 'recoil' +import { usePolygon } from '@/hooks/usePolygon' +import { useState } from 'react' +import { usePopup } from '@/hooks/usePopup' + +export const FLOW_DIRECTION_TYPE = { + EIGHT_AZIMUTH: 'eightAzimuth', + TWENTY_FOUR_AZIMUTH: 'twentyFourAzimuth', +} + +export function useFlowDirectionSetting(id) { + const canvas = useRecoilValue(canvasState) + const { drawDirectionArrow } = usePolygon() + const { closePopup } = usePopup() + + const [type, setType] = useState(FLOW_DIRECTION_TYPE.EIGHT_AZIMUTH) + + const changeSurfaceFlowDirection = (roof, direction, orientation) => { + roof.set({ + direction: direction, + surfaceCompass: orientation, + }) + drawDirectionArrow(roof) + canvas?.renderAll() + closePopup(id) + } + + return { changeSurfaceFlowDirection, type, setType } +} diff --git a/src/hooks/popup/useOrientation.js b/src/hooks/popup/useOrientation.js new file mode 100644 index 00000000..4b8f4574 --- /dev/null +++ b/src/hooks/popup/useOrientation.js @@ -0,0 +1,25 @@ +import { useRecoilState, useRecoilValue } from 'recoil' +import { canvasState } from '@/store/canvasAtom' +import { usePolygon } from '@/hooks/usePolygon' +import { POLYGON_TYPE } from '@/common/common' +import { compasDegAtom } from '@/store/orientationAtom' + +// 모듈,회로 구성 탭 기본설정 > 방위설정 탭 +export function useOrientation() { + const canvas = useRecoilValue(canvasState) + const [compasDeg, setCompasDeg] = useRecoilState(compasDegAtom) + + const { drawDirectionArrow } = usePolygon() + + const nextStep = () => { + const roofs = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF) + roofs.forEach((roof) => { + roof.set({ + moduleCompass: compasDeg, + }) + drawDirectionArrow(roof) + }) + } + + return { nextStep, compasDeg, setCompasDeg } +} diff --git a/src/hooks/roofcover/useOuterLineWall.js b/src/hooks/roofcover/useOuterLineWall.js index b2244da8..7c4d5091 100644 --- a/src/hooks/roofcover/useOuterLineWall.js +++ b/src/hooks/roofcover/useOuterLineWall.js @@ -1,6 +1,6 @@ import { useEffect, useRef } from 'react' import { distanceBetweenPoints } from '@/util/canvas-util' -import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil' +import { useRecoilState, useRecoilValue, useResetRecoilState, useSetRecoilState } from 'recoil' import { adsorptionPointAddModeState, adsorptionPointModeState, @@ -65,6 +65,7 @@ export function useOuterLineWall(id, propertiesId) { const [arrow1, setArrow1] = useRecoilState(outerLineArrow1State) const [arrow2, setArrow2] = useRecoilState(outerLineArrow2State) const [points, setPoints] = useRecoilState(outerLinePointsState) + const resetPoints = useResetRecoilState(outerLinePointsState) const [type, setType] = useRecoilState(outerLineTypeState) const [angle1, setAngle1] = useRecoilState(outerLineAngle1State) const [angle2, setAngle2] = useRecoilState(outerLineAngle2State) @@ -88,6 +89,13 @@ export function useOuterLineWall(id, propertiesId) { clear() return () => { initEvent() + + canvas + .getObjects() + .filter((obj) => obj.name === 'startPoint') + .forEach((obj) => { + canvas.remove(obj) + }) } }, [verticalHorizontalMode, points, adsorptionPointAddMode, adsorptionPointMode, adsorptionRange, interval, tempGridMode]) diff --git a/src/hooks/roofcover/useRoofAllocationSetting.js b/src/hooks/roofcover/useRoofAllocationSetting.js index b99af935..9a9973cb 100644 --- a/src/hooks/roofcover/useRoofAllocationSetting.js +++ b/src/hooks/roofcover/useRoofAllocationSetting.js @@ -107,18 +107,16 @@ export function useRoofAllocationSetting(id) { } }) }) - if (currentObject) { - console.log(currentObject.name) - if (currentObject.name.toLowerCase().includes('line')) { - currentObject.set({ - strokeWidth: 4, - stroke: '#EA10AC', - }) - } + if (currentObject && currentObject.name && ['auxiliaryLine', 'ridge', 'hip'].includes(currentObject.name)) { + currentObject.set({ + strokeWidth: 4, + stroke: '#EA10AC', + }) } }, [currentObject]) useEffect(() => { + // canvas.getObjects().filter((obj) => obj.type === 'QLine') const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF) if (roofBases.length === 0) { swalFire({ text: '할당할 지붕이 없습니다.' }) @@ -126,10 +124,6 @@ export function useRoofAllocationSetting(id) { } }, []) - useEffect(() => { - console.log('editingLines', editingLines) - }, [editingLines]) - const onAddRoofMaterial = () => { setValues([...values, selectedRoofMaterial]) } @@ -163,7 +157,7 @@ export function useRoofAllocationSetting(id) { const checkInnerLines = () => { const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF) // roofPolygon.innerLines let result = false - console.log(roofBases) + roofBases.forEach((roof) => { roof.innerLines.forEach((line) => { if (!line.attributes.actualSize || line.attributes?.actualSize === 0) { @@ -182,7 +176,7 @@ export function useRoofAllocationSetting(id) { } const apply = () => { - const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF) + const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF && !obj.isFixed) const wallLines = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.WALL) roofBases.forEach((roofBase) => { try { @@ -205,6 +199,10 @@ export function useRoofAllocationSetting(id) { const roofs = canvas.getObjects().filter((obj) => obj.name === 'roof') roofs.forEach((roof) => { + if (roof.isFixed) return + roof.set({ + isFixed: true, + }) setSurfaceShapePattern(roof, roofDisplay.column) drawDirectionArrow(roof) }) diff --git a/src/hooks/roofcover/useRoofShapeSetting.js b/src/hooks/roofcover/useRoofShapeSetting.js index 21948de0..af55e557 100644 --- a/src/hooks/roofcover/useRoofShapeSetting.js +++ b/src/hooks/roofcover/useRoofShapeSetting.js @@ -167,8 +167,6 @@ export function useRoofShapeSetting(id) { ] const handleSave = () => { - //기존 wallLine 삭제 - let outerLines let direction @@ -379,20 +377,20 @@ export function useRoofShapeSetting(id) { } // 기존 wallLine, roofBase 제거 - canvas + /*canvas .getObjects() .filter((obj) => obj.name === POLYGON_TYPE.WALL) .forEach((line) => { canvas.remove(line) - }) + })*/ - canvas + /*canvas .getObjects() .filter((obj) => obj.name === POLYGON_TYPE.ROOF) .forEach((obj) => { canvas.remove(...obj.innerLines) canvas.remove(obj) - }) + })*/ const polygon = addPolygonByLines(outerLines, { name: POLYGON_TYPE.WALL, direction }) polygon.lines = [...outerLines] @@ -402,7 +400,6 @@ export function useRoofShapeSetting(id) { canvas?.renderAll() roof.drawHelpLine() - // setShowRoofShapeSettingModal(false) isFixRef.current = true closePopup(id) } diff --git a/src/hooks/surface/useSurfaceShapeBatch.js b/src/hooks/surface/useSurfaceShapeBatch.js index d2c6aee7..77beb6b1 100644 --- a/src/hooks/surface/useSurfaceShapeBatch.js +++ b/src/hooks/surface/useSurfaceShapeBatch.js @@ -13,6 +13,8 @@ import { usePopup } from '@/hooks/usePopup' import { roofDisplaySelector } from '@/store/settingAtom' import { usePolygon } from '@/hooks/usePolygon' import { fontSelector } from '@/store/fontAtom' +import { slopeSelector } from '@/store/commonAtom' +import { QLine } from '@/components/fabric/QLine' export function useSurfaceShapeBatch() { const { getMessage } = useMessage() @@ -22,6 +24,7 @@ export function useSurfaceShapeBatch() { const canvas = useRecoilValue(canvasState) const globalPitch = useRecoilValue(globalPitchState) const roofDisplay = useRecoilValue(roofDisplaySelector) + const slope = useRecoilValue(slopeSelector(globalPitch)) const { swalFire } = useSwal() const { addCanvasMouseEventListener, initEvent } = useEvent() const { closePopup } = usePopup() @@ -126,6 +129,7 @@ export function useSurfaceShapeBatch() { addCanvasMouseEventListener('mouse:down', (e) => { isDrawing = false obj.set('name', POLYGON_TYPE.ROOF) + obj.set('surfaceId', surfaceId) initEvent() setSurfaceShapePattern(obj, roofDisplay.column) closePopup(id) @@ -580,18 +584,19 @@ export function useSurfaceShapeBatch() { text: '배치면 내용을 전부 삭제하시겠습니까?', type: 'confirm', confirmFn: () => { - canvas?.getObjects().forEach((obj) => { - if ( - obj.name === POLYGON_TYPE.ROOF || - obj.name === BATCH_TYPE.OPENING || - obj.name === BATCH_TYPE.SHADOW || - obj.name === BATCH_TYPE.TRIANGLE_DORMER || - obj.name === BATCH_TYPE.PENTAGON_DORMER || - obj.name === 'lengthText' - ) { - canvas?.remove(obj) - } - }) + // canvas?.getObjects().forEach((obj) => { + // if ( + // obj.name === POLYGON_TYPE.ROOF || + // obj.name === BATCH_TYPE.OPENING || + // obj.name === BATCH_TYPE.SHADOW || + // obj.name === BATCH_TYPE.TRIANGLE_DORMER || + // obj.name === BATCH_TYPE.PENTAGON_DORMER || + // obj.name === 'lengthText' + // ) { + // canvas?.remove(obj) + // } + // }) + canvas.clear() swalFire({ text: '삭제 완료 되었습니다.' }) }, // denyFn: () => { @@ -823,10 +828,113 @@ export function useSurfaceShapeBatch() { canvas.renderAll() } + const changeSurfaceLinePropertyEvent = () => { + let tmpLines = [] + const roof = canvas.getActiveObject() + + if (roof) { + roof.set({ + selectable: false, + }) + + roof.lines.forEach((obj, index) => { + const tmpLine = new QLine([obj.x1, obj.y1, obj.x2, obj.y2], { + ...obj, + stroke: 'rgb(3, 255, 0)', + strokeWidth: 8, + selectable: true, + name: 'lineProperty', + lineIndex: index, + }) + + tmpLines.push(tmpLine) + canvas.add(tmpLine) + }) + + addCanvasMouseEventListener('mouse:down', (e) => { + const selectedLine = e.target + if (selectedLine && selectedLine.name !== 'roof') { + tmpLines.forEach((line) => { + line.set({ + stroke: 'rgb(3, 255, 0)', + name: 'lineProperty', + }) + }) + selectedLine.set({ + stroke: 'red', + name: 'selectedLineProperty', + }) + } else { + tmpLines.forEach((line) => { + line.set({ + stroke: 'rgb(3, 255, 0)', + name: 'lineProperty', + }) + }) + } + }) + + canvas.renderAll() + } + canvas.discardActiveObject() + } + + const changeSurfaceLineProperty = (property, roof) => { + if (!property) { + swalFire({ text: getMessage('modal.line.property.change'), icon: 'error' }) + return + } + + const selectedLine = canvas.getActiveObjects()[0] //배열로 떨어짐 한개만 선택가능 + if (selectedLine && selectedLine.name === 'selectedLineProperty') { + swalFire({ + text: getMessage('modal.line.property.change.confirm'), + type: 'confirm', + confirmFn: () => { + const lineIndex = selectedLine.lineIndex + roof.lines[lineIndex].attributes = { + ...roof.lines[lineIndex].attributes, + type: property.value, + } + canvas.renderAll() + }, + }) + } else { + swalFire({ text: getMessage('modal.line.property.change.unselect'), icon: 'error' }) + } + } + + const changeSurfaceLinePropertyReset = (roof) => { + const lines = canvas.getObjects().filter((obj) => obj.name === 'lineProperty' || obj.name === 'selectedLineProperty') + + lines.forEach((line) => { + canvas.remove(line) + }) + + if (roof) { + roof.set({ + selectable: true, + }) + } + canvas?.renderAll() + } + + const changeSurfaceFlowDirection = (roof, direction, orientation) => { + roof.set({ + direction: direction, + }) + drawDirectionArrow(roof) + canvas?.renderAll() + } + return { applySurfaceShape, deleteAllSurfacesAndObjects, moveSurfaceShapeBatch, resizeSurfaceShapeBatch, + + changeSurfaceLinePropertyEvent, + changeSurfaceLineProperty, + changeSurfaceLinePropertyReset, } } diff --git a/src/hooks/useCanvas.js b/src/hooks/useCanvas.js index 6ffb03fc..46dbbb35 100644 --- a/src/hooks/useCanvas.js +++ b/src/hooks/useCanvas.js @@ -109,11 +109,24 @@ export function useCanvas(id) { OBJECT_PROTOTYPE.forEach((type) => { type.toObject = function (propertiesToInclude) { let source = {} + for (let key in this) { if (typeof this[key] !== 'function' && SAVE_KEY.includes(key)) { source.key = this[key] } } + + //QLine에 커스텀 어트리뷰트 넣기 + if (this.type === 'QLine') { + if (this.attributes) { + this.attributes.type = this.attributes.type || 'default' + source.attributes = { + ...this.attributes, + type: this.attributes.type, + } + } + } + return fabric.util.object.extend(this.callSuper('toObject', propertiesToInclude), source) } }) diff --git a/src/hooks/useCanvasEvent.js b/src/hooks/useCanvasEvent.js index 42266c8a..0e8cd64b 100644 --- a/src/hooks/useCanvasEvent.js +++ b/src/hooks/useCanvasEvent.js @@ -225,7 +225,9 @@ export function useCanvasEvent() { if (deselected?.length > 0) { deselected.forEach((obj) => { if (obj.type === 'QPolygon') { - obj.set({ stroke: 'black' }) + if (obj.name !== 'moduleSetupSurface') { + obj.set({ stroke: 'black' }) + } } }) } diff --git a/src/hooks/useContextMenu.js b/src/hooks/useContextMenu.js index 64892dce..5dae113c 100644 --- a/src/hooks/useContextMenu.js +++ b/src/hooks/useContextMenu.js @@ -24,7 +24,6 @@ import { useCommonUtils } from './common/useCommonUtils' import { useMessage } from '@/hooks/useMessage' import { useCanvasEvent } from '@/hooks/useCanvasEvent' import { contextMenuListState, contextMenuState } from '@/store/contextMenu' -import ImageSizeSetting from '@/components/floor-plan/modal/image/ImageSizeSetting' import PanelEdit from '@/components/floor-plan/modal/module/PanelEdit' import DimensionLineSetting from '@/components/floor-plan/modal/dimensionLine/DimensionLineSetting' import ColumnRemove from '@/components/floor-plan/modal/module/column/ColumnRemove' @@ -34,6 +33,7 @@ import RowInsert from '@/components/floor-plan/modal/module/row/RowInsert' import CircuitNumberEdit from '@/components/floor-plan/modal/module/CircuitNumberEdit' import { useObjectBatch } from '@/hooks/object/useObjectBatch' import { useSurfaceShapeBatch } from '@/hooks/surface/useSurfaceShapeBatch' +import { fontSelector, globalFontAtom } from '@/store/fontAtom' export function useContextMenu() { const currentMenu = useRecoilValue(currentMenuState) // 현재 메뉴 @@ -52,6 +52,9 @@ export function useContextMenu() { const { handleZoomClear } = useCanvasEvent() const { moveObjectBatch } = useObjectBatch({}) const { moveSurfaceShapeBatch } = useSurfaceShapeBatch() + const [globalFont, setGlobalFont] = useRecoilState(globalFontAtom) + const commonTextFont = useRecoilValue(fontSelector('commonText')) + const currentMenuSetting = () => { switch (currentMenu) { case MENU.PLAN_DRAWING: @@ -95,11 +98,6 @@ export function useContextMenu() { case MENU.ROOF_COVERING.DEFAULT: setContextMenu([ [ - { - id: 'refresh', - name: getMessage('refresh'), - fn: () => handleZoomClear(), - }, { id: 'roofMaterialPlacement', name: getMessage('contextmenu.roof.material.placement'), @@ -121,11 +119,6 @@ export function useContextMenu() { name: getMessage('contextmenu.wallline.remove'), fn: () => deleteOuterLineObject(), }, - { - id: 'imageSizeEdit', - name: getMessage('modal.image.size.setting'), - component: , - }, ], [ { @@ -176,6 +169,7 @@ export function useContextMenu() { { id: 'sizeEdit', name: getMessage('contextmenu.size.edit'), + component: , }, { id: 'remove', @@ -192,16 +186,12 @@ export function useContextMenu() { shortcut: ['c', 'C'], name: `${getMessage('contextmenu.copy')}(C)`, }, - { - id: 'imageSizeEdit', - name: getMessage('modal.image.size.setting'), - component: , - }, ], [ { id: 'roofMaterialEdit', name: getMessage('contextmenu.roof.material.edit'), + component: , }, { id: 'linePropertyEdit', @@ -211,6 +201,7 @@ export function useContextMenu() { { id: 'flowDirectionEdit', name: getMessage('contextmenu.flow.direction.edit'), + component: , }, ], ]) @@ -257,8 +248,10 @@ export function useContextMenu() { }, [currentContextMenu]) useEffect(() => { - console.log('currentObject', currentObject) + console.log(currentObject) + if (currentObject?.name) { + console.log('object', currentObject) switch (currentObject.name) { case 'triangleDormer': case 'pentagonDormer': @@ -336,7 +329,7 @@ export function useContextMenu() { { id: 'linePropertyEdit', name: getMessage('contextmenu.line.property.edit'), - component: , + component: , }, { id: 'flowDirectionEdit', @@ -423,7 +416,26 @@ export function useContextMenu() { { id: 'commonTextFontSetting', name: getMessage('contextmenu.font.setting'), - component: , + component: ( + { + setGlobalFont((prev) => { + return { + ...prev, + commonText: { + fontFamily: font.fontFamily, + fontWeight: font.fontWeight, + fontSize: font.fontSize, + fontColor: font.fontColor, + }, + } + }) + }} + /> + ), }, { id: 'commonTextEdit', diff --git a/src/hooks/useEvent.js b/src/hooks/useEvent.js index 891ad101..ba441b75 100644 --- a/src/hooks/useEvent.js +++ b/src/hooks/useEvent.js @@ -219,6 +219,12 @@ export function useEvent() { mouseEventListeners.current.length = 0 // 배열 초기화 } + const addTargetMouseEventListener = (eventType, target, handler) => { + target.off(eventType) + target.on(eventType, handler) + mouseEventListeners.current.push({ eventType, handler }) + } + /** * document 이벤트의 경우 이 함수를 통해서만 등록 * @param eventType @@ -264,6 +270,7 @@ export function useEvent() { return { addDocumentEventListener, addCanvasMouseEventListener, + addTargetMouseEventListener, removeAllMouseEventListeners, removeAllDocumentEventListeners, removeDocumentEvent, diff --git a/src/hooks/usePlan.js b/src/hooks/usePlan.js index ab67452e..b0ed47d6 100644 --- a/src/hooks/usePlan.js +++ b/src/hooks/usePlan.js @@ -57,6 +57,7 @@ export function usePlan() { */ const currentCanvasData = (mode = '') => { removeMouseLines() + canvas.discardActiveObject() if (mode === 'save') { const groups = canvas.getObjects().filter((obj) => obj.type === 'group') diff --git a/src/hooks/usePolygon.js b/src/hooks/usePolygon.js index 9b3a8e98..56052379 100644 --- a/src/hooks/usePolygon.js +++ b/src/hooks/usePolygon.js @@ -1,7 +1,7 @@ import { ANGLE_TYPE, canvasState, currentAngleTypeSelector, fontFamilyState, fontSizeState, pitchTextSelector } from '@/store/canvasAtom' import { useRecoilValue } from 'recoil' import { fabric } from 'fabric' -import { getDegreeByChon, getDirectionByPoint, isPointOnLine } from '@/util/canvas-util' +import { findAndRemoveClosestPoint, getDegreeByChon, getDegreeInOrientation, getDirectionByPoint, isPointOnLine } from '@/util/canvas-util' import { QPolygon } from '@/components/fabric/QPolygon' import { isSamePoint, removeDuplicatePolygons } from '@/util/qpolygon-utils' import { flowDisplaySelector } from '@/store/settingAtom' @@ -86,8 +86,8 @@ export const usePolygon = () => { parentDirection: line.direction, parentDegree: degree, parentId: polygon.id, - planeSize, - actualSize, + planeSize: planeSize ?? length, + actualSize: actualSize ?? length, editable: false, selectable: true, lockRotation: true, @@ -181,7 +181,7 @@ export const usePolygon = () => { polygon.canvas .getObjects() - .filter((obj) => obj.name === 'flowText' && obj.parent === polygon.arrow) + .filter((obj) => obj.name === 'flowText' && obj.parentId === polygon.arrow?.id) .forEach((obj) => polygon.canvas.remove(obj)) let arrow = null @@ -265,6 +265,8 @@ export const usePolygon = () => { direction: direction, parent: polygon, stickeyPoint: stickeyPoint, + surfaceCompass: polygon.surfaceCompass, + moduleCompass: polygon.moduleCompass, visible: isFlowDisplay, pitch: polygon.pitch, parentId: polygon.id, @@ -274,7 +276,177 @@ export const usePolygon = () => { polygon.arrow = arrow polygon.canvas.add(arrow) polygon.canvas.renderAll() - drawDirectionStringToArrow(polygon.canvas, 0) + drawDirectionStringToArrow2(polygon) + // drawDirectionStringToArrow() + } + + //arrow의 compass 값으로 방향 글자 설정 필요 + const drawDirectionStringToArrow2 = (polygon) => { + const { direction, surfaceCompass, moduleCompass, arrow } = polygon + if (moduleCompass === null || moduleCompass === undefined) { + const textObj = new fabric.Text(`${currentAngleType === ANGLE_TYPE.SLOPE ? arrow.pitch : getDegreeByChon(arrow.pitch)}${pitchText}`, { + fontFamily: flowFontOptions.fontFamily.value, + fontWeight: flowFontOptions.fontWeight.value.toLowerCase().includes('bold') ? 'bold' : 'normal', + fontStyle: flowFontOptions.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal', + fontSize: flowFontOptions.fontSize.value, + fill: flowFontOptions.fontColor.value, + originX: 'center', + originY: 'center', + pitch: arrow.pitch, + name: 'flowText', + selectable: false, + left: arrow.stickeyPoint.x, + top: arrow.stickeyPoint.y, + parent: arrow, + parentId: arrow.id, + visible: isFlowDisplay, + }) + + polygon.canvas.add(textObj) + return + } + + let text = '' + + const compassType = (375 - getDegreeInOrientation(moduleCompass)) / 15 + + if ([1, 25].includes(compassType)) { + direction === 'north' ? (text = '北') : direction === 'south' ? (text = '南') : direction === 'west' ? (text = '西') : (text = '東') + } else if ([2, 3].includes(compassType)) { + direction === 'north' + ? (text = '北北東') + : direction === 'south' + ? (text = '南南西') + : direction === 'west' + ? (text = '西北西') + : (text = '東南東') + } else if ([4].includes(compassType)) { + direction === 'north' ? (text = '北東') : direction === 'south' ? (text = '南西') : direction === 'west' ? (text = '北西') : (text = '南東') + } else if ([5, 6].includes(compassType)) { + direction === 'north' + ? (text = '東北東') + : direction === 'south' + ? (text = '西南西') + : direction === 'west' + ? (text = '北北西') + : (text = '南南東') + } else if ([7].includes(compassType)) { + direction === 'north' ? (text = '東') : direction === 'south' ? (text = '西') : direction === 'west' ? (text = '北') : (text = '南') + } else if ([8, 9].includes(compassType)) { + direction === 'north' + ? (text = '東南東') + : direction === 'south' + ? (text = '西北西') + : direction === 'west' + ? (text = '北北東') + : (text = '南南西') + } else if ([10].includes(compassType)) { + direction === 'north' ? (text = '南東') : direction === 'south' ? (text = '北西') : direction === 'west' ? (text = '南西') : (text = '北東') + } else if ([11, 12].includes(compassType)) { + direction === 'north' + ? (text = '南南東') + : direction === 'south' + ? (text = '北北西') + : direction === 'west' + ? (text = '東北東') + : (text = '西南西') + } else if ([13].includes(compassType)) { + direction === 'north' ? (text = '南') : direction === 'south' ? (text = '北') : direction === 'west' ? (text = '東') : (text = '西') + } else if ([14, 15].includes(compassType)) { + direction === 'north' + ? (text = '南南西') + : direction === 'south' + ? (text = '北北東') + : direction === 'west' + ? (text = '東南東') + : (text = '西北西') + } else if ([16].includes(compassType)) { + direction === 'north' ? (text = '南西') : direction === 'south' ? (text = '北東') : direction === 'west' ? (text = '南東') : (text = '北西') + } else if ([17, 18].includes(compassType)) { + direction === 'north' + ? (text = '西南西') + : direction === 'south' + ? (text = '東北東') + : direction === 'west' + ? (text = '南南東') + : (text = '北北西') + } else if ([19].includes(compassType)) { + direction === 'north' ? (text = '西') : direction === 'south' ? (text = '東') : direction === 'west' ? (text = '南') : (text = '北') + } else if ([20, 21].includes(compassType)) { + direction === 'north' + ? (text = '西北西') + : direction === 'south' + ? (text = '東南東') + : direction === 'west' + ? (text = '南南西') + : (text = '北北東') + } else if ([22].includes(compassType)) { + direction === 'north' ? (text = '北西') : direction === 'south' ? (text = '南東') : direction === 'west' ? (text = '南西') : (text = '北東') + } else if ([23, 24].includes(compassType)) { + direction === 'north' + ? (text = '北北西') + : direction === 'south' + ? (text = '南南東') + : direction === 'west' + ? (text = '西南西') + : (text = '東北東') + } + + // 東,西,南,北 + if ([360].includes(surfaceCompass)) { + text = '南' + } else if ([345, 330].includes(surfaceCompass)) { + text = '南南東' + } else if ([315].includes(surfaceCompass)) { + text = '南東' + } else if ([300, 285].includes(surfaceCompass)) { + text = '東南東' + } else if ([270].includes(surfaceCompass)) { + text = '東' + } else if ([255, 240].includes(surfaceCompass)) { + text = '東北東' + } else if ([225].includes(surfaceCompass)) { + text = '北東' + } else if ([210, 195].includes(surfaceCompass)) { + text = '北北東' + } else if ([180].includes(surfaceCompass)) { + text = '北' + } else if ([165, 150].includes(surfaceCompass)) { + text = '北北西' + } else if ([135].includes(surfaceCompass)) { + text = '北西' + } else if ([120, 105].includes(surfaceCompass)) { + text = '西北西' + } else if ([90].includes(surfaceCompass)) { + text = '西' + } else if ([75, 60].includes(surfaceCompass)) { + text = '西南西' + } else if ([45].includes(surfaceCompass)) { + text = '西南' + } else if ([30, 15].includes(surfaceCompass)) { + text = '西西南' + } + + const textObj = new fabric.Text(`${text} (${currentAngleType === ANGLE_TYPE.SLOPE ? arrow.pitch : getDegreeByChon(arrow.pitch)}${pitchText})`, { + fontFamily: flowFontOptions.fontFamily.value, + fontWeight: flowFontOptions.fontWeight.value.toLowerCase().includes('bold') ? 'bold' : 'normal', + fontStyle: flowFontOptions.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal', + fontSize: flowFontOptions.fontSize.value, + fill: flowFontOptions.fontColor.value, + pitch: arrow.pitch, + originX: 'center', + originY: 'center', + name: 'flowText', + originText: text, + selectable: false, + left: arrow.stickeyPoint.x, + top: arrow.stickeyPoint.y, + parent: arrow, + parentId: arrow.id, + visible: isFlowDisplay, + }) + + polygon.canvas.add(textObj) } /** @@ -473,10 +645,11 @@ export const usePolygon = () => { const textStr = `${txt} (${currentAngleType === ANGLE_TYPE.SLOPE ? arrow.pitch : getDegreeByChon(arrow.pitch)}${pitchText})` const text = new fabric.Text(`${textStr}`, { + fontFamily: flowFontOptions.fontFamily.value, + fontWeight: flowFontOptions.fontWeight.value.toLowerCase().includes('bold') ? 'bold' : 'normal', + fontStyle: flowFontOptions.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal', fontSize: flowFontOptions.fontSize.value, fill: flowFontOptions.fontColor.value, - fontFamily: flowFontOptions.fontFamily.value, - fontWeight: flowFontOptions.fontWeight.value, pitch: arrow.pitch, originX: 'center', originY: 'center', @@ -496,62 +669,159 @@ export const usePolygon = () => { const splitPolygonWithLines = (polygon) => { polygon.set({ visible: false }) let innerLines = [...polygon.innerLines] + + canvas.renderAll() let polygonLines = [...polygon.lines] const roofs = [] - let delIndexs = [] - let newLines = [] + //polygonLines를 순회하며 innerLines와 교차하는 점을 line의 속성에 배열로 저장한다. + polygonLines.forEach((line) => { + let startPoint // 시작점 + let endPoint // 끝점 + if (line.x1 < line.x2) { + startPoint = { x: line.x1, y: line.y1 } + endPoint = { x: line.x2, y: line.y2 } + } else if (line.x1 > line.x2) { + startPoint = { x: line.x2, y: line.y2 } + endPoint = { x: line.x1, y: line.y1 } + } else { + if (line.y1 < line.y2) { + startPoint = { x: line.x1, y: line.y1 } + endPoint = { x: line.x2, y: line.y2 } + } else { + startPoint = { x: line.x2, y: line.y2 } + endPoint = { x: line.x1, y: line.y1 } + } + } - polygonLines.forEach((line, index) => { - line.tempIndex = index + line.startPoint = startPoint + line.endPoint = endPoint + }) + innerLines.forEach((line) => { + let startPoint // 시작점 + let endPoint // 끝점 + if (line.x1 < line.x2) { + startPoint = { x: line.x1, y: line.y1 } + endPoint = { x: line.x2, y: line.y2 } + } else if (line.x1 > line.x2) { + startPoint = { x: line.x2, y: line.y2 } + endPoint = { x: line.x1, y: line.y1 } + } else { + if (line.y1 < line.y2) { + startPoint = { x: line.x1, y: line.y1 } + endPoint = { x: line.x2, y: line.y2 } + } else { + startPoint = { x: line.x2, y: line.y2 } + endPoint = { x: line.x1, y: line.y1 } + } + } + + line.startPoint = startPoint + line.endPoint = endPoint + }) + + polygonLines.forEach((line) => { + line.set({ strokeWidth: 10 }) + canvas.add(line) + }) + canvas.renderAll() + + polygonLines.forEach((line) => { + const intersections = [] innerLines.forEach((innerLine) => { - let newLine1, newLine2 if (isPointOnLine(line, innerLine.startPoint)) { - // 해당 line을 startPoint로 나눈 line2개를 canvas에 추가 하고 기존 line을 제거한다. - newLine1 = new QLine([line.x1, line.y1, innerLine.startPoint.x, innerLine.startPoint.y], { - fontSize: polygon.fontSize, - stroke: 'black', - strokeWidth: 3, - }) - - newLine2 = new QLine([innerLine.startPoint.x, innerLine.startPoint.y, line.x2, line.y2], { - fontSize: polygon.fontSize, - stroke: 'black', - strokeWidth: 3, - }) - delIndexs.push(polygonLines.indexOf(line)) - canvas.remove(polygonLines[polygonLines.indexOf(line)]) - if (newLine1.length / 10 > 10) { - newLines.push(newLine1) - } - if (newLine2.length / 10 > 10) { - newLines.push(newLine2) + if (isSamePoint(line.startPoint, innerLine.startPoint) || isSamePoint(line.endPoint, innerLine.startPoint)) { + return } + intersections.push(innerLine.startPoint) } if (isPointOnLine(line, innerLine.endPoint)) { - newLine1 = new QLine([line.x1, line.y1, innerLine.endPoint.x, innerLine.endPoint.y], { - fontSize: polygon.fontSize, - stroke: 'black', - strokeWidth: 3, - }) - - newLine2 = new QLine([innerLine.endPoint.x, innerLine.endPoint.y, line.x2, line.y2], { - fontSize: polygon.fontSize, - stroke: 'black', - strokeWidth: 3, - }) - delIndexs.push(polygonLines.indexOf(line)) - canvas.remove(polygonLines[polygonLines.indexOf(line)]) - if (newLine1.length / 10 > 10) { - newLines.push(newLine1) - } - if (newLine2.length / 10 > 10) { - newLines.push(newLine2) + if (isSamePoint(line.startPoint, innerLine.endPoint) || isSamePoint(line.endPoint, innerLine.endPoint)) { + return } + intersections.push(innerLine.endPoint) } }) + line.set({ intersections }) }) - polygonLines = polygonLines.filter((line) => !delIndexs.includes(line.tempIndex)) + + const divideLines = polygonLines.filter((line) => line.intersections.length > 0) + let newLines = [] + + divideLines.forEach((line) => { + const { intersections, startPoint, endPoint } = line + + if (intersections.length === 1) { + // 한 점만 교차하는 경우 + const newLinePoint1 = [line.x1, line.y1, intersections[0].x, intersections[0].y] + const newLinePoint2 = [intersections[0].x, intersections[0].y, line.x2, line.y2] + const newLine1 = new QLine(newLinePoint1, { + stroke: 'blue', + strokeWidth: 3, + fontSize: polygon.fontSize, + attributes: line.attributes, + }) + const newLine2 = new QLine(newLinePoint2, { + stroke: 'blue', + strokeWidth: 3, + fontSize: polygon.fontSize, + attributes: line.attributes, + }) + newLine1.attributes = { + ...line.attributes, + planeSize: Math.round(Math.sqrt(Math.pow(newLine1.x1 - newLine1.x2, 2) + Math.pow(newLine1.y1 - newLine1.y2, 2))) * 10, + actualSize: Math.round(Math.sqrt(Math.pow(newLine1.x1 - newLine1.x2, 2) + Math.pow(newLine1.y1 - newLine1.y2, 2))) * 10, + } + newLine2.attributes = { + ...line.attributes, + planeSize: Math.round(Math.sqrt(Math.pow(newLine2.x1 - newLine2.x2, 2) + Math.pow(newLine2.y1 - newLine2.y2, 2))) * 10, + actualSize: Math.round(Math.sqrt(Math.pow(newLine2.x1 - newLine2.x2, 2) + Math.pow(newLine2.y1 - newLine2.y2, 2))) * 10, + } + newLines.push(newLine1) + newLines.push(newLine2) + } else { + // 두 점 이상 교차하는 경우 + //1. intersections중에 startPoint와 가장 가까운 점을 찾는다. + //2. 가장 가까운 점을 기준으로 line을 나눈다. + + let currentPoint = startPoint + + while (intersections.length !== 0) { + const minDistancePoint = findAndRemoveClosestPoint(currentPoint, intersections) + const newLinePoint = [currentPoint.x, currentPoint.y, minDistancePoint.x, minDistancePoint.y] + const newLine = new QLine(newLinePoint, { + stroke: 'blue', + strokeWidth: 3, + fontSize: polygon.fontSize, + attributes: line.attributes, + }) + newLine.attributes = { + ...line.attributes, + planeSize: Math.round(Math.sqrt(Math.pow(newLine.x1 - newLine.x2, 2) + Math.pow(newLine.y1 - newLine.y2, 2))) * 10, + actualSize: Math.round(Math.sqrt(Math.pow(newLine.x1 - newLine.x2, 2) + Math.pow(newLine.y1 - newLine.y2, 2))) * 10, + } + newLines.push(newLine) + currentPoint = minDistancePoint + } + + const newLinePoint = [currentPoint.x, currentPoint.y, endPoint.x, endPoint.y] + const newLine = new QLine(newLinePoint, { + stroke: 'blue', + strokeWidth: 3, + fontSize: polygon.fontSize, + attributes: line.attributes, + }) + newLine.attributes = { + ...line.attributes, + planeSize: Math.round(Math.sqrt(Math.pow(newLine.x1 - newLine.x2, 2) + Math.pow(newLine.y1 - newLine.y2, 2))) * 10, + actualSize: Math.round(Math.sqrt(Math.pow(newLine.x1 - newLine.x2, 2) + Math.pow(newLine.y1 - newLine.y2, 2))) * 10, + } + newLines.push(newLine) + } + }) + + //polygonLines에서 divideLines를 제거하고 newLines를 추가한다. + polygonLines = polygonLines.filter((line) => !divideLines.includes(line)) polygonLines = [...polygonLines, ...newLines] const allLines = [...polygonLines, ...innerLines] @@ -583,6 +853,9 @@ export const usePolygon = () => { }) polygonLines.forEach((line) => { + line.set({ strokeWidth: 5, stroke: 'green' }) + canvas.add(line) + canvas.renderAll() const startPoint = line.startPoint // 시작점 let arrivalPoint = line.endPoint // 도착점 @@ -646,17 +919,18 @@ export const usePolygon = () => { roofPoints.push(currentPoint) cnt++ if (cnt > 100) { - throw new Error('무한루프') + break } } roofs.push(roofPoints) + canvas.remove(line) + canvas.renderAll() }) - const newRoofs = removeDuplicatePolygons(roofs) + const newRoofs = removeDuplicatePolygons(roofs.filter((roof) => roof.length < 100)) newRoofs.forEach((roofPoint, index) => { let defense, pitch - const polygonLines = [...polygon.lines] let representLines = [] let representLine @@ -728,7 +1002,7 @@ export const usePolygon = () => { //allLines중 생성된 roof와 관련있는 line을 찾는다. - roof.lines = [...polygon.lines, ...polygon.innerLines].filter((line) => { + roof.lines = [...polygonLines, ...polygon.innerLines].filter((line) => { let startFlag = false let endFlag = false const startPoint = line.startPoint diff --git a/src/lib/file.js b/src/lib/file.js deleted file mode 100644 index 418fbe96..00000000 --- a/src/lib/file.js +++ /dev/null @@ -1,22 +0,0 @@ -'use server' -import path from 'path' -import multer from 'multer' - -export const upload = (files) => { - console.log(files) - const storage = multer.diskStorage({ - destination: (req, file, callback) => { - const extension = path.extname(file.originalname) - const basename = path.basename(file.originalname, extension) - callback(null, `/public/upload/${basename}-${Date.now()}${extension}`) - }, - filename: (req, file, callback) => { - callback(null, `${file.fieldname}-${Date.now()}${path.extname(file.originalname)}`) - } - }) - const test = multer({ - storage: storage - }).array(files.name, 5) - - console.log(test) -} diff --git a/src/lib/user.js b/src/lib/user.js deleted file mode 100644 index d79e958a..00000000 --- a/src/lib/user.js +++ /dev/null @@ -1,41 +0,0 @@ -'use server' - -import { getSession } from './authActions' - -const { PrismaClient } = require('@prisma/client') - -const prisma = new PrismaClient() - -export async function getUserByIdAndPassword({ userId, password }) { - return prisma.m_USER.findFirst({ - where: { - USER_ID: userId, - PASSWORD: password, - }, - }) -} - -export async function getUser(userId) { - return prisma.m_USER.findUnique({ - where: { - user_id: userId, - }, - }) -} - -export async function getUsers() { - return prisma.m_USER.findMany({ - where: { - // USER_ID: 'daiwajoho01', - USER_ID: { in: ['daiwajoho01', 'daiwajoho', 'daiwabutsuryu'] }, - }, - }) -} - -export async function checkSession() { - const session = await getSession() - - return { - session, - } -} diff --git a/src/locales/ja.json b/src/locales/ja.json index 15f2473b..64902f47 100644 --- a/src/locales/ja.json +++ b/src/locales/ja.json @@ -616,9 +616,9 @@ "stuff.planReqPopup.title": "設計依頼のインポート", "stuff.temp.subTitle": "商品情報", "stuff.temp.subTitle2": "作図", - "stuff.detail.header.message1": "存在しないものです。", - "stuff.detail.header.message2": "商品番号がコピーされました。", - "stuff.detail.header.message3": "存在しないものです。", + "stuff.detail.header.notExistObjectNo": "存在しないものです。", + "stuff.detail.header.successCopy": "商品番号がコピーされました。", + "stuff.detail.header.failCopy": "存在しないものです。", "stuff.detail.header.objectNo": "商品番号のコピーに失敗しました。", "stuff.detail.header.specificationConfirmDate": "仕様拡張日", "stuff.detail.header.lastEditDatetime": "更新日時", @@ -694,7 +694,8 @@ "stuff.planReqPopup.search.period": "期間検索", "stuff.planReqPopup.search.schDateGbnS": "提出日", "stuff.planReqPopup.search.schDateGbnR": "受付日", - "stuff.planReqPopup.error.message1": "設計依頼を選択してください。", + "stuff.planReqPopup.error.message1": "設計依頼を選択してください.", + "stuff.planReqPopup.error.message2": "販売店を選択してください.", "stuff.search.title": "物件状況", "stuff.search.btn1": "新規 物件 登録", "stuff.search.btn2": "照会", @@ -830,10 +831,10 @@ "estimate.detail.header.fileList2": "添付ファイル一覧", "estimate.detail.header.specialEstimate": "見積もりの具体的な", "estimate.detail.header.specialEstimateProductInfo": "製品情報", - "estimate.detail.sepcialEstimateProductInfo.totPcs": "数量 (PCS)", - "estimate.detail.sepcialEstimateProductInfo.vol": "容量 (Kw)", - "estimate.detail.sepcialEstimateProductInfo.netAmt": "供給価格", - "estimate.detail.sepcialEstimateProductInfo.vat": "付加価値税 (10%)", + "estimate.detail.sepcialEstimateProductInfo.totAmount": "数量 (PCS)", + "estimate.detail.sepcialEstimateProductInfo.totVolKw": "容量 (Kw)", + "estimate.detail.sepcialEstimateProductInfo.supplyPrice": "供給価格", + "estimate.detail.sepcialEstimateProductInfo.vatPrice": "付加価値税 (10%)", "estimate.detail.sepcialEstimateProductInfo.totPrice": "総額", "estimate.detail.sepcialEstimateProductInfo.pkgUnitPrice": "住宅PKG単価 (W)", "estimate.detail.sepcialEstimateProductInfo.pkgWeight": "PKG容量 (Kw)", @@ -847,15 +848,15 @@ "estimate.detail.showPrice.description2": "追加, 変更資材", "estimate.detail.showPrice.description3": "添付必須", "estimate.detail.showPrice.description4": "クリックして製品の特異性を確認する", - "estimate.detail.showPrice.btn2": "製品を追加", - "estimate.detail.showPrice.btn3": "製品削除", - "estimate.detail.itemTableHeader.col1": "アイテム", - "estimate.detail.itemTableHeader.col2": "品番", - "estimate.detail.itemTableHeader.col3": "型板", - "estimate.detail.itemTableHeader.col4": "数量", - "estimate.detail.itemTableHeader.col5": "単位", - "estimate.detail.itemTableHeader.col6": "単価", - "estimate.detail.itemTableHeader.col7": "金額 (税別別)", + "estimate.detail.showPrice.addItem": "製品を追加", + "estimate.detail.showPrice.delItem": "製品削除", + "estimate.detail.itemTableHeader.dispOrder": "アイテム", + "estimate.detail.itemTableHeader.itemId": "品番", + "estimate.detail.itemTableHeader.itemNo": "型板", + "estimate.detail.itemTableHeader.amount": "数量", + "estimate.detail.itemTableHeader.unit": "単位", + "estimate.detail.itemTableHeader.salePrice": "単価", + "estimate.detail.itemTableHeader.saleTotPrice": "金額 (税別別)", "estimate.detail.docPopup.title": "ドキュメントダウンロードオプションの設定", "estimate.detail.docPopup.explane": "ダウンロードする文書のオプションを選択したら、 [文書のダウンロード]ボタンをクリックします.", "estimate.detail.docPopup.schUnitPriceFlg": "ダウンロードファイル", @@ -874,6 +875,8 @@ "estimate.detail.docPopup.schDrawingFlg.schDrawingFlg1": "含まない", "estimate.detail.docPopup.close": "閉じる", "estimate.detail.docPopup.docDownload": "文書のダウンロード", + "estimate.detail.productFeaturesPopup.title": "製品特異事項", + "estimate.detail.productFeaturesPopup.close": "閉じる", "estimate.detail.save.alertMsg": "保存されている見積書で製品を変更した場合、図面や回路には反映されません.", "estimate.detail.save.requiredMsg": "ファイル添付が必須のアイテムがあります。ファイルを添付するか、後日添付をチェックしてください.", "estimate.detail.reset.confirmMsg": "保存した見積書情報が初期化され、図面情報が反映されます。本当に初期化しますか?", diff --git a/src/locales/ko.json b/src/locales/ko.json index d52334bf..5b47e7fd 100644 --- a/src/locales/ko.json +++ b/src/locales/ko.json @@ -400,6 +400,9 @@ "modal.module.circuit.number.edit": "모듈 일괄 회로 번호 변경", "modal.module.circuit.number.edit.info": "회로 번호를 입력해주세요.", "modal.module.circuit.number": "회로 번호", + "modal.line.property.change": "변경할 속성을 선택해 주세요.", + "modal.line.property.change.unselect": "변경할 라인을 선택해 주세요.", + "modal.line.property.change.confirm": "속성을 변경하시겠습니까?", "common.message.no.data": "No data", "common.message.no.dataDown": "No data to download", "common.message.noData": "No data to display", @@ -495,6 +498,7 @@ "commons.east": "동", "commons.south": "남", "commons.north": "북", + "commons.none": "선택안함", "font.style.normal": "보통", "font.style.italic": "기울임꼴", "font.style.bold": "굵게", @@ -622,9 +626,9 @@ "stuff.planReqPopup.title": "설계의뢰 불러오기", "stuff.temp.subTitle": "물건정보", "stuff.temp.subTitle2": "도면작성", - "stuff.detail.header.message1": "존재하지 않는 물건입니다.", - "stuff.detail.header.message2": "물건번호가 복사되었습니다.", - "stuff.detail.header.message3": "물건번호 복사에 실패했습니다.", + "stuff.detail.header.notExistObjectNo": "존재하지 않는 물건입니다.", + "stuff.detail.header.successCopy": "물건번호가 복사되었습니다.", + "stuff.detail.header.failCopy": "물건번호 복사에 실패했습니다.", "stuff.detail.header.objectNo": "물건번호", "stuff.detail.header.specificationConfirmDate": "사양확장일", "stuff.detail.header.lastEditDatetime": "갱신일시", @@ -701,6 +705,7 @@ "stuff.planReqPopup.search.schDateGbnS": "제출일", "stuff.planReqPopup.search.schDateGbnR": "접수일", "stuff.planReqPopup.error.message1": "설계의뢰를 선택해주세요.", + "stuff.planReqPopup.error.message2": "판매점을 선택해주세요.", "stuff.search.title": "물건현황", "stuff.search.btn1": "신규 물건 등록", "stuff.search.btn2": "조회", @@ -836,10 +841,10 @@ "estimate.detail.header.fileList2": "첨부파일 목록", "estimate.detail.header.specialEstimate": "견적특이사항", "estimate.detail.header.specialEstimateProductInfo": "제품정보", - "estimate.detail.sepcialEstimateProductInfo.totPcs": "수량 (PCS)", - "estimate.detail.sepcialEstimateProductInfo.vol": "용량 (Kw)", - "estimate.detail.sepcialEstimateProductInfo.netAmt": "공급가액", - "estimate.detail.sepcialEstimateProductInfo.vat": "부가세 (10%)", + "estimate.detail.sepcialEstimateProductInfo.totAmount": "수량 (PCS)", + "estimate.detail.sepcialEstimateProductInfo.totVolKw": "용량 (Kw)", + "estimate.detail.sepcialEstimateProductInfo.supplyPrice": "공급가액", + "estimate.detail.sepcialEstimateProductInfo.vatPrice": "부가세 (10%)", "estimate.detail.sepcialEstimateProductInfo.totPrice": "총액", "estimate.detail.sepcialEstimateProductInfo.pkgUnitPrice": "주택PKG단가 (W)", "estimate.detail.sepcialEstimateProductInfo.pkgWeight": "PKG 용량 (Kw)", @@ -853,15 +858,15 @@ "estimate.detail.showPrice.description2": "추가, 변경 자재", "estimate.detail.showPrice.description3": "첨부필수", "estimate.detail.showPrice.description4": "클릭하여 제품 특이사항 확인", - "estimate.detail.showPrice.btn2": "제품추가", - "estimate.detail.showPrice.btn3": "제품삭제", - "estimate.detail.itemTableHeader.col1": "Item", - "estimate.detail.itemTableHeader.col2": "품번", - "estimate.detail.itemTableHeader.col3": "형명", - "estimate.detail.itemTableHeader.col4": "수량", - "estimate.detail.itemTableHeader.col5": "단위", - "estimate.detail.itemTableHeader.col6": "단가", - "estimate.detail.itemTableHeader.col7": "금액(부가세별도)", + "estimate.detail.showPrice.addItem": "제품추가", + "estimate.detail.showPrice.delItem": "제품삭제", + "estimate.detail.itemTableHeader.dispOrder": "Item", + "estimate.detail.itemTableHeader.itemId": "품번", + "estimate.detail.itemTableHeader.itemNo": "형명", + "estimate.detail.itemTableHeader.amount": "수량", + "estimate.detail.itemTableHeader.unit": "단위", + "estimate.detail.itemTableHeader.salePrice": "단가", + "estimate.detail.itemTableHeader.saleTotPrice": "금액(부가세별도)", "estimate.detail.docPopup.title": "문서다운로드 옵션설정", "estimate.detail.docPopup.explane": "다운로드할 문서 옵션을 선택한 후 문서 다운로드 버튼을 클릭합니다.", "estimate.detail.docPopup.schUnitPriceFlg": "다운로드 파일", @@ -880,6 +885,8 @@ "estimate.detail.docPopup.schDrawingFlg.schDrawingFlg1": "미포함", "estimate.detail.docPopup.close": "닫기", "estimate.detail.docPopup.docDownload": "문서 다운로드", + "estimate.detail.productFeaturesPopup.title": "제품특이사항", + "estimate.detail.productFeaturesPopup.close": "닫기", "estimate.detail.save.alertMsg": "저장되었습니다. 견적서에서 제품을 변경할 경우, 도면 및 회로에 반영되지 않습니다.", "estimate.detail.save.requiredMsg": "파일첨부가 필수인 아이템이 있습니다. 파일을 첨부하거나 후일첨부를 체크해주십시오.", "estimate.detail.reset.confirmMsg": "저장된 견적서 정보가 초기화되고, 도면정보가 반영됩니다. 정말로 초기화 하시겠습니까?", diff --git a/src/store/canvasAtom.js b/src/store/canvasAtom.js index 0242a7b2..588808b1 100644 --- a/src/store/canvasAtom.js +++ b/src/store/canvasAtom.js @@ -363,3 +363,24 @@ export const showAngleUnitSelector = selector({ return roofAngleSet === 'slope' ? '寸' : '°' }, }) + +export const moduleSetupSurfaceState = atom({ + key: 'moduleSetupSurfaceState', + default: [], + dangerouslyAllowMutability: true, +}) + +export const moduleInstSurfaceSelector = selector({ + key: 'moduleInstSurfaceSelector', + get: ({ get, parentId }) => { + const moduleSetupSurfaceStateValue = get(moduleSetupSurfaceState) + return moduleSetupSurfaceStateValue.filter((obj) => obj.parentId === parentId) + }, +}) + +//셀 그린 이후에 생성하는 state +export const moduleIsSetupState = atom({ + key: 'moduleIsSetupState', + default: [], + dangerouslyAllowMutability: true, +}) diff --git a/src/store/commonUtilsAtom.js b/src/store/commonUtilsAtom.js index 545f8196..29ef7982 100644 --- a/src/store/commonUtilsAtom.js +++ b/src/store/commonUtilsAtom.js @@ -11,10 +11,6 @@ export const dimensionLineSettingsState = atom({ default: { pixel: 1, color: '#000000', - font: 'Arial', - fontColor: '#000000', - fontSize: 15, - fontStyle: 'normal', }, }) diff --git a/src/store/orientationAtom.js b/src/store/orientationAtom.js new file mode 100644 index 00000000..d403c221 --- /dev/null +++ b/src/store/orientationAtom.js @@ -0,0 +1,6 @@ +import { atom } from 'recoil' + +export const compasDegAtom = atom({ + key: 'compasDegAtom', + default: 0, +}) diff --git a/src/styles/_contents.scss b/src/styles/_contents.scss index 6715d48b..441fccb4 100644 --- a/src/styles/_contents.scss +++ b/src/styles/_contents.scss @@ -17,1444 +17,1497 @@ // } // } // CanvasMenu -.canvas-menu-wrap{ - position: fixed; - top: 46px; - left: 0; - display: block; - width: 100%; - min-width: 1280px; - padding-bottom: 0; - background-color: #383838; - transition: padding .17s ease-in-out; - z-index: 999; - .canvas-menu-inner{ - position: relative; - display: flex; - align-items: center; - padding: 0 40px 0 20px; - background-color: #2C2C2C; - height: 46.8px; - z-index: 999; - .canvas-menu-list{ - display: flex; - align-items: center; - height: 100%; - .canvas-menu-item{ - display: flex; - align-items: center; - height: 100%; - button{ - display: flex; - align-items: center; - font-size: 12px; - height: 100%; - color: #fff; - font-weight: 600; - padding: 15px 20px; - opacity: 0.55; - transition: all .17s ease-in-out; - .menu-icon{ - display: block; - width: 14px; - height: 14px; - background-repeat: no-repeat; - background-position: center; - background-size: contain; - margin-right: 10px; - &.con00{background-image: url(/static/images/canvas/menu_icon00.svg);} - &.con01{background-image: url(/static/images/canvas/menu_icon01.svg);} - &.con02{background-image: url(/static/images/canvas/menu_icon02.svg);} - &.con03{background-image: url(/static/images/canvas/menu_icon03.svg);} - &.con04{background-image: url(/static/images/canvas/menu_icon04.svg);} - &.con05{background-image: url(/static/images/canvas/menu_icon05.svg);} - &.con06{background-image: url(/static/images/canvas/menu_icon06.svg);} - } - } - &.active{ - background-color: #383838; - button{ - opacity: 1; - } - } - } - } - .canvas-side-btn-wrap{ - display: flex; - align-items: center; - margin-left: auto; - .select-box{ - width: 124px; - margin: 0 5px; - height: 30px; - > div{ - width: 100%; - } - } - .btn-from{ - display: flex; - align-items: center; - gap: 5px; - button{ - display: block; - width: 30px; - height: 30px; - border-radius: 2px; - background-color: #3D3D3D; - background-position: center; - background-repeat: no-repeat; - background-size: 15px 15px; - transition: all .17s ease-in-out; - &.btn01{background-image: url(../../public/static/images/canvas/side_icon03.svg);} - &.btn02{background-image: url(../../public/static/images/canvas/side_icon02.svg);} - &.btn03{background-image: url(../../public/static/images/canvas/side_icon01.svg);} - &.btn04{background-image: url(../../public/static/images/canvas/side_icon04.svg);} - &.btn05{background-image: url(../../public/static/images/canvas/side_icon05.svg);} - &.btn06{background-image: url(../../public/static/images/canvas/side_icon06.svg);} - &.btn07{background-image: url(../../public/static/images/canvas/side_icon07.svg);} - &.btn08{background-image: url(../../public/static/images/canvas/side_icon08.svg);} - &.btn09{background-image: url(../../public/static/images/canvas/side_icon09.svg);} - &:hover{ - background-color: #1083E3; - } - &.active{ - background-color: #1083E3; - } - } - } - .ico-btn-from{ - display: flex; - align-items: center; - gap: 5px; - button{ - .ico{ - display: block; - width: 15px; - height: 15px; - background-repeat: no-repeat; - background-position: center; - background-size: contain; - &.ico01{background-image: url(../../public/static/images/canvas/ico-flx01.svg);} - &.ico02{background-image: url(../../public/static/images/canvas/ico-flx02.svg);} - &.ico03{background-image: url(../../public/static/images/canvas/ico-flx03.svg);} - &.ico04{background-image: url(../../public/static/images/canvas/ico-flx04.svg);} - } - .name{ - font-size: 12px; - color: #fff; - } - } - &.form06{ - .name{ - font-size: 13px; - } - } - } - .vertical-horizontal{ - display: flex; - min-width: 170px; - height: 28px; - margin-right: 5px; - border-radius: 2px; - background: #373737; - line-height: 28px; - overflow: hidden; - span{ - padding: 0 10px; - font-size: 13px; - color: #fff; - } - button{ - margin-left: auto; - height: 100%; - background-color: #4B4B4B; - font-size: 13px; - font-weight: 400; - color: #fff; - padding: 0 7.5px; - transition: all .17s ease-in-out; - } - &.on{ - button{ - background-color: #1083E3; - } - } - } - .size-control{ - display: flex; - align-items: center; - justify-content: center; - gap: 10px; - background-color: #3D3D3D; - border-radius: 2px; - width: 100px; - height: 30px; - margin: 0 5px; - span{ - font-size: 13px; - color: #fff; - } - .control-btn{ - display: block; - width: 12px; - height: 12px; - background-repeat: no-repeat; - background-size: cover; - background-position: center; - &.minus{ - background-image: url(../../public/static/images/canvas/minus.svg); - } - &.plus{ - background-image: url(../../public/static/images/canvas/plus.svg); - } - } - } - } - } - .canvas-depth2-wrap{ - position: absolute; - top: -100%; - left: 0; - background-color: #383838; - width: 100%; - height: 50px; - transition: all .17s ease-in-out; - .canvas-depth2-inner{ - display: flex; - align-items: center; - padding: 0 40px; - height: 100%; - .canvas-depth2-list{ - display: flex; - align-items: center ; - height: 100%; - .canvas-depth2-item{ - display: flex; - align-items: center; - margin-right: 26px; - height: 100%; - button{ - position: relative; - opacity: 0.55; - color: #fff; - font-size: 12px; - font-weight: normal; - height: 100%; - padding-right: 12px; - } - &.active{ - button{ - opacity: 1; - font-weight: 600; - &:after{ - content: ''; - position: absolute; - top: 50%; - right: 0; - transform: translateY(-50%); - width: 5px; - height: 8px; - background: url(../../public/static/images/canvas/depth2-arr.svg) no-repeat center; - } - } - } - } - } - .canvas-depth2-btn-list{ - display: flex; - align-items: center; - margin-left: auto; - height: 100%; - .depth2-btn-box{ - display: flex; - align-items: center; - margin-right: 34px; - height: 100%; - transition: all .17s ease-in-out; - button{ - position: relative; - font-size: 12px; - font-weight: 400; - height: 100%; - color: #fff; - padding-right: 12px; - &:after{ - content: ''; - position: absolute; - top: 50%; - right: 0; - transform: translateY(-50%); - width: 5px; - height: 8px; - background: url(../../public/static/images/canvas/depth2-arr.svg) no-repeat center; - } - } - &:last-child{ - margin-right: 0; - } - &.mouse{ - opacity: 0.55; - } - } - } - } - &.active{ - top: 47px; - } - } - &.active{ - padding-bottom: 50px; - } -} - -// canvas-layout -.canvas-content{ - padding-top: 46.8px; - transition: all .17s ease-in-out; - .canvas-frame{ - height: calc(100vh - 129.3px); - } - &.active{ - padding-top: calc(46.8px + 50px); - .canvas-frame{ - height: calc(100vh - 179.4px); - } - } -} -.canvas-layout{ - padding-top: 37px; - .canvas-page-list{ - position: fixed; - top: 92.8px; - left: 0; - display: flex; - background-color: #1C1C1C; - border-top: 1px solid #000; - width: 100%; - min-width: 1280px; - transition: all .17s ease-in-out; - z-index: 99; - &.active{ - top: calc(92.8px + 50px); - } - .canvas-plane-wrap{ - display: flex; - align-items: center; - max-width: calc(100% - 45px); - .canvas-page-box{ - display: flex; - align-items: center; - background-color: #1c1c1c; - padding: 9.6px 20px; - border-right:1px solid #000; - min-width: 0; - transition: all .17s ease-in-out; - span{ - display: flex; - align-items: center; - width: 100%; - font-size: 12px; - font-family: 'Pretendard', sans-serif; - color: #AAA; - white-space: nowrap; - text-overflow: ellipsis; - overflow: hidden; - } - .close{ - flex: none; - display: block; - width: 7px; - height: 8px; - margin-left: 15px; - background: url(../../public/static/images/canvas/plan_close_gray.svg)no-repeat center; - background-size: cover; - } - &.on{ - background-color: #fff; - span{ - font-weight: 600; - color: #101010; - } - .close{ - background: url(../../public/static/images/canvas/plan_close_black.svg)no-repeat center; - } - &:hover{ - background-color: #fff; - } - } - &:hover{ - background-color: #000; - } - } - } - .plane-add{ - display: flex; - align-items: center; - justify-content: center; - width: 45px; - padding: 13.5px 0; - background-color: #1C1C1C; - border-right: 1px solid #000; - transition: all .17s ease-in-out; - span{ - display: block; - width: 9px; - height: 9px; - background: url(../../public/static/images/canvas/plane_add.svg)no-repeat center; - background-size: cover; - } - &:hover{ - background-color: #000; - } - } - } -} - -.canvas-frame{ +.canvas-menu-wrap { + position: fixed; + top: 46px; + left: 0; + display: block; + width: 100%; + min-width: 1280px; + padding-bottom: 0; + background-color: #383838; + transition: padding 0.17s ease-in-out; + z-index: 999; + .canvas-menu-inner { position: relative; - // height: calc(100% - 36.5px); - background-color: #F4F4F7; - overflow: auto; - transition: all .17s ease-in-out; - // &::-webkit-scrollbar { - // width: 10px; - // height: 10px; - // background-color: #fff; - // } - // &::-webkit-scrollbar-thumb { - // background-color: #C1CCD7; - // border-radius: 30px; - // } - // &::-webkit-scrollbar-track { - // background-color: #fff; - // } - .canvas-container{ - margin: 0 auto; - background-color: #fff; - } - canvas{ - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - } -} - -// sub-page -.sub-header{ - position: fixed; - top: 46px; - left: 0; - width: 100%; - min-width: 1280px; - height: 46px; - border-bottom: 1px solid #000; - background: #2C2C2C; + display: flex; + align-items: center; + padding: 0 40px 0 20px; + background-color: #2c2c2c; + height: 46.8px; z-index: 999; - .sub-header-inner{ + .canvas-menu-list { + display: flex; + align-items: center; + height: 100%; + .canvas-menu-item { display: flex; align-items: center; height: 100%; - padding: 0 100px; - .sub-header-title-wrap{ - display: flex; - align-items: center; - .title-item{ - position: relative; - padding: 0 24px; - a{ - display: flex; - align-items: center; - .icon{ - width: 22px; - height: 22px; - margin-right: 8px; - background-repeat: no-repeat; - background-position: center; - background-size: cover; - &.drawing{background-image: url(../../public/static/images/main/drawing_icon.svg);} - } - } - &:after{ - content: ''; - position: absolute; - top: 50%; - right: 0; - transform: translateY(-50%); - width: 1px; - height: 16px; - background-color: #D9D9D9; - } - &:first-child{ - padding-left: 0; - } - &:last-child{ - padding-right: 0; - &:after{ - display: none; - } - } + button { + display: flex; + align-items: center; + font-size: 12px; + height: 100%; + color: #fff; + font-weight: 600; + padding: 15px 20px; + opacity: 0.55; + transition: all 0.17s ease-in-out; + .menu-icon { + display: block; + width: 14px; + height: 14px; + background-repeat: no-repeat; + background-position: center; + background-size: contain; + margin-right: 10px; + &.con00 { + background-image: url(/static/images/canvas/menu_icon00.svg); } + &.con01 { + background-image: url(/static/images/canvas/menu_icon01.svg); + } + &.con02 { + background-image: url(/static/images/canvas/menu_icon02.svg); + } + &.con03 { + background-image: url(/static/images/canvas/menu_icon03.svg); + } + &.con04 { + background-image: url(/static/images/canvas/menu_icon04.svg); + } + &.con05 { + background-image: url(/static/images/canvas/menu_icon05.svg); + } + &.con06 { + background-image: url(/static/images/canvas/menu_icon06.svg); + } + } } - .sub-header-title{ - font-size: 16px; + &.active { + background-color: #383838; + button { + opacity: 1; + } + } + } + } + .canvas-side-btn-wrap { + display: flex; + align-items: center; + margin-left: auto; + .select-box { + width: 124px; + margin: 0 5px; + height: 30px; + > div { + width: 100%; + } + } + .btn-from { + display: flex; + align-items: center; + gap: 5px; + button { + display: block; + width: 30px; + height: 30px; + border-radius: 2px; + background-color: #3d3d3d; + background-position: center; + background-repeat: no-repeat; + background-size: 15px 15px; + transition: all 0.17s ease-in-out; + &.btn01 { + background-image: url(../../public/static/images/canvas/side_icon03.svg); + } + &.btn02 { + background-image: url(../../public/static/images/canvas/side_icon02.svg); + } + &.btn03 { + background-image: url(../../public/static/images/canvas/side_icon01.svg); + } + &.btn04 { + background-image: url(../../public/static/images/canvas/side_icon04.svg); + } + &.btn05 { + background-image: url(../../public/static/images/canvas/side_icon05.svg); + } + &.btn06 { + background-image: url(../../public/static/images/canvas/side_icon06.svg); + } + &.btn07 { + background-image: url(../../public/static/images/canvas/side_icon07.svg); + } + &.btn08 { + background-image: url(../../public/static/images/canvas/side_icon08.svg); + } + &.btn09 { + background-image: url(../../public/static/images/canvas/side_icon09.svg); + } + &.btn10 { + background-image: url(../../public/static/images/canvas/side_icon10.svg); + background-size: 15px 14px; + } + &:hover { + background-color: #1083e3; + } + &.active { + background-color: #1083e3; + } + } + } + .ico-btn-from { + display: flex; + align-items: center; + gap: 5px; + button { + .ico { + display: block; + width: 15px; + height: 15px; + background-repeat: no-repeat; + background-position: center; + background-size: contain; + &.ico01 { + background-image: url(../../public/static/images/canvas/ico-flx01.svg); + } + &.ico02 { + background-image: url(../../public/static/images/canvas/ico-flx02.svg); + } + &.ico03 { + background-image: url(../../public/static/images/canvas/ico-flx03.svg); + } + &.ico04 { + background-image: url(../../public/static/images/canvas/ico-flx04.svg); + } + } + .name { + font-size: 12px; color: #fff; - font-weight: 600; + } } - .sub-header-location{ - margin-left: auto; - display: flex; - align-items: center; - .location-item{ - position: relative; - display: flex; - align-items: center; - padding: 0 10px; - span{ - display: flex; - font-size: 12px; - color: #AAA; - font-weight: normal; - cursor: default; - } - &:after{ - content: ''; - position: absolute; - top: 50%; - right: 0; - transform: translateY(-50%); - width: 4px; - height: 6px; - background: url(../../public/static/images/main/loaction_arr.svg)no-repeat center; - } - &:first-child{ - padding-left: 0; - } - &:last-child{ - padding-right: 0; - span{ - color: #fff; - } - &:after{ - display: none; - } - } - } + &.form06 { + .name { + font-size: 13px; + } } - } -} - -// sub content -.sub-content{ - padding-top: 46px; - .sub-content-inner{ - max-width: 1760px; - margin: 0 auto; - padding: 20px 20px 0; - .sub-content-box{ - margin-bottom: 20px; - &:last-child{ - margin-bottom: 0; - } - } - } - &.estimate{ + } + .vertical-horizontal { display: flex; - flex-direction: column; - padding-top: 0; - .sub-content-inner{ - flex: 1; - width: 100%; + min-width: 170px; + height: 28px; + margin-right: 5px; + border-radius: 2px; + background: #373737; + line-height: 28px; + overflow: hidden; + span { + padding: 0 10px; + font-size: 13px; + color: #fff; } - } -} -.sub-table-box{ - padding: 20px; - border-radius: 6px; - border: 1px solid #E9EAED; - background: #FFF; - box-shadow: 0px 3px 30px 0px rgba(0, 0, 0, 0.02); - .table-box-title-wrap{ - display: flex; - align-items: center; - margin-bottom: 15px; - .title-wrap{ - display: flex; - align-items: center; - h3{ - display: block; - font-size: 15px; - color: #101010; - font-weight: 600; - margin-right: 14px; - &.product{ - margin-right: 10px; - } - } - .product_tit{ - position: relative; - font-size: 15px; - font-weight: 600; - color: #1083E3; - padding-left: 10px; - &::before{ - content: ''; - position: absolute; - top: 50%; - left: 0; - transform: translateY(-50%); - width: 1px; - height: 11px; - background-color: #D9D9D9; - } - } - .option{ - padding-left: 5px; - font-size: 13px; - color: #101010; - font-weight: 400; - } - .info-wrap{ - display: flex; - align-items: center; - li{ - position: relative; - padding: 0 6px; - font-size: 12px; - color: #101010; - font-weight: normal; - span{ - font-weight: 600; - &.red{ - color: #E23D70; - } - } - &:after{ - content: ''; - position: absolute; - top: 50%; - right: 0; - transform: translateY(-50%); - width: 1px; - height: 11px; - background-color: #D9D9D9; - } - &:first-child{padding-left: 0;} - &:last-child{padding-right: 0;&::after{display: none;}} - } - } + button { + margin-left: auto; + height: 100%; + background-color: #4b4b4b; + font-size: 13px; + font-weight: 400; + color: #fff; + padding: 0 7.5px; + transition: all 0.17s ease-in-out; } - } - .left-unit-box{ - margin-left: auto; - display: flex; - align-items: center; - } - .promise-gudie{ - display: block; - font-size: 13px; - font-weight: 700; - color: #101010; - margin-bottom: 20px; - } - .important{ - color: #f00; - } - .sub-center-footer{ + &.on { + button { + background-color: #1083e3; + } + } + } + .size-control { display: flex; align-items: center; justify-content: center; - margin-top: 20px; - } - .sub-right-footer{ - display: flex; - align-items: center; - justify-content: flex-end; - margin-top: 20px; - } -} -.pagination-wrap{ - margin-top: 24px; -} - -.infomation-wrap{ - margin-bottom: 30px; -} - -.infomation-box-wrap{ - display: flex; - gap: 10px; - .sub-table-box{ - flex: 1 ; - } - .info-title{ - font-size: 14px; - font-weight: 500; - color: #344356; - margin-bottom: 10px; - } - .info-inner{ - position: relative; - font-size: 13px; - color: #344356; - .copy-ico{ - position: absolute; - bottom: 0; - right: 0; - width: 16px; - height: 16px; - background: url(../../public/static/images/sub/copy_ico.svg)no-repeat center; - background-size: cover; + gap: 10px; + background-color: #3d3d3d; + border-radius: 2px; + width: 100px; + height: 30px; + margin: 0 5px; + span { + font-size: 13px; + color: #fff; } - } -} - -// 견적서 -.estimate-list-wrap{ - display: flex; - align-items: center; - margin-bottom: 10px; - &.one{ - .estimate-box{ - &:last-child{ - min-width: unset; - } + .control-btn { + display: block; + width: 12px; + height: 12px; + background-repeat: no-repeat; + background-size: cover; + background-position: center; + &.minus { + background-image: url(../../public/static/images/canvas/minus.svg); + } + &.plus { + background-image: url(../../public/static/images/canvas/plus.svg); + } } + } } - .estimate-box{ - flex: 1 ; - display: flex; - align-items: center; - &:last-child{ - flex: none; - min-width: 220px; - } - .estimate-tit{ - width: 105px; - height: 30px; - line-height: 30px; - background-color: #F4F4F7; - border-radius: 100px; - text-align: center; - font-size: 13px; - font-weight: 500; - color: #344356; - } - .estimate-name{ - font-size: 13px; - color: #344356; - margin-left: 14px; - font-weight: 400; - &.blue{ - font-size: 16px; - font-weight: 700; - color: #1083E3; - } - &.red{ - font-size: 16px; - font-weight: 700; - color: #D72A2A; - } - } - } - &:last-child{ - margin-bottom: 0; - } -} - -// file drag box -.drag-file-box{ - padding: 10px; - .btn-area{ - padding-bottom: 15px; - border-bottom: 1px solid #ECF0F4; - .file-upload{ - display: inline-block; - height: 30px; - background-color: #94A0AD; - padding: 0 10px; - border-radius: 2px; - font-size: 13px; - line-height: 30px; - color: #fff; - font-weight: 500; - cursor: pointer; - transition: background .15s ease-in-out; - &:hover{ - background-color: #607F9A; - } - } - } - .drag-file-area{ - position: relative; - margin-top: 15px; - p{ - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - font-size: 13px; - color: #ccc; - font-weight: 400; - cursor: default; - } - } - .file-list{ - .file-item{ - margin-bottom: 15px; - span{ - position: relative; - font-size: 13px; - color: #45576F; - font-weight: 400; - white-space: nowrap; - padding-right: 55px; - button{ - position: absolute; - top: 50%; - right: 0; - transform: translateY(-50%); - width: 15px; - height: 15px; - background: url(../../public/static/images/sub/file_delete.svg)no-repeat center; - background-size: cover; - } - } - &:last-child{ - margin-bottom: 0; - } - } - } -} - -.estimate-arr-btn{ - display: block; - width: 20px; - height: 20px; - background-color: #94A0AD; - border: 1px solid #94A0AD; - background-position: center; - background-repeat: no-repeat; - background-image: url(../../public/static/images/canvas/estiment_arr.svg); - background-size: 11px 7px; - border-radius: 2px; - &.up{ - rotate: 180deg; - } - &.on{ - background-color: #fff; - border-color: #C2D0DD; - background-image: url(../../public/static/images/canvas/estiment_arr_color.svg) - } -} -.estimate-check-wrap{ - .estimate-check-inner{ - display: block; - } - &.hide{ - border-bottom: 1px solid #ECF0F4; - margin-bottom: 15px; - .estimate-check-inner{ - display: none; - } - } -} - -.special-note-check-wrap{ - display: grid; - grid-template-columns: repeat(5, 1fr); - border-radius: 3px; - margin-bottom: 30px; - .special-note-check-item{ - padding: 14px 10px; - border: 1px solid #ECF0F4; - margin-top: -1px; - margin-right: -1px; - &.act{ - background-color: #F7F9FA; - } - } -} - -.calculation-estimate{ - border: 1px solid #ECF0F4; - border-radius: 3px; - padding: 24px; - max-height: 350px; - overflow-y: auto; - margin-bottom: 30px; - dl{ - margin-bottom: 35px; - &:last-child{ - margin-bottom: 0; - } - dt{ - font-size: 13px; - font-weight: 600; - color: #1083E3; - margin-bottom: 15px; - } - dd{ - font-size: 12px; - font-weight: 400; - color: #45576F; - margin-bottom: 8px; - &:last-child{ - margin-bottom: 0; - } - } - } - &::-webkit-scrollbar { - width: 4px; - background-color: transparent; - } - &::-webkit-scrollbar-thumb { - background-color: #d9dee2; - } - &::-webkit-scrollbar-track { - background-color: transparent; - } -} -.esimate-wrap{ - margin-bottom: 20px; -} - -.estimate-product-option{ - display: flex; - align-items: center; - margin-bottom: 15px; - .product-price-wrap{ - display: flex; - align-items: center; - .product-price-tit{ - font-size: 13px; - font-weight: 400; - color: #45576F; - margin-right: 10px; - } - .select-wrap{ - width: 110px; - } - } - .product-edit-wrap{ - display: flex; - align-items: center; - margin-left: auto; - .product-edit-explane{ - display: flex; - align-items: center; - margin-right: 15px; - .explane-item{ - position: relative; - display: flex; - align-items: center; - padding: 0 10px; - font-size: 12px; - font-weight: 400; - span{ - width: 20px; - height: 20px; - margin-right: 5px; - background-size: cover; - background-repeat: no-repeat; - background-position: center; - } - &:before{ - content: ''; - position: absolute; - top: 50%; - left: 0; - transform: translateY(-50%); - width: 1px; - height: 12px; - background-color: #D9D9D9; - } - &:first-child{ - padding-left: 0; - &::before{ - display: none; - } - } - &:last-child{ - padding-right: 0; - } - &.item01{ - color: #3BBB48; - span{ - background-image: url(../../public/static/images/sub/open_ico.svg); - } - } - &.item02{ - color: #909000; - span{ - background-image: url(../../public/static/images/sub/change_ico.svg); - } - } - &.item03{ - color: #0191C9; - span{ - background-image: url(../../public/static/images/sub/attachment_ico.svg); - } - } - &.item04{ - color: #F16A6A; - span{ - background-image: url(../../public/static/images/sub/click_check_ico.svg); - } - } - } - } - .product-edit-btn{ - display: flex; - align-items: center; - button{ - display: flex; - align-items: center; - span{ - width: 13px; - height: 13px; - margin-right: 5px; - background-size: cover; - &.plus{ - background: url(../../public/static/images/sub/plus_btn.svg)no-repeat center; - } - &.minus{ - background: url(../../public/static/images/sub/minus_btn.svg)no-repeat center; - } - } - } - } - } -} - -// 발전시물레이션 -.chart-wrap{ - display: flex; - gap: 20px; + } + .canvas-depth2-wrap { + position: absolute; + top: -100%; + left: 0; + background-color: #383838; width: 100%; - .sub-table-box{ - height: 100%; - } - .chart-inner{ - flex: 1; - .chart-box{ - margin-bottom: 30px; - } - } - .chart-table-wrap{ + height: 50px; + transition: all 0.17s ease-in-out; + .canvas-depth2-inner { + display: flex; + align-items: center; + padding: 0 40px; + height: 100%; + .canvas-depth2-list { display: flex; - flex-direction: column; - flex: none; - width: 650px; - .sub-table-box{ - flex: 1; - &:first-child{ - margin-bottom: 20px; - } - } - } -} - -.chart-month-table{ - table{ - table-layout: fixed; - border-collapse:collapse; - border: 1px solid #ECF0F4; - border-radius: 4px; - thead{ - th{ - padding: 4.5px 0; - border-bottom: 1px solid #ECF0F4; - text-align: center; - font-size: 13px; - color: #45576F; - font-weight: 500; - background-color: #F8F9FA; - } - } - tbody{ - td{ - font-size: 13px; - color: #45576F; - text-align: center; - padding: 4.5px 0; - } - } - } -} - -.simulation-guide-wrap{ - display: flex; - padding: 20px; - .simulation-tit-wrap{ - flex: none; - padding-right: 40px; - border-right: 1px solid #EEEEEE; - span{ - display: block; + align-items: center; + height: 100%; + .canvas-depth2-item { + display: flex; + align-items: center; + margin-right: 26px; + height: 100%; + button { position: relative; - padding-left: 60px; - font-size: 15px; - color: #14324F; - font-weight: 600; - &::before{ + opacity: 0.55; + color: #fff; + font-size: 12px; + font-weight: normal; + height: 100%; + padding-right: 12px; + } + &.active { + button { + opacity: 1; + font-weight: 600; + &:after { content: ''; position: absolute; top: 50%; - left: 0; + right: 0; transform: translateY(-50%); - width: 40px; - height: 40px; - background: url(../../public/static/images/sub/simulation_guide.svg)no-repeat center; - background-size: cover; + width: 5px; + height: 8px; + background: url(../../public/static/images/canvas/depth2-arr.svg) no-repeat center; + } } + } } - } - .simulation-guide-box{ - flex: 1; - padding-left: 40px; - dl{ - margin-bottom: 25px; - dt{ - font-size: 13px; - color: #101010; - font-weight: 600; - margin-bottom: 5px; - } - dd{ - font-size: 12px; - color: #45576F; - font-weight: 400; - line-height: 24px; - } - &:last-child{ - margin-bottom: 0; - } - } - ul, ol{ - list-style: unset; - } - } -} - -.module-total{ - display: flex; - align-items: center; - background-color: #F8F9FA; - padding: 9px 0; - margin-right: 4px; - border: 1px solid #ECF0F4; - border-top: none; - .total-title{ - flex: 1; - text-align: center; - font-size: 13px; - color: #344356; - font-weight: 500; - } - .total-num{ - flex: none; - width: 121px; - text-align: center; - font-size: 15px; - color: #344356; - font-weight: 500; - } -} - -// 물건상세 -.information-help-wrap{ - display: flex; - padding: 24px; - background-color: #F4F4F4; - border-radius: 4px; - margin-bottom: 15px; - .information-help-tit-wrap{ - position: relative; + } + .canvas-depth2-btn-list { display: flex; align-items: center; - padding-right: 40px; - border-right: 1px solid #E0E0E3; - .help-tit-icon{ - width: 40px; - height: 40px; - border-radius: 50%; - margin-right: 10px; - background: #fff url(../../public/static/images/sub/information_help.svg)no-repeat center; - background-size: 20px 20px; - } - .help-tit{ - font-size: 13px; - font-weight: 600; - color: #45576F; - } - } - .information-help-guide{ - padding-left: 40px; - span{ - display: block; + margin-left: auto; + height: 100%; + .depth2-btn-box { + display: flex; + align-items: center; + margin-right: 34px; + height: 100%; + transition: all 0.17s ease-in-out; + button { + position: relative; font-size: 12px; font-weight: 400; - color: #45576F; - margin-bottom: 7px; - &:last-child{ - margin-bottom: 0; + height: 100%; + color: #fff; + padding-right: 12px; + &:after { + content: ''; + position: absolute; + top: 50%; + right: 0; + transform: translateY(-50%); + width: 5px; + height: 8px; + background: url(../../public/static/images/canvas/depth2-arr.svg) no-repeat center; } + } + &:last-child { + margin-right: 0; + } + &.mouse { + opacity: 0.55; + } } + } } + &.active { + top: 47px; + } + } + &.active { + padding-bottom: 50px; + } } -.community-search-warp{ +// canvas-layout +.canvas-content { + padding-top: 46.8px; + transition: all 0.17s ease-in-out; + .canvas-frame { + height: calc(100vh - 129.3px); + } + &.active { + padding-top: calc(46.8px + 50px); + .canvas-frame { + height: calc(100vh - 179.4px); + } + } +} +.canvas-layout { + padding-top: 37px; + .canvas-page-list { + position: fixed; + top: 92.8px; + left: 0; + display: flex; + background-color: #1c1c1c; + border-top: 1px solid #000; + width: 100%; + min-width: 1280px; + transition: all 0.17s ease-in-out; + z-index: 99; + &.active { + top: calc(92.8px + 50px); + } + .canvas-plane-wrap { + display: flex; + align-items: center; + max-width: calc(100% - 45px); + .canvas-page-box { + display: flex; + align-items: center; + background-color: #1c1c1c; + padding: 9.6px 20px; + border-right: 1px solid #000; + min-width: 0; + transition: all 0.17s ease-in-out; + span { + display: flex; + align-items: center; + width: 100%; + font-size: 12px; + font-family: 'Pretendard', sans-serif; + color: #aaa; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + } + .close { + flex: none; + display: block; + width: 7px; + height: 8px; + margin-left: 15px; + background: url(../../public/static/images/canvas/plan_close_gray.svg) no-repeat center; + background-size: cover; + } + &.on { + background-color: #fff; + span { + font-weight: 600; + color: #101010; + } + .close { + background: url(../../public/static/images/canvas/plan_close_black.svg) no-repeat center; + } + &:hover { + background-color: #fff; + } + } + &:hover { + background-color: #000; + } + } + } + .plane-add { + display: flex; + align-items: center; + justify-content: center; + width: 45px; + padding: 13.5px 0; + background-color: #1c1c1c; + border-right: 1px solid #000; + transition: all 0.17s ease-in-out; + span { + display: block; + width: 9px; + height: 9px; + background: url(../../public/static/images/canvas/plane_add.svg) no-repeat center; + background-size: cover; + } + &:hover { + background-color: #000; + } + } + } +} + +.canvas-frame { + position: relative; + // height: calc(100% - 36.5px); + background-color: #f4f4f7; + overflow: auto; + transition: all 0.17s ease-in-out; + // &::-webkit-scrollbar { + // width: 10px; + // height: 10px; + // background-color: #fff; + // } + // &::-webkit-scrollbar-thumb { + // background-color: #C1CCD7; + // border-radius: 30px; + // } + // &::-webkit-scrollbar-track { + // background-color: #fff; + // } + .canvas-container { + margin: 0 auto; + background-color: #fff; + } + canvas { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + } +} + +// sub-page +.sub-header { + position: fixed; + top: 46px; + left: 0; + width: 100%; + min-width: 1280px; + height: 46px; + border-bottom: 1px solid #000; + background: #2c2c2c; + z-index: 999; + .sub-header-inner { display: flex; - flex-direction: column; align-items: center; - padding: 10px 0 30px 0; - border-bottom: 1px solid #E5E5E5; - margin-bottom: 24px; - .community-search-box{ + height: 100%; + padding: 0 100px; + .sub-header-title-wrap { + display: flex; + align-items: center; + .title-item { + position: relative; + padding: 0 24px; + a { + display: flex; + align-items: center; + .icon { + width: 22px; + height: 22px; + margin-right: 8px; + background-repeat: no-repeat; + background-position: center; + background-size: cover; + &.drawing { + background-image: url(../../public/static/images/main/drawing_icon.svg); + } + } + } + &:after { + content: ''; + position: absolute; + top: 50%; + right: 0; + transform: translateY(-50%); + width: 1px; + height: 16px; + background-color: #d9d9d9; + } + &:first-child { + padding-left: 0; + } + &:last-child { + padding-right: 0; + &:after { + display: none; + } + } + } + } + .sub-header-title { + font-size: 16px; + color: #fff; + font-weight: 600; + } + .sub-header-location { + margin-left: auto; + display: flex; + align-items: center; + .location-item { position: relative; display: flex; align-items: center; - width: 580px; - height: 45px; - padding: 0 45px 0 20px; - margin-bottom: 20px; - border-radius: 2px; - border: 1px solid #101010; - .community-input{ - width: 100%; - height: 100%; - font-size: 13px; - font-weight: 400; - color: #101010; - &::placeholder{ - color: #C8C8C8; - } + padding: 0 10px; + span { + display: flex; + font-size: 12px; + color: #aaa; + font-weight: normal; + cursor: default; } - .community-search-ico{ - position: absolute; - top: 50%; - right: 20px; - transform: translateY(-50%); - flex: none; - width: 21px; - height: 100%; - background: url(../../public/static/images/sub/community_search.svg)no-repeat center; - background-size: 21px 21px; - z-index: 3; - } - } - .community-search-keyword{ - font-size: 13px; - font-weight: 400; - color: #45576F; - span{ - font-weight: 600; - color: #F16A6A; + &:after { + content: ''; + position: absolute; + top: 50%; + right: 0; + transform: translateY(-50%); + width: 4px; + height: 6px; + background: url(../../public/static/images/main/loaction_arr.svg) no-repeat center; } + &:first-child { + padding-left: 0; + } + &:last-child { + padding-right: 0; + span { + color: #fff; + } + &:after { + display: none; + } + } + } } + } } -// 자료 다운로드 -.file-down-list{ - display: grid; - grid-template-columns: repeat(3, 1fr); - gap: 14px; - .file-down-item{ +// sub content +.sub-content { + padding-top: 46px; + .sub-content-inner { + max-width: 1760px; + margin: 0 auto; + padding: 20px 20px 0; + .sub-content-box { + margin-bottom: 20px; + &:last-child { + margin-bottom: 0; + } + } + } + &.estimate { + display: flex; + flex-direction: column; + padding-top: 0; + .sub-content-inner { + flex: 1; + width: 100%; + } + } +} +.sub-table-box { + padding: 20px; + border-radius: 6px; + border: 1px solid #e9eaed; + background: #fff; + box-shadow: 0px 3px 30px 0px rgba(0, 0, 0, 0.02); + .table-box-title-wrap { + display: flex; + align-items: center; + margin-bottom: 15px; + .title-wrap { + display: flex; + align-items: center; + h3 { + display: block; + font-size: 15px; + color: #101010; + font-weight: 600; + margin-right: 14px; + &.product { + margin-right: 10px; + } + } + .product_tit { + position: relative; + font-size: 15px; + font-weight: 600; + color: #1083e3; + padding-left: 10px; + &::before { + content: ''; + position: absolute; + top: 50%; + left: 0; + transform: translateY(-50%); + width: 1px; + height: 11px; + background-color: #d9d9d9; + } + } + .option { + padding-left: 5px; + font-size: 13px; + color: #101010; + font-weight: 400; + } + .info-wrap { display: flex; align-items: center; - padding: 24px; - border-radius: 4px; - border: 1px solid #E5E5E5; - background: #FFF; - transition: all .15s ease-in-out; - .file-item-info{ - .item-num{ - display: inline-block; - padding: 6px 17.5px; - border-radius: 60px; - background-color: #F4F4F7; - font-size: 13px; - font-weight: 600; - color: #101010; - margin-bottom: 15px; + li { + position: relative; + padding: 0 6px; + font-size: 12px; + color: #101010; + font-weight: normal; + span { + font-weight: 600; + &.red { + color: #e23d70; } - .item-name{ - font-size: 16px; - color: #101010; - font-weight: 500; - margin-bottom: 13px; - } - .item-date{ - font-size: 13px; - font-weight: 400; - color: #344356; + } + &:after { + content: ''; + position: absolute; + top: 50%; + right: 0; + transform: translateY(-50%); + width: 1px; + height: 11px; + background-color: #d9d9d9; + } + &:first-child { + padding-left: 0; + } + &:last-child { + padding-right: 0; + &::after { + display: none; } + } } - .file-down-box{ - display: flex; - align-items: center; - flex: none; - margin-left: auto; - height: 100%; - .file-down-btn{ - width: 36px; - height: 36px; - background: url(../../public/static/images/sub/file_down_btn.svg)no-repeat center; - background-size: cover; - } - } - &:hover{ - background-color: #F4F4F7; - } + } } -} - -.file-down-nodata{ + } + .left-unit-box { + margin-left: auto; + display: flex; + align-items: center; + } + .promise-gudie { + display: block; + font-size: 13px; + font-weight: 700; + color: #101010; + margin-bottom: 20px; + } + .important { + color: #f00; + } + .sub-center-footer { display: flex; align-items: center; justify-content: center; - width: 100%; - height: 148px; - padding: 24px; - border-radius: 4px; - border: 1px solid #E5E5E5; - font-size: 16px; + margin-top: 20px; + } + .sub-right-footer { + display: flex; + align-items: center; + justify-content: flex-end; + margin-top: 20px; + } +} +.pagination-wrap { + margin-top: 24px; +} + +.infomation-wrap { + margin-bottom: 30px; +} + +.infomation-box-wrap { + display: flex; + gap: 10px; + .sub-table-box { + flex: 1; + } + .info-title { + font-size: 14px; font-weight: 500; color: #344356; + margin-bottom: 10px; + } + .info-inner { + position: relative; + font-size: 13px; + color: #344356; + .copy-ico { + position: absolute; + bottom: 0; + right: 0; + width: 16px; + height: 16px; + background: url(../../public/static/images/sub/copy_ico.svg) no-repeat center; + background-size: cover; + } + } +} + +// 견적서 +.estimate-list-wrap { + display: flex; + align-items: center; + margin-bottom: 10px; + &.one { + .estimate-box { + &:last-child { + min-width: unset; + } + } + } + .estimate-box { + flex: 1; + display: flex; + align-items: center; + &:last-child { + flex: none; + min-width: 220px; + } + .estimate-tit { + width: 105px; + height: 30px; + line-height: 30px; + background-color: #f4f4f7; + border-radius: 100px; + text-align: center; + font-size: 13px; + font-weight: 500; + color: #344356; + } + .estimate-name { + font-size: 13px; + color: #344356; + margin-left: 14px; + font-weight: 400; + &.blue { + font-size: 16px; + font-weight: 700; + color: #1083e3; + } + &.red { + font-size: 16px; + font-weight: 700; + color: #d72a2a; + } + } + } + &:last-child { + margin-bottom: 0; + } +} + +// file drag box +.drag-file-box { + padding: 10px; + .btn-area { + padding-bottom: 15px; + border-bottom: 1px solid #ecf0f4; + .file-upload { + display: inline-block; + height: 30px; + background-color: #94a0ad; + padding: 0 10px; + border-radius: 2px; + font-size: 13px; + line-height: 30px; + color: #fff; + font-weight: 500; + cursor: pointer; + transition: background 0.15s ease-in-out; + &:hover { + background-color: #607f9a; + } + } + } + .drag-file-area { + position: relative; + margin-top: 15px; + p { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + font-size: 13px; + color: #ccc; + font-weight: 400; + cursor: default; + } + } + .file-list { + .file-item { + margin-bottom: 15px; + span { + position: relative; + font-size: 13px; + color: #45576f; + font-weight: 400; + white-space: nowrap; + padding-right: 55px; + button { + position: absolute; + top: 50%; + right: 0; + transform: translateY(-50%); + width: 15px; + height: 15px; + background: url(../../public/static/images/sub/file_delete.svg) no-repeat center; + background-size: cover; + } + } + &:last-child { + margin-bottom: 0; + } + } + } +} + +.estimate-arr-btn { + display: block; + width: 20px; + height: 20px; + background-color: #94a0ad; + border: 1px solid #94a0ad; + background-position: center; + background-repeat: no-repeat; + background-image: url(../../public/static/images/canvas/estiment_arr.svg); + background-size: 11px 7px; + border-radius: 2px; + &.up { + rotate: 180deg; + } + &.on { + background-color: #fff; + border-color: #c2d0dd; + background-image: url(../../public/static/images/canvas/estiment_arr_color.svg); + } +} +.estimate-check-wrap { + .estimate-check-inner { + display: block; + } + &.hide { + border-bottom: 1px solid #ecf0f4; + margin-bottom: 15px; + .estimate-check-inner { + display: none; + } + } +} + +.special-note-check-wrap { + display: grid; + grid-template-columns: repeat(5, 1fr); + border-radius: 3px; + margin-bottom: 30px; + .special-note-check-item { + padding: 14px 10px; + border: 1px solid #ecf0f4; + margin-top: -1px; + margin-right: -1px; + &.act { + background-color: #f7f9fa; + } + } +} + +.calculation-estimate { + border: 1px solid #ecf0f4; + border-radius: 3px; + padding: 24px; + max-height: 350px; + overflow-y: auto; + margin-bottom: 30px; + dl { + margin-bottom: 35px; + &:last-child { + margin-bottom: 0; + } + dt { + font-size: 13px; + font-weight: 600; + color: #1083e3; + margin-bottom: 15px; + } + dd { + font-size: 12px; + font-weight: 400; + color: #45576f; + margin-bottom: 8px; + &:last-child { + margin-bottom: 0; + } + } + } + &::-webkit-scrollbar { + width: 4px; + background-color: transparent; + } + &::-webkit-scrollbar-thumb { + background-color: #d9dee2; + } + &::-webkit-scrollbar-track { + background-color: transparent; + } +} +.esimate-wrap { + margin-bottom: 20px; +} + +.estimate-product-option { + display: flex; + align-items: center; + margin-bottom: 15px; + .product-price-wrap { + display: flex; + align-items: center; + .product-price-tit { + font-size: 13px; + font-weight: 400; + color: #45576f; + margin-right: 10px; + } + .select-wrap { + width: 110px; + } + } + .product-edit-wrap { + display: flex; + align-items: center; + margin-left: auto; + .product-edit-explane { + display: flex; + align-items: center; + margin-right: 15px; + .explane-item { + position: relative; + display: flex; + align-items: center; + padding: 0 10px; + font-size: 12px; + font-weight: 400; + span { + width: 20px; + height: 20px; + margin-right: 5px; + background-size: cover; + background-repeat: no-repeat; + background-position: center; + } + &:before { + content: ''; + position: absolute; + top: 50%; + left: 0; + transform: translateY(-50%); + width: 1px; + height: 12px; + background-color: #d9d9d9; + } + &:first-child { + padding-left: 0; + &::before { + display: none; + } + } + &:last-child { + padding-right: 0; + } + &.item01 { + color: #3bbb48; + span { + background-image: url(../../public/static/images/sub/open_ico.svg); + } + } + &.item02 { + color: #909000; + span { + background-image: url(../../public/static/images/sub/change_ico.svg); + } + } + &.item03 { + color: #0191c9; + span { + background-image: url(../../public/static/images/sub/attachment_ico.svg); + } + } + &.item04 { + color: #f16a6a; + span { + background-image: url(../../public/static/images/sub/click_check_ico.svg); + } + } + } + } + .product-edit-btn { + display: flex; + align-items: center; + button { + display: flex; + align-items: center; + span { + width: 13px; + height: 13px; + margin-right: 5px; + background-size: cover; + &.plus { + background: url(../../public/static/images/sub/plus_btn.svg) no-repeat center; + } + &.minus { + background: url(../../public/static/images/sub/minus_btn.svg) no-repeat center; + } + } + } + } + } +} + +// 발전시물레이션 +.chart-wrap { + display: flex; + gap: 20px; + width: 100%; + .sub-table-box { + height: 100%; + } + .chart-inner { + flex: 1; + .chart-box { + margin-bottom: 30px; + } + } + .chart-table-wrap { + display: flex; + flex-direction: column; + flex: none; + width: 650px; + .sub-table-box { + flex: 1; + &:first-child { + margin-bottom: 20px; + } + } + } +} + +.chart-month-table { + table { + table-layout: fixed; + border-collapse: collapse; + border: 1px solid #ecf0f4; + border-radius: 4px; + thead { + th { + padding: 4.5px 0; + border-bottom: 1px solid #ecf0f4; + text-align: center; + font-size: 13px; + color: #45576f; + font-weight: 500; + background-color: #f8f9fa; + } + } + tbody { + td { + font-size: 13px; + color: #45576f; + text-align: center; + padding: 4.5px 0; + } + } + } +} + +.simulation-guide-wrap { + display: flex; + padding: 20px; + .simulation-tit-wrap { + flex: none; + padding-right: 40px; + border-right: 1px solid #eeeeee; + span { + display: block; + position: relative; + padding-left: 60px; + font-size: 15px; + color: #14324f; + font-weight: 600; + &::before { + content: ''; + position: absolute; + top: 50%; + left: 0; + transform: translateY(-50%); + width: 40px; + height: 40px; + background: url(../../public/static/images/sub/simulation_guide.svg) no-repeat center; + background-size: cover; + } + } + } + .simulation-guide-box { + flex: 1; + padding-left: 40px; + dl { + margin-bottom: 25px; + dt { + font-size: 13px; + color: #101010; + font-weight: 600; + margin-bottom: 5px; + } + dd { + font-size: 12px; + color: #45576f; + font-weight: 400; + line-height: 24px; + } + &:last-child { + margin-bottom: 0; + } + } + ul, + ol { + list-style: unset; + } + } +} + +.module-total { + display: flex; + align-items: center; + background-color: #f8f9fa; + padding: 9px 0; + margin-right: 4px; + border: 1px solid #ecf0f4; + border-top: none; + .total-title { + flex: 1; + text-align: center; + font-size: 13px; + color: #344356; + font-weight: 500; + } + .total-num { + flex: none; + width: 121px; + text-align: center; + font-size: 15px; + color: #344356; + font-weight: 500; + } +} + +// 물건상세 +.information-help-wrap { + display: flex; + padding: 24px; + background-color: #f4f4f4; + border-radius: 4px; + margin-bottom: 15px; + .information-help-tit-wrap { + position: relative; + display: flex; + align-items: center; + padding-right: 40px; + border-right: 1px solid #e0e0e3; + .help-tit-icon { + width: 40px; + height: 40px; + border-radius: 50%; + margin-right: 10px; + background: #fff url(../../public/static/images/sub/information_help.svg) no-repeat center; + background-size: 20px 20px; + } + .help-tit { + font-size: 13px; + font-weight: 600; + color: #45576f; + } + } + .information-help-guide { + padding-left: 40px; + span { + display: block; + font-size: 12px; + font-weight: 400; + color: #45576f; + margin-bottom: 7px; + &:last-child { + margin-bottom: 0; + } + } + } +} + +.community-search-warp { + display: flex; + flex-direction: column; + align-items: center; + padding: 10px 0 30px 0; + border-bottom: 1px solid #e5e5e5; + margin-bottom: 24px; + .community-search-box { + position: relative; + display: flex; + align-items: center; + width: 580px; + height: 45px; + padding: 0 45px 0 20px; + margin-bottom: 20px; + border-radius: 2px; + border: 1px solid #101010; + .community-input { + width: 100%; + height: 100%; + font-size: 13px; + font-weight: 400; + color: #101010; + &::placeholder { + color: #c8c8c8; + } + } + .community-search-ico { + position: absolute; + top: 50%; + right: 20px; + transform: translateY(-50%); + flex: none; + width: 21px; + height: 100%; + background: url(../../public/static/images/sub/community_search.svg) no-repeat center; + background-size: 21px 21px; + z-index: 3; + } + } + .community-search-keyword { + font-size: 13px; + font-weight: 400; + color: #45576f; + span { + font-weight: 600; + color: #f16a6a; + } + } +} + +// 자료 다운로드 +.file-down-list { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 14px; + .file-down-item { + display: flex; + align-items: center; + padding: 24px; + border-radius: 4px; + border: 1px solid #e5e5e5; + background: #fff; + transition: all 0.15s ease-in-out; + .file-item-info { + .item-num { + display: inline-block; + padding: 6px 17.5px; + border-radius: 60px; + background-color: #f4f4f7; + font-size: 13px; + font-weight: 600; + color: #101010; + margin-bottom: 15px; + } + .item-name { + font-size: 16px; + color: #101010; + font-weight: 500; + margin-bottom: 13px; + } + .item-date { + font-size: 13px; + font-weight: 400; + color: #344356; + } + } + .file-down-box { + display: flex; + align-items: center; + flex: none; + margin-left: auto; + height: 100%; + .file-down-btn { + width: 36px; + height: 36px; + background: url(../../public/static/images/sub/file_down_btn.svg) no-repeat center; + background-size: cover; + } + } + &:hover { + background-color: #f4f4f7; + } + } +} + +.file-down-nodata { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + height: 148px; + padding: 24px; + border-radius: 4px; + border: 1px solid #e5e5e5; + font-size: 16px; + font-weight: 500; + color: #344356; } //신규물건 등록 -.product-input-wrap{ - display: flex; - align-items: center; - width: 200px; - height: 30px; - background-color: #FAFAFA; - border: 1px solid #EEE; - padding: 0 10px; - input{ - font-size: 13px; - font-weight: 400; - color: #999999; - padding: 0; - width: 100%; - height: 100%; - flex: 1 ; - background-color: inherit; - } - .product-delete{ - flex: none; - display: block; - width: 15px; - height: 100%; - background: url(../../public/static/images/sub/product-del.svg)no-repeat center; - background-size: 15px 15px; - } +.product-input-wrap { + display: flex; + align-items: center; + width: 200px; + height: 30px; + background-color: #fafafa; + border: 1px solid #eee; + padding: 0 10px; + input { + font-size: 13px; + font-weight: 400; + color: #999999; + padding: 0; + width: 100%; + height: 100%; + flex: 1; + background-color: inherit; + } + .product-delete { + flex: none; + display: block; + width: 15px; + height: 100%; + background: url(../../public/static/images/sub/product-del.svg) no-repeat center; + background-size: 15px 15px; + } } @media screen and (max-width: 1800px) { - .canvas-menu-wrap{ - .canvas-menu-inner{ - .canvas-menu-list{ - .canvas-menu-item button{ - .menu-icon{ - margin-right: 5px; - } - } - .canvas-menu-item{ - button{ - padding: 15px 15px; - font-size: 11px; - } - } - } + .canvas-menu-wrap { + .canvas-menu-inner { + .canvas-menu-list { + .canvas-menu-item button { + .menu-icon { + margin-right: 5px; + } } - .canvas-depth2-wrap{ - .canvas-depth2-inner{ - .canvas-depth2-list{ - .canvas-depth2-item{ - button{ - font-size: 11px; - } - } - } - } + .canvas-menu-item { + button { + padding: 15px 15px; + font-size: 11px; + } } - } + } + } + .canvas-depth2-wrap { + .canvas-depth2-inner { + .canvas-depth2-list { + .canvas-depth2-item { + button { + font-size: 11px; + } + } + } + } + } + } } @media screen and (max-width: 1600px) { - .canvas-menu-wrap{ - .canvas-menu-inner{ - .canvas-menu-list{ - .canvas-menu-item button{ - .menu-icon{ - display: none; - } - } - } + .canvas-menu-wrap { + .canvas-menu-inner { + .canvas-menu-list { + .canvas-menu-item button { + .menu-icon { + display: none; + } } - } - .canvas-content{ - .canvas-frame{ - height: calc(100vh - 129.5px); - } - &.active{ - .canvas-frame{ - height: calc(100vh - 179.5px); - } - } - } + } + } + } + .canvas-content { + .canvas-frame { + height: calc(100vh - 129.5px); + } + &.active { + .canvas-frame { + height: calc(100vh - 179.5px); + } + } + } } @media screen and (max-width: 1500px) { - .canvas-menu-wrap{ - .canvas-menu-inner{ - .canvas-menu-list{ - .canvas-menu-item{ - button{ - padding: 15px 10px; - font-size: 10px; - } - } - } - .canvas-side-btn-wrap{ - .btn-from{ - gap: 3px; - } - .vertical-horizontal{ - margin-right: 3px; - min-width: 150px; - } - .select-box{ - width: 100px; - margin: 0 3px; - } - .size-control{ - width: 90px; - margin: 0 3px; - } - } + .canvas-menu-wrap { + .canvas-menu-inner { + .canvas-menu-list { + .canvas-menu-item { + button { + padding: 15px 10px; + font-size: 10px; + } } + } + .canvas-side-btn-wrap { + .btn-from { + gap: 3px; + } + .vertical-horizontal { + margin-right: 3px; + min-width: 150px; + } + .select-box { + width: 100px; + margin: 0 3px; + } + .size-control { + width: 90px; + margin: 0 3px; + } + } } - .sub-header{ - .sub-header-inner{ - .sub-header-title{ - font-size: 15px; + } + .sub-header { + .sub-header-inner { + .sub-header-title { + font-size: 15px; + } + .sub-header-title-wrap { + .title-item { + a { + .icon { + width: 20px; + height: 20px; } - .sub-header-title-wrap{ - .title-item{ - a{ - .icon{ - width: 20px; - height: 20px; - } - } - } - } - } - } - + } + } + } + } + } } diff --git a/src/styles/_modal.scss b/src/styles/_modal.scss index 01b47c47..2914875d 100644 --- a/src/styles/_modal.scss +++ b/src/styles/_modal.scss @@ -4,1910 +4,2128 @@ $pop-bold-weight: 500; $pop-normal-size: 12px; $alert-color: #101010; -@keyframes mountpop{ - from{opacity: 0; scale: 0.95;} - to{opacity: 1; scale: 1;} +@keyframes mountpop { + from { + opacity: 0; + scale: 0.95; + } + to { + opacity: 1; + scale: 1; + } } -@keyframes unmountpop{ - from{opacity: 1; scale: 1;} - to{opacity: 0; scale: 0.95;} +@keyframes unmountpop { + from { + opacity: 1; + scale: 1; + } + to { + opacity: 0; + scale: 0.95; + } } -.normal-font{ - font-size: 12px; - font-weight: 400; - color: #fff; +.normal-font { + font-size: 12px; + font-weight: 400; + color: #fff; } -.bold-font{ - font-size: 12px; - font-weight: 500; - color: #fff; +.bold-font { + font-size: 12px; + font-weight: 500; + color: #fff; } -.modal-pop-wrap{ - position: fixed; - top: 0; - left: 0; - width: 100%; - height: -webkit-fit-content; - height: -moz-fit-content; - height: fit-content; - border: 1px solid #000; - border-radius: 4px; - background-color: #272727; - z-index: 9999999; - &.xsm{ - width: 200px; +.modal-pop-wrap { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: -webkit-fit-content; + height: -moz-fit-content; + height: fit-content; + border: 1px solid #000; + border-radius: 4px; + background-color: #272727; + z-index: 9999999; + &.xsm { + width: 200px; + } + &.xxxm { + width: 240px; + } + &.xxm { + width: 270px; + } + &.xm { + width: 300px; + } + &.ssm { + width: 380px; + } + &.sm { + width: 580px; + } + &.r { + width: 400px; + } + &.lr { + width: 440px; + } + &.lr-2 { + width: 450px; + } + &.lrr { + width: 480px; + } + &.ml { + width: 530px; + } + &.l-2 { + width: 640px; + } + &.lx-2 { + width: 740px; + } + &.lx { + width: 770px; + } + &.l { + width: 800px; + } + &.mount { + animation: mountpop 0.17s ease-in-out forwards; + } + &.unmount { + animation: unmountpop 0.17s ease-in-out forwards; + } + &.alert { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background-color: transparent; + border: none; + .modal-head { + background-color: transparent; + padding: 0 0 8px; + .modal-close { + width: 20px; + height: 20px; + background: url(../../public/static/images/canvas/alert_close.svg) no-repeat center; + } } - &.xxxm{ - width: 240px; - } - &.xxm{ - width: 270px; - } - &.xm{ - width: 300px; - } - &.ssm{ - width: 380px; - } - &.sm{ - width: 580px; - } - &.r{ - width: 400px; - } - &.lr{ - width: 440px; - } - &.lr-2{ - width: 450px; - } - &.lrr{ - width: 480px; - } - &.ml{ - width: 530px; - } - &.l-2{ - width: 640px; - } - &.lx-2{ - width: 740px; - } - &.lx{ - width: 770px; - } - &.l{ - width: 800px; - } - &.mount{ - animation: mountpop .17s ease-in-out forwards; - } - &.unmount{ - animation: unmountpop .17s ease-in-out forwards; - } - &.alert{ - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - background-color: transparent; - border: none; - .modal-head{ - background-color: transparent; - padding: 0 0 8px; - .modal-close{ - width: 20px; - height: 20px; - background: url(../../public/static/images/canvas/alert_close.svg)no-repeat center; - } - } - .modal-body{ - background-color: #fff; - padding: 22px; - border-radius: 4px; - border: 1px solid #101010; - color: $alert-color; - .alert-title{ - font-size: 13px; - font-weight: 700; - color: $alert-color; - margin-bottom: 15px; - } - } - } -} -.modal-head{ - display: flex; - align-items: center; - padding: 10px 24px; - background-color: #000; - // overflow: hidden; - h1.title{ + .modal-body { + background-color: #fff; + padding: 22px; + border-radius: 4px; + border: 1px solid #101010; + color: $alert-color; + .alert-title { font-size: 13px; - color: $pop-color; font-weight: 700; - } - .modal-close{ - margin-left: auto; - color: transparent; - font-size: 0; - width: 10px; - height: 10px; - background: url(../../public/static/images/canvas/modal_close.svg)no-repeat center; - } -} -.modal-body{ - padding: 24px; - .modal-btn-wrap{ - display: flex; - align-items: center; - gap: 5px; - button{ - flex: 1; - } - &.sub{ - button{ - flex: 1 1 auto; - padding: 0; - } - margin-bottom: 14px; - } - } - .modal-check-btn-wrap{ - margin-top: 15px; - .check-wrap-title{ - font-size: $pop-normal-size; - color: $pop-color; - font-weight: 600; - &.light{ - font-weight: $pop-normal-weight; - } - } - .flex-check-box{ - display: flex; - flex-wrap: wrap; - gap: 10px; - margin-top: 15px; - &.for2{ - justify-content: flex-end; - button{ - width: calc(50% - 5px); - } - &.btn{ - gap: 5px; - button{ - width: calc(50% - 2.5px); - } - } - } - &.for-line{ - button{ - flex: 1; - } - } - } - } - .outer-line-wrap{ - border-top: 1px solid #3C3C3C; - margin-top: 10px; - padding-top: 15px; - margin-bottom: 15px; - > div{ - margin-bottom: 15px; - &:last-child{ - margin-bottom: 0; - } - } - } - .modal-guide{ - display: block; - font-size: $pop-normal-size; color: $alert-color; - font-weight: $pop-normal-weight; + margin-bottom: 15px; + } } + } } - -.adsorption-point{ +.modal-head { + display: flex; + align-items: center; + padding: 10px 24px; + background-color: #000; + // overflow: hidden; + h1.title { + font-size: 13px; + color: $pop-color; + font-weight: 700; + } + .modal-close { + margin-left: auto; + color: transparent; + font-size: 0; + width: 10px; + height: 10px; + background: url(../../public/static/images/canvas/modal_close.svg) no-repeat center; + } +} +.modal-body { + padding: 24px; + .modal-btn-wrap { display: flex; align-items: center; - background-color: #3A3A3A; - border-radius: 3px; - padding-left: 11px; - overflow: hidden; - transition: all 0.17s ease-in-out; - span{ - font-size: $pop-normal-size; - color: #898989; + gap: 5px; + button { + flex: 1; } - i{ - display: flex; - align-items: center; - padding: 0 7px; - margin-left: auto; - height: 100%; - font-size: 13px; - color: #898989; + &.sub { + button { + flex: 1 1 auto; + padding: 0; + } + margin-bottom: 14px; } - &.act{ - i{ - color: $pop-color; - background-color: #1083E3; + } + .modal-check-btn-wrap { + margin-top: 15px; + .check-wrap-title { + font-size: $pop-normal-size; + color: $pop-color; + font-weight: 600; + &.light { + font-weight: $pop-normal-weight; + } + } + .flex-check-box { + display: flex; + flex-wrap: wrap; + gap: 10px; + margin-top: 15px; + &.for2 { + justify-content: flex-end; + button { + width: calc(50% - 5px); } + &.btn { + gap: 5px; + button { + width: calc(50% - 2.5px); + } + } + } + &.for-line { + button { + flex: 1; + } + } } + } + .outer-line-wrap { + border-top: 1px solid #3c3c3c; + margin-top: 10px; + padding-top: 15px; + margin-bottom: 15px; + > div { + margin-bottom: 15px; + &:last-child { + margin-bottom: 0; + } + } + } + .modal-guide { + display: block; + font-size: $pop-normal-size; + color: $alert-color; + font-weight: $pop-normal-weight; + } +} + +.adsorption-point { + display: flex; + align-items: center; + background-color: #3a3a3a; + border-radius: 3px; + padding-left: 11px; + overflow: hidden; + transition: all 0.17s ease-in-out; + span { + font-size: $pop-normal-size; + color: #898989; + } + i { + display: flex; + align-items: center; + padding: 0 7px; + margin-left: auto; + height: 100%; + font-size: 13px; + color: #898989; + } + &.act { + i { + color: $pop-color; + background-color: #1083e3; + } + } } // grid-option -.grid-check-form{ +.grid-check-form { + display: flex; + align-items: center; + gap: 15px; + padding-bottom: 15px; + &.border { + border-bottom: 1px solid #424242; + } +} +.grid-option-wrap { + .grid-option-box { display: flex; align-items: center; - gap: 15px; - padding-bottom: 15px; - &.border{ - border-bottom: 1px solid #424242; + background-color: transparent; + border: 1px solid #3d3d3d; + border-radius: 2px; + padding: 15px 10px; + gap: 20px; + margin-bottom: 10px; + .grid-input-form { + display: flex; + align-items: center; + span { + flex: none; + font-size: $pop-normal-size; + color: $pop-color; + font-weight: $pop-bold-weight; + } + .input-grid { + width: 54px; + input { + width: 100%; + } + } } + &:last-child { + margin-bottom: 0; + } + } } -.grid-option-wrap{ - .grid-option-box{ - display: flex; - align-items: center; - background-color: transparent; - border: 1px solid #3D3D3D; - border-radius: 2px; - padding: 15px 10px; - gap: 20px; - margin-bottom: 10px; - .grid-input-form{ - display: flex; - align-items: center; - span{ - flex: none; - font-size: $pop-normal-size; - color: $pop-color; - font-weight: $pop-bold-weight; - } - .input-grid{ - width: 54px; - input{ - width: 100%; - } - } - } - &:last-child{ - margin-bottom: 0; - } - } +.select-form { + .sort-select { + width: 100%; + } } -.select-form{ - .sort-select{width: 100%;} +.grid-select { + flex: 1; + &.no-flx { + flex: unset; + } + .sort-select { + width: 100%; + background-color: #313131; + min-width: auto; + font-size: 12px; + border: none; + p { + font-size: 12px; + } + > ul { + border: none; + } + } + &.right { + p { + text-align: right; + } + ul { + li { + justify-content: flex-end; + } + } + } } -.grid-select{ - flex: 1; - &.no-flx{ - flex: unset; - } - .sort-select{ - width: 100%; - background-color: #313131; - min-width: auto; - font-size: 12px; - border: none; - p{ - font-size: 12px; - } - > ul{ - border: none; - } - } - &.right{ - p{ - text-align: right; - } - ul{ - li{ - justify-content: flex-end; - } - } - } -} -.grid-btn-wrap{ - padding-top: 15px; - text-align: right; - button{ - padding: 0 10px; - } +.grid-btn-wrap { + padding-top: 15px; + text-align: right; + button { + padding: 0 10px; + } } // grid copy -.grid-option-tit{ - font-size: $pop-normal-size; - color: $pop-color; - font-weight: $pop-normal-weight; - padding-bottom: 15px; - +.grid-option-tit { + font-size: $pop-normal-size; + color: $pop-color; + font-weight: $pop-normal-weight; + padding-bottom: 15px; } -.grid-direction{ - display: flex; - align-items: center; - gap: 5px; - flex: 1; +.grid-direction { + display: flex; + align-items: center; + gap: 5px; + flex: 1; } -.direction{ - width: 22px; - height: 22px; - background-color: #757575; - background-image: url(../../public/static/images/canvas/grid_option_arr.svg); - background-repeat: no-repeat; - background-position: center; - background-size: 16px 15px; - border-radius: 50%; - transition: all .15s ease-in-out; - opacity: 0.6; - &.down{transform: rotate(180deg);} - &.left{transform: rotate(-90deg);} - &.right{transform: rotate(90deg);} - &:hover, - &.act{ - opacity: 1; - } +.direction { + width: 22px; + height: 22px; + background-color: #757575; + background-image: url(../../public/static/images/canvas/grid_option_arr.svg); + background-repeat: no-repeat; + background-position: center; + background-size: 16px 15px; + border-radius: 50%; + transition: all 0.15s ease-in-out; + opacity: 0.6; + &.down { + transform: rotate(180deg); + } + &.left { + transform: rotate(-90deg); + } + &.right { + transform: rotate(90deg); + } + &:hover, + &.act { + opacity: 1; + } } // grid-move -.move-form{ - width: 100%; - p{ - font-size: $pop-normal-size; - color: $pop-color; - font-weight: $pop-bold-weight; - } +.move-form { + width: 100%; + p { + font-size: $pop-normal-size; + color: $pop-color; + font-weight: $pop-bold-weight; + } } -.input-move-wrap{ - display: flex; - align-items: center; - gap: 5px; - span{ - color: $pop-color; - font-size: $pop-normal-size; - } - .input-move{ - width: 130px; - input{ - width: 100%; - } +.input-move-wrap { + display: flex; + align-items: center; + gap: 5px; + span { + color: $pop-color; + font-size: $pop-normal-size; + } + .input-move { + width: 130px; + input { + width: 100%; } + } } -.direction-move-wrap{ - flex: none; - display: grid; - grid-template-columns: 1fr 1fr; - gap: 5px; - margin-left: auto; +.direction-move-wrap { + flex: none; + display: grid; + grid-template-columns: 1fr 1fr; + gap: 5px; + margin-left: auto; } // 배치면 초기 설정 -.placement-table{ - table{ - table-layout: fixed; - tr{ - th{ - font-size: $pop-normal-size; - color: $pop-color; - font-weight: $pop-bold-weight; - padding: 18px 0; - border-bottom: 1px solid #424242; - vertical-align: middle; - .tip-wrap{ - display: flex; - align-items: center; - } - } - td{ - font-size: $pop-normal-size; - color: $pop-color; - border-bottom: 1px solid #424242; - padding: 18px 0 18px 20px; - vertical-align: middle; - .flex-box{ - display: flex; - align-items: center; - height: 100%; - } - } - &:first-child{ - td, - th{ - padding-top: 0; - } - } - } - } - .tooltip{ - position: relative; - display: block; - width: 15px; - height: 15px; - margin-left: 5px; - background: url(../../public/static/images/canvas/pop_tip.svg)no-repeat center; - background-size: cover; - } - &.light{ - padding: 0; - th,td{ - color: $alert-color; - border-bottom: none; - border-top: 1px solid #EFEFEF; - } - th{ - padding: 14px 0; - } - tr{ - &:first-child{ - td, - th{ - padding-top: 14px; - } - } - &:last-child{ - td, - th{ - padding-bottom: 0px; - } - } - } - } -} - -.pop-form-radio{ - display: flex; - align-items: center; - gap: 10px; -} -.placement-option{ - display: flex; - align-items: center; - gap: 20px; -} -.select-wrap{ - .sort-select{ - width: 100%; - } -} -.flex-ment{ - display: flex; - align-items: center; - gap: 5px; - span{ +.placement-table { + table { + table-layout: fixed; + tr { + th { font-size: $pop-normal-size; color: $pop-color; - font-weight: $pop-normal-weight; + font-weight: $pop-bold-weight; + padding: 18px 0; + border-bottom: 1px solid #424242; + vertical-align: middle; + .tip-wrap { + display: flex; + align-items: center; + } + } + td { + font-size: $pop-normal-size; + color: $pop-color; + border-bottom: 1px solid #424242; + padding: 18px 0 18px 20px; + vertical-align: middle; + .flex-box { + display: flex; + align-items: center; + } + } + &:first-child { + td, + th { + padding-top: 0; + } + } } - + } + .tooltip { + position: relative; + display: block; + width: 15px; + height: 15px; + margin-left: 5px; + background: url(../../public/static/images/canvas/pop_tip.svg) no-repeat center; + background-size: cover; + } + &.light { + padding: 0; + th, + td { + color: $alert-color; + border-bottom: none; + border-top: 1px solid #efefef; + } + th { + padding: 14px 0; + } + tr { + &:first-child { + td, + th { + padding-top: 14px; + } + } + &:last-child { + td, + th { + padding-bottom: 0px; + } + } + } + } } -.img-edit-wrap{ - flex: none; - .img-edit-btn{ - display: flex; - align-items: center; - height: 30px; - padding: 0 10px; - font-size: 12px; - font-weight: 400; - color: #101010; - background-color: #fff; - border-radius: 2px; - cursor: pointer; - transition: all .15s ease-in-out; - .img-edit{ - width: 16px; - height: 16px; - background: url(../../public/static/images/canvas/img_edit_ico.svg)no-repeat center; - background-size: cover; - margin-right: 5px; - } - &:hover{ - background-color: #ebebeb; - } - } +.pop-form-radio { + display: flex; + align-items: center; + gap: 10px; } -.img-name-wrap{ +.placement-option { + display: flex; + align-items: center; + gap: 20px; +} +.select-wrap { + .sort-select { + width: 100%; + } +} +.flex-ment { + display: flex; + align-items: center; + gap: 5px; + span { + font-size: $pop-normal-size; + color: $pop-color; + font-weight: $pop-normal-weight; + } +} + +.img-edit-wrap { + flex: none; + .img-edit-btn { display: flex; align-items: center; - width: 100%; - margin-left: 10px; - input{ - flex: 1; - + height: 30px; + padding: 0 10px; + font-size: 12px; + font-weight: 400; + color: #101010; + background-color: #fff; + border-radius: 2px; + cursor: pointer; + transition: all 0.15s ease-in-out; + .img-edit { + width: 16px; + height: 16px; + background: url(../../public/static/images/canvas/img_edit_ico.svg) no-repeat center; + background-size: cover; + margin-right: 5px; } - .img-check{ - flex: none; - width: 18px; - height: 18px; - margin-left: 5px; - background-repeat: no-repeat; - background-position: center; - background-size: cover; - background-image: url(../../public/static/images/canvas/img_check_fail.svg); + &:hover { + background-color: #ebebeb; } + } +} +.img-name-wrap { + display: flex; + align-items: center; + width: 100%; + margin-left: 10px; + input { + flex: 1; + } + .img-check { + flex: none; + width: 18px; + height: 18px; + margin-left: 5px; + background-repeat: no-repeat; + background-position: center; + background-size: cover; + background-image: url(../../public/static/images/canvas/img_check_fail.svg); + } } -.for-address{ - input{ - flex: 1; +.for-address { + input { + flex: 1; + } + .check-address { + flex: none; + width: 18px; + height: 18px; + margin-left: 5px; + background-repeat: no-repeat; + background-position: center; + background-size: cover; + &.fail { + background-image: url(../../public/static/images/canvas/img_check_fail.svg); } - .check-address{ - flex: none; - width: 18px; - height: 18px; - margin-left: 5px; - background-repeat: no-repeat; - background-position: center; - background-size: cover; - &.fail{background-image: url(../../public/static/images/canvas/img_check_fail.svg);} - &.success{background-image: url(../../public/static/images/canvas/img_check_success.svg);} + &.success { + background-image: url(../../public/static/images/canvas/img_check_success.svg); } + } } // 외벽선 그리기 -.outline-wrap{ - padding: 24px 0; - border-top: 1px solid #424242; - - .outline-inner{ - display: flex; - align-items: center; - margin-bottom: 14px; - &:last-child{ - margin-bottom: 0; - } - .outline-form{ - // width: 50%; - margin-right: 15px; - } - } - &:last-child{ - border-bottom: 1px solid #424242; - } -} -.outline-form{ +.outline-wrap { + padding: 24px 0; + border-top: 1px solid #424242; + + .outline-inner { display: flex; align-items: center; - - span{ - width: 60px; - flex: none; - font-size: $pop-normal-size; - font-weight: $pop-bold-weight; - color: $pop-color; - margin-right: 10px; - &.thin{ - width: auto; - font-weight: $pop-normal-weight; - margin-right: 0; - } + margin-bottom: 14px; + &:last-child { + margin-bottom: 0; } + .outline-form { + // width: 50%; + margin-right: 15px; + } + } + &:last-child { + border-bottom: 1px solid #424242; + } +} +.outline-form { + display: flex; + align-items: center; - .reset-btn{ - flex: none; - width: 30px; - height: 30px; - background: transparent; - border: 1px solid #484848; - border-radius: 2px; - margin-left: 5px; - background-image: url(../../public/static/images/canvas/reset_ico.svg); - background-repeat: no-repeat; - background-size: 12px 12px; - background-position: center; - } - &:last-child{ - margin-right: 0; + span { + width: 60px; + flex: none; + font-size: $pop-normal-size; + font-weight: $pop-bold-weight; + color: $pop-color; + margin-right: 10px; + &.thin { + width: auto; + font-weight: $pop-normal-weight; + margin-right: 0; } + } + + .reset-btn { + flex: none; + width: 30px; + height: 30px; + background: transparent; + border: 1px solid #484848; + border-radius: 2px; + margin-left: 5px; + background-image: url(../../public/static/images/canvas/reset_ico.svg); + background-repeat: no-repeat; + background-size: 12px 12px; + background-position: center; + } + &:last-child { + margin-right: 0; + } } -.cul-wrap{ +.cul-wrap { + display: flex; + .outline-box { + width: 50%; + margin-right: 15px; + .outline-form { + width: 100%; + margin-bottom: 14px; + margin-right: 0; + &:last-child { + margin-bottom: 0; + } + } + } + .cul-box { display: flex; - .outline-box{ - width: 50%; - margin-right: 15px; - .outline-form{ - width: 100%; - margin-bottom: 14px; - margin-right: 0; - &:last-child{ - margin-bottom: 0; - } - } - } - .cul-box{ - display: flex; - align-items: center; - justify-content: center; - width: 50%; - background-color: #3D3D3D; - border-radius: 2px ; - } + align-items: center; + justify-content: center; + width: 50%; + background-color: #3d3d3d; + border-radius: 2px; + } } // 외벽선 속성 설정 -.properties-guide{ - font-size: $pop-normal-size; - color: #AAA; - font-weight: $pop-normal-weight; - margin-bottom: 14px; +.properties-guide { + font-size: $pop-normal-size; + color: #aaa; + font-weight: $pop-normal-weight; + margin-bottom: 14px; } -.setting-tit{ - font-size: 13px; - color: $pop-color; - font-weight: $pop-bold-weight; - margin-bottom: 10px; +.setting-tit { + font-size: 13px; + color: $pop-color; + font-weight: $pop-bold-weight; + margin-bottom: 10px; } -.properties-setting-wrap{ - &.outer{ - margin-top: 24px; - } - .setting-btn-wrap{ - display: flex; - align-items: center; - padding: 14px 0; - border-top: 1px solid #424242; - border-bottom: 1px solid #424242; - .setting-btn{ - display: block; - width: 100%; - height: 40px; - font-size: 13px; - color: #fff; - font-weight: 700; - border-radius: 2px; - transition: all .15s ease-in-out; - &.green{ - background-color: #305941; - border: 1px solid #45CD7D; - &:hover{ - background-color: #3a6b4e; - } - } - &.blue{ - background-color: #2E5360; - border: 1px solid #3FBAE6; - &:hover{ - background-color: #365f6e; - } - } - } - } -} - -// 지붕형상 설정 -.roof-shape-menu{ - display: grid; - grid-template-columns: 1fr 1fr 1fr 1fr; - grid-template-rows: 1fr 1fr; - gap: 24px 10px; - margin-bottom: 24px; - .shape-box{ - display: flex; - align-items: center; - justify-content: center; - width: 100%; - padding: 13px; - background-color: #3D3D3D; - transition: background .15s ease-in-out; - img{ - max-width: 100%; - } - } - .shape-title{ - font-size: $pop-normal-size; - font-weight: $pop-bold-weight; - color: $pop-color; - margin-top: 10px; - text-align: center; - transition: color .15s ease-in-out; - } - .shape-menu-box{ - &.act, - &:hover{ - .shape-box{background-color: #008BFF;} - .shape-title{color: #008BFF;} - } - } -} - -.setting-box{ +.properties-setting-wrap { + &.outer { + margin-top: 24px; + } + .setting-btn-wrap { + display: flex; + align-items: center; padding: 14px 0; border-top: 1px solid #424242; border-bottom: 1px solid #424242; -} -.padding-form{ - padding-left: 23px; -} -.discrimination-box{ - padding: 16px 12px; - border: 1px solid #3D3D3D; - border-radius: 2px; + .setting-btn { + display: block; + width: 100%; + height: 40px; + font-size: 13px; + color: #fff; + font-weight: 700; + border-radius: 2px; + transition: all 0.15s ease-in-out; + &.green { + background-color: #305941; + border: 1px solid #45cd7d; + &:hover { + background-color: #3a6b4e; + } + } + &.blue { + background-color: #2e5360; + border: 1px solid #3fbae6; + &:hover { + background-color: #365f6e; + } + } + } + } } -.modal-bottom-border-bx{ - margin-top: 24px; - padding-bottom: 14px; - border-bottom: 1px solid #424242; +// 지붕형상 설정 +.roof-shape-menu { + display: grid; + grid-template-columns: 1fr 1fr 1fr 1fr; + grid-template-rows: 1fr 1fr; + gap: 24px 10px; + margin-bottom: 24px; + .shape-box { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + padding: 13px; + background-color: #3d3d3d; + transition: background 0.15s ease-in-out; + img { + max-width: 100%; + } + } + .shape-title { + font-size: $pop-normal-size; + font-weight: $pop-bold-weight; + color: $pop-color; + margin-top: 10px; + text-align: center; + transition: color 0.15s ease-in-out; + } + .shape-menu-box { + &.act, + &:hover { + .shape-box { + background-color: #008bff; + } + .shape-title { + color: #008bff; + } + } + } +} + +.setting-box { + padding: 14px 0; + border-top: 1px solid #424242; + border-bottom: 1px solid #424242; +} +.padding-form { + padding-left: 23px; +} +.discrimination-box { + padding: 16px 12px; + border: 1px solid #3d3d3d; + border-radius: 2px; +} + +.modal-bottom-border-bx { + margin-top: 24px; + padding-bottom: 14px; + border-bottom: 1px solid #424242; } // 처마∙케라바 변경 -.eaves-keraba-table{ - display: table; - border-collapse: collapse; - .eaves-keraba-item{ - display: table-row; - .eaves-keraba-th, - .eaves-keraba-td{ - font-size: $pop-normal-size; - color: $pop-color; - font-weight: $pop-normal-weight; - display: table-cell; - vertical-align: middle; - padding-bottom: 14px; - } - .eaves-keraba-td{ - padding-left: 10px; - } - .eaves-keraba-ico{ - display: flex; - align-items: center; - justify-content: center; - padding: 5px; - background-color: #3D3D3D; - border: 1px solid #3D3D3D; - border-radius: 2px; - cursor: pointer; - &.act{ - border: 1px solid #ED0004; - } - } - &:last-child{ - .eaves-keraba-th, - .eaves-keraba-td{ - padding-bottom: 0; - } - } +.eaves-keraba-table { + display: table; + border-collapse: collapse; + .eaves-keraba-item { + display: table-row; + .eaves-keraba-th, + .eaves-keraba-td { + font-size: $pop-normal-size; + color: $pop-color; + font-weight: $pop-normal-weight; + display: table-cell; + vertical-align: middle; + padding-bottom: 14px; } + .eaves-keraba-td { + padding-left: 10px; + } + .eaves-keraba-ico { + display: flex; + align-items: center; + justify-content: center; + padding: 5px; + background-color: #3d3d3d; + border: 1px solid #3d3d3d; + border-radius: 2px; + cursor: pointer; + &.act { + border: 1px solid #ed0004; + } + } + &:last-child { + .eaves-keraba-th, + .eaves-keraba-td { + padding-bottom: 0; + } + } + } } -.guide{ - font-size: $pop-normal-size; - font-weight: $pop-normal-weight; - color: $pop-color; - margin-bottom: 24px; - &.sm{ - margin-bottom: 15px; - } - span{ - display: block; - } +.guide { + font-size: $pop-normal-size; + font-weight: $pop-normal-weight; + color: $pop-color; + margin-bottom: 24px; + &.sm { + margin-bottom: 15px; + } + span { + display: block; + } } // 지붕면 할당 -.allocation-select-wrap{ +.allocation-select-wrap { + display: flex; + align-items: center; + padding-bottom: 14px; + border-bottom: 1px solid #424242; + margin-bottom: 14px; + span { + font-size: $pop-normal-size; + color: $pop-color; + font-weight: $pop-bold-weight; + margin-right: 10px; + } + .allocation-edit { display: flex; align-items: center; - padding-bottom: 14px; - border-bottom: 1px solid #424242; - margin-bottom: 14px; - span{ - font-size: $pop-normal-size; - color: $pop-color; - font-weight: $pop-bold-weight; - margin-right: 10px; - } - .allocation-edit{ - display: flex; - align-items: center; - height: 30px; - padding: 0 10px; - margin-left: 5px; - font-size: $pop-normal-size; - color: $pop-color; - font-weight: $pop-normal-weight; - border: 1px solid #484848; - background-color: #323234; - i{ - display: block; - width: 12px; - height: 12px; - margin-right: 5px; - background: url(../../public/static/images/canvas/allocation_edit.svg)no-repeat center; - background-size: cover; - } + height: 30px; + padding: 0 10px; + margin-left: 5px; + font-size: $pop-normal-size; + color: $pop-color; + font-weight: $pop-normal-weight; + border: 1px solid #484848; + background-color: #323234; + i { + display: block; + width: 12px; + height: 12px; + margin-right: 5px; + background: url(../../public/static/images/canvas/allocation_edit.svg) no-repeat center; + background-size: cover; } + } } -.block-box{ - display: flex; - align-items: center; +.block-box { + display: flex; + align-items: center; + gap: 10px; + margin-bottom: 10px; + .flex-ment { gap: 10px; - margin-bottom: 10px; - .flex-ment{ - gap: 10px; - .dec{ - text-decoration: underline; - } - .delete{ - display: block; - width: 15px; - height: 15px; - background: url(../../public/static/images/canvas/allocation_delete.svg)no-repeat center; - background-size: cover; - } + .dec { + text-decoration: underline; } - &:last-child{ - margin-bottom: 0; + .delete { + display: block; + width: 15px; + height: 15px; + background: url(../../public/static/images/canvas/allocation_delete.svg) no-repeat center; + background-size: cover; } + } + &:last-child { + margin-bottom: 0; + } } -.icon-btn-wrap{ - flex: 1; +.icon-btn-wrap { + flex: 1; + display: flex; + align-items: center; + gap: 5px; + button { display: flex; align-items: center; - gap: 5px; - button{ - display: flex; - align-items: center; - justify-content: center; - width: 100%; - height: 30px; - font-size: $pop-normal-size; - font-weight: $pop-normal-weight; - color: $pop-color; - border: 1px solid #646464; - border-radius: 2px; - padding: 0 10px; - transition: all .15s ease-in-out; - i{ - height: 15px; - display: block; - margin-left: 10px; - background-repeat: no-repeat; - background-position: center; - background-size: cover; - transition: all .15s ease-in-out; - &.allocation01{ - background-image: url(../../public/static/images/canvas/allocation_icon01_white.svg); - width: 15px; - } - &.allocation02{ - background-image: url(../../public/static/images/canvas/allocation_icon02_white.svg); - width: 18px; - } - } - &.act, - &:hover{ - color: #101010; - border: 1px solid #101010; - background-color: #fff; - i{ - &.allocation01{ - background-image: url(../../public/static/images/canvas/allocation_icon01_black.svg); - } - &.allocation02{ - background-image: url(../../public/static/images/canvas/allocation_icon02_black.svg); - } - } - } - } -} - -// 경사설정 -.slope-wrap{ - padding-bottom: 24px; - border-bottom: 1px solid #424242; -} - -// 면형상 배치 -.plane-frame-wrap{ - display: flex; - gap: 10px; - .plane-shape-wrap{ - flex: none; - width: 73px; - } -} - -.plane-shape-menu{ - display: grid; - grid-template-columns: repeat(2, 1fr); - gap: 5px; - .shape-menu-box{ - border-radius: 2px; - width: 34px; - height: 34px; - background-color: #373737; - border: 1px solid #676767; - transition: background .15s ease-in-out, border .15s ease-in-out; - .shape-box{ - display: flex; - justify-content: center; - align-items: center; - position: relative; - width: 100%; - height: 100%; - border-radius: 2px; - } - &.act, - &:hover{ - border-color: #008BFF; - background-color: #008BFF; - } - } -} - -.shape-library{ - display: flex; - flex-direction: column; - align-items: center; justify-content: center; - gap: 5px; - padding-top: 5px; - .library-btn{ - width: 100%; - height: 34px; - border: 1px solid #6C6C6C; - border-radius: 2px; - background-color: #373737; - background-repeat: no-repeat; - background-position: center; - transition: all .15s ease-in-out; - &.ico01{background-image: url(../../public/static/images/canvas/shape_labrary01.svg); background-size: 19px 18px;} - &.ico02{background-image: url(../../public/static/images/canvas/shape_labrary02.svg); background-size: 15px 20px;} - &.ico03{background-image: url(../../public/static/images/canvas/shape_labrary03.svg); background-size: 19px 16px;} - &:hover{ - border-color: #1083E3; - background-color: #1083E3; - } - } -} - -.plane-detail-wrap{ - display: flex; - flex-direction: column; - flex: 1; -} -.plane-shape-wrapper{ - flex: 1; - display: flex; - flex-direction: column; - gap: 10px; - .plane-box{ - width: 100%; - padding: 10px 18px; - border-radius: 2px; - background-color: #313131; - border: 1px solid #484848; - .plane-box-tit{ - font-size: $pop-normal-size; - font-weight: 600; - color: $pop-color; - margin-bottom: 10px; - } - &.shape-box{ - .shape-box-inner{ - display: flex; - gap:15px; - min-height: 140px; - .shape-img{ - position: relative; - flex: none; - width: 150px; - background-color: #fff; - border-radius: 2px; - img{ - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - } - } - .shape-data{ - flex: 1; - .eaves-keraba-table{ - .eaves-keraba-item{ - .eaves-keraba-th, - .eaves-keraba-td{ - padding-bottom: 10px; - } - &:last-child{ - .eaves-keraba-th, - .eaves-keraba-td{ - padding-bottom: 0px; - } - } - } - } - } - } - } - &.direction-box{ - flex: 1; - display: flex; - flex-direction: column; - .plane-direction-box{ - flex: 1; - display: flex; - align-items: center; - justify-content: center; - width: 100%; - } - } - } -} -.plane-direction{ - width: 150px; - position: relative; - height: 120px; - span{ - position: absolute; - font-size: 12px; - font-weight: 500; - color: #B1B1B1; - &.top{top: 0; left: 50%; transform: translateX(-50%);} - &.right{top: 50%; right: 0; transform: translateY(-50%);} - &.bottom{bottom: 0; left: 50%; transform: translateX(-50%);} - &.left{top: 50%; left: 0; transform: translateY(-50%);} - } - .plane-btn{ - position: absolute; - width: 28px; - height: 28px; - background-color: #777777; - background-image: url(../../public/static/images/canvas/plane_arr.svg); - background-size: 12px 13px; - background-repeat: no-repeat; - background-position: center; - border-radius: 50%; - transition: all .15s ease-in-out; - &.up{top: 22px; left: 50%; transform: translateX(-50%);} - &.right{top: 50%; right: 32px; transform: translateY(-50%) rotate(90deg);} - &.down{bottom: 22px; left: 50%; transform: translateX(-50%) rotate(180deg);} - &.left{top: 50%; left: 32px; transform: translateY(-50%) rotate(270deg);} - &:hover, - &.act{ - background-color: #fff; - background-image: url(../../public/static/images/canvas/plane_arr_act.svg); - } - } -} - -.plane-tab-guide{ + width: 100%; + height: 30px; font-size: $pop-normal-size; font-weight: $pop-normal-weight; color: $pop-color; - margin-top: 10px; -} -.plane-shape-btn{ - padding-top: 10px; - margin-top: auto; - button{ - display: block; - width: 100%; + border: 1px solid #646464; + border-radius: 2px; + padding: 0 10px; + transition: all 0.15s ease-in-out; + i { + height: 15px; + display: block; + margin-left: 10px; + background-repeat: no-repeat; + background-position: center; + background-size: cover; + transition: all 0.15s ease-in-out; + &.allocation01 { + background-image: url(../../public/static/images/canvas/allocation_icon01_white.svg); + width: 15px; + } + &.allocation02 { + background-image: url(../../public/static/images/canvas/allocation_icon02_white.svg); + width: 18px; + } } + &.act, + &:hover { + color: #101010; + border: 1px solid #101010; + background-color: #fff; + i { + &.allocation01 { + background-image: url(../../public/static/images/canvas/allocation_icon01_black.svg); + } + &.allocation02 { + background-image: url(../../public/static/images/canvas/allocation_icon02_black.svg); + } + } + } + } } -// 오브젝트 배치 -.mb-box{ - margin-bottom: 24px; +// 경사설정 +.slope-wrap { + padding-bottom: 24px; + border-bottom: 1px solid #424242; } -.object-direction-wrap{ - display: flex; - align-items: center; - justify-content: center; -} -.discrimination-tit{ - font-size: 13px; - color: #fff; - font-weight: 500; +// 면형상 배치 +.plane-frame-wrap { + display: flex; + gap: 10px; + .plane-shape-wrap { + flex: none; + width: 73px; + } } -.object-size-wrap{ - display: flex; - min-height: 206px; - gap: 24px; - margin-top: 14px; - .object-size-img{ - position: relative; - flex: none; - width: 200px; - background-color: #fff; - img{ +.plane-shape-menu { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 5px; + .shape-menu-box { + border-radius: 2px; + width: 34px; + height: 34px; + background-color: #373737; + border: 1px solid #676767; + transition: + background 0.15s ease-in-out, + border 0.15s ease-in-out; + .shape-box { + display: flex; + justify-content: center; + align-items: center; + position: relative; + width: 100%; + height: 100%; + border-radius: 2px; + } + &.act, + &:hover { + border-color: #008bff; + background-color: #008bff; + } + } +} + +.shape-library { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 5px; + padding-top: 5px; + .library-btn { + width: 100%; + height: 34px; + border: 1px solid #6c6c6c; + border-radius: 2px; + background-color: #373737; + background-repeat: no-repeat; + background-position: center; + transition: all 0.15s ease-in-out; + &.ico01 { + background-image: url(../../public/static/images/canvas/shape_labrary01.svg); + background-size: 19px 18px; + } + &.ico02 { + background-image: url(../../public/static/images/canvas/shape_labrary02.svg); + background-size: 15px 20px; + } + &.ico03 { + background-image: url(../../public/static/images/canvas/shape_labrary03.svg); + background-size: 19px 16px; + } + &:hover { + border-color: #1083e3; + background-color: #1083e3; + } + } +} + +.plane-detail-wrap { + display: flex; + flex-direction: column; + flex: 1; +} +.plane-shape-wrapper { + flex: 1; + display: flex; + flex-direction: column; + gap: 10px; + .plane-box { + width: 100%; + padding: 10px 18px; + border-radius: 2px; + background-color: #313131; + border: 1px solid #484848; + .plane-box-tit { + font-size: $pop-normal-size; + font-weight: 600; + color: $pop-color; + margin-bottom: 10px; + } + &.shape-box { + .shape-box-inner { + display: flex; + gap: 15px; + min-height: 140px; + .shape-img { + position: relative; + flex: none; + width: 150px; + background-color: #fff; + border-radius: 2px; + img { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); + } } - } -} - -// 표시변경 -.display-change-wrap{ - margin: 24px 0; -} -.warning{ - font-size: $pop-normal-size; - font-weight: $pop-normal-weight; - color: #FFAFAF; -} - -// 각 변 속성 변경 -.radio-grid-wrap{ - display: grid; - grid-template-columns: repeat(3, 1fr); - gap: 24px 15px; -} - -// 면 흐름 설정 -.drawing-flow-wrap{ - display: flex; - gap: 10px; - .discrimination-box{ - flex: 1; - display: flex; - flex-direction: column; - .object-direction-wrap{ - flex: 1; - } - &:first-child{ - flex: none; - width: 195px; - } - } -} - -.compas-box{ - display: flex; - align-items: center; - justify-content: center; -} -.compas-box-inner { - position: relative; - width: 200px; - height: 200px; - border-radius: 50%; - - .circle { - position: absolute; - width: 12px; - height: 12px; - border: 1px solid #fff; - border-radius: 50%; - top: 95%; - left: 50%; - transform-origin: 0 -90px; /* 중심에서 반지름 거리만큼 떨어져 위치 */ - cursor:pointer; - z-index: 3; - /* 0번을 180도 위치(아래)에, 13번을 0도 위치(위)에 배치 */ - i{ - position: absolute; - top: 12.5px; - left: 50%; - font-size: 11px; - color: #8B8B8B; - font-weight: 500; - -webkit-user-select: none; - -moz-user-select: none; - -ms-use-select: none; - user-select: none; - } - &:nth-child(1) { transform: rotate(180deg) translate(-50%, -50%); i{transform: translateX(-50%) rotate(180deg);}} - &:nth-child(2) { transform: rotate(195deg) translate(-50%, -50%); i{transform: translateX(-50%) rotate(165deg);}} - &:nth-child(3) { transform: rotate(210deg) translate(-50%, -50%); i{transform: translateX(-50%) rotate(150deg);}} - &:nth-child(4) { transform: rotate(225deg) translate(-50%, -50%); i{transform: translateX(-50%) rotate(135deg);}} - &:nth-child(5) { transform: rotate(240deg) translate(-50%, -50%); i{transform: translateX(-50%) rotate(120deg);}} - &:nth-child(6) { transform: rotate(255deg) translate(-50%, -50%); i{transform: translateX(-50%) rotate(105deg);}} - &:nth-child(7) { transform: rotate(270deg) translate(-50%, -50%); i{transform: translateX(-50%) rotate(90deg);}} - &:nth-child(8) { transform: rotate(285deg) translate(-50%, -50%); i{transform: translateX(-50%) rotate(75deg);}} - &:nth-child(9) { transform: rotate(300deg) translate(-50%, -50%); i{transform: translateX(-50%) rotate(60deg);}} - &:nth-child(10) { transform: rotate(315deg) translate(-50%, -50%); i{transform: translateX(-50%) rotate(45deg);}} - &:nth-child(11) { transform: rotate(330deg) translate(-50%, -50%); i{transform: translateX(-50%) rotate(30deg);}} - &:nth-child(12) { transform: rotate(345deg) translate(-50%, -50%); i{transform: translateX(-50%) rotate(15deg);}} - &:nth-child(13) { transform: rotate(0deg) translate(-50%, -50%); i{transform: translateX(-50%) rotate(0deg);}} - &:nth-child(14) { transform: rotate(15deg) translate(-50%, -50%); i{transform: translateX(-50%) rotate(-15deg);}} - &:nth-child(15) { transform: rotate(30deg) translate(-50%, -50%); i{transform: translateX(-50%) rotate(-30deg);}} - &:nth-child(16) { transform: rotate(45deg) translate(-50%, -50%); i{transform: translateX(-50%) rotate(-45deg);}} - &:nth-child(17) { transform: rotate(60deg) translate(-50%, -50%); i{transform: translateX(-50%) rotate(-60deg);}} - &:nth-child(18) { transform: rotate(75deg) translate(-50%, -50%); i{transform: translateX(-50%) rotate(-75deg);}} - &:nth-child(19) { transform: rotate(90deg) translate(-50%, -50%); i{transform: translateX(-50%) rotate(-90deg);}} - &:nth-child(20) { transform: rotate(105deg) translate(-50%, -50%); i{transform: translateX(-50%) rotate(-105deg);}} - &:nth-child(21) { transform: rotate(120deg) translate(-50%, -50%); i{transform: translateX(-50%) rotate(-120deg);}} - &:nth-child(22) { transform: rotate(135deg) translate(-50%, -50%); i{transform: translateX(-50%) rotate(-135deg);}} - &:nth-child(23) { transform: rotate(150deg) translate(-50%, -50%); i{transform: translateX(-50%) rotate(-150deg);}} - &:nth-child(24) { transform: rotate(165deg) translate(-50%, -50%); i{transform: translateX(-50%) rotate(-165deg);}} - &.act{ - &::after{ - content: ''; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - width: 5px; - height: 5px; - background-color: #fff; - border-radius: 50%; - } - i{ - color: #fff; - } - } - } - .compas{ - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - width: 148px; - height: 148px; - border: 4px solid #fff; - border-radius: 50%; - .compas-arr{ - width: 100%; - height: 100%; - background: url(../../public/static/images/canvas/compas.svg)no-repeat center; - background-size: 122px 122px; - } - } -} - -.draw-flow-wrap{ - margin: 10px 0; -} - -// 지붕모듈선택 -.roof-module-tab{ - display: flex; - align-items: center; - gap: 10px; - margin-bottom: 14px; - .module-tab-bx{ - flex: 1; - height: 34px; - line-height: 31px; - border: 1px solid #484848; - border-radius: 2px; - background-color: transparent; - font-size: 12px; - color: #AAA; - text-align: center; - cursor: default; - transition: all .15s ease-in-out; - &.act{ - background-color: #1083E3; - border: 1px solid #1083E3; - color: #fff; - font-weight: 500; - } - } - .tab-arr{ - display: block; - width: 9px; - height: 14px; - background-repeat: no-repeat; - background-position: center; - background-size: cover; - background-image: url(../../public/static/images/canvas/module_tab_arr.svg); - transition: all .15s ease-in-out; - &.act{ - background-image: url(../../public/static/images/canvas/module_tab_arr_white.svg); - } - } -} - -.roof-module-compas{ - margin-bottom: 24px; - .compas-box-inner{ - width: 280px; - height: 253px; - .circle{ - top: 86%; - &:nth-child(1), - &:nth-child(7), - &:nth-child(13), - &:nth-child(19){ - &::before{ - content: ''; - position: absolute; - top: 20px; - left: 50%; - transform: translateX(-50%); - width: 1px; - height: 6px; - background-color: #8B8B8B; + .shape-data { + flex: 1; + .eaves-keraba-table { + .eaves-keraba-item { + .eaves-keraba-th, + .eaves-keraba-td { + padding-bottom: 10px; + } + &:last-child { + .eaves-keraba-th, + .eaves-keraba-td { + padding-bottom: 0px; } + } } - i{ - top: 32px; - } - &.act{ - i{color: #8B8B8B;} - } + } } + } } -} -.center-wrap{ - display: flex; - flex-direction: column; - align-items: center; - gap: 20px; -} - -.module-table-flex-wrap{ - display: flex; - gap: 10px; - .outline-form{ + &.direction-box { + flex: 1; + display: flex; + flex-direction: column; + .plane-direction-box { flex: 1; - } -} - -.module-box-tab{ - display: flex; - .module-btn{ - flex: 1; - border-top: 1px solid #505050; - border-bottom: 1px solid #505050; - border-right: 1px solid #505050; - background-color: #454545; - color: #fff; - height: 30px; - font-size: 12px; - font-weight: 400; - transition: all .15s ease-in-out; - &:first-child{ - border-left: 1px solid #505050; - } - &.act{ - border-color: #fff; - background-color: #fff; - color: #101010; - } - } -} - -.module-table-box{ - flex: 1; - background-color: #3D3D3D; - border-radius: 2px; - .module-table-inner{ - padding: 10px; - .outline-form{ - span{ - width: auto; - } - } - .module-table-tit{ - padding: 10px 0; - font-size: 12px; - color: #fff; - border-bottom: 1px solid #4D4D4D; - } - .eaves-keraba-table{ - width: 100%; - margin-top: 15px; - .eaves-keraba-th{ - width: 72px; - } - .eaves-keraba-th, - .eaves-keraba-td{ - padding-bottom: 5px; - } - } - .self-table-tit{ - font-size: 12px; - font-weight: 500; - color: #fff; - padding-bottom: 15px; - } - } - .warning-guide{ - padding: 20px; - .warning{ - color: #FFCACA; - max-height: 55px; - overflow-y: auto; - padding-right: 30px; - &::-webkit-scrollbar { - width: 4px; - background-color: transparent; - } - &::-webkit-scrollbar-thumb { - background-color: #D9D9D9; - } - &::-webkit-scrollbar-track { - background-color: transparent; - } - } - } -} - -.module-self-table{ - display: table; - border-top: 1px solid #4D4D4D; - border-collapse: collapse; - width: 100%; - .self-table-item{ - display: table-row; - .self-item-td, - .self-item-th{ - display: table-cell; - vertical-align: middle; - border-bottom: 1px solid #4D4D4D; - } - .self-item-th{ - width: 60px; - font-size: 12px; - font-weight: 500; - color: #fff; - } - .self-item-td{ - font-size: 12px; - font-weight: 400; - color: #fff; - padding: 15px 20px; - } - } -} - -.self-table-flx{ - display: flex; - align-items: center; - margin-top: 15px; - button{ - margin-left: auto; - } -} -.hexagonal-wrap{ - .hexagonal-item{ - padding: 15px 0; - border-bottom: 1px solid #4D4D4D; - &:first-child{ - padding-top: 0; - } - &:last-child{ - padding-bottom: 0; - border: none; - } - .hexagonal-flx-auto{ - display: flex; - justify-content: space-between; - } - .hexagonal-flx{ - display: flex; - align-items: center; - button{ - margin-left: auto; - } - } - } -} - -// 회로 및 가대설정 -.circuit-check-inner{ - padding: 5px 0; -} - -.x-scroll-table{ - overflow-x: auto; - padding-bottom: 5px; - .roof-module-table{ - min-width: 1200px; - } - &::-webkit-scrollbar { - height: 4px; - background-color: transparent; - } - &::-webkit-scrollbar-thumb { - background-color: #D9D9D9; - } - &::-webkit-scrollbar-track { - background-color: transparent; - } -} - -.circuit-right-wrap{ - display: flex; - justify-content: flex-end; -} - -.circuit-data-form{ - display: flex; - flex-direction: column; - gap: 5px; - min-height: 60px; - padding: 12px; - border: 1px solid rgba(255, 255, 255, 0.20); - span{ - display: inline-flex; - align-items: center; - .del{ - display: block; - margin-left: 10px; - width: 15px; - height: 15px; - background: url(../../public/static/images/canvas/circuit_del.svg)no-repeat center; - background-size: cover; - } - } -} -.circuit-table-tit{ - color: #fff; - font-size: 12px; - font-weight: 600; - padding: 11px 10px; - background-color: #474747; - border: 1px solid #505050; - border-bottom: none; -} - -.circuit-overflow{ - max-height: 400px; - overflow-y: auto; - margin-bottom: 15px; - &::-webkit-scrollbar { - width: 4px; - background-color: transparent; - } - &::-webkit-scrollbar-thumb { - background-color: #D9D9D9; - } - &::-webkit-scrollbar-track { - background-color: transparent; - } -} - -.circuit-table-flx-wrap{ - display: flex; - gap: 10px; - margin-bottom: 10px; - .circuit-table-flx-box{ - flex: 1; - display: flex; - flex-direction: column; - .bottom-wrap{ - margin-top: auto; - } - .roof-module-table{ - table{ - table-layout: fixed; - } - } - } -} - -.circuit-count-input{ - display: flex; - align-items: center; - gap: 10px; -} - -// 모듈부가기능 -.additional-radio-wrap{ - display: grid; - grid-template-columns: repeat(2, 1fr); - gap: 15px 0; - margin-bottom: 24px; -} -.additional-wrap{ - padding: 24px 0; - border-top: 1px solid #424242; -} - -.additional-color-wrap{ - display: flex; - align-items: center; - padding: 5px 0; - gap: 15px; - .additional-color-box{ - display: flex; - align-items: center; - gap: 8px; - .additional-color{ - display: block; - width: 16px; - height: 16px; - &.pink{ - border: 2px solid #ce1c9c; - background-color: #16417D; - } - &.white{ - border: 2px solid #FFF; - background-color: #001027; - } - } - } -} - -// color setting -.color-setting-wrap{ - padding-bottom: 15px; - border-bottom: 1px solid #424242; - .color-tit{ - font-size: 13px; - font-weight: 500; - color: #ffffff; - margin-bottom: 10px; - } - .color-picker{ - .react-colorful{ - width: 100%; - height: auto; - gap: 20px; - .react-colorful__pointer{ - width: 15px; - height: 15px; - border: 4px solid #Fff; - } - .react-colorful__saturation{ - border-radius: 2px; - height: 200px; - border-bottom: 5px solid #000; - } - .react-colorful__last-control{ - border-radius: 2px; - height: 10px; - } - } - .hex-color-box{ - display: flex; - align-items: center; - margin-top: 15px; - .color-box-tit{ - font-size: 12px; - color: #fff; - font-weight: 500; - margin-right: 10px; - } - .color-hex-input{ - width: 150px; - margin-right: 5px; - input{ - width: 100%; - } - } - .color-box{ - display: block; - width: 30px; - height: 30px; - border-radius: 4px; - } - } - .default-color-wrap{ - margin-top: 25px; - .default-tit{ - font-size: 12px; - font-weight: 500; - color: #fff; - margin-bottom: 10px; - } - .color-button-wrap{ - display: grid; - grid-template-columns: repeat(8, 1fr); - gap: 21px; - .default-color{ - display: block; - width: 100%; - height: 30px; - border-radius: 4px; - } - } - } - } -} - -// 글꼴 설정 팝업 -.font-option-warp{ - display: grid; - grid-template-columns: repeat(2, 1fr); - gap: 15px 5px; - margin-bottom: 15px; - .font-option-item{ - .option-item-tit{ - font-size: 12px; - font-weight: 500; - color: #fff; - margin-bottom: 10px; - } - } -} -.font-ex-wrap{ - margin-bottom: 15px; - .font-ex-tit{ - font-size: 12px; - font-weight: 500; - color: #fff; - margin-bottom: 10px; - } - .font-ex-box{ display: flex; align-items: center; justify-content: center; width: 100%; - min-height: 80px; - background-color: #fff; + } } - + } } - -// 치수선 설정 -.font-btn-wrap{ - margin-bottom: 15px; - button{ - width: 100%; - height: 30px; - line-height: 28px; +.plane-direction { + width: 150px; + position: relative; + height: 120px; + span { + position: absolute; + font-size: 12px; + font-weight: 500; + color: #b1b1b1; + &.top { + top: 0; + left: 50%; + transform: translateX(-50%); } -} - -.line-color-wrap{ - margin-bottom: 15px; - .color-btn{ - display: block; - width: 100%; - height: 30px; - border-radius: 2px; + &.right { + top: 50%; + right: 0; + transform: translateY(-50%); } -} - -.form-box{ - width: 100%; - background-color: #fff; - padding: 10px 15px 20px; - .line-form{ - position: relative; - display: flex; - flex-direction: column; - justify-content: flex-end; - min-width: 102px; - min-height: 40px; - margin: 0 auto; - - &::before{ - content: ''; - position: absolute; - bottom: 0px; - left: 0; - width: 100%; - height: 40px; - border-left: 1px dashed #101010; - border-right: 1px dashed #101010; - } - .line-font-box{ - .font{ - display: block; - padding-bottom: 15px; - color: #101010; - text-align: center; - line-height: 1; - } - .line{ - position: relative; - display: block; - width: 100%; - height: 1px; - border-radius: 30px; - &::before{ - content: ''; - position: absolute; - top: 50%; - transform: translateY(-50%) rotate(45deg); - left: 1px; - width: 9px; - height:+ 9px; - border: 1px solid; - border-color: inherit; - border-top: none; - border-right: none; - } - &::after{ - content: ''; - position: absolute; - top: 50%; - transform: translateY(-50%) rotate(45deg); - right: 1px; - width: 9px; - height: 9px; - border: 1px solid; - border-color: inherit; - border-bottom: none; - border-left: none; - } - } - } + &.bottom { + bottom: 0; + left: 50%; + transform: translateX(-50%); } + &.left { + top: 50%; + left: 0; + transform: translateY(-50%); + } + } + .plane-btn { + position: absolute; + width: 28px; + height: 28px; + background-color: #777777; + background-image: url(../../public/static/images/canvas/plane_arr.svg); + background-size: 12px 13px; + background-repeat: no-repeat; + background-position: center; + border-radius: 50%; + transition: all 0.15s ease-in-out; + &.up { + top: 22px; + left: 50%; + transform: translateX(-50%); + } + &.right { + top: 50%; + right: 32px; + transform: translateY(-50%) rotate(90deg); + } + &.down { + bottom: 22px; + left: 50%; + transform: translateX(-50%) rotate(180deg); + } + &.left { + top: 50%; + left: 32px; + transform: translateY(-50%) rotate(270deg); + } + &:hover, + &.act { + background-color: #fff; + background-image: url(../../public/static/images/canvas/plane_arr_act.svg); + } + } } -// 사이즈 변경 -.size-inner-warp{ - position: relative; +.plane-tab-guide { + font-size: $pop-normal-size; + font-weight: $pop-normal-weight; + color: $pop-color; + margin-top: 10px; } -.size-check-wrap{ - position: relative; +.plane-shape-btn { + padding-top: 10px; + margin-top: auto; + button { display: block; - width: 132px; - height: 132px; - margin: 0 auto; - .size-btn{ - position: absolute; - width: 16px; - height: 16px; - border: 1px solid #fff; - border-radius: 50%; - &.act{ - &::after{ - content: ''; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - width: 8px; - height: 8px; - background-color: #fff; - border-radius: 50%; - } - } - &:nth-child(1){ top: 0; left: 0; } - &:nth-child(2){ top: 0; right: 0; } - &:nth-child(3){ bottom: 0; left: 0; } - &:nth-child(4){ bottom: 0; right: 0; } + width: 100%; + } +} + +// 오브젝트 배치 +.mb-box { + margin-bottom: 24px; +} + +.object-direction-wrap { + display: flex; + align-items: center; + justify-content: center; +} +.discrimination-tit { + font-size: 13px; + color: #fff; + font-weight: 500; +} + +.object-size-wrap { + display: flex; + min-height: 206px; + gap: 24px; + margin-top: 14px; + .object-size-img { + position: relative; + flex: none; + width: 200px; + background-color: #fff; + img { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); } - .size-box{ + } +} + +// 표시변경 +.display-change-wrap { + margin: 24px 0; +} +.warning { + font-size: $pop-normal-size; + font-weight: $pop-normal-weight; + color: #ffafaf; +} + +// 각 변 속성 변경 +.radio-grid-wrap { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 24px 15px; +} + +// 면 흐름 설정 +.drawing-flow-wrap { + display: flex; + gap: 10px; + .discrimination-box { + flex: 1; + display: flex; + flex-direction: column; + .object-direction-wrap { + flex: 1; + } + &:first-child { + flex: none; + width: 195px; + } + } +} + +.compas-box { + display: flex; + align-items: center; + justify-content: center; +} +.compas-box-inner { + position: relative; + width: 200px; + height: 200px; + border-radius: 50%; + + .circle { + position: absolute; + width: 12px; + height: 12px; + border: 1px solid #fff; + border-radius: 50%; + top: 95%; + left: 50%; + transform-origin: 0 -90px; /* 중심에서 반지름 거리만큼 떨어져 위치 */ + cursor: pointer; + z-index: 3; + /* 0번을 180도 위치(아래)에, 13번을 0도 위치(위)에 배치 */ + i { + position: absolute; + top: 12.5px; + left: 50%; + font-size: 11px; + color: #8b8b8b; + font-weight: 500; + -webkit-user-select: none; + -moz-user-select: none; + -ms-use-select: none; + user-select: none; + } + &:nth-child(1) { + transform: rotate(180deg) translate(-50%, -50%); + i { + transform: translateX(-50%) rotate(180deg); + } + } + &:nth-child(2) { + transform: rotate(195deg) translate(-50%, -50%); + i { + transform: translateX(-50%) rotate(165deg); + } + } + &:nth-child(3) { + transform: rotate(210deg) translate(-50%, -50%); + i { + transform: translateX(-50%) rotate(150deg); + } + } + &:nth-child(4) { + transform: rotate(225deg) translate(-50%, -50%); + i { + transform: translateX(-50%) rotate(135deg); + } + } + &:nth-child(5) { + transform: rotate(240deg) translate(-50%, -50%); + i { + transform: translateX(-50%) rotate(120deg); + } + } + &:nth-child(6) { + transform: rotate(255deg) translate(-50%, -50%); + i { + transform: translateX(-50%) rotate(105deg); + } + } + &:nth-child(7) { + transform: rotate(270deg) translate(-50%, -50%); + i { + transform: translateX(-50%) rotate(90deg); + } + } + &:nth-child(8) { + transform: rotate(285deg) translate(-50%, -50%); + i { + transform: translateX(-50%) rotate(75deg); + } + } + &:nth-child(9) { + transform: rotate(300deg) translate(-50%, -50%); + i { + transform: translateX(-50%) rotate(60deg); + } + } + &:nth-child(10) { + transform: rotate(315deg) translate(-50%, -50%); + i { + transform: translateX(-50%) rotate(45deg); + } + } + &:nth-child(11) { + transform: rotate(330deg) translate(-50%, -50%); + i { + transform: translateX(-50%) rotate(30deg); + } + } + &:nth-child(12) { + transform: rotate(345deg) translate(-50%, -50%); + i { + transform: translateX(-50%) rotate(15deg); + } + } + &:nth-child(13) { + transform: rotate(0deg) translate(-50%, -50%); + i { + transform: translateX(-50%) rotate(0deg); + } + } + &:nth-child(14) { + transform: rotate(15deg) translate(-50%, -50%); + i { + transform: translateX(-50%) rotate(-15deg); + } + } + &:nth-child(15) { + transform: rotate(30deg) translate(-50%, -50%); + i { + transform: translateX(-50%) rotate(-30deg); + } + } + &:nth-child(16) { + transform: rotate(45deg) translate(-50%, -50%); + i { + transform: translateX(-50%) rotate(-45deg); + } + } + &:nth-child(17) { + transform: rotate(60deg) translate(-50%, -50%); + i { + transform: translateX(-50%) rotate(-60deg); + } + } + &:nth-child(18) { + transform: rotate(75deg) translate(-50%, -50%); + i { + transform: translateX(-50%) rotate(-75deg); + } + } + &:nth-child(19) { + transform: rotate(90deg) translate(-50%, -50%); + i { + transform: translateX(-50%) rotate(-90deg); + } + } + &:nth-child(20) { + transform: rotate(105deg) translate(-50%, -50%); + i { + transform: translateX(-50%) rotate(-105deg); + } + } + &:nth-child(21) { + transform: rotate(120deg) translate(-50%, -50%); + i { + transform: translateX(-50%) rotate(-120deg); + } + } + &:nth-child(22) { + transform: rotate(135deg) translate(-50%, -50%); + i { + transform: translateX(-50%) rotate(-135deg); + } + } + &:nth-child(23) { + transform: rotate(150deg) translate(-50%, -50%); + i { + transform: translateX(-50%) rotate(-150deg); + } + } + &:nth-child(24) { + transform: rotate(165deg) translate(-50%, -50%); + i { + transform: translateX(-50%) rotate(-165deg); + } + } + &.act { + &::after { + content: ''; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); - width: 100px; - height: 100px; + width: 5px; + height: 5px; background-color: #fff; + border-radius: 50%; + } + i { + color: #fff; + } } -} - -.size-option-top{ - margin-bottom: 15px; -} -.size-option-side{ + } + .compas { position: absolute; top: 50%; - left: 0; - transform: translateY(-50%); -} -.size-option-wrap{ - width: 88px; - margin: 0 auto; - .size-option{ - display: flex; - align-items: center; - input{ - width: 100%; - flex: 1; - } - span{ - flex: none; - } + left: 50%; + transform: translate(-50%, -50%); + width: 148px; + height: 148px; + border: 4px solid #fff; + border-radius: 50%; + .compas-arr { + width: 100%; + height: 100%; + background: url(../../public/static/images/canvas/compas.svg) no-repeat center; + background-size: 122px 122px; } + } +} + +.draw-flow-wrap { + margin: 10px 0; +} + +// 지붕모듈선택 +.roof-module-tab { + display: flex; + align-items: center; + gap: 10px; + margin-bottom: 14px; + .module-tab-bx { + flex: 1; + height: 34px; + line-height: 31px; + border: 1px solid #484848; + border-radius: 2px; + background-color: transparent; + font-size: 12px; + color: #aaa; + text-align: center; + cursor: default; + transition: all 0.15s ease-in-out; + &.act { + background-color: #1083e3; + border: 1px solid #1083e3; + color: #fff; + font-weight: 500; + } + } + .tab-arr { + display: block; + width: 9px; + height: 14px; + background-repeat: no-repeat; + background-position: center; + background-size: cover; + background-image: url(../../public/static/images/canvas/module_tab_arr.svg); + transition: all 0.15s ease-in-out; + &.act { + background-image: url(../../public/static/images/canvas/module_tab_arr_white.svg); + } + } +} + +.roof-module-compas { + margin-bottom: 24px; + .compas-box-inner { + width: 280px; + height: 253px; + .circle { + top: 86%; + &:nth-child(1), + &:nth-child(7), + &:nth-child(13), + &:nth-child(19) { + &::before { + content: ''; + position: absolute; + top: 20px; + left: 50%; + transform: translateX(-50%); + width: 1px; + height: 6px; + background-color: #8b8b8b; + } + } + i { + top: 32px; + } + &.act { + i { + color: #8b8b8b; + } + } + } + } +} +.center-wrap { + display: flex; + flex-direction: column; + align-items: center; + gap: 20px; +} + +.module-table-flex-wrap { + display: flex; + gap: 10px; + .outline-form { + flex: 1; + } +} + +.module-box-tab { + display: flex; + .module-btn { + flex: 1; + border-top: 1px solid #505050; + border-bottom: 1px solid #505050; + border-right: 1px solid #505050; + background-color: #454545; + color: #fff; + height: 30px; + font-size: 12px; + font-weight: 400; + transition: all 0.15s ease-in-out; + &:first-child { + border-left: 1px solid #505050; + } + &.act { + border-color: #fff; + background-color: #fff; + color: #101010; + } + } +} + +.module-table-box { + flex: 1; + background-color: #3d3d3d; + border-radius: 2px; + .module-table-inner { + padding: 10px; + .outline-form { + span { + width: auto; + } + } + .module-table-tit { + padding: 10px 0; + font-size: 12px; + color: #fff; + border-bottom: 1px solid #4d4d4d; + } + .eaves-keraba-table { + width: 100%; + margin-top: 15px; + .eaves-keraba-th { + width: 72px; + } + .eaves-keraba-th, + .eaves-keraba-td { + padding-bottom: 5px; + } + } + .self-table-tit { + font-size: 12px; + font-weight: 500; + color: #fff; + padding-bottom: 15px; + } + } + .warning-guide { + padding: 20px; + .warning { + color: #ffcaca; + max-height: 55px; + overflow-y: auto; + padding-right: 30px; + &::-webkit-scrollbar { + width: 4px; + background-color: transparent; + } + &::-webkit-scrollbar-thumb { + background-color: #d9d9d9; + } + &::-webkit-scrollbar-track { + background-color: transparent; + } + } + } +} + +.module-self-table { + display: table; + border-top: 1px solid #4d4d4d; + border-collapse: collapse; + width: 100%; + .self-table-item { + display: table-row; + .self-item-td, + .self-item-th { + display: table-cell; + vertical-align: middle; + border-bottom: 1px solid #4d4d4d; + } + .self-item-th { + width: 60px; + font-size: 12px; + font-weight: 500; + color: #fff; + } + .self-item-td { + font-size: 12px; + font-weight: 400; + color: #fff; + padding: 15px 20px; + } + } +} + +.self-table-flx { + display: flex; + align-items: center; + margin-top: 15px; + button { + margin-left: auto; + } +} +.hexagonal-wrap { + .hexagonal-item { + padding: 15px 0; + border-bottom: 1px solid #4d4d4d; + &:first-child { + padding-top: 0; + } + &:last-child { + padding-bottom: 0; + border: none; + } + .hexagonal-flx-auto { + display: flex; + justify-content: space-between; + } + .hexagonal-flx { + display: flex; + align-items: center; + button { + margin-left: auto; + } + } + } +} + +// 회로 및 가대설정 +.circuit-check-inner { + padding: 5px 0; +} + +.x-scroll-table { + overflow-x: auto; + padding-bottom: 5px; + .roof-module-table { + min-width: 1200px; + } + &::-webkit-scrollbar { + height: 4px; + background-color: transparent; + } + &::-webkit-scrollbar-thumb { + background-color: #d9d9d9; + } + &::-webkit-scrollbar-track { + background-color: transparent; + } +} + +.circuit-right-wrap { + display: flex; + justify-content: flex-end; +} + +.circuit-data-form { + display: flex; + flex-direction: column; + gap: 5px; + min-height: 60px; + padding: 12px; + border: 1px solid rgba(255, 255, 255, 0.2); + span { + display: inline-flex; + align-items: center; + .del { + display: block; + margin-left: 10px; + width: 15px; + height: 15px; + background: url(../../public/static/images/canvas/circuit_del.svg) no-repeat center; + background-size: cover; + } + } +} +.circuit-table-tit { + color: #fff; + font-size: 12px; + font-weight: 600; + padding: 11px 10px; + background-color: #474747; + border: 1px solid #505050; + border-bottom: none; +} + +.circuit-overflow { + max-height: 400px; + overflow-y: auto; + margin-bottom: 15px; + &::-webkit-scrollbar { + width: 4px; + background-color: transparent; + } + &::-webkit-scrollbar-thumb { + background-color: #d9d9d9; + } + &::-webkit-scrollbar-track { + background-color: transparent; + } +} + +.circuit-table-flx-wrap { + display: flex; + gap: 10px; + margin-bottom: 10px; + .circuit-table-flx-box { + flex: 1; + display: flex; + flex-direction: column; + .bottom-wrap { + margin-top: auto; + } + .roof-module-table { + table { + table-layout: fixed; + } + } + } +} + +.circuit-count-input { + display: flex; + align-items: center; + gap: 10px; +} + +// 모듈부가기능 +.additional-radio-wrap { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 15px 0; + margin-bottom: 24px; +} +.additional-wrap { + padding: 24px 0; + border-top: 1px solid #424242; +} + +.additional-color-wrap { + display: flex; + align-items: center; + padding: 5px 0; + gap: 15px; + .additional-color-box { + display: flex; + align-items: center; + gap: 8px; + .additional-color { + display: block; + width: 16px; + height: 16px; + &.pink { + border: 2px solid #ce1c9c; + background-color: #16417d; + } + &.white { + border: 2px solid #fff; + background-color: #001027; + } + } + } +} + +// color setting +.color-setting-wrap { + padding-bottom: 15px; + border-bottom: 1px solid #424242; + .color-tit { + font-size: 13px; + font-weight: 500; + color: #ffffff; + margin-bottom: 10px; + } + .color-picker { + .react-colorful { + width: 100%; + height: auto; + gap: 20px; + .react-colorful__pointer { + width: 15px; + height: 15px; + border: 4px solid #fff; + } + .react-colorful__saturation { + border-radius: 2px; + height: 200px; + border-bottom: 5px solid #000; + } + .react-colorful__last-control { + border-radius: 2px; + height: 10px; + } + } + .hex-color-box { + display: flex; + align-items: center; + margin-top: 15px; + .color-box-tit { + font-size: 12px; + color: #fff; + font-weight: 500; + margin-right: 10px; + } + .color-hex-input { + width: 150px; + margin-right: 5px; + input { + width: 100%; + } + } + .color-box { + display: block; + width: 30px; + height: 30px; + border-radius: 4px; + } + } + .default-color-wrap { + margin-top: 25px; + .default-tit { + font-size: 12px; + font-weight: 500; + color: #fff; + margin-bottom: 10px; + } + .color-button-wrap { + display: grid; + grid-template-columns: repeat(8, 1fr); + gap: 21px; + .default-color { + display: block; + width: 100%; + height: 30px; + border-radius: 4px; + } + } + } + } +} + +// 글꼴 설정 팝업 +.font-option-warp { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 15px 5px; + margin-bottom: 15px; + .font-option-item { + .option-item-tit { + font-size: 12px; + font-weight: 500; + color: #fff; + margin-bottom: 10px; + } + } +} +.font-ex-wrap { + margin-bottom: 15px; + .font-ex-tit { + font-size: 12px; + font-weight: 500; + color: #fff; + margin-bottom: 10px; + } + .font-ex-box { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + min-height: 80px; + background-color: #fff; + } +} + +// 치수선 설정 +.font-btn-wrap { + margin-bottom: 15px; + button { + width: 100%; + height: 30px; + line-height: 28px; + } +} + +.line-color-wrap { + margin-bottom: 15px; + .color-btn { + display: block; + width: 100%; + height: 30px; + border-radius: 2px; + } +} + +.form-box { + width: 100%; + background-color: #fff; + padding: 10px 15px 20px; + .line-form { + position: relative; + display: flex; + flex-direction: column; + justify-content: flex-end; + min-width: 102px; + min-height: 40px; + margin: 0 auto; + + &::before { + content: ''; + position: absolute; + bottom: 0px; + left: 0; + width: 100%; + height: 40px; + border-left: 1px dashed #101010; + border-right: 1px dashed #101010; + } + .line-font-box { + .font { + display: block; + padding-bottom: 15px; + color: #101010; + text-align: center; + line-height: 1; + } + .line { + position: relative; + display: block; + width: 100%; + height: 1px; + border-radius: 30px; + &::before { + content: ''; + position: absolute; + top: 50%; + transform: translateY(-50%) rotate(45deg); + left: 1px; + width: 9px; + height: +9px; + border: 1px solid; + border-color: inherit; + border-top: none; + border-right: none; + } + &::after { + content: ''; + position: absolute; + top: 50%; + transform: translateY(-50%) rotate(45deg); + right: 1px; + width: 9px; + height: 9px; + border: 1px solid; + border-color: inherit; + border-bottom: none; + border-left: none; + } + } + } + } +} + +// 사이즈 변경 +.size-inner-warp { + position: relative; +} +.size-check-wrap { + position: relative; + display: block; + width: 132px; + height: 132px; + margin: 0 auto; + .size-btn { + position: absolute; + width: 16px; + height: 16px; + border: 1px solid #fff; + border-radius: 50%; + &.act { + &::after { + content: ''; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 8px; + height: 8px; + background-color: #fff; + border-radius: 50%; + } + } + &:nth-child(1) { + top: 0; + left: 0; + } + &:nth-child(2) { + top: 0; + right: 0; + } + &:nth-child(3) { + bottom: 0; + left: 0; + } + &:nth-child(4) { + bottom: 0; + right: 0; + } + } + .size-box { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 100px; + height: 100px; + background-color: #fff; + } +} + +.size-option-top { + margin-bottom: 15px; +} +.size-option-side { + position: absolute; + top: 50%; + left: 0; + transform: translateY(-50%); +} +.size-option-wrap { + width: 88px; + margin: 0 auto; + .size-option { + display: flex; + align-items: center; + input { + width: 100%; + flex: 1; + } + span { + flex: none; + } + } } //이미지 크기 설정 -.range-wrap{ - display: flex; - align-items: center; - input{ - flex: 1; - margin-right: 10px; +.range-wrap { + display: flex; + align-items: center; + input { + flex: 1; + margin-right: 10px; + } + label { + flex: none; + text-align: right; + width: 38px; + font-size: 13px; + color: #fff; + font-weight: 500; + } +} + +// 이미지 불러오기 +.img-flex-box { + display: flex; + align-items: center; +} +.img-load-from { + margin-top: 20px; + .img-load-item { + border-top: 1px solid #424242; + padding: 18px 0; + .d-check-radio { + margin-bottom: 20px; } - label{ - flex: none; - text-align: right; - width: 38px; - font-size: 13px; - color: #fff; - font-weight: 500; - } -} \ No newline at end of file + } + border-bottom: 1px solid #424242; +} diff --git a/src/styles/_reset.scss b/src/styles/_reset.scss index 711ef44a..c871f7bb 100644 --- a/src/styles/_reset.scss +++ b/src/styles/_reset.scss @@ -1,912 +1,1106 @@ * { - -webkit-text-size-adjust:none; - -moz-text-size-adjust:none; - -ms-text-size-adjust:none; - text-size-adjust: none; - box-sizing: content-box + -webkit-text-size-adjust: none; + -moz-text-size-adjust: none; + -ms-text-size-adjust: none; + text-size-adjust: none; + box-sizing: content-box; } -*, ::after, ::before { - box-sizing: border-box; +*, +::after, +::before { + box-sizing: border-box; } -html, body{ - font-size: 16px; +html, +body { + font-size: 16px; } -html, body, div, span, applet, object, iframe, -h1, h2, h3, h4, h5, h6, p, blockquote, pre, -a, abbr, acronym, address, big, cite, code, -del, dfn, em, img, ins, kbd, q, s, samp, -small, strike, strong, sub, sup, tt, var, -b, u, i, center, -dl, dt, dd, ol, ul, li, -fieldset, form, label, legend, -table, caption, tbody, tfoot, thead, tr, th, td, -article, aside, canvas, details, embed, -figure, figcaption, footer, header, hgroup, -menu, nav, output, ruby, section, summary, -time, mark, audio, video { - margin: 0; - padding: 0; - border: 0; - font: inherit; - vertical-align: baseline; - font-family: 'Noto Sans JP', sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - font-smooth: never; +html, +body, +div, +span, +applet, +object, +iframe, +h1, +h2, +h3, +h4, +h5, +h6, +p, +blockquote, +pre, +a, +abbr, +acronym, +address, +big, +cite, +code, +del, +dfn, +em, +img, +ins, +kbd, +q, +s, +samp, +small, +strike, +strong, +sub, +sup, +tt, +var, +b, +u, +i, +center, +dl, +dt, +dd, +ol, +ul, +li, +fieldset, +form, +label, +legend, +table, +caption, +tbody, +tfoot, +thead, +tr, +th, +td, +article, +aside, +canvas, +details, +embed, +figure, +figcaption, +footer, +header, +hgroup, +menu, +nav, +output, +ruby, +section, +summary, +time, +mark, +audio, +video { + margin: 0; + padding: 0; + border: 0; + font: inherit; + vertical-align: baseline; + font-family: 'Noto Sans JP', sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + font-smooth: never; } /* HTML5 display-role reset for older browsers */ -article, aside, details, figcaption, figure, -footer, header, hgroup, menu, nav, section { - display: block; +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +menu, +nav, +section { + display: block; } body { - line-height: 1.4; + line-height: 1.4; +} +body:first-of-type caption { + display: none; } -body:first-of-type caption { display:none;} -ol, ul { - list-style: none; +ol, +ul { + list-style: none; } -blockquote, q { - quotes: none; +blockquote, +q { + quotes: none; } -blockquote:before, blockquote:after, -q:before, q:after { - content: ''; - content: none; +blockquote:before, +blockquote:after, +q:before, +q:after { + content: ''; + content: none; } table { - width: 100%; - border-collapse: separate; - border-spacing:0; - border:0 none; + width: 100%; + border-collapse: separate; + border-spacing: 0; + border: 0 none; } -caption, th, td { - text-align:left; - font-weight: normal; - border:0; +caption, +th, +td { + text-align: left; + font-weight: normal; + border: 0; } -a { - cursor:pointer; - color:#000; +a { + cursor: pointer; + color: #000; } -a, a:hover, a:active { - text-decoration:none; - -webkit-tap-highlight-color: transparent; +a, +a:hover, +a:active { + text-decoration: none; + -webkit-tap-highlight-color: transparent; } /*form_style*/ -input, select, textarea, button, a, label { - -webkit-tap-highlight-color:rgba(0,0,0,0); +input, +select, +textarea, +button, +a, +label { + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } -button,input[type=text], input[type=button] { - -webkit-appearance: none; - -webkit-border-radius: 0; - -webkit-appearance:none; - appearance: none; - border-radius: 0 +button, +input[type='text'], +input[type='button'] { + -webkit-appearance: none; + -webkit-border-radius: 0; + -webkit-appearance: none; + appearance: none; + border-radius: 0; } -input[type=checkbox], input[type=radio] { - box-sizing: border-box; - padding: 0; +input[type='checkbox'], +input[type='radio'] { + box-sizing: border-box; + padding: 0; } -input, select, button { - border:0 none; - outline:none; - margin:0; +input, +select, +button { + border: 0 none; + outline: none; + margin: 0; } select { - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; } select::-ms-expand { - display: none; + display: none; } ::-webkit-input-placeholder { - line-height:1; - font-weight:300; - font-size:0.938rem; - letter-spacing:-0.6px; - color:#8b8b8b; + line-height: 1; + font-weight: 300; + font-size: 0.938rem; + letter-spacing: -0.6px; + color: #8b8b8b; } -.log-box ::-webkit-input-placeholder{ - color:#8b8b8b; +.log-box ::-webkit-input-placeholder { + color: #8b8b8b; } -button{ - background: transparent; - font-family: 'Noto Sans JP', sans-serif; - border: none; - padding: 0; - margin: 0; - line-height: 1.4; - color: inherit; - outline: none; - cursor: pointer; +button { + background: transparent; + font-family: 'Noto Sans JP', sans-serif; + border: none; + padding: 0; + margin: 0; + line-height: 1.4; + color: inherit; + outline: none; + cursor: pointer; } -.pre{ - font-family: 'Pretendard', sans-serif !important; +.pre { + font-family: 'Pretendard', sans-serif !important; } -.no-click{ - cursor: no-drop !important; +.no-click { + cursor: no-drop !important; } // margin -.mt5{margin-top: 5px !important;} -.mt10{margin-top: 10px !important;} -.mt15{margin-top: 15px !important;} -.mb5{margin-bottom: 5px !important;} -.mb10{margin-bottom: 10px !important;} -.mb15{margin-bottom: 15px !important;} -.mr5{margin-right: 5px !important;} -.mr10{margin-right: 10px !important;} -.mr15{margin-right: 15px !important;} -.ml5{margin-left: 5px !important;} -.ml10{margin-left: 10px !important;} -.ml15{margin-left: 15px !important;} - -// align -.al-l{text-align: left !important;} -.al-r{text-align: right !important;} -.al-c{text-align: center !important;} - - -// button -.btn-frame{ - display: inline-block; - padding: 0 7px; - height: 34px; - line-height: 34px; - border-radius: 2px; - color: #fff; - font-size: 12px; - font-weight: 400; - border: 1px solid #000; - text-align: center; - font-family: 'Pretendard', sans-serif; - transition: all .17s ease-in-out; - cursor: pointer; - &.block{ - width: 100%; - } - &.small{ - font-family: 'Noto Sans JP', sans-serif; - height: 30px; - line-height: 30px; - font-size: 13px; - } - - &.deepgray{ - background-color: #2C2C2C; - border: 1px solid #484848; - } - &.gray{ - background-color: #3C3C3C; - border: 1px solid #545454; - } - &.dark{ - background-color: #1C1C1C; - border: 1px solid #484848; - } - &.modal{ - font-family: 'Noto Sans JP', sans-serif; - background-color: #272727; - border: 1px solid #484848; - color: #aaa; - &:hover{ - background-color: #1083E3; - border: 1px solid #1083E3; - color: #fff; - font-weight: 500; - } - } - &.sub-tab{ - height: 30px; - padding: 0 10px; - line-height: 28px; - font-family: 'Noto Sans JP', sans-serif; - background-color: #2D2D2D; - border: 1px solid #393939; - color: #aaa; - &.act, - &:hover{ - background-color: #414E6C; - border: 1px solid #414E6C; - color: #fff; - font-weight: 500; - } - } - &.roof{ - height: 30px; - padding: 0 10px; - line-height: 28px; - font-family: 'Noto Sans JP', sans-serif; - background-color: transparent; - border: 1px solid #484848; - color: #fff; - &.blue{ - background-color: #414E6C; - border: 1px solid #414E6C; - &:hover{ - background-color: #414E6C; - border: 1px solid #414E6C; - } - } - &.white{ - background-color: #fff; - border: 1px solid #fff; - color: #101010; - &:hover{ - background-color: #fff; - border: 1px solid #fff; - color: #101010; - } - } - &:hover{ - font-weight: 400; - background-color: transparent; - border: 1px solid #484848; - color: #fff; - } - } - &.self{ - height: 30px; - padding: 0 10px; - line-height: 28px; - font-family: 'Noto Sans JP', sans-serif; - background-color: transparent; - border: 1px solid #676767; - color: #AAAAAA; - &:hover{ - background-color: #272727; - border-color: #676767; - font-weight: 400; - } - } - &:hover, - &.act{ - background-color: #1083E3; - border: 1px solid #1083E3; - color: #fff; - font-weight: 500; - } - &.block{ - display: block; - width: 100%; - } - &.ico-flx{ - display: flex; - align-items: center; - .ico{ - margin-right: 10px; - } - &:hover, - &.act{ - font-weight: 400; - } - } +.mt5 { + margin-top: 5px !important; +} +.mt10 { + margin-top: 10px !important; +} +.mt15 { + margin-top: 15px !important; +} +.mb5 { + margin-bottom: 5px !important; +} +.mb10 { + margin-bottom: 10px !important; +} +.mb15 { + margin-bottom: 15px !important; +} +.mr5 { + margin-right: 5px !important; +} +.mr10 { + margin-right: 10px !important; +} +.mr15 { + margin-right: 15px !important; +} +.ml5 { + margin-left: 5px !important; +} +.ml10 { + margin-left: 10px !important; +} +.ml15 { + margin-left: 15px !important; } -.btn-origin{ - display: inline-block; +// align +.al-l { + text-align: left !important; +} +.al-r { + text-align: right !important; +} +.al-c { + text-align: center !important; +} + +// button +.btn-frame { + display: inline-block; + padding: 0 7px; + height: 34px; + line-height: 34px; + border-radius: 2px; + color: #fff; + font-size: 12px; + font-weight: 400; + border: 1px solid #000; + text-align: center; + font-family: 'Pretendard', sans-serif; + transition: all 0.17s ease-in-out; + cursor: pointer; + &.block { + width: 100%; + } + &.small { + font-family: 'Noto Sans JP', sans-serif; + height: 30px; + line-height: 30px; + font-size: 13px; + } + + &.deepgray { + background-color: #2c2c2c; + border: 1px solid #484848; + } + &.gray { + background-color: #3c3c3c; + border: 1px solid #545454; + } + &.dark { + background-color: #1c1c1c; + border: 1px solid #484848; + } + &.modal { + font-family: 'Noto Sans JP', sans-serif; + background-color: #272727; + border: 1px solid #484848; + color: #aaa; + &:hover { + background-color: #1083e3; + border: 1px solid #1083e3; + color: #fff; + font-weight: 500; + } + } + &.sub-tab { height: 30px; padding: 0 10px; - border-radius: 2px; - background-color: #101010; + line-height: 28px; + font-family: 'Noto Sans JP', sans-serif; + background-color: #2d2d2d; + border: 1px solid #393939; + color: #aaa; + &.act, + &:hover { + background-color: #414e6c; + border: 1px solid #414e6c; + color: #fff; + font-weight: 500; + } + } + &.roof { + height: 30px; + padding: 0 10px; + line-height: 28px; + font-family: 'Noto Sans JP', sans-serif; + background-color: transparent; + border: 1px solid #484848; color: #fff; - font-size: 13px; - font-weight: 400; - transition: all .15s ease-in-out; - &.navy{ - background-color: #304961; - &:hover{ - background-color: #233546; - } + &.blue { + background-color: #414e6c; + border: 1px solid #414e6c; + &:hover { + background-color: #414e6c; + border: 1px solid #414e6c; + } } - &.grey{ - background-color: #94A0AD; - &:hover{ - background-color: #607F9A; - } - } - &.green{ - background-color: #A6BBA8; - &:hover{ - background-color: #98af9b; - } - } - &.white{ - border: 1px solid #94A0AD; + &.white { + background-color: #fff; + border: 1px solid #fff; + color: #101010; + &:hover { background-color: #fff; - color: #94A0AD; - &:hover{ - background-color: #fff; - } + border: 1px solid #fff; + color: #101010; + } } + &:hover { + font-weight: 400; + background-color: transparent; + border: 1px solid #484848; + color: #fff; + } + } + &.self { + height: 30px; + padding: 0 10px; + line-height: 28px; + font-family: 'Noto Sans JP', sans-serif; + background-color: transparent; + border: 1px solid #676767; + color: #aaaaaa; + &:hover { + background-color: #272727; + border-color: #676767; + font-weight: 400; + } + } + &:hover, + &.act { + background-color: #1083e3; + border: 1px solid #1083e3; + color: #fff; + font-weight: 500; + } + &.block { + display: block; + width: 100%; + } + &.ico-flx { + display: flex; + align-items: center; + .ico { + margin-right: 10px; + } + &:hover, + &.act { + font-weight: 400; + } + } +} + +.btn-origin { + display: inline-block; + height: 30px; + padding: 0 10px; + border-radius: 2px; + background-color: #101010; + color: #fff; + font-size: 13px; + font-weight: 400; + transition: all 0.15s ease-in-out; + &.navy { + background-color: #304961; + &:hover { + background-color: #233546; + } + } + &.grey { + background-color: #94a0ad; + &:hover { + background-color: #607f9a; + } + } + &.green { + background-color: #a6bba8; + &:hover { + background-color: #98af9b; + } + } + &.white { + border: 1px solid #94a0ad; + background-color: #fff; + color: #94a0ad; + &:hover { + background-color: #fff; + } + } } // select -.sort-select{ - position: relative; - display: inline-block; - min-width: 100px; - height: 30px; - line-height: 30px; - padding: 0 25px 0 10px; - background-color: #373737; - border: 1px solid #3F3F3F; - border-radius: 2px; - border-top-left-radius: 2px; +.sort-select { + position: relative; + display: inline-block; + min-width: 100px; + height: 30px; + line-height: 30px; + padding: 0 25px 0 10px; + background-color: #373737; + border: 1px solid #3f3f3f; + border-radius: 2px; + border-top-left-radius: 2px; + color: #fff; + cursor: pointer; + p { + font-size: 13px; color: #fff; - cursor: pointer; - p{ - font-size: 13px; + height: 100%; + overflow: hidden; + text-overflow: ellipsis; + display: -webkit-box; + -webkit-line-clamp: 1; + -webkit-box-orient: vertical; + } + .select-item-wrap { + position: absolute; + top: 100%; + left: -1px; + clip-path: inset(0 0 100% 0); + width: calc(100% + 2px); + padding: 8px 0; + max-height: 100px; + overflow-y: auto; + background-color: #373737; + border: 1px solid #3f3f3f; + border-radius: 2px; + transition: all 0.17s ease-in-out; + visibility: hidden; + z-index: 999; + .select-item { + display: flex; + align-items: center; + padding: 8px 20px; + line-height: 1.4; + transition: all 0.17s ease-in-out; + button { + font-size: 12px; color: #fff; - height: 100%; - overflow: hidden; - text-overflow: ellipsis; - display: -webkit-box; - -webkit-line-clamp: 1; - -webkit-box-orient: vertical; + line-height: 1.4; + } + &:hover { + background-color: #2c2c2c; + } } - .select-item-wrap{ - position: absolute; - top: 100%; - left: -1px; - clip-path:inset(0 0 100% 0); - width: calc(100% + 2px); - padding: 8px 0; - max-height: 100px; - overflow-y: auto; - background-color: #373737; - border: 1px solid #3F3F3F; - border-radius: 2px; - transition: all 0.17s ease-in-out; - visibility: hidden; - z-index: 999; - .select-item{ - display: flex; - align-items: center; - padding: 8px 20px; - line-height: 1.4; - transition: all .17s ease-in-out; - button{ - font-size: 12px; - color: #fff; - line-height: 1.4; - } - &:hover{ - background-color: #2C2C2C; - } - } - &::-webkit-scrollbar { - width: 2px; - background-color: transparent; - - } - &::-webkit-scrollbar-thumb { - background-color: #5a5a5a; - border-radius: 10px; - } - &::-webkit-scrollbar-track { - background-color: transparent; - } + &::-webkit-scrollbar { + width: 2px; + background-color: transparent; } - &::after{ - content: ''; - position: absolute; - top: 50%; - right: 7px; - transform: translateY(-50%); - width: 10px; - height: 6px; - background: url(/static/images/common/select-arr.svg) no-repeat center; - background-size: cover; - transition: all .17s ease-in-out; + &::-webkit-scrollbar-thumb { + background-color: #5a5a5a; + border-radius: 10px; } - &.active{ - .select-item-wrap{ - clip-path: inset(0 0 0 0); - visibility: visible; - } - &:after{ - transform: translateY(-50%) rotate(-180deg); - } + &::-webkit-scrollbar-track { + background-color: transparent; } + } + &::after { + content: ''; + position: absolute; + top: 50%; + right: 7px; + transform: translateY(-50%); + width: 10px; + height: 6px; + background: url(/static/images/common/select-arr.svg) no-repeat center; + background-size: cover; + transition: all 0.17s ease-in-out; + } + &.active { + .select-item-wrap { + clip-path: inset(0 0 0 0); + visibility: visible; + } + &:after { + transform: translateY(-50%) rotate(-180deg); + } + } } -.select-light{ - position: relative; +.select-light { + position: relative; + display: block; + width: 100%; + height: 30px; + background: #fff url(../../public/static/images/common/select_light_arr.svg) calc(100% - 11px) center no-repeat; + background-size: 10px 6px; + border: 1px solid #eee; + padding: 0 30px 0 10px; + font-size: 13px; + color: #45576f; + font-family: 'Noto Sans JP', sans-serif; + cursor: pointer; + &:disabled { + opacity: 1; + background-color: #fafafa; + color: #999; + cursor: default; + } + &.black { + color: #101010; + } + &.dark { + background: #323234 url(../../public/static/images/common/select_dark_arr.svg) calc(100% - 11px) center no-repeat; + color: #898989; + font-size: 12px; + border-radius: 2px; + border: none; + } +} + +// input +.form-input { + label { + display: block; + color: #aaa; + font-size: 12px; + font-weight: 500; + margin-bottom: 10px; + } +} +input[type='password'], +input[type='number'], +input[type='text'] { + &.input-origin { + display: inline-block; + height: 30px; + line-height: 30px; + border-radius: 2px; + background-color: #323234; + border: 1px solid #323234; + color: #fff; + font-size: 12px; + font-weight: 500; + font-family: 'Pretendard', sans-serif; + padding: 0 10px; + letter-spacing: 0px; + text-align: right; + transition: border 0.15s ease-in-out; + &:focus { + border: 1px solid #1083e3; + } + &::placeholder { + font-family: 'Noto Sans JP', sans-serif; + opacity: 1; + font-size: 12px; + letter-spacing: 0px; + } + &.block { + width: 100%; + } + &:read-only { + color: #aaa; + &:focus { + border: 1px solid #323234; + } + } + &.plane { + font-family: 'Noto Sans JP', sans-serif; + border: 1px solid #525252; + background-color: transparent; + } + } + &.input-light { display: block; width: 100%; height: 30px; - background: #FFF url(../../public/static/images/common/select_light_arr.svg) calc(100% - 11px) center no-repeat; - background-size: 10px 6px; + padding: 0 10px; border: 1px solid #eee; - padding: 0 30px 0 10px; - font-size: 13px; - color: #45576F; + border-radius: 2px; + background-color: #fff; font-family: 'Noto Sans JP', sans-serif; - cursor: pointer; - &:disabled{ - opacity: 1; - background-color: #FAFAFA; - color: #999; - cursor: default; + font-size: 13px; + color: #45576f; + font-weight: normal; + transition: border-color 0.17s ease-in-out; + text-align: left; + &:focus { + border-color: #94a0ad; } - &.black{ - color: #101010; - } - &.dark{ - background: #323234 url(../../public/static/images/common/select_dark_arr.svg) calc(100% - 11px) center no-repeat; - color: #898989; - font-size: 12px; - border-radius: 2px; - border: none; + &:read-only { + background-color: #fafafa; + color: #999999; + &:focus { + border-color: #eee; + } } + } } - -// input -.form-input{ - label{ - display: block; - color: #aaa; - font-size: 12px; - font-weight: 500; - margin-bottom: 10px; - } -} -input[type=password], -input[type=number], -input[type=text]{ - &.input-origin{ - display: inline-block; - height: 30px; - line-height: 30px; - border-radius: 2px; - background-color: #323234; - border: 1px solid #323234; - color: #fff; - font-size: 12px; - font-weight: 500; - font-family: 'Pretendard', sans-serif; - padding: 0 10px; - letter-spacing: 0px; - text-align: right; - transition: border .15s ease-in-out; - &:focus{ - border: 1px solid #1083E3; - } - &::placeholder{ - opacity: 1; - font-size: 12px; - letter-spacing: 0px; - } - &.block{ - width: 100%; - } - &:read-only{ - color: #AAA; - &:focus{ - border: 1px solid #323234; - } - } - &.plane{ - font-family: 'Noto Sans JP', sans-serif; - border: 1px solid #525252; - background-color: transparent; - } - } - &.input-light{ - display: block; - width: 100%; - height: 30px; - padding: 0 10px; - border: 1px solid #eee; - border-radius: 2px; - background-color: #fff; - font-family: 'Noto Sans JP', sans-serif; - font-size: 13px; - color: #45576F; - font-weight: normal; - transition: border-color .17s ease-in-out; - text-align: left; - &:focus{ - border-color: #94A0AD; - } - &:read-only{ - background-color: #FAFAFA; - color: #999999; - &:focus{ - border-color: #eee; - } - } - } -} - - - // check-btn -.check-btn{ - display: flex; - align-items: center; - height: 30px; - background-color: #3A3A3A; - border-radius: 3px; - transition: all .17s ease-in-out; - .check-area{ - flex: none; - width: 30px; - height: 100%; - border-right: 1px solid #272727; - background: url(../../public/static/images/canvas/check-grey.svg)no-repeat center; - background-size: 11px 9px; +.check-btn { + display: flex; + align-items: center; + height: 30px; + background-color: #3a3a3a; + border-radius: 3px; + transition: all 0.17s ease-in-out; + .check-area { + flex: none; + width: 30px; + height: 100%; + border-right: 1px solid #272727; + background: url(../../public/static/images/canvas/check-grey.svg) no-repeat center; + background-size: 11px 9px; + } + .title-area { + padding: 0 10px; + font-size: 12px; + color: #898989; + font-weight: 400; + } + &.block { + width: 100%; + } + &:hover, + &.act { + background-color: #fff; + .check-area { + border-right: 1px solid #101010; + background: url(../../public/static/images/canvas/check-black.svg) no-repeat center; } - .title-area{ - padding: 0 10px; - font-size: 12px; - color: #898989; - font-weight: 400; - } - &.block{ - width: 100%; - } - &:hover, - &.act{ - background-color: #fff; - .check-area{ - border-right: 1px solid #101010; - background: url(../../public/static/images/canvas/check-black.svg)no-repeat center; - } - .title-area{ - color: #101010; - font-weight: 600; - } + .title-area { + color: #101010; + font-weight: 600; } + } } // arr-btn -.arr-btn{ - display: block; - height: 30px; - border-radius: 3px; - background-color: #3A3A3A; - padding: 0 11px; - text-align: left; - transition: all .17s ease-in-out; - span{ - position: relative; - font-size: 12px; - color: #898989; - font-weight: 400; - padding-right: 15px; - &:after{ - content: ''; - position: absolute; - top: 50%; - right: 0; - transform: translateY(-50%); - width: 5px; - height: 8px; - background: url(../../public/static/images/canvas/arr_btn_ico.svg)no-repeat center; - } +.arr-btn { + display: block; + height: 30px; + border-radius: 3px; + background-color: #3a3a3a; + padding: 0 11px; + text-align: left; + transition: all 0.17s ease-in-out; + span { + position: relative; + font-size: 12px; + color: #898989; + font-weight: 400; + padding-right: 15px; + &:after { + content: ''; + position: absolute; + top: 50%; + right: 0; + transform: translateY(-50%); + width: 5px; + height: 8px; + background: url(../../public/static/images/canvas/arr_btn_ico.svg) no-repeat center; + } + } + &:hover, + &.act { + background-color: #fff; + span { + color: #101010; + font-weight: 500; + &:after { + background: url(../../public/static/images/canvas/arr_btn_ico_black.svg) no-repeat center; + } + } + } + &.dark { + text-align: center; + background-color: #272727; + border: 1px solid #484848; + span { + color: #fff; + &:after { + background: url(../../public/static/images/canvas/arr_btn_ico_white.svg) no-repeat center; + } } &:hover, - &.act{ - background-color: #fff; - span{ - color: #101010; - font-weight: 500; - &:after{ - background: url(../../public/static/images/canvas/arr_btn_ico_black.svg)no-repeat center; - } - } - } - &.dark{ - text-align: center; - background-color: #272727; - border: 1px solid #484848; - span{ - color: #Fff; - &:after{ - background: url(../../public/static/images/canvas/arr_btn_ico_white.svg)no-repeat center; - } - } - &:hover, - &.act{ - background-color: #1083E3; - border: 1px solid #1083E3; - } + &.act { + background-color: #1083e3; + border: 1px solid #1083e3; } + } } // radio .d-check-radio, -.d-check-box{ - line-height: 1.1; +.d-check-box { + line-height: 1.1; + cursor: pointer; + input[type='checkbox'], + input[type='radio'] { + position: static; + margin-left: 0; cursor: pointer; - input[type=checkbox], - input[type=radio]{ - position: static; - margin-left: 0; - cursor: pointer; - opacity: 0; - z-index: 1; - flex: 0 0 auto; + opacity: 0; + z-index: 1; + flex: 0 0 auto; + } + label { + position: relative; + padding-left: 10px; + margin-bottom: 0; + word-break: break-all; + line-height: 1.2; + display: inline; + vertical-align: top; + color: #fff; + font-size: 13px; + font-weight: 400; + cursor: pointer; + } + &.light { + label { + color: #45576f; } - label{ - position: relative; - padding-left: 10px; - margin-bottom: 0; - word-break: break-all; - line-height: 1.2; - display: inline; - vertical-align: top; - color: #fff; - font-size: 13px; - font-weight: 400; - cursor: pointer; - } - &.light{ - label{ - color: #45576F; - } - } - &.no-text{ - label{ - padding-left: 0; - } + } + &.no-text { + label { + padding-left: 0; } + } } .d-check-radio { - label{ - &::before{ - cursor: pointer; - content: ""; - display: inline-block; - position: absolute; - width: 17px; - height: 17px; - top:2px; - left: 0; - margin-left: -12px; - border: 1px solid #999999; - border-radius: 100%; - background-color: transparent; - text-align:center; - font-size:13px; - line-height:1.4; - transition: border 0.15s ease-in-out, color 0.15s ease-in-out; - } - &::after{ - cursor: pointer; - content: ""; - display: inline-block; - position: absolute; - width: 9px; - height: 9px; - top:6px; - left: 4px; - margin-left: -12px; - border: none; - border-radius: 100%; - background-color: #fff; - text-align:center; - font-size:13px; - line-height:1.4; - opacity: 0; - visibility: hidden; - transition: opacity 0.15s ease-in-out, color 0.15s ease-in-out; - } + label { + &::before { + cursor: pointer; + content: ''; + display: inline-block; + position: absolute; + width: 17px; + height: 17px; + top: 2px; + left: 0; + margin-left: -12px; + border: 1px solid #999999; + border-radius: 100%; + background-color: transparent; + text-align: center; + font-size: 13px; + line-height: 1.4; + transition: + border 0.15s ease-in-out, + color 0.15s ease-in-out; } - &.light{ - label{ - &:before{ - border-color: #D6D6D7; - } - &:after{ - background-color: #697C8F; - } - } + &::after { + cursor: pointer; + content: ''; + display: inline-block; + position: absolute; + width: 9px; + height: 9px; + top: 6px; + left: 4px; + margin-left: -12px; + border: none; + border-radius: 100%; + background-color: #fff; + text-align: center; + font-size: 13px; + line-height: 1.4; + opacity: 0; + visibility: hidden; + transition: + opacity 0.15s ease-in-out, + color 0.15s ease-in-out; } - input[type=radio]:checked + label::after{ - opacity: 1; - visibility: visible; + } + &.light { + label { + &:before { + border-color: #d6d6d7; + } + &:after { + background-color: #697c8f; + } } - &.pop{ - label{ - font-size: 12px; - &:before{ - width: 16px; - height: 16px; - border-color: #fff; - } - &:after{ - width: 8px; - height: 8px; - background-color: #fff; - } - } + } + input[type='radio']:checked + label::after { + opacity: 1; + visibility: visible; + } + &.pop { + label { + font-size: 12px; + &:before { + width: 16px; + height: 16px; + border-color: #fff; + } + &:after { + width: 8px; + height: 8px; + background-color: #fff; + } } + } } // check-box -.d-check-box{ - label{ - &.red{color: #FFCACA;} - &::before{ - cursor: pointer; - content: ""; - display: inline-block; - position: absolute; - width: 17px; - height: 17px; - top: 2px; - left: 0; - margin-left: -12px; - border: 1px solid #D6D6D7; - background-color: #fff; - transition: border 0.15s ease-in-out, color 0.15s ease-in-out; - } - &:after{ - cursor: pointer; - content: ""; - display: inline-block; - position: absolute; - width: 16px; - height: 16px; - top:0; - left: 0; - margin-left: -.8rem; - transition: border 0.05s ease-in-out, color 0.05s ease-in-out; - } +.d-check-box { + label { + &.red { + color: #ffcaca; } - input[type=checkbox]:checked + label::after{ - content: ""; - display: inline-block; - position: absolute; - top: 1px; - left: -1px; - width: 5px; - height: 8px; - border: 2px solid #697C8F; - border-left: none; - border-top: none; - transform: translate(7.75px,4.5px) rotate(45deg); - -ms-transform: translate(7.75px,4.5px) rotate(45deg); + &::before { + cursor: pointer; + content: ''; + display: inline-block; + position: absolute; + width: 17px; + height: 17px; + top: 2px; + left: 0; + margin-left: -12px; + border: 1px solid #d6d6d7; + background-color: #fff; + transition: + border 0.15s ease-in-out, + color 0.15s ease-in-out; } - &.pop{ - label{ - &:before{ - background-color: transparent; - } - } - input[type=checkbox]:checked + label::after{ - border-color: #fff; - } - &.no-text{ - label{ - padding-left: 0; - } - } + &:after { + cursor: pointer; + content: ''; + display: inline-block; + position: absolute; + width: 16px; + height: 16px; + top: 0; + left: 0; + margin-left: -0.8rem; + transition: + border 0.05s ease-in-out, + color 0.05s ease-in-out; } - &.sel{ - input[type=checkbox]:checked + label{ - color: #D7C863; - } - input[type=checkbox]:checked + label::before, - input[type=checkbox]:checked + label::after{ - border-color: #D7C863; - } + } + input[type='checkbox']:checked + label::after { + content: ''; + display: inline-block; + position: absolute; + top: 1px; + left: -1px; + width: 5px; + height: 8px; + border: 2px solid #697c8f; + border-left: none; + border-top: none; + transform: translate(7.75px, 4.5px) rotate(45deg); + -ms-transform: translate(7.75px, 4.5px) rotate(45deg); + } + &.pop { + label { + &:before { + background-color: transparent; + } } + input[type='checkbox']:checked + label::after { + border-color: #fff; + } + &.no-text { + label { + padding-left: 0; + } + } + } + &.sel { + input[type='checkbox']:checked + label { + color: #d7c863; + } + input[type='checkbox']:checked + label::before, + input[type='checkbox']:checked + label::after { + border-color: #d7c863; + } + } } // date-picker -.date-picker{ - svg{display: none;} - .react-datepicker-wrapper{ - width: 100%; - } - input[type=text]{ - display: block; - width: 100%; - height: 30px; - padding: 0 34px 0 10px; - border-radius: 2px; - border: 1px solid #eee; - font-size: 13px; - color: #45576F; - font-weight: normal; - font-family: 'Noto Sans JP', sans-serif; - background: #fff url(../../public/static/images/common/datepicker.svg) calc(100% - 11px) center no-repeat; - background-size: 14px 15px; - cursor: pointer; - } +.date-picker { + svg { + display: none; + } + .react-datepicker-wrapper { + width: 100%; + } + input[type='text'] { + display: block; + width: 100%; + height: 30px; + padding: 0 34px 0 10px; + border-radius: 2px; + border: 1px solid #eee; + font-size: 13px; + color: #45576f; + font-weight: normal; + font-family: 'Noto Sans JP', sans-serif; + background: #fff url(../../public/static/images/common/datepicker.svg) calc(100% - 11px) center no-repeat; + background-size: 14px 15px; + cursor: pointer; + } } // react select -.react-select-custom{ - width: 100%; - .custom__control{ - height: 30px; - min-height: unset; - border-radius: 2px; - border-color: #EEE; - background-color: #fff; - &:hover{ - border-color: #EEE; - } +.react-select-custom { + width: 100%; + .custom__control { + height: 30px; + min-height: unset; + border-radius: 2px; + border-color: #eee; + background-color: #fff; + &:hover { + border-color: #eee; } - .custom__control--is-focused{ - border-color: #EEE; - box-shadow: unset; - &:hover{ - border-color: #EEE; + } + .custom__control--is-focused { + border-color: #eee; + box-shadow: unset; + &:hover { + border-color: #eee; + } + } + .custom__value-container { + height: 100%; + padding: 0 10px; + } + .custom__placeholder, + .custom__single-value { + margin: 0; + } + .custom__single-value { + font-size: 13px; + color: #45576f; + font-weight: 400; + } + .custom__placeholder { + font-size: 13px; + color: #bbbbbb; + font-weight: 400; + } + .custom__indicator { + padding: 0; + svg { + display: none; + } + } + .custom__clear-indicator { + width: 30px; + height: 100%; + background: url(../../public/static/images/common/select_del.svg) no-repeat center; + background-size: 8px 8px; + } + .custom__dropdown-indicator { + width: 30px; + height: 100%; + background: url(../../public/static/images/common/select_light_arr.svg) no-repeat center; + } - } + .custom__menu { + margin: 1px 0; + border-radius: 2px; + overflow: hidden; + } + .custom__menu-list { + padding: 0; + } + .custom__option { + font-size: 13px; + padding: 7px 10px; + color: #45576f; + } + .custom__option--is-selected { + color: #fff; + } + // disable + &.custom--is-disabled { + .custom__control { + background-color: #fafafa; } - .custom__value-container { - height: 100%; - padding: 0 10px; - } - .custom__placeholder, - .custom__single-value{ - margin: 0; - } - .custom__single-value{ - font-size: 13px; - color: #45576F; - font-weight: 400; - } - .custom__placeholder{ - font-size: 13px; - color: #bbbbbb; - font-weight: 400; - } - .custom__indicator{ - padding: 0; - svg{ - display: none; - } - } - .custom__clear-indicator{ - width: 30px; - height: 100%; - background: url(../../public/static/images/common/select_del.svg) no-repeat center; - background-size: 8px 8px; - } - .custom__dropdown-indicator{ - width: 30px; - height: 100%; - background: url(../../public/static/images/common/select_light_arr.svg) no-repeat center; + .custom__single-value { + color: #999999; } + } +} - .custom__menu { - margin: 1px 0; - border-radius: 2px; - overflow: hidden; - } - .custom__menu-list { - padding: 0; - } - .custom__option{ - font-size: 13px; - padding: 7px 10px; - color: #45576F; - } - .custom__option--is-selected{ - color: #fff; - } - // disable - &.custom--is-disabled{ - .custom__control{ - background-color: #FAFAFA ; - } - .custom__single-value{ - color: #999999; - } - } -} \ No newline at end of file +// toggle +.toggle-btn { + position: relative; + display: inline-block; + width: 55px; + height: 20px; + input { + display: none; + } +} +.slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #454545; + transition: 0.4s; + border-radius: 10px; + text-align: center; + line-height: 20px; + color: white; + font-size: 12px; + font-weight: 400; + &:after { + content: 'OFF'; + position: absolute; + right: 7px; + color: white; + font-size: 12px; + font-weight: 400; + } + &:before { + position: absolute; + content: ''; + height: 16px; + width: 16px; + left: 2px; + bottom: 2px; + background-color: white; + transition: 0.4s; + border-radius: 50%; + } +} +input:checked + .slider { + background-color: #2563eb; + &:after { + content: 'ON'; + left: 10px; + right: auto; + } + &:before { + transform: translateX(35px); + } +} diff --git a/src/util/canvas-util.js b/src/util/canvas-util.js index a78beb15..65c46b74 100644 --- a/src/util/canvas-util.js +++ b/src/util/canvas-util.js @@ -792,7 +792,7 @@ export const rectToPolygon = (rect) => { } //면형상 선택 클릭시 지붕 패턴 입히기 -export function setSurfaceShapePattern(polygon, mode = 'onlyBorder') { +export function setSurfaceShapePattern(polygon, mode = 'onlyBorder', trestleMode = false) { const ratio = window.devicePixelRatio || 1 let width = 265 / 10 @@ -820,6 +820,12 @@ export function setSurfaceShapePattern(polygon, mode = 'onlyBorder') { ctx.lineWidth = mode === 'allPainted' ? 1 : 0.4 ctx.fillStyle = mode === 'allPainted' ? 'rgba(0, 159, 64, 0.7)' : 'white' + if (trestleMode) { + ctx.strokeStyle = 'black' + ctx.lineWidth = 0.2 + ctx.fillStyle = 'rgba(0, 0, 0, 0.1)' + } + if (polygon.direction === 'east' || polygon.direction === 'west') { offset = roofStyle === 1 ? 0 : patternSize.height / 2 for (let col = 0; col <= cols; col++) { @@ -830,7 +836,7 @@ export function setSurfaceShapePattern(polygon, mode = 'onlyBorder') { ctx.moveTo(x, yStart) // 선 시작점 ctx.lineTo(x, yEnd) // 선 끝점 ctx.stroke() - if (mode === 'allPainted') { + if (mode === 'allPainted' || trestleMode) { ctx.fillRect(x, yStart, patternSize.width, yEnd - yStart) } @@ -842,7 +848,7 @@ export function setSurfaceShapePattern(polygon, mode = 'onlyBorder') { ctx.moveTo(xStart, y) // 선 시작점 ctx.lineTo(xEnd, y) // 선 끝점 ctx.stroke() - if (mode === 'allPainted') { + if (mode === 'allPainted' || trestleMode) { ctx.fillRect(xStart, y, xEnd - xStart, patternSize.height) } } @@ -855,7 +861,7 @@ export function setSurfaceShapePattern(polygon, mode = 'onlyBorder') { ctx.moveTo(0, y) // 선 시작점 ctx.lineTo(patternSourceCanvas.width, y) // 선 끝점 ctx.stroke() - if (mode === 'allPainted') { + if (mode === 'allPainted' || trestleMode) { ctx.fillRect(0, y, patternSourceCanvas.width, patternSize.height) } @@ -868,7 +874,7 @@ export function setSurfaceShapePattern(polygon, mode = 'onlyBorder') { ctx.moveTo(x, yStart) // 선 시작점 ctx.lineTo(x, yEnd) // 선 끝점 ctx.stroke() - if (mode === 'allPainted') { + if (mode === 'allPainted' || trestleMode) { ctx.fillRect(x, yStart, patternSize.width, yEnd - yStart) } } @@ -954,3 +960,44 @@ export const getAllRelatedObjects = (id, canvas) => { return result } + +// 모듈,회로 구성에서 사용하는 degree 범위 별 값 +export const getDegreeInOrientation = (degree) => { + if (degree >= 352) { + return 0 + } + if (degree % 15 === 0) return degree + + let value = Math.floor(degree / 15) + const remain = ((degree / 15) % 1).toFixed(5) + + if (remain > 0.4) { + value++ + } + + return value * 15 +} + +export function findAndRemoveClosestPoint(targetPoint, points) { + if (points.length === 0) { + return null + } + + let closestPoint = points[0] + let closestDistance = distanceBetweenPoints(targetPoint, points[0]) + let closestIndex = 0 + + for (let i = 1; i < points.length; i++) { + const distance = distanceBetweenPoints(targetPoint, points[i]) + if (distance < closestDistance) { + closestDistance = distance + closestPoint = points[i] + closestIndex = i + } + } + + // Remove the closest point from the array + points.splice(closestIndex, 1) + + return closestPoint +} diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js index 40eaec1b..3ed8f9a1 100644 --- a/src/util/qpolygon-utils.js +++ b/src/util/qpolygon-utils.js @@ -2443,6 +2443,10 @@ const changeGableRoof = (currentRoof, canvas) => { hip2.attributes.planeSize = Math.round(Math.sqrt(Math.pow(hip2.x1 - hip2.x2, 2) + Math.pow(hip2.y1 - hip2.y2, 2))) * 10 hip2.attributes.actualSize = Math.round(Math.sqrt(Math.pow(hip2.attributes.planeSize, 2) + Math.pow(hip2Height, 2))) // roof.innerLines.push(hip2) + hip1.set({ visible: false }) + hip1.setViewLengthText(false) + hip2.set({ visible: false }) + hip2.setViewLengthText(false) canvas?.renderAll() } } @@ -2925,6 +2929,13 @@ const changeJerkInHeadRoof = (currentRoof, canvas) => { hip2.attributes.actualSize = Math.round(Math.sqrt(Math.pow(hip2.attributes.planeSize, 2) + Math.pow(hip2Height, 2))) canvas?.add(hip2) // roof.innerLines.push(hip2) + + hip1.set({ visible: false }) + hip1.setViewLengthText(false) + gable3.set({ visible: false }) + gable3.setViewLengthText(false) + hip2.set({ visible: false }) + hip2.setViewLengthText(false) } } }