diff --git a/src/app/[locale]/management/stuff/detail/page.jsx b/src/app/[locale]/management/stuff/detail/page.jsx index 8b84287a..6759b282 100644 --- a/src/app/[locale]/management/stuff/detail/page.jsx +++ b/src/app/[locale]/management/stuff/detail/page.jsx @@ -1,11 +1,15 @@ import React from 'react' import Hero from '@/components/Hero' import StuffDetail from '@/components/management/StuffDetail' +import Link from 'next/link' export default function ManagementStuffDetailPage() { return ( <>

물건정보

+ +

도면작성

+
diff --git a/src/app/[locale]/management/stuff/tempdetail/page.jsx b/src/app/[locale]/management/stuff/tempdetail/page.jsx new file mode 100644 index 00000000..8b84287a --- /dev/null +++ b/src/app/[locale]/management/stuff/tempdetail/page.jsx @@ -0,0 +1,15 @@ +import React from 'react' +import Hero from '@/components/Hero' +import StuffDetail from '@/components/management/StuffDetail' +export default function ManagementStuffDetailPage() { + return ( + <> +
+

물건정보

+
+
+ +
+ + ) +} diff --git a/src/common/common.js b/src/common/common.js index aaba3f45..c78673ee 100644 --- a/src/common/common.js +++ b/src/common/common.js @@ -1,19 +1,22 @@ -export const STEP = { +export const MENU = { INITIAL_CANVAS_SETTING: 'initialCanvasSetting', // 배치면 초기설정 ROOF_COVERING: { EXTERIOR_WALL_LINE: 'exteriorWallLine', // 외벽선 그리기 ROOF_SHAPE_SETTINGS: 'roofShapeSettings', // 지붕형상 설정 ROOF_SHAPE_EDITING: 'roofShapeEditing', // 지붕형상 편집 HELP_LINE_DRAWING: 'helpLineDrawing', // 보조선 그리기 + DEFAULT: 'roofCoveringDefault', // 아무것도 선택 안할 경우 }, // 지붕덮개 BATCH_CANVAS: { BATCH_DRAWING: 'batchDrawing', // 배치면 그리기 SURFACE_SHAPE_BATCH: 'surfaceShapeBatch', // 면형상 배치 OBJECT_BATCH: 'objectBatch', // 오브젝트 배치 + DEFAULT: 'batchCanvasDefault', // default }, // 배치면 MODULE_CIRCUIT_SETTING: { BASIC_SETTING: 'basicSetting', // 기본설정 CIRCUIT_TRESTLE_SETTING: 'circuitTrestleSetting', // 회로가대설정 + DEFAULT: 'moduleCircuitSettingDefault', }, // 모듈회로구성 ESTIMATE: 'estimate', // todo 견적서 POWER_GENERATION_SIMULATION: 'powerGenerationSimulation', // todo 발전 시뮬레이션 diff --git a/src/components/floor-plan/CanvasLayout.jsx b/src/components/floor-plan/CanvasLayout.jsx index 5be6a114..71c48845 100644 --- a/src/components/floor-plan/CanvasLayout.jsx +++ b/src/components/floor-plan/CanvasLayout.jsx @@ -1,45 +1,47 @@ 'use client' -import { useState } from "react" -import CanvasFrame from "./CanvasFrame"; +import { useState } from 'react' +import CanvasFrame from './CanvasFrame' +import { useRecoilState, useRecoilValue } from 'recoil' +import { currentMenuState, stepState } from '@/store/canvasAtom' -export default function CanvasLayout () { - const [plans, setPlans] = useState([{ id: 0, name: 'Plan 1' }, { id: 1, name: 'Plan 2' }, { id: 2, name: 'Plan 3' }]); - const [idxNum, setIdxNum] = useState(null); +export default function CanvasLayout() { + const [plans, setPlans] = useState([ + { id: 0, name: 'Plan 1' }, + { id: 1, name: 'Plan 2' }, + { id: 2, name: 'Plan 3' }, + ]) + const [idxNum, setIdxNum] = useState(null) - const onClickPlane = (num) => { - setIdxNum(num); - } + const onClickPlane = (num) => { + setIdxNum(num) + } - const handleDeletePlan = (e, id) => { - e.stopPropagation(); // 이벤트 버블링 방지 - setPlans(plans.filter(plan => plan.id !== id)); // 삭제할 아이디와 다른 아이템만 남김 - } + const handleDeletePlan = (e, id) => { + e.stopPropagation() // 이벤트 버블링 방지 + setPlans(plans.filter((plan) => plan.id !== id)) // 삭제할 아이디와 다른 아이템만 남김 + } - const addNewPlan = () => { - setPlans([...plans, { id: plans.length, name: `Plan ${plans.length + 1}` }]); - } + const addNewPlan = () => { + setPlans([...plans, { id: plans.length, name: `Plan ${plans.length + 1}` }]) + } - return( -
-
-
- {plans.map((plan, idx) => ( - - ))} -
- -
- + return ( +
+
+
+ {plans.map((plan, idx) => ( + + ))}
- ) -} \ No newline at end of file + +
+ +
+ ) +} diff --git a/src/components/floor-plan/CanvasMenu.jsx b/src/components/floor-plan/CanvasMenu.jsx index 47b1ec88..d9fb24b9 100644 --- a/src/components/floor-plan/CanvasMenu.jsx +++ b/src/components/floor-plan/CanvasMenu.jsx @@ -1,12 +1,14 @@ 'use client' import { useState } from 'react' -import MenuDepth01 from './MenuDepth01' import { useRecoilState } from 'recoil' import { modalState } from '@/store/modalAtom' import { settingModalFirstOptionsState } from '@/store/settingAtom' import QSelectBox from '@/components/common/select/QSelectBox' import { useMessage } from '@/hooks/useMessage' import { post } from '@/lib/Axios' +import { currentMenuState } from '@/store/canvasAtom' +import { MENU } from '@/common/common' +import RoofCoveringMenu from '@/components/floor-plan/RoofCoveringMenu' export default function CanvasMenu() { const [objectNo, setObjectNo] = useState('test123240912001') @@ -15,11 +17,10 @@ export default function CanvasMenu() { const [vertical, setVertical] = useState(true) const { getMessage } = useMessage() const SelectOption = [{ name: '瓦53A' }, { name: '瓦53A' }] - const onClickNav = (number) => { - setMenuNumber(number) - if (menuNumber === number) { - setMenuNumber(null) - } + const [currentMenu, setCurrentMenu] = useRecoilState(currentMenuState) + + const onClickNav = (menu) => { + setCurrentMenu(menu) } const settingsModalOptions = useRecoilState(settingModalFirstOptionsState) @@ -70,27 +71,31 @@ export default function CanvasMenu() { } return ( -
+
    -
  • onClickNav(0)}> +
  • onClickNav(1)}>
  • -
  • onClickNav(1)}> - -
  • + { +
  • onClickNav(MENU.INITIAL_CANVAS_SETTING)} + > + +
  • + }
  • { - setModalOption({ ...modalOption, outerwall: true }) - onClickNav(2) - }} + className={`canvas-menu-item ${Object.values(MENU.ROOF_COVERING).includes(currentMenu) ? 'active' : ''}`} + onClick={() => onClickNav(MENU.ROOF_COVERING.DEFAULT)} >
-
- {menuNumber === 2 && } +
+ {Object.values(MENU.ROOF_COVERING).includes(currentMenu) && } + {/*{menuNumber === 2 && } {menuNumber === 3 && } - {menuNumber === 4 && } + {menuNumber === 4 && }*/}
) diff --git a/src/components/floor-plan/RoofCoveringMenu.jsx b/src/components/floor-plan/RoofCoveringMenu.jsx new file mode 100644 index 00000000..26da4bda --- /dev/null +++ b/src/components/floor-plan/RoofCoveringMenu.jsx @@ -0,0 +1,54 @@ +'use client' + +import { useMessage } from '@/hooks/useMessage' +import { useRecoilState, useSetRecoilState } from 'recoil' +import { currentMenuState } from '@/store/canvasAtom' +import { MENU } from '@/common/common' +import { modalState } from '@/store/modalAtom' +import { ToggleonMouse } from '@/components/header/Header' + +export default function RoofCoveringMenu() { + const { getMessage } = useMessage() + const [currentMenu, setCurrentMenu] = useRecoilState(currentMenuState) + + const setModalState = useSetRecoilState(modalState) + + const onClickNav = (menu) => { + setCurrentMenu(menu) + if (menu === MENU.ROOF_COVERING.EXTERIOR_WALL_LINE) { + setModalState((prev) => ({ ...prev, outerwall: true })) + } else { + setModalState((prev) => ({ ...prev, outerwall: false })) + } + } + + return ( +
+
    +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
+
    +
  • ToggleonMouse(e, 'add', 'ul')} onMouseLeave={(e) => ToggleonMouse(e, 'remove', 'ul')}> + +
  • +
  • ToggleonMouse(e, 'add', 'ul')} onMouseLeave={(e) => ToggleonMouse(e, 'remove', 'ul')}> + +
  • +
  • ToggleonMouse(e, 'add', 'ul')} onMouseLeave={(e) => ToggleonMouse(e, 'remove', 'ul')}> + +
  • +
+
+ ) +} diff --git a/src/components/floor-plan/modal/outerlinesetting/OuterLineWall.jsx b/src/components/floor-plan/modal/outerlinesetting/OuterLineWall.jsx index 1ba1c597..c5bd5686 100644 --- a/src/components/floor-plan/modal/outerlinesetting/OuterLineWall.jsx +++ b/src/components/floor-plan/modal/outerlinesetting/OuterLineWall.jsx @@ -1,68 +1,443 @@ 'use client' -import { useState } from 'react' +import { useCallback, useEffect, useRef } from 'react' import WithDraggable from '@/components/common/draggable/withDraggable' import { modalState } from '@/store/modalAtom' -import { useRecoilState } from 'recoil' +import { useRecoilState, useRecoilValue } from 'recoil' import { useMessage } from '@/hooks/useMessage' +import { useEvent } from '@/hooks/useEvent' +import { canvasState } from '@/store/canvasAtom' +import { + OUTER_LINE_TYPE, + outerLineArrow1State, + outerLineArrow2State, + outerLineLength1State, + outerLineLength2State, + outerLinePointsState, + outerLineTypeState, +} from '@/store/outerLineAtom' +import { QLine } from '@/components/fabric/QLine' +import { useLine } from '@/hooks/useLine' export default function OuterLineWall() { const [modalOption, setModalOption] = useRecoilState(modalState) //modal 열림닫힘 state - const [buttonAct, setButtonAct] = useState(1) - const [close, setClose] = useState(false) const { getMessage } = useMessage() - const HandleClickClose = () => { - setClose(true) - setTimeout(() => { - setModalOption({ ...modalOption, outerwall: false }) - setClose(false) - }, 180) + const { addCanvasMouseEventListener, addDocumentEventListener, removeAllDocumentEventListeners } = useEvent() + const { addLineText, removeLineText } = useLine() + const length1Ref = useRef(null) + const length2Ref = useRef(null) + const [length1, setLength1] = useRecoilState(outerLineLength1State) + const [length2, setLength2] = useRecoilState(outerLineLength2State) + const [arrow1, setArrow1] = useRecoilState(outerLineArrow1State) + const [arrow2, setArrow2] = useRecoilState(outerLineArrow2State) + const [points, setPoints] = useRecoilState(outerLinePointsState) + const [type, setType] = useRecoilState(outerLineTypeState) + const arrow1Ref = useRef(arrow1) + const arrow2Ref = useRef(arrow2) + + const canvas = useRecoilValue(canvasState) + + useEffect(() => { + addCanvasMouseEventListener('mouse:down', mouseDown) + }, []) + + useEffect(() => { + arrow1Ref.current = arrow1 + }, [arrow1]) + + useEffect(() => { + arrow2Ref.current = arrow2 + }, [arrow2]) + + useEffect(() => { + removeAllDocumentEventListeners() + addDocumentEventListener('keydown', document, keydown[type]) + clear() + }, [type]) + + const clear = () => { + setLength1(0) + setLength2(0) + + setArrow1('') + setArrow2('') } + + const mouseDown = (e) => { + const pointer = canvas.getPointer(e.e) + + setPoints((prev) => [...prev, pointer]) + } + + useEffect(() => { + canvas + ?.getObjects() + .filter((obj) => obj.name === 'outerLine') + .forEach((obj) => { + canvas?.remove(obj) + removeLineText(obj) + }) + canvas?.remove(canvas?.getObjects().find((obj) => obj.name === 'startPoint')) + if (points.length === 0) { + return + } + + if (points.length === 1) { + const point = new fabric.Circle({ + radius: 5, + fill: 'transparent', + stroke: 'red', + left: points[0].x - 5, + top: points[0].y - 5, + selectable: false, + name: 'startPoint', + }) + + canvas?.add(point) + } else { + points.forEach((point, idx) => { + if (idx === 0) { + return + } + drawLine(points[idx - 1], point) + }) + } + }, [points]) + + const drawLine = (point1, point2) => { + const line = new QLine([point1.x, point1.y, point2.x, point2.y], { + stroke: 'black', + strokeWidth: 1, + selectable: false, + name: 'outerLine', + }) + + canvas?.add(line) + addLineText(line) + } + + // 직각 완료될 경우 확인 + const checkRightAngle = () => { + const length1Num = Number(length1Ref.current.value) / 10 + const length2Num = Number(length2Ref.current.value) / 10 + console.log(length1Num, length2Num, arrow1Ref.current, arrow2Ref.current) + + if (points.length === 0) { + return + } + + if (length1Num === 0 || length2Num === 0 || arrow1Ref.current === '' || arrow2Ref.current === '') { + return + } + + if (arrow1Ref.current === '↓' && arrow2Ref.current === '→') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x + length2Num, y: prev[prev.length - 1].y + length1Num }] + }) + } else if (arrow1Ref.current === '↓' && arrow2Ref.current === '←') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x - length2Num, y: prev[prev.length - 1].y + length1Num }] + }) + } else if (arrow1Ref.current === '↑' && arrow2Ref.current === '→') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x + length2Num, y: prev[prev.length - 1].y - length1Num }] + }) + } else if (arrow1Ref.current === '↑' && arrow2Ref.current === '←') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x - length2Num, y: prev[prev.length - 1].y - length1Num }] + }) + } else if (arrow1Ref.current === '→' && arrow2Ref.current === '↓') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x + length1Num, y: prev[prev.length - 1].y + length2Num }] + }) + } else if (arrow1Ref.current === '→' && arrow2Ref.current === '↑') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x + length1Num, y: prev[prev.length - 1].y - length2Num }] + }) + } else if (arrow1Ref.current === '←' && arrow2Ref.current === '↓') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x - length1Num, y: prev[prev.length - 1].y + length2Num }] + }) + } else if (arrow1Ref.current === '←' && arrow2Ref.current === '↑') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x - length1Num, y: prev[prev.length - 1].y - length2Num }] + }) + } + } + + const keydown = { + outerLine: (e) => { + const key = e.key + + if (!length1Ref.current) { + return + } + + const lengthNum = Number(length1Ref.current.value) / 10 + if (lengthNum === 0) { + return + } + switch (key) { + case 'Down': // IE/Edge에서 사용되는 값 + case 'ArrowDown': { + setArrow1('↓') + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x, y: prev[prev.length - 1].y + lengthNum }] + }) + break + } + case 'Up': // IE/Edge에서 사용되는 값 + case 'ArrowUp': + setArrow1('↑') + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x, y: prev[prev.length - 1].y - lengthNum }] + }) + break + case 'Left': // IE/Edge에서 사용되는 값 + case 'ArrowLeft': + setArrow1('←') + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x - lengthNum, y: prev[prev.length - 1].y }] + }) + break + case 'Right': // IE/Edge에서 사용되는 값 + case 'ArrowRight': + setArrow1('→') + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x + lengthNum, y: prev[prev.length - 1].y }] + }) + break + } + }, + rightAngle: (e) => { + const key = e.key + + const activeElem = document.activeElement + const length1Num = Number(length1Ref.current.value) / 10 + const length2Num = Number(length2Ref.current.value) / 10 + + switch (key) { + case 'Down': // IE/Edge에서 사용되는 값 + case 'ArrowDown': { + if (activeElem === length1Ref.current) { + setArrow1('↓') + arrow1Ref.current = '↓' + } else if (activeElem === length2Ref.current) { + if (arrow1Ref.current === '↓' || arrow1Ref.current === '↑') { + break + } + setArrow2('↓') + arrow2Ref.current = '↓' + checkRightAngle() + } + + break + } + case 'Up': // IE/Edge에서 사용되는 값 + case 'ArrowUp': + if (activeElem === length1Ref.current) { + setArrow1('↑') + arrow1Ref.current = '↑' + } else if (activeElem === length2Ref.current) { + if (arrow1Ref.current === '↓' || arrow1Ref.current === '↑') { + break + } + setArrow2('↑') + arrow2Ref.current = '↑' + checkRightAngle() + } + + break + case 'Left': // IE/Edge에서 사용되는 값 + case 'ArrowLeft': + if (activeElem === length1Ref.current) { + setArrow1('←') + arrow1Ref.current = '←' + } else if (activeElem === length2Ref.current) { + if (arrow1Ref.current === '←' || arrow1Ref.current === '→') { + break + } + setArrow2('←') + arrow2Ref.current = '←' + checkRightAngle() + } + + break + case 'Right': // IE/Edge에서 사용되는 값 + case 'ArrowRight': + if (activeElem === length1Ref.current) { + setArrow1('→') + arrow1Ref.current = '→' + } else if (activeElem === length2Ref.current) { + if (arrow1Ref.current === '←' || arrow1Ref.current === '→') { + break + } + setArrow2('→') + arrow2Ref.current = '→' + checkRightAngle() + } + + break + } + }, + } + + /** + * 일변전으로 돌아가기 + */ + const handleRollback = () => { + //points의 마지막 요소를 제거 + setPoints((prev) => prev.slice(0, prev.length - 1)) + } + + const handleClickClose = () => { + setModalOption({ ...modalOption, outerwall: false }) + } + + const handleFix = () => {} return ( -
+

{getMessage('modal.cover.outline.drawing')}

-
- - - - -

{getMessage('modal.cover.outline.setting')}

-
-
- - + {type === OUTER_LINE_TYPE.OUTER_LINE ? ( +
+
+ + { + setLength1(e.target.value.replace(/[^-0-9]/g, '')) + }} + placeholder="3000" + /> +
+
+ + +
-
- - + ) : type === OUTER_LINE_TYPE.RIGHT_ANGLE ? ( +
+
+ + { + setLength1(e.target.value.replace(/[^-0-9]/g, '')) + }} + placeholder="3000" + /> +
+
+ + +
+
+ + { + setLength2(e.target.value.replace(/[^-0-9]/g, '')) + }} + placeholder="3000" + /> +
+
+ + +
-
+ ) : ( + <> + )}
- - - {params.value} + {params.value}
) } @@ -148,8 +162,15 @@ export default function Stuff() { return } else { console.log(' 상세이동::::::::', event.data) + //T 면 임시 R은 진짜 if (event.data.objectNo) { - router.push(`${pathname}/detail?objectNo=${event.data.objectNo.toString()}`) + if (event.data.objectNo.substring(0, 1) === 'R') { + console.log('진짜:::::::::') + router.push(`${pathname}/detail?objectNo=${event.data.objectNo.toString()}`) + } else { + console.log('임시:::::::::::::::::') + router.push(`${pathname}/tempdetail?objectNo=${event.data.objectNo.toString()}`) + } } } } @@ -175,45 +196,6 @@ export default function Stuff() { errCount++ } }) - - async function fetchDelete(data) { - console.log('물건삭제API호출!!!!!!!!!', data) - //행추가말고 api데이터만 보냄 - // let newData = data.filter((item) => item.company != null) - // console.log('삭제에 전송되는 데이타::', newData) - // await del({ url: '', data:newData }) - await get({ url: 'https://www.ag-grid.com/example-assets/space-mission-data.json' }) - // try { - // const res = await del({url:'', data:newData}) - - // if(!res || res.length === 0) { - - // } else { - fetchData() - // } - // } catch (error) { - // console.error('Data Delete error:', error); - // } - } - - // 삭제API 완료 후 fetchData Api호출 - async function fetchData() { - console.log('물건삭제후 조회API호출!!!!!!!!!!!!!', stuffSearchParams) - const data = await get({ url: 'https://www.ag-grid.com/example-assets/space-mission-data.json' }) - setGridProps({ ...gridProps, gridData: data, count: data.length }) - setGridCount(data.length) - //data.length = 10 - //setGridProps({ ...gridProps, gridData: data, count: data.length-1}) - //setGridCount(data.length - 1 ) - } - - if (errCount === 0) { - // console.log('errCount::::::::', errCount) - fetchDelete(data) - // fetchData() - } else { - alert('물건정보가 있는 행만 선택해주세요') - } } //행추가 @@ -265,15 +247,14 @@ export default function Stuff() { schDateType: 'U', schFromDt: dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD'), schToDt: dayjs(new Date()).format('YYYY-MM-DD'), - startRow: (curPage - 1) * size + 1, - endRow: curPage * size, + startRow: (curPage - 1) * defaultSize + 1, + endRow: curPage * defaultSize, + schSelSaleStoreId: '', + schSortType: 'R', } async function fetchData() { console.log('화면진입:::::::::::::', params) - console.log('현재페이지::::::', curPage) - console.log('페이지당 게시물수::::::', size) - //api에 넘길값 startRow, endRow // let startRow // let endRow @@ -306,6 +287,9 @@ export default function Stuff() { useEffect(() => { if (stuffSearchParams?.code === 'E') { + stuffSearchParams.startRow = (curPage - 1) * defaultSize + 1 + stuffSearchParams.endRow = curPage * defaultSize + stuffSearchParams.schSortType = defaultSortType console.log('조회 눌럿을때 ::::::::::::::', stuffSearchParams) async function fetchData() { const apiUrl = `/api/object/list?saleStoreId=201TES01&${queryStringFormatter(stuffSearchParams)}` @@ -314,6 +298,9 @@ export default function Stuff() { if (!isEmptyArray(res)) { setGridProps({ ...gridProps, gridData: res, count: res.length }) setGridCount(res.length) + } else { + setGridProps({ ...gridProps, gridData: [], count: 0 }) + setGridCount(0) } }) } @@ -321,6 +308,56 @@ export default function Stuff() { } }, [stuffSearchParams]) + //페이지 갯수 변경 이벤트 + const onChangePerPage = (e) => { + let startRow = (curPage - 1) * e.target.value + 1 + stuffSearchParams.startRow = startRow + stuffSearchParams.endRow = curPage * e.target.value + setDefaultSize(e.target.value) + setStuffSearch({ + ...stuffSearch, + code: 'S', + startRow: startRow, + endRow: curPage * e.target.value, + }) + console.log('셋팅된 검색조건:::', stuffSearchParams) + //조회API호출 + const apiUrl = `/api/object/list?saleStoreId=201TES01&${queryStringFormatter(stuffSearchParams)}` + get({ url: apiUrl }).then((res) => { + console.log('보여줄개수바꿨을때 조회 ::::::::::', res) + if (!isEmptyArray(res)) { + setGridProps({ ...gridProps, gridData: res, count: res.length }) + setGridCount(res.length) + } else { + setGridProps({ ...gridProps, gridData: [], count: 0 }) + setGridCount(0) + } + }) + } + + //최근 등록일 수정일 정렬 이벤트 + const onChangeSortType = (e) => { + stuffSearchParams.schSortType = e.target.value + console.log('셋팅된 검색조건:::', stuffSearchParams) + setDefaultSortType(e.target.value) + setStuffSearch({ + ...stuffSearch, + code: 'S', + schSortType: e.target.value, + }) + const apiUrl = `/api/object/list?saleStoreId=201TES01&${queryStringFormatter(stuffSearchParams)}` + // console.log('apiUrl::', apiUrl) + get({ url: apiUrl }).then((res) => { + console.log('정렬바꿨을때 조회 ::::::::::', res) + if (!isEmptyArray(res)) { + setGridProps({ ...gridProps, gridData: res, count: res.length }) + setGridCount(res.length) + } else { + setGridProps({ ...gridProps, gridData: [], count: 0 }) + setGridCount(0) + } + }) + } return ( <>
@@ -328,6 +365,15 @@ export default function Stuff() { 전체 : {gridCount} // 선택 : {selectedRowDataCount} + +
{/*
+
+ {saleStoreList?.length > 0 && ( + + {(option) => {option.saleStoreName}} + + )} + +
@@ -313,9 +410,34 @@ export default function StuffDetail() {
+ {powerSimAreaList?.length > 0 && ( + + )}
+
+ +
@@ -354,7 +476,15 @@ export default function StuffDetail() {
- + +
+ +
@@ -379,9 +509,51 @@ export default function StuffDetail() { />
- + {!isFormValid ? ( + <> + + + ) : ( + + )} + + + - )) ||
상세:::::::::::
} + )) || ( + <> + {objectNo.substring(0, 1) === 'R' ? ( + <> + + + + + + + ) : ( + <> + + + + + + )} + + //
+ // EDIT모드 + + // {/* {objectNo.substring(0,1) ? 'R' ? <>RRRRRRR : <>TTTTTTTT} */} + // {/* + // + // + // + // */} + //
+ )} {/*
diff --git a/src/components/management/StuffSearchCondition.jsx b/src/components/management/StuffSearchCondition.jsx index f0d665d0..58aa4ce9 100644 --- a/src/components/management/StuffSearchCondition.jsx +++ b/src/components/management/StuffSearchCondition.jsx @@ -2,10 +2,12 @@ import React, { useEffect } from 'react' import { useState } from 'react' -import { Input, RadioGroup, Radio, Button } from '@nextui-org/react' +import { Input, RadioGroup, Radio, Button, Autocomplete, AutocompleteItem } from '@nextui-org/react' import RangeDatePicker from '@/components/common/datepicker/RangeDatePicker' import { useRecoilState, useResetRecoilState } from 'recoil' import { stuffSearchState } from '@/store/stuffAtom' +import { isEmptyArray } from '@/util/common-utils' +import { get } from '@/lib/Axios' import dayjs from 'dayjs' import isLeapYear from 'dayjs/plugin/isLeapYear' // 윤년 판단 플러그인 dayjs.extend(isLeapYear) @@ -33,13 +35,16 @@ export default function StuffSearchCondition() { const [receiveUser, setReceiveUser] = useState('') //담당자 const [dispCompanyName, setDispCompanyName] = useState('') //견적처 const [dateType, setDateType] = useState('U') //갱신일(U)/등록일(R) + const [schSelSaleStoreId, setSchSelSaleStoreId] = useState('') //판매대리점 선택 + const [schSelSaleStoreList, setSchSelSaleStoreList] = useState([]) //판매대리점 자동완성 SELECT // 조회 const onSubmit = () => { let diff = dayjs(endRangeDate).diff(startRangeDate, 'day') if (diff > 366) { return alert('최대1년 조회 가능합니다.') } + setStuffSearch({ schObjectNo: stuffSearch?.schObjectNo ? stuffSearch.schObjectNo : objectNo, schSaleStoreId: stuffSearch?.schSaleStoreId ? stuffSearch.schSaleStoreId : saleStoreId, @@ -53,6 +58,10 @@ export default function StuffSearchCondition() { schFromDt: dayjs(startRangeDate).format('YYYY-MM-DD'), schToDt: dayjs(endRangeDate).format('YYYY-MM-DD'), code: 'E', + schSelSaleStoreId: stuffSearch?.schSelSaleStoreId ? stuffSearch.schSelSaleStoreId : schSelSaleStoreId, + startRow: stuffSearch?.startRow ? stuffSearch.startRow : 1, + endRow: stuffSearch?.endRow ? stuffSearch.endRow : 100, + schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'R', }) } @@ -68,9 +77,31 @@ export default function StuffSearchCondition() { setDispCompanyName('') setDateType('U') setDateRange([dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD'), dayjs(new Date()).format('YYYY-MM-DD')]) + setSchSelSaleStoreId('') resetStuffRecoil() } + useEffect(() => { + get({ url: `/api/object/saleStore/201TES01/list` }).then((res) => { + if (!isEmptyArray(res)) { + // console.log('판매점 결과:::::', res) + setSchSelSaleStoreList(res) + } + }) + }, []) + + //판매대리점 자동완성 변경 + const onSelectionChange = (key) => { + // console.log('자동완성값변경', key) + if (key == null) { + setSchSelSaleStoreId('') + setStuffSearch({ ...stuffSearch, schSelSaleStoreId: '' }) + } else { + setSchSelSaleStoreId(key) + setStuffSearch({ ...stuffSearch, schSelSaleStoreId: key }) + } + } + //x로 날짜 비웠을때 기본값으로 셋팅 useEffect(() => { if (!startRangeDate && !endRangeDate) { @@ -88,7 +119,7 @@ export default function StuffSearchCondition() { return ( <>
- +
@@ -220,6 +251,17 @@ export default function StuffSearchCondition() { setStuffSearch({ ...stuffSearch, code: 'S', schDispCompanyName: e.target.value }) }} /> + {schSelSaleStoreList?.length > 0 && ( + + {(option) => {option.saleStoreName}} + + )}
) } else { diff --git a/src/hooks/useEvent.js b/src/hooks/useEvent.js index 2f32d044..715a27b0 100644 --- a/src/hooks/useEvent.js +++ b/src/hooks/useEvent.js @@ -1,11 +1,13 @@ import { useEffect, useRef } from 'react' import { useRecoilValue } from 'recoil' -import { canvasState, stepState } from '@/store/canvasAtom' +import { canvasState, currentMenuState } from '@/store/canvasAtom' +import { fabric } from 'fabric' export function useEvent() { const canvas = useRecoilValue(canvasState) - const step = useRecoilValue(stepState) + const currentMenu = useRecoilValue(currentMenuState) const keyboardEventListeners = useRef([]) + const mouseEventListeners = useRef([]) useEffect(() => { if (!canvas) { @@ -16,47 +18,56 @@ export function useEvent() { canvas.off(key) } }) - removeAllKeyboardEventListeners() - addEvent(step) - }, [step]) + removeAllMouseEventListeners() + removeAllDocumentEventListeners() + addDefaultEvent() + }, [currentMenu, canvas]) - const addEvent = (step) => { + const addDefaultEvent = () => { //default Event 추가 - canvas?.on('mouse:move', defaultMouseMoveEvent) - addKeyboardEventListener('keydown', document, defaultKeyboardEvent) + addCanvasMouseEventListener('mouse:move', defaultMouseMoveEvent) + addCanvasMouseEventListener('mouse:out', defaultMouseOutEvent) + addDocumentEventListener('keydown', document, defaultKeyboardEvent) + } - if (step === 1) { - canvas?.on('mouse:down', (e) => { - canvas?.add(new fabric.Rect({ width: 100, height: 100, fill: 'red', left: e.pointer.x, top: e.pointer.y })) - }) - addKeyboardEventListener('keydown', document, (e) => { - if (e.key === 'Escape') { - console.log(1111) - } - }) - } else if (step === 2) { - canvas?.on('mouse:down', (e) => { - canvas?.add(new fabric.Circle({ radius: 50, fill: 'blue', left: e.pointer.x, top: e.pointer.y })) - }) - addKeyboardEventListener('keydown', document, (e) => { - if (e.key === 'Escape') { - console.log(2222) - } - }) - } else { - canvas?.on('mouse:down', (e) => { - canvas?.add(new fabric.Triangle({ width: 100, height: 100, fill: 'green', left: e.pointer.x, top: e.pointer.y })) - }) - addKeyboardEventListener('keydown', document, (e) => { - if (e.key === 'Escape') { - console.log(333) - } - }) - } + const defaultMouseOutEvent = (e) => { + removeMouseLine() } const defaultMouseMoveEvent = (e) => { - console.log('defaultMouseMoveEvent') + removeMouseLine() + // 가로선 + const pointer = canvas.getPointer(e.e) + const horizontalLine = new fabric.Line([0, pointer.y, 2 * canvas.width, pointer.y], { + stroke: 'red', + strokeWidth: 1, + selectable: false, + name: 'mouseLine', + }) + + // 세로선 + const verticalLine = new fabric.Line([pointer.x, 0, pointer.x, 2 * canvas.height], { + stroke: 'red', + strokeWidth: 1, + selectable: false, + name: 'mouseLine', + }) + + // 선들을 캔버스에 추가합니다. + canvas?.add(horizontalLine, verticalLine) + + // 캔버스를 다시 그립니다. + canvas?.renderAll() + } + + const removeMouseLine = () => { + // 캔버스에서 마우스 선을 찾아 제거합니다. + canvas + ?.getObjects() + .filter((obj) => obj.name === 'mouseLine') + .forEach((line) => { + canvas?.remove(line) + }) } const defaultKeyboardEvent = (e) => { @@ -65,21 +76,42 @@ export function useEvent() { } } + const addCanvasMouseEventListener = (eventType, handler) => { + canvas.on(eventType, handler) + mouseEventListeners.current.push({ eventType, handler }) + } + + const removeAllMouseEventListeners = () => { + mouseEventListeners.current.forEach(({ eventType, handler }) => { + canvas.off(eventType, handler) + }) + mouseEventListeners.current.length = 0 // 배열 초기화 + } + /** - * document 키보드 이벤트 임의로 직접 등록한 이벤트의 경우 remove가 안되기 때문에 이 함수를 통해서만 등록해야 함. + * document 이벤트의 경우 이 함수를 통해서만 등록 * @param eventType * @param element * @param handler */ - function addKeyboardEventListener(eventType, element, handler) { + const addDocumentEventListener = (eventType, element, handler) => { element.addEventListener(eventType, handler) keyboardEventListeners.current.push({ eventType, element, handler }) } - function removeAllKeyboardEventListeners() { + /** + * document에 등록되는 event 제거 + */ + const removeAllDocumentEventListeners = () => { keyboardEventListeners.current.forEach(({ eventType, element, handler }) => { element.removeEventListener(eventType, handler) }) keyboardEventListeners.current.length = 0 // 배열 초기화 } + + return { + addDocumentEventListener, + addCanvasMouseEventListener, + removeAllDocumentEventListeners, + } } diff --git a/src/hooks/useLine.js b/src/hooks/useLine.js new file mode 100644 index 00000000..81ebd1db --- /dev/null +++ b/src/hooks/useLine.js @@ -0,0 +1,43 @@ +import { useRecoilValue } from 'recoil' +import { canvasState, fontSizeState } from '@/store/canvasAtom' + +export const useLine = () => { + const canvas = useRecoilValue(canvasState) + const fontSize = useRecoilValue(fontSizeState) + + const addLineText = (line) => { + removeLineText(line) + + const text = new fabric.Text(getLengthByLine(line).toFixed(0), { + left: (line.x2 + line.x1) / 2, + top: (line.y2 + line.y1) / 2, + parent: line, + name: 'lengthTxt', + fontSize: fontSize, + }) + + canvas?.add(text) + } + + const removeLineText = (line) => { + canvas?.remove(canvas?.getObjects().find((obj) => obj.parent === line)) + } + + const getLengthByLine = (line) => { + const scaleX = line.scaleX + const scaleY = line.scaleY + const x1 = line.left + const y1 = line.top + const x2 = line.left + line.width * scaleX + const y2 = line.top + line.height * scaleY + const dx = x2 - x1 + const dy = y2 - y1 + + return Number(Math.sqrt(dx * dx + dy * dy).toFixed(0)) * 10 + } + + return { + addLineText, + removeLineText, + } +} diff --git a/src/hooks/usePolygon.js b/src/hooks/usePolygon.js new file mode 100644 index 00000000..72354443 --- /dev/null +++ b/src/hooks/usePolygon.js @@ -0,0 +1,8 @@ +import { canvasState } from '@/store/canvasAtom' +import { useRecoilValue } from 'recoil' + +export const usePolygon = () => { + const canvas = useRecoilValue(canvasState) + + return {} +} diff --git a/src/locales/ko.json b/src/locales/ko.json index cf7d758f..82b187bc 100644 --- a/src/locales/ko.json +++ b/src/locales/ko.json @@ -118,7 +118,7 @@ "common.message.multi.insert": "Total {0} cases ({1} successes, {2} failures {3})", "common.message.error": "Error occurred, please contact site administrator.", "common.message.data.save": "Do you want to save it?", - "common.message.data.delete": "Do you want to delete it?", + "common.message.data.delete": "정말로 삭제하시겠습니까?", "common.message.data.exists": "{0} is data that already exists.", "common.message.data.no.exists": "{0} is data that does not exist.", "common.message.all": "All", diff --git a/src/store/canvasAtom.js b/src/store/canvasAtom.js index 4f274620..48dced68 100644 --- a/src/store/canvasAtom.js +++ b/src/store/canvasAtom.js @@ -1,4 +1,5 @@ import { atom } from 'recoil' +import { MENU } from '@/common/common' export const canvasState = atom({ key: 'canvasState', @@ -180,7 +181,7 @@ export const objectPlacementModeState = atom({ default: { width: 0, height: 0, areaBoundary: false, inputType: 'free', batchType: 'opening' }, }) -export const stepState = atom({ - key: 'step', - default: 0, +export const currentMenuState = atom({ + key: 'currentMenu', + default: MENU.INITIAL_CANVAS_SETTING, }) diff --git a/src/store/outerLineAtom.js b/src/store/outerLineAtom.js new file mode 100644 index 00000000..f8494bea --- /dev/null +++ b/src/store/outerLineAtom.js @@ -0,0 +1,65 @@ +import { atom, selector } from 'recoil' + +export const OUTER_LINE_TYPE = { + OUTER_LINE: 'outerLine', // 외벽선 + RIGHT_ANGLE: 'rightAngle', // 직각 + LEE_GUBAE: 'leeGubae', // 이구배 + ANGLE: 'angle', // 각도 + DIAGONAL_LINE: 'diagonalLine', // 대각선 +} + +/** + * 외벽선 작성에서 사용하는 recoilState + */ + +export const outerLineLength1State = atom({ + //길이1 + key: 'outerLineLength1State', + default: 0, +}) + +export const outerLineLength2State = atom({ + // 길이2 + key: 'outerLineLength2State', + default: 0, +}) + +export const outerLineArrow1State = atom({ + // 방향1 + key: 'outerLineArrow1State', + default: '', +}) + +export const outerLineArrow2State = atom({ + // 방향2 + key: 'outerLineArrow2State', + default: '', +}) + +export const outerLineAngle1State = atom({ + // 각도1 + key: 'outerLineAngle1State', + default: 0, +}) + +export const outerLineAngle2State = atom({ + // 각도2 + key: 'outerLineAngle2State', + default: 0, +}) + +export const outerLineDiagonalState = atom({ + // 대각선 + key: 'outerLineDiagonalState', + default: 0, +}) + +export const outerLineTypeState = atom({ + key: 'outerLineTypeState', + default: OUTER_LINE_TYPE.OUTER_LINE, +}) + +export const outerLinePointsState = atom({ + key: 'outerLinePointsState', + default: [], +}) diff --git a/src/store/stuffAtom.js b/src/store/stuffAtom.js index a122543c..5de2c04e 100644 --- a/src/store/stuffAtom.js +++ b/src/store/stuffAtom.js @@ -17,6 +17,10 @@ export const stuffSearchState = atom({ schFromDt: dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD'), //시작일 schToDt: dayjs(new Date()).format('YYYY-MM-DD'), //종료일 code: 'S', + schSelSaleStoreId: '', //판매대리점 선택 + startRow: 1, + endRow: 100, + schSortType: 'R', //정렬조건 (R:최근등록일 U:최근수정일) }, dangerouslyAllowMutability: true, }) diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js index 7a20a2b4..641016d5 100644 --- a/src/util/qpolygon-utils.js +++ b/src/util/qpolygon-utils.js @@ -992,7 +992,7 @@ export const splitPolygonWithLines = (polygon) => { /** * 좌표 테스트용 */ - allLines.forEach((line) => { + /*allLines.forEach((line) => { const text = new fabric.Text(`(${line.startPoint.x},${line.startPoint.y})`, { left: line.startPoint.x, top: line.startPoint.y, @@ -1021,7 +1021,7 @@ export const splitPolygonWithLines = (polygon) => { polygon.canvas.add(text) polygon.canvas.renderAll() - }) + })*/ /** * 좌표 테스트용 끝 */ @@ -2732,6 +2732,7 @@ const setGableRoof = (polygon, ridge, hip1, hip2, offset, canvas) => { if (ridge.direction === 'top') { if (ridge.y1 > y1 && ridge.y2 > y1) { offset = Math.abs(ridge.y1 - y1) - offset + offset = offset < 0 ? 0 : offset ridge.set({ x1: ridge.x1, y1: ridge.y1, @@ -2763,6 +2764,7 @@ const setGableRoof = (polygon, ridge, hip1, hip2, offset, canvas) => { } if (ridge.y1 < y1 && ridge.y2 < y1) { offset = Math.abs(ridge.y2 - y1) - offset + offset = offset < 0 ? 0 : offset ridge.set({ x1: ridge.x1, y1: ridge.y1 - offset, @@ -2795,6 +2797,7 @@ const setGableRoof = (polygon, ridge, hip1, hip2, offset, canvas) => { if (ridge.direction === 'bottom') { if (ridge.y1 > y1 && ridge.y2 > y1) { offset = Math.abs(ridge.y1 - y1) - offset + offset = offset < 0 ? 0 : offset ridge.set({ x1: ridge.x1, y1: ridge.y1 - offset, @@ -2825,6 +2828,7 @@ const setGableRoof = (polygon, ridge, hip1, hip2, offset, canvas) => { } if (ridge.y1 < y1 && ridge.y2 < y1) { offset = Math.abs(ridge.y2 - y1) - offset + offset = offset < 0 ? 0 : offset ridge.set({ x1: ridge.x1, y1: ridge.y1, @@ -2858,6 +2862,7 @@ const setGableRoof = (polygon, ridge, hip1, hip2, offset, canvas) => { if (ridge.direction === 'right') { if (ridge.x1 > x1 && ridge.x2 > x1) { offset = Math.abs(ridge.x1 - x1) - offset + offset = offset < 0 ? 0 : offset ridge.set({ x1: ridge.x1 - offset, y1: ridge.y1, @@ -2888,6 +2893,7 @@ const setGableRoof = (polygon, ridge, hip1, hip2, offset, canvas) => { } if (ridge.x1 < x1 && ridge.x2 < x1) { offset = Math.abs(ridge.x2 - x1) - offset + offset = offset < 0 ? 0 : offset ridge.set({ x1: ridge.x1, y1: ridge.y1, @@ -2920,6 +2926,7 @@ const setGableRoof = (polygon, ridge, hip1, hip2, offset, canvas) => { if (ridge.direction === 'left') { if (ridge.x1 > x1 && ridge.x2 > x1) { offset = Math.abs(ridge.x1 - x1) - offset + offset = offset < 0 ? 0 : offset ridge.set({ x1: ridge.x1, y1: ridge.y1, @@ -2950,6 +2957,7 @@ const setGableRoof = (polygon, ridge, hip1, hip2, offset, canvas) => { } if (ridge.x1 < x1 && ridge.x2 < x1) { offset = Math.abs(ridge.x2 - x1) - offset + offset = offset < 0 ? 0 : offset ridge.set({ x1: ridge.x1 - offset, y1: ridge.y1,