diff --git a/src/components/floor-plan/CanvasMenu.jsx b/src/components/floor-plan/CanvasMenu.jsx index 34a7b6c4..cd655841 100644 --- a/src/components/floor-plan/CanvasMenu.jsx +++ b/src/components/floor-plan/CanvasMenu.jsx @@ -212,7 +212,7 @@ export default function CanvasMenu(props) { }, [canvasSetting]) const checkMenuState = (menu) => { - return ([2, 3].some((num) => num === canvasSetting?.roofSizeSet) && menu.index === 2) || (menuNumber === 4 && menu.index === 2) + return (['2', '3'].includes(canvasSetting?.roofSizeSet) && menu.index === 2) || (menuNumber === 4 && menu.index === 2) } // 발전시물레이션 Excel/PDF 다운 @@ -283,7 +283,7 @@ export default function CanvasMenu(props) { key={`canvas-menu-${menu.index}`} className={`canvas-menu-item ${menuNumber === menu.index ? 'active' : ''}`} onClick={() => { - if ([2, 3].some((num) => num === canvasSetting?.roofSizeSet) && menu.index === 2) return + if (['2', '3'].includes(canvasSetting?.roofSizeSet) && menu.index === 2) return if (menuNumber === 4 && menu.index === 2) return onClickNav(menu) }} diff --git a/src/components/floor-plan/modal/grid/DotLineGrid.jsx b/src/components/floor-plan/modal/grid/DotLineGrid.jsx index 9e402a71..67c4cdf3 100644 --- a/src/components/floor-plan/modal/grid/DotLineGrid.jsx +++ b/src/components/floor-plan/modal/grid/DotLineGrid.jsx @@ -29,6 +29,8 @@ export default function DotLineGrid(props) { const { SelectOptions, currentSetting, setCurrentSetting, dotLineGridSettingState, setSettingModalGridOptions, setDotLineGridSettingState } = useCanvasSetting() + const [copyCurrentSetting, setCopyCurrentSetting] = useState({ ...currentSetting }) + // 데이터를 최초 한 번만 조회 useEffect(() => { console.log('DotLineGrid useEffect 실행') @@ -57,7 +59,7 @@ export default function DotLineGrid(props) { const handleCheckBoxChange = (e) => { const { value, checked } = e.target - setCurrentSetting((prev) => { + setCopyCurrentSetting((prev) => { return { ...prev, [value]: checked, @@ -66,23 +68,23 @@ export default function DotLineGrid(props) { } const handleSave = async () => { - if (!currentSetting.DOT && !currentSetting.LINE) { + /*if (!currentSetting.DOT && !currentSetting.LINE) { swalFire({ text: '배치할 그리드를 설정해주세요.' }) return - } + }*/ setDotLineGridSettingState((prev) => { return { ...prev, INTERVAL: { - type: currentSetting.INTERVAL.type, - horizontalInterval: currentSetting.INTERVAL.horizontalInterval, - verticalInterval: currentSetting.INTERVAL.verticalInterval, - ratioInterval: currentSetting.INTERVAL.ratioInterval, - dimension: currentSetting.INTERVAL.dimension, + type: copyCurrentSetting.INTERVAL.type, + horizontalInterval: copyCurrentSetting.INTERVAL.horizontalInterval, + verticalInterval: copyCurrentSetting.INTERVAL.verticalInterval, + ratioInterval: copyCurrentSetting.INTERVAL.ratioInterval, + dimension: copyCurrentSetting.INTERVAL.dimension, }, - DOT: currentSetting.DOT, - LINE: currentSetting.LINE, + DOT: copyCurrentSetting.DOT, + LINE: copyCurrentSetting.LINE, } //setDotLineGridSettingState({ ...currentSetting }) }) @@ -90,16 +92,18 @@ export default function DotLineGrid(props) { setSettingsData({ ...settingsData, INTERVAL: { - type: currentSetting.INTERVAL.type, - horizontalInterval: currentSetting.INTERVAL.horizontalInterval, - verticalInterval: currentSetting.INTERVAL.verticalInterval, - ratioInterval: currentSetting.INTERVAL.ratioInterval, - dimension: currentSetting.INTERVAL.dimension, + type: copyCurrentSetting.INTERVAL.type, + horizontalInterval: copyCurrentSetting.INTERVAL.horizontalInterval, + verticalInterval: copyCurrentSetting.INTERVAL.verticalInterval, + ratioInterval: copyCurrentSetting.INTERVAL.ratioInterval, + dimension: copyCurrentSetting.INTERVAL.dimension, }, - DOT: currentSetting.DOT, - LINE: currentSetting.LINE, + DOT: copyCurrentSetting.DOT, + LINE: copyCurrentSetting.LINE, }) + setCurrentSetting({ ...copyCurrentSetting }) + setIsShow(false) closePopup(id, isConfig) } @@ -107,7 +111,7 @@ export default function DotLineGrid(props) { const handleRadioChange = (e) => { const { value, name, checked, selected } = e.target - setCurrentSetting((prev) => { + setCopyCurrentSetting((prev) => { return { ...prev, INTERVAL: { @@ -120,7 +124,7 @@ export default function DotLineGrid(props) { const changeInput = (value, e) => { const { name } = e.target - setCurrentSetting((prev) => { + setCopyCurrentSetting((prev) => { return { ...prev, INTERVAL: { @@ -133,7 +137,7 @@ export default function DotLineGrid(props) { const changeDimension = (result) => { const { value } = result - setCurrentSetting((prev) => { + setCopyCurrentSetting((prev) => { return { ...prev, INTERVAL: { @@ -146,7 +150,7 @@ export default function DotLineGrid(props) { // 초기화 const reset = () => { - canvas + /*canvas ?.getObjects() .filter((obj) => obj.name === 'lineGrid') .forEach((obj) => canvas?.remove(obj)) @@ -154,9 +158,9 @@ export default function DotLineGrid(props) { ?.getObjects() .filter((obj) => obj.name === 'dotGrid') .forEach((obj) => canvas?.remove(obj)) - +*/ // resetDotLineGridSetting() - setCurrentSetting({ + setCopyCurrentSetting({ INTERVAL: { type: 2, // 1: 가로,세로 간격 수동, 2: 비율 간격 ratioInterval: 910, @@ -188,11 +192,11 @@ export default function DotLineGrid(props) {
- +
- +
@@ -205,8 +209,8 @@ export default function DotLineGrid(props) { id="ra01" value={1} onChange={handleRadioChange} - checked={(currentSetting.DOT || currentSetting.LINE) && currentSetting.INTERVAL.type === 1} - readOnly={!currentSetting.DOT && !currentSetting.LINE} + checked={(copyCurrentSetting.DOT || copyCurrentSetting.LINE) && copyCurrentSetting.INTERVAL.type === 1} + readOnly={!copyCurrentSetting.DOT && !copyCurrentSetting.LINE} />
@@ -217,7 +221,7 @@ export default function DotLineGrid(props) { type="text" className="input-origin" name={`horizontalInterval`} - value={currentSetting.INTERVAL.horizontalInterval} + value={copyCurrentSetting.INTERVAL.horizontalInterval} onChange={(e) => onlyNumberInputChange(e, changeInput)} /> @@ -230,7 +234,7 @@ export default function DotLineGrid(props) { type="text" className="input-origin" name={`verticalInterval`} - value={currentSetting.INTERVAL.verticalInterval} + value={copyCurrentSetting.INTERVAL.verticalInterval} onChange={(e) => onlyNumberInputChange(e, changeInput)} /> @@ -245,8 +249,8 @@ export default function DotLineGrid(props) { id="ra02" value={2} onChange={handleRadioChange} - checked={(currentSetting.DOT || currentSetting.LINE) && currentSetting.INTERVAL.type === 2} - readOnly={!currentSetting.DOT && !currentSetting.LINE} + checked={(copyCurrentSetting.DOT || copyCurrentSetting.LINE) && copyCurrentSetting.INTERVAL.type === 2} + readOnly={!copyCurrentSetting.DOT && !copyCurrentSetting.LINE} /> @@ -257,14 +261,21 @@ export default function DotLineGrid(props) { type="text" className="input-origin" name={`ratioInterval`} - value={currentSetting.INTERVAL.ratioInterval} + value={copyCurrentSetting.INTERVAL.ratioInterval} onChange={(e) => onlyNumberInputChange(e, changeInput)} /> mm
- +
diff --git a/src/components/floor-plan/modal/module/PanelEdit.jsx b/src/components/floor-plan/modal/module/PanelEdit.jsx index dccedba2..5d38f9a8 100644 --- a/src/components/floor-plan/modal/module/PanelEdit.jsx +++ b/src/components/floor-plan/modal/module/PanelEdit.jsx @@ -14,7 +14,9 @@ import { useModule } from '@/hooks/module/useModule' export const PANEL_EDIT_TYPE = { MOVE: 'move', + MOVE_ALL: 'moveAll', COPY: 'copy', + COPY_ALL: 'copyAll', COLUMN_MOVE: 'columnMove', COLUMN_COPY: 'columnCopy', ROW_MOVE: 'rowMove', @@ -29,7 +31,7 @@ export default function PanelEdit(props) { const [direction, setDirection] = useState('up') const { getMessage } = useMessage() const canvas = useRecoilValue(canvasState) - const { moduleMove, moduleCopy, moduleMultiMove, moduleMultiCopy } = useModule() + const { moduleMove, moduleCopy, moduleMultiMove, moduleMultiCopy, moduleMoveAll, moduleCopyAll } = useModule() useEffect(() => { if (canvas) { @@ -44,9 +46,15 @@ export default function PanelEdit(props) { case PANEL_EDIT_TYPE.MOVE: moduleMove(length, direction) break + case PANEL_EDIT_TYPE.MOVE_ALL: + moduleMoveAll(length, direction) + break case PANEL_EDIT_TYPE.COPY: moduleCopy(length, direction) break + case PANEL_EDIT_TYPE.COPY_ALL: + moduleCopyAll(length, direction) + break case PANEL_EDIT_TYPE.COLUMN_MOVE: moduleMultiMove('column', length, direction) break diff --git a/src/components/floor-plan/modal/movement/MovementSetting.jsx b/src/components/floor-plan/modal/movement/MovementSetting.jsx index 424a697e..416afba8 100644 --- a/src/components/floor-plan/modal/movement/MovementSetting.jsx +++ b/src/components/floor-plan/modal/movement/MovementSetting.jsx @@ -1,9 +1,7 @@ import { useMessage } from '@/hooks/useMessage' import WithDraggable from '@/components/common/draggable/WithDraggable' -import { useState } from 'react' import FlowLine from '@/components/floor-plan/modal/movement/type/FlowLine' import Updown from '@/components/floor-plan/modal/movement/type/Updown' -import { usePopup } from '@/hooks/usePopup' import { useMovementSetting } from '@/hooks/roofcover/useMovementSetting' export default function MovementSetting({ id, pos = { x: 50, y: 230 } }) { diff --git a/src/components/floor-plan/modal/movement/type/FlowLine.jsx b/src/components/floor-plan/modal/movement/type/FlowLine.jsx index 96d9b3d3..565a9ff8 100644 --- a/src/components/floor-plan/modal/movement/type/FlowLine.jsx +++ b/src/components/floor-plan/modal/movement/type/FlowLine.jsx @@ -1,5 +1,7 @@ import { useMessage } from '@/hooks/useMessage' import { useState } from 'react' +import { currentObjectState } from '@/store/canvasAtom' +import { useRecoilValue } from 'recoil' const FLOW_LINE_TYPE = { DOWN_LEFT: 'downLeft', @@ -9,7 +11,18 @@ const FLOW_LINE_TYPE = { export default function FlowLine({ FLOW_LINE_REF }) { const { getMessage } = useMessage() const [type, setType] = useState(FLOW_LINE_TYPE.DOWN_LEFT) - + const [filledInput, setFilledInput] = useState('') + const currentObject = useRecoilValue(currentObjectState) + const handleFocus = () => { + if (currentObject === null) { + FLOW_LINE_REF.FILLED_INPUT_REF.current.value = '' + FLOW_LINE_REF.FILLED_INPUT_REF.current.blur() + } + } + const handleInput = (e) => { + const value = e.target.value.replace(/^0+/, '') + setFilledInput(value.replace(/[^0-9]/g, '')) + } return ( <>
@@ -57,7 +70,14 @@ export default function FlowLine({ FLOW_LINE_REF }) {
- +
mm
diff --git a/src/components/floor-plan/modal/placementShape/PlacementShapeSetting.jsx b/src/components/floor-plan/modal/placementShape/PlacementShapeSetting.jsx index e7287e5b..1ca8b420 100644 --- a/src/components/floor-plan/modal/placementShape/PlacementShapeSetting.jsx +++ b/src/components/floor-plan/modal/placementShape/PlacementShapeSetting.jsx @@ -28,7 +28,7 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, set const roofMaterials = useRecoilValue(roofMaterialsAtom) const globalLocale = useRecoilValue(globalLocaleStore) - const { basicSetting, setBasicSettings, fetchBasicSettings, basicSettingSave, addedRoofs, setAddedRoofs } = useCanvasSetting() + const { basicSetting, setBasicSettings, basicSettingSave, addedRoofs, setAddedRoofs } = useCanvasSetting() const { findCommonCode } = useCommonCode() const [raftCodes, setRaftCodes] = useState([]) // 서까래 정보 const [currentRoof, setCurrentRoof] = useState(addedRoofs[0]) // 현재 선택된 지붕재 정보 @@ -43,7 +43,6 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, set // 데이터를 최초 한 번만 조회 useEffect(() => { - //fetchBasicSettings() const raftCodeList = findCommonCode('203800') setRaftCodes(raftCodeList) }, []) diff --git a/src/components/floor-plan/modal/roofAllocation/RoofAllocationSetting.jsx b/src/components/floor-plan/modal/roofAllocation/RoofAllocationSetting.jsx index a949fd2e..d8f7a8ae 100644 --- a/src/components/floor-plan/modal/roofAllocation/RoofAllocationSetting.jsx +++ b/src/components/floor-plan/modal/roofAllocation/RoofAllocationSetting.jsx @@ -73,119 +73,121 @@ export default function RoofAllocationSetting(props) { {getMessage('modal.common.add')}
-
- {currentRoofList.map((roof, index) => { - return ( -
-
- - -
-
-
-
-
- handleChangeRoofMaterial(e, index)} - /> -
- {index === 0 && {getMessage('modal.roof.alloc.default.roof.material')}} - {index !== 0 && } -
+
+
+ {currentRoofList.map((roof, index) => { + return ( +
+
+ +
- {(roof.widAuth || roof.lenAuth) && ( +
- {roof.widAuth && ( -
- W -
- -
+
+
+ handleChangeRoofMaterial(e, index)} + />
- )} - {roof.lenAuth && ( -
- L -
- -
-
- )} + {index === 0 && {getMessage('modal.roof.alloc.default.roof.material')}} + {index !== 0 && } +
- )} - {(roof.raftAuth || roof.roofPchAuth) && ( -
- {roof.raftAuth && ( -
+ {(roof.widAuth || roof.lenAuth) && ( +
+ {roof.widAuth && (
- {getMessage('modal.placement.initial.setting.rafter')} - {raftCodes.length > 0 && ( -
- handleChangeRaft(e, index)} - /> -
- )} -
-
- )} - {roof.roofPchAuth && ( -
-
- {getMessage('hajebichi')} -
- + W +
+
-
- )} -
- )} -
-
- - + )} + {roof.lenAuth && ( +
+ L +
+ +
+
+ )} +
+ )} + {(roof.raftAuth || roof.roofPchAuth) && ( +
+ {roof.raftAuth && ( +
+
+ {getMessage('modal.placement.initial.setting.rafter')} + {raftCodes.length > 0 && ( +
+ handleChangeRaft(e, index)} + /> +
+ )} +
+
+ )} + {roof.roofPchAuth && ( +
+
+ {getMessage('hajebichi')} +
+ +
+
+
+ )} +
+ )} +
+
+ + +
-
- ) - })} + ) + })} +
+
)} -
- {recentNoticeList.length > 0 ? ( - <> -
{dayjs(recentNoticeList[0]?.regDt).format('YYYY.MM.DD')}
-
{recentNoticeList[0]?.title}
-
') : '' }} - >
- - ) : ( - - )} -
+ {recentNoticeList.length > 0 ? ( +
+
{dayjs(recentNoticeList[0]?.regDt).format('YYYY.MM.DD')}
+
{recentNoticeList[0]?.title}
+
') : '' }} + >
+
+ ) : ( +
+

{getMessage('main.content.noBusiness')}

+
+ )}
-
    - {recentFaqList.length > 0 ? ( - <> - {recentFaqList.map((row) => { - return ( -
  • -
    -
    FAQ {row.rowNumber}
    -
    {row.title}
    -
    {dayjs(row.regDt).format('YYYY.MM.DD')}
    -
    -
  • - ) - })} - - ) : ( - - )} -
+ {recentFaqList.length > 0 ? ( +
    + {recentFaqList.map((row) => { + return ( +
  • +
    +
    FAQ {row.totCnt - row.rowNumber}
    +
    {row.title}
    +
    {dayjs(row.regDt).format('YYYY.MM.DD')}
    +
    +
  • + ) + })} +
+ ) : ( +
+

{getMessage('main.content.noBusiness')}

+
+ )}
- -
diff --git a/src/components/management/Stuff.jsx b/src/components/management/Stuff.jsx index 99c9666b..cc75a4c4 100644 --- a/src/components/management/Stuff.jsx +++ b/src/components/management/Stuff.jsx @@ -30,7 +30,7 @@ export default function Stuff() { const [pageNo, setPageNo] = useState(1) //현재 페이지 번호 const [pageSize, setPageSize] = useState(100) //페이지 당 게시물 수 const [totalCount, setTotalCount] = useState(0) //총 갯수 - const [defaultSortType, setDefaultSortType] = useState('R') + const [defaultSortType, setDefaultSortType] = useState('U') const globalLocaleState = useRecoilValue(globalLocaleStore) const { get } = useAxios(globalLocaleState) @@ -207,6 +207,7 @@ export default function Stuff() { schReceiveUser: stuffSearchParams?.schReceiveUser, schDispCompanyName: stuffSearchParams?.schDispCompanyName, schDateType: stuffSearchParams.schDateType, + schTempFlg: stuffSearchParams.schTempFlg, //임시저장 물건 schFromDt: dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD'), schToDt: dayjs(new Date()).format('YYYY-MM-DD'), startRow: (stuffSearch.pageNo - 1) * stuffSearchParams.pageSize + 1, @@ -249,12 +250,13 @@ export default function Stuff() { schReceiveUser: '', schDispCompanyName: '', schDateType: 'U', + schTempFlg: '', 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, - schSortType: 'R', + schSortType: 'U', code: 'E', pageNo: 1, pageSize: 100, @@ -322,13 +324,14 @@ export default function Stuff() { schReceiveUser: '', schDispCompanyName: '', schDateType: 'U', + schTempFlg: '', schFromDt: dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD'), schToDt: dayjs(new Date()).format('YYYY-MM-DD'), startRow: 1, endRow: 100, schSelSaleStoreId: '', schOtherSelSaleStoreId: '', - schSortType: 'R', + schSortType: 'U', code: 'S', pageNo: 1, pageSize: 100, @@ -438,8 +441,8 @@ export default function Stuff() {
diff --git a/src/components/management/StuffDetail.jsx b/src/components/management/StuffDetail.jsx index f31bbcaa..77bbafcc 100644 --- a/src/components/management/StuffDetail.jsx +++ b/src/components/management/StuffDetail.jsx @@ -53,7 +53,7 @@ export default function StuffDetail() { const formInitValue = { // 물건번호 T...(임시) R...(진짜) planReqNo: '', //설계의뢰No - receiveUser: '', //담당자 + receiveUser: session?.userNm, //담당자 로그인사용자명 디폴트 objectStatusId: '0', //물건구분(신축:0 기축 : 1) objectName: '', //물건명 objectNameOmit: '', //경칭선택 @@ -1745,7 +1745,7 @@ export default function StuffDetail() { onChange={onSelectionChange2} getOptionLabel={(x) => x.saleStoreName} getOptionValue={(x) => x.saleStoreId} - isDisabled={otherSaleStoreList.length > 1 ? false : true} + isDisabled={otherSaleStoreList != null && otherSaleStoreList.length > 0 ? false : true} isClearable={true} value={otherSaleStoreList.filter(function (option) { return option.saleStoreId === otherSelOptions diff --git a/src/components/management/StuffSearchCondition.jsx b/src/components/management/StuffSearchCondition.jsx index 3b093201..a72fda59 100644 --- a/src/components/management/StuffSearchCondition.jsx +++ b/src/components/management/StuffSearchCondition.jsx @@ -60,6 +60,7 @@ export default function StuffSearchCondition() { const [schSelSaleStoreId, setSchSelSaleStoreId] = useState('') //판매대리점 선택 const [receiveUser, setReceiveUser] = useState('') //담당자 const [dateType, setDateType] = useState('U') //갱신일(U)/등록일(R) + const [tempFlg, setTempFlg] = useState('') //임시저장여부 const [schSelSaleStoreList, setSchSelSaleStoreList] = useState([]) //판매대리점 자동완성 SELECT 전체 const [favoriteStoreList, setFavoriteStoreList] = useState([]) //즐겨찾기한 판매점목록 @@ -95,12 +96,13 @@ export default function StuffSearchCondition() { schOtherSelSaleStoreId: stuffSearch?.schOtherSelSaleStoreId ? stuffSearch.schOtherSelSaleStoreId : '', schReceiveUser: receiveUser ? receiveUser.trim() : '', schDateType: dateType, + schTempFlg: tempFlg, //임시저장물건 schFromDt: startDate ? dayjs(startDate).format('YYYY-MM-DD') : '', schToDt: endDate ? dayjs(endDate).format('YYYY-MM-DD') : '', code: 'E', startRow: 1, endRow: 1 * stuffSearch?.pageSize, - schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'R', + schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'U', pageNo: 1, pageSize: stuffSearch?.pageSize, }) @@ -115,12 +117,13 @@ export default function StuffSearchCondition() { schOtherSelSaleStoreId: stuffSearch?.schOtherSelSaleStoreId ? stuffSearch.schOtherSelSaleStoreId : '', schReceiveUser: receiveUser ? receiveUser.trim() : '', schDateType: dateType, + schTempFlg: tempFlg, //임시저장물건 schFromDt: startDate ? dayjs(startDate).format('YYYY-MM-DD') : '', schToDt: endDate ? dayjs(endDate).format('YYYY-MM-DD') : '', code: 'E', startRow: stuffSearch?.startRow ? stuffSearch.startRow : 1, endRow: stuffSearch?.endRow ? stuffSearch.endRow : 100, - schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'R', + schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'U', pageNo: stuffSearch?.pageNo, pageSize: stuffSearch?.pageSize, }) @@ -136,12 +139,13 @@ export default function StuffSearchCondition() { schOtherSelSaleStoreId: otherSaleStoreId, schReceiveUser: receiveUser.trim(), schDateType: dateType, + schTempFlg: tempFlg, //임시저장물건 schFromDt: startDate ? dayjs(startDate).format('YYYY-MM-DD') : '', schToDt: endDate ? dayjs(endDate).format('YYYY-MM-DD') : '', code: 'E', startRow: 1, endRow: 100, - schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'R', + schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'U', }) } else if (stuffSearch.code === 'E') { if (session.storeId !== 'T01' && session.storeLvl === '1') { @@ -155,12 +159,13 @@ export default function StuffSearchCondition() { schOtherSelSaleStoreId: otherSaleStoreId, schReceiveUser: stuffSearch?.schReceiveUser ? stuffSearch.schReceiveUser.trim() : receiveUser.trim(), schDateType: dateType, + schTempFlg: tempFlg, //임시저장물건 schFromDt: startDate ? dayjs(startDate).format('YYYY-MM-DD') : '', schToDt: endDate ? dayjs(endDate).format('YYYY-MM-DD') : '', code: 'E', startRow: stuffSearch?.startRow ? stuffSearch.startRow : 1, endRow: stuffSearch?.endRow ? stuffSearch.endRow : 100, - schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'R', + schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'U', pageNo: stuffSearch?.pageNo, pageSize: stuffSearch?.pageSize, }) @@ -176,12 +181,13 @@ export default function StuffSearchCondition() { schOtherSelSaleStoreId: stuffSearch?.schOtherSelSaleStoreId ? stuffSearch.schOtherSelSaleStoreId : '', schReceiveUser: receiveUser ? receiveUser.trim() : '', schDateType: dateType, + schTempFlg: tempFlg, //임시저장물건 schFromDt: startDate ? dayjs(startDate).format('YYYY-MM-DD') : '', schToDt: endDate ? dayjs(endDate).format('YYYY-MM-DD') : '', code: 'E', startRow: 1, endRow: 1 * stuffSearch?.pageSize, - schSortType: 'R', + schSortType: 'U', pageNo: 1, pageSize: stuffSearch?.pageSize, }) @@ -196,12 +202,13 @@ export default function StuffSearchCondition() { schOtherSelSaleStoreId: otherSaleStoreId, schReceiveUser: stuffSearch?.schReceiveUser ? stuffSearch.schReceiveUser.trim() : receiveUser.trim(), schDateType: dateType, + schTempFlg: tempFlg, //임시저장물건 schFromDt: startDate ? dayjs(startDate).format('YYYY-MM-DD') : '', schToDt: endDate ? dayjs(endDate).format('YYYY-MM-DD') : '', code: 'E', startRow: stuffSearch?.startRow ? stuffSearch.startRow : 1, endRow: stuffSearch?.endRow ? stuffSearch.endRow : 100, - schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'R', + schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'U', pageNo: stuffSearch?.pageNo, pageSize: stuffSearch?.pageSize, }) @@ -217,12 +224,13 @@ export default function StuffSearchCondition() { schOtherSelSaleStoreId: otherSaleStoreId, schReceiveUser: stuffSearch?.schReceiveUser ? stuffSearch.schReceiveUser.trim() : receiveUser.trim(), schDateType: dateType, + schTempFlg: tempFlg, //임시저장물건 schFromDt: startDate ? dayjs(startDate).format('YYYY-MM-DD') : '', schToDt: endDate ? dayjs(endDate).format('YYYY-MM-DD') : '', code: 'E', startRow: stuffSearch?.startRow ? stuffSearch.startRow : 1, endRow: stuffSearch?.endRow ? stuffSearch.endRow : 100, - schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'R', + schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'U', pageNo: stuffSearch?.pageNo, pageSize: stuffSearch?.pageSize, }) @@ -238,12 +246,13 @@ export default function StuffSearchCondition() { schOtherSelSaleStoreId: otherSaleStoreId, schReceiveUser: receiveUser.trim(), schDateType: dateType, + schTempFlg: tempFlg, //임시저장물건 schFromDt: startDate ? dayjs(startDate).format('YYYY-MM-DD') : '', schToDt: endDate ? dayjs(endDate).format('YYYY-MM-DD') : '', code: 'E', startRow: stuffSearch?.startRow ? stuffSearch.startRow : 1, endRow: stuffSearch?.endRow ? stuffSearch.endRow : 100, - schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'R', + schSortType: stuffSearch?.schSortType ? stuffSearch.schSortType : 'U', pageNo: stuffSearch?.pageNo, pageSize: stuffSearch?.pageSize, }) @@ -261,6 +270,7 @@ export default function StuffSearchCondition() { dispCompanyNameRef.current.value = '' receiveUserRef.current.value = '' stuffSearch.schDateType = 'U' + stuffSearch.schTempFlg = '' setObjectNo('') setAddress('') setobjectName('') @@ -268,6 +278,7 @@ export default function StuffSearchCondition() { setReceiveUser('') setDispCompanyName('') setDateType('U') + setTempFlg('') setStartDate(dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD')) setEndDate(dayjs(new Date()).format('YYYY-MM-DD')) if (session?.storeId === 'T01') { @@ -285,12 +296,12 @@ export default function StuffSearchCondition() { schSelSaleStoreId: '', schOtherSelSaleStoreId: '', schDateType: 'U', + schTempFlg: '', startRow: 1, endRow: 100, - schSortType: 'R', + schSortType: 'U', pageNo: 1, pageSize: 100, - // code: 'S', }) } else { if (otherSaleStoreList.length > 1) { @@ -303,10 +314,11 @@ export default function StuffSearchCondition() { stuffSearch.schReceiveUser = '' stuffSearch.schDispCompanyName = '' stuffSearch.schDateType = 'U' + stuffSearch.schTempFlg = '' stuffSearch.startRow = 1 stuffSearch.endRow = 100 - stuffSearch.schSortType = 'R' + stuffSearch.schSortType = 'U' stuffSearch.pageNo = 1 stuffSearch.pageSize = 100 } else { @@ -317,10 +329,11 @@ export default function StuffSearchCondition() { stuffSearch.schReceiveUser = '' stuffSearch.schDispCompanyName = '' stuffSearch.schDateType = 'U' + stuffSearch.schTempFlg = '' stuffSearch.startRow = 1 stuffSearch.endRow = 100 - stuffSearch.schSortType = 'R' + stuffSearch.schSortType = 'U' stuffSearch.pageNo = 1 stuffSearch.pageSize = 100 } @@ -543,13 +556,14 @@ export default function StuffSearchCondition() { stuffSearch.schReceiveUser = '' stuffSearch.schDispCompanyName = '' stuffSearch.schDateType = 'U' + stuffSearch.schTempFlg = '' stuffSearch.schFromDt = dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD') stuffSearch.schToDt = dayjs(new Date()).format('YYYY-MM-DD') stuffSearch.startRow = 1 stuffSearch.endRow = 100 stuffSearch.schSelSaleStoreId = '' stuffSearch.schOtherSelSaleStoreId = '' - stuffSearch.schSortType = 'R' + stuffSearch.schSortType = 'U' stuffSearch.pageNo = 1 stuffSearch.pageSize = 100 @@ -575,13 +589,14 @@ export default function StuffSearchCondition() { stuffSearch.schReceiveUser = '' stuffSearch.schDispCompanyName = '' stuffSearch.schDateType = 'U' + stuffSearch.schTempFlg = '' stuffSearch.schFromDt = dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD') stuffSearch.schToDt = dayjs(new Date()).format('YYYY-MM-DD') stuffSearch.startRow = 1 stuffSearch.endRow = 100 stuffSearch.schSelSaleStoreId = '' stuffSearch.schOtherSelSaleStoreId = '' - stuffSearch.schSortType = 'R' + stuffSearch.schSortType = 'U' stuffSearch.pageNo = 1 stuffSearch.pageSize = 100 setSchSelSaleStoreId('') @@ -597,6 +612,17 @@ export default function StuffSearchCondition() { setDispCompanyName(stuffSearch.schDispCompanyName ? stuffSearch.schDispCompanyName : dispCompanyName) setReceiveUser(stuffSearch.schReceiveUser ? stuffSearch.schReceiveUser : receiveUser) setDateType(stuffSearch.schDateType ? stuffSearch.schDateType : dateType) + setTempFlg(stuffSearch.schTempFlg ? stuffSearch.schTempFlg : tempFlg) + } + + if (stuffSearch.schDateType === 'R') { + setDateType('R') + } + + if (stuffSearch.schTempFlg === '0') { + setTempFlg('0') + } else if (stuffSearch.schTempFlg === '1') { + setTempFlg('1') } }, [stuffSearch]) @@ -648,8 +674,8 @@ export default function StuffSearchCondition() { - - + {/* + */} @@ -685,22 +711,7 @@ export default function StuffSearchCondition() { />
- {getMessage('stuff.search.schAddress')} - -
- { - stuffSearch.schAddress = addressRef.current.value - setAddress(addressRef.current.value) - }} - onKeyUp={handleByOnKeyUp} - /> -
- + {getMessage('stuff.search.schDispCompanyName')}
@@ -751,8 +762,127 @@ export default function StuffSearchCondition() { />
- {getMessage('stuff.search.schSelSaleStoreId')} + {getMessage('stuff.search.schAddress')} + +
+ { + stuffSearch.schAddress = addressRef.current.value + setAddress(addressRef.current.value) + }} + onKeyUp={handleByOnKeyUp} + /> +
+ + + + {getMessage('stuff.search.period')} +
+
+
+ { + setDateType(e.target.value) + stuffSearch.schDateType = e.target.value + }} + /> + +
+
+ { + setDateType(e.target.value) + stuffSearch.schDateType = e.target.value + }} + /> + +
+
+
+
+ +
+ ~ +
+ +
+
+
+ + {getMessage('stuff.search.schTempFlgT')} + +
+
+
+ { + // let tempFlg = e.target.value + // setTempFlg(tempFlg) + // console.log('포함을 눌렀어:::::11:::::::', e.target.value) + // console.log('포함을 눌렀어::::22::::::::', tempFlg) + setTempFlg(e.target.value) + stuffSearch.schTempFlg = e.target.value + }} + /> + +
+
+ { + setTempFlg(e.target.value) + stuffSearch.schTempFlg = e.target.value + }} + /> + +
+
+ { + setTempFlg(e.target.value) + stuffSearch.schTempFlg = e.target.value + }} + /> + +
+
+
+ + + + {getMessage('stuff.search.schSelSaleStoreId')} +
{session?.storeId === 'T01' && ( @@ -876,52 +1006,6 @@ export default function StuffSearchCondition() {
- - {getMessage('stuff.search.period')} - -
-
-
- { - setDateType(e.target.value) - stuffSearch.schDateType = e.target.value - }} - /> - -
-
- { - setDateType(e.target.value) - stuffSearch.schDateType = e.target.value - }} - /> - -
-
-
-
- -
- ~ -
- -
-
-
- -
diff --git a/src/hooks/module/useModule.js b/src/hooks/module/useModule.js index 34f3f7bd..a71744c6 100644 --- a/src/hooks/module/useModule.js +++ b/src/hooks/module/useModule.js @@ -26,7 +26,8 @@ export const MODULE_INSERT_TYPE = { } export const MODULE_ALIGN_TYPE = { - TOP: 'top', + VERTICAL: 'vertical', + HORIZONTAL: 'horizontal', } export function useModule() { @@ -83,6 +84,162 @@ export function useModule() { } } } + const moduleMultiMove = (type, length, direction) => { + if (canvas.getActiveObjects().length === 0) return + if (canvas.getActiveObjects().length > 1) { + swalFire({ + title: '여러 개의 모듈을 선택할 수 없습니다.', + icon: 'error', + type: 'alert', + }) + canvas.discardActiveObject() + return + } + const activeModule = canvas.getObjects().filter((obj) => canvas.getActiveObjects()[0].id === obj.id)[0] + const modules = type === 'row' ? getRowModules(activeModule) : getColumnModules(activeModule) + const otherModules = getOtherModules(modules) + const objects = getObjects() + const moduleSetupSurface = canvas + .getObjects() + .filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE && obj.id === activeModule.surfaceId)[0] + let isWarning = false + + modules.forEach((module) => { + const { top, left } = getPosotion(module, direction, length, false) + module.originPos = { + top: module.top, + left: module.left, + fill: module.fill, + } + + module.set({ top, left }) + module.setCoords() + canvas.renderAll() + + if (otherModules.length > 0) { + if (isOverlapOtherModules(module, otherModules) || isOverlapObjects(module, objects) || isOutsideSurface(module, moduleSetupSurface)) { + isWarning = true + module.set({ fill: 'red' }) + } + } + }) + + canvas.renderAll() + if (isWarning) { + swalFire({ + title: getMessage('can.not.move.module'), + icon: 'error', + type: 'alert', + confirmFn: () => { + modules.forEach((module) => { + module.set({ top: module.originPos.top, left: module.originPos.left, fill: module.originPos.fill }) + module.setCoords() + }) + canvas.renderAll() + }, + }) + } + } + + const moduleMoveAll = (length, direction) => { + const moduleSetupSurface = canvas.getObjects().filter((obj) => canvas.getActiveObjects()[0].id === obj.id)[0] + const modules = canvas.getObjects().filter((obj) => obj.surfaceId === moduleSetupSurface.id && obj.name === POLYGON_TYPE.MODULE) + const objects = getObjects() + + let isWarning = false + + modules.forEach((module) => { + const { top, left } = getPosotion(module, direction, length, false) + module.originPos = { + top: module.top, + left: module.left, + fill: module.fill, + } + + module.set({ top, left }) + module.setCoords() + canvas.renderAll() + + if (isOverlapObjects(module, objects) || isOutsideSurface(module, moduleSetupSurface)) { + isWarning = true + module.set({ fill: 'red' }) + } + }) + + canvas.renderAll() + if (isWarning) { + swalFire({ + title: getMessage('can.not.move.module'), + icon: 'error', + type: 'alert', + confirmFn: () => { + modules.forEach((module) => { + module.set({ top: module.originPos.top, left: module.originPos.left, fill: module.originPos.fill }) + module.setCoords() + }) + canvas.renderAll() + }, + }) + } + } + + const moduleCopyAll = (length, direction) => { + const moduleSetupSurface = canvas.getObjects().filter((obj) => canvas.getActiveObjects()[0].id === obj.id)[0] + const modules = canvas.getObjects().filter((obj) => obj.surfaceId === moduleSetupSurface.id && obj.name === POLYGON_TYPE.MODULE) + const objects = getObjects() + const copyModules = [] + let copyModule = null + let isWarning = false + let moduleLength = 0 + if (['up', 'down'].includes(direction)) { + modules.sort((a, b) => a.top - b.top) + moduleLength = Number(modules[modules.length - 1].top) + Number(modules[modules.length - 1].height) - Number(modules[0].top) + } else if (['left', 'right'].includes(direction)) { + modules.sort((a, b) => a.left - b.left) + moduleLength = Number(modules[modules.length - 1].left) + Number(modules[modules.length - 1].width) - Number(modules[0].left) + } + + modules.forEach((module) => { + const { top, left } = getPosotion(module, direction, Number(length) + Number(moduleLength), false) + module.clone((obj) => { + obj.set({ + parentId: module.parentId, + initOptions: module.initOptions, + direction: module.direction, + arrow: module.arrow, + name: module.name, + type: module.type, + length: module.length, + points: module.points, + surfaceId: module.surfaceId, + left, + top, + id: uuidv4(), + }) + copyModule = obj + canvas.add(obj) + copyModules.push(obj) + obj.setCoords() + }) + if (isOverlapObjects(copyModule, objects) || isOutsideSurface(copyModule, moduleSetupSurface)) { + isWarning = true + copyModule.set({ fill: 'red' }) + } + canvas.renderAll() + }) + + if (isWarning) { + swalFire({ + title: getMessage('can.not.copy.module'), + icon: 'error', + type: 'alert', + confirmFn: () => { + canvas.remove(...copyModules) + canvas.renderAll() + }, + }) + } + } const moduleCopy = (length, direction) => { if (canvas.getActiveObjects().length === 0) return @@ -144,63 +301,6 @@ export function useModule() { } } - const moduleMultiMove = (type, length, direction) => { - if (canvas.getActiveObjects().length === 0) return - if (canvas.getActiveObjects().length > 1) { - swalFire({ - title: '여러 개의 모듈을 선택할 수 없습니다.', - icon: 'error', - type: 'alert', - }) - canvas.discardActiveObject() - return - } - const activeModule = canvas.getObjects().filter((obj) => canvas.getActiveObjects()[0].id === obj.id)[0] - const modules = type === 'row' ? getRowModules(activeModule) : getColumnModules(activeModule) - const otherModules = getOtherModules(modules) - const objects = getObjects() - const moduleSetupSurface = canvas - .getObjects() - .filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE && obj.id === activeModule.surfaceId)[0] - let isWarning = false - - modules.forEach((module) => { - const { top, left } = getPosotion(module, direction, length, false) - module.originPos = { - top: module.top, - left: module.left, - fill: module.fill, - } - - module.set({ top, left }) - module.setCoords() - canvas.renderAll() - - if (otherModules.length > 0) { - if (isOverlapOtherModules(module, otherModules) || isOverlapObjects(module, objects) || isOutsideSurface(module, moduleSetupSurface)) { - isWarning = true - module.set({ fill: 'red' }) - } - } - }) - - canvas.renderAll() - if (isWarning) { - swalFire({ - title: getMessage('can.not.move.module'), - icon: 'error', - type: 'alert', - confirmFn: () => { - modules.forEach((module) => { - module.set({ top: module.originPos.top, left: module.originPos.left, fill: module.originPos.fill }) - module.setCoords() - }) - canvas.renderAll() - }, - }) - } - } - const moduleMultiCopy = (type, length, direction) => { if (canvas.getActiveObjects().length === 0) return if (canvas.getActiveObjects().length > 1) { @@ -694,7 +794,68 @@ export function useModule() { } } - const alignModule = (type) => {} + const alignModule = (type) => { + const moduleSetupSurface = canvas.getObjects().filter((obj) => canvas.getActiveObjects()[0].id === obj.id)[0] + const modules = canvas.getObjects().filter((obj) => obj.surfaceId === moduleSetupSurface.id && obj.name === POLYGON_TYPE.MODULE) + const objects = getObjects() + let [top, bottom, left, right] = [0, 0, 0, 0] + + top = Math.min(...modules.map((module) => module.top)) + bottom = Math.max(...modules.map((module) => module.top + module.height)) + left = Math.min(...modules.map((module) => module.left)) + right = Math.max(...modules.map((module) => module.left + module.width)) + const moduleSurfacePos = { + top: Math.min(...moduleSetupSurface.points.map((point) => point.y)), + left: Math.min(...moduleSetupSurface.points.map((point) => point.x)), + } + const [height, width] = [bottom - top, right - left] + const verticalCenterLength = moduleSurfacePos.top + moduleSetupSurface.height / 2 - (top + height / 2) + const horizontalCenterLength = moduleSurfacePos.left + moduleSetupSurface.width / 2 - (left + width / 2) + let isWarning = false + + canvas.discardActiveObject() + modules.forEach((module) => { + module.originPos = { + left: module.left, + top: module.top, + fill: module.fill, + } + if (type === MODULE_ALIGN_TYPE.VERTICAL) { + module.set({ top: module.top + verticalCenterLength }) + } else if (type === MODULE_ALIGN_TYPE.HORIZONTAL) { + module.set({ left: module.left + horizontalCenterLength }) + } + + canvas.renderAll() + module.setCoords() + if (isOverlapObjects(module, objects) || isOutsideSurface(module, moduleSetupSurface)) { + isWarning = true + module.set({ fill: 'red' }) + } + }) + canvas.renderAll() + if (isWarning) { + swalFire({ + title: getMessage('can.not.align.module'), + icon: 'error', + type: 'alert', + confirmFn: () => { + modules.forEach((module) => { + module.set({ top: module.originPos.top, left: module.originPos.left, fill: module.originPos.fill }) + module.setCoords() + }) + canvas.renderAll() + }, + }) + } + } + + const modulesRemove = () => { + const activeModule = canvas.getObjects().filter((obj) => canvas.getActiveObjects()[0].id === obj.id)[0] + const modules = canvas.getObjects().filter((obj) => obj.surfaceId === activeModule.surfaceId && obj.name === POLYGON_TYPE.MODULE) + canvas.remove(...modules) + canvas.renderAll() + } const isOverlapOtherModules = (module, otherModules) => { return otherModules.some( @@ -765,11 +926,15 @@ export function useModule() { return { moduleMove, moduleMultiMove, + moduleMoveAll, moduleCopy, moduleMultiCopy, + moduleCopyAll, moduleColumnRemove, moduleRowRemove, moduleColumnInsert, muduleRowInsert, + modulesRemove, + alignModule, } } diff --git a/src/hooks/option/useCanvasSetting.js b/src/hooks/option/useCanvasSetting.js index 2c710708..e4c02664 100644 --- a/src/hooks/option/useCanvasSetting.js +++ b/src/hooks/option/useCanvasSetting.js @@ -272,7 +272,7 @@ export function useCanvasSetting() { // 기본설정(PlacementShapeSetting) 조회 및 초기화 const fetchBasicSettings = async () => { try { - await get({ url: `/api/canvas-management/canvas-basic-settings/by-object/${correntObjectNo}` }).then((res) => { + await get({ url: `/api/canvas-management/canvas-basic-settings/by-object/0/${correntObjectNo}` }).then((res) => { let roofsRow = {} let roofsArray = {} @@ -762,7 +762,6 @@ export function useCanvasSetting() { setCanvasSetting, basicSetting, setBasicSettings, - fetchBasicSettings, basicSettingSave, settingsData, setSettingsData, diff --git a/src/hooks/roofcover/useMovementSetting.js b/src/hooks/roofcover/useMovementSetting.js index 6e06212e..4a979438 100644 --- a/src/hooks/roofcover/useMovementSetting.js +++ b/src/hooks/roofcover/useMovementSetting.js @@ -5,6 +5,8 @@ import { useMessage } from '@/hooks/useMessage' import { useEffect, useRef, useState } from 'react' import { useEvent } from '@/hooks/useEvent' import { LINE_TYPE, POLYGON_TYPE } from '@/common/common' +import { useSwal } from '@/hooks/useSwal' +import { QPolygon } from '@/components/fabric/QPolygon' import { getDegreeByChon } from '@/util/canvas-util' //동선이동 형 올림 내림 @@ -26,6 +28,7 @@ export function useMovementSetting(id) { ] const [type, setType] = useState(TYPE.FLOW_LINE) const typeRef = useRef(type) + const { swalFire } = useSwal() const FLOW_LINE_REF = { POINTER_INPUT_REF: useRef(null), @@ -69,6 +72,7 @@ export function useMovementSetting(id) { roof.innerLines.forEach((line) => { if (line.name === LINE_TYPE.SUBLINE.RIDGE) { line.bringToFront() + line.set({ visible: true }) line.set({ selectable: true }) line.set({ strokeWidth: 4 }) line.set({ stroke: '#1083E3' }) @@ -89,7 +93,6 @@ export function useMovementSetting(id) { canvas.renderAll() addCanvasMouseEventListener('mouse:move', mouseMoveEvent) addCanvasMouseEventListener('mouse:down', mouseDownEvent) - return () => { initEvent() @@ -178,6 +181,36 @@ export function useMovementSetting(id) { updownMoveEvent(e) } } + + function edgesIntersection(edgeA, edgeB) { + const den = + (edgeB.vertex2.y - edgeB.vertex1.y) * (edgeA.vertex2.x - edgeA.vertex1.x) - + (edgeB.vertex2.x - edgeB.vertex1.x) * (edgeA.vertex2.y - edgeA.vertex1.y) + + if (den === 0) { + return null // lines are parallel or coincident + } + + const ua = + ((edgeB.vertex2.x - edgeB.vertex1.x) * (edgeA.vertex1.y - edgeB.vertex1.y) - + (edgeB.vertex2.y - edgeB.vertex1.y) * (edgeA.vertex1.x - edgeB.vertex1.x)) / + den + + const ub = + ((edgeA.vertex2.x - edgeA.vertex1.x) * (edgeA.vertex1.y - edgeB.vertex1.y) - + (edgeA.vertex2.y - edgeA.vertex1.y) * (edgeA.vertex1.x - edgeB.vertex1.x)) / + den + + // Edges are not intersecting but the lines defined by them are + const isIntersectionOutside = ua < 0 || ub < 0 || ua > 1 || ub > 1 + + return { + x: edgeA.vertex1.x + ua * (edgeA.vertex2.x - edgeA.vertex1.x), + y: edgeA.vertex1.y + ua * (edgeA.vertex2.y - edgeA.vertex1.y), + isIntersectionOutside, + } + } + //동선 이동 마우스 클릭 이벤트 const saveFlowLine = (e) => { const target = selectedObject.current @@ -187,22 +220,23 @@ export function useMovementSetting(id) { let newPoint = [] if (Math.sign(target.x1 - target.x2) !== 0) { + const sign = FLOW_LINE_REF.UP_RIGHT_RADIO_REF.current.checked ? 1 : -1 newPoint = [ target.x1, - target.y1 - Number(FLOW_LINE_REF.FILLED_INPUT_REF.current.value / 10), + target.y1 - sign * Number(FLOW_LINE_REF.FILLED_INPUT_REF.current.value / 10), target.x2, - target.y2 - Number(FLOW_LINE_REF.FILLED_INPUT_REF.current.value / 10), + target.y2 - sign * Number(FLOW_LINE_REF.FILLED_INPUT_REF.current.value / 10), ] } else { + const sign = FLOW_LINE_REF.UP_RIGHT_RADIO_REF.current.checked ? -1 : 1 newPoint = [ - target.x1 + Number(FLOW_LINE_REF.FILLED_INPUT_REF.current.value / 10), + target.x1 - sign * Number(FLOW_LINE_REF.FILLED_INPUT_REF.current.value / 10), target.y1, - target.x2 + Number(FLOW_LINE_REF.FILLED_INPUT_REF.current.value / 10), + target.x2 - sign * Number(FLOW_LINE_REF.FILLED_INPUT_REF.current.value / 10), target.y2, ] } - - clearRef() + newPoint = newPoint.map((point) => Math.round(point * 10) / 10) const cloned = new fabric.Line(newPoint, {}) let currentObject = selectedObject.current const currentX1 = currentObject.x1, @@ -210,39 +244,94 @@ export function useMovementSetting(id) { currentX2 = currentObject.x2, currentY2 = currentObject.y2 + const currentMidX = (currentX1 + currentX2) / 2 + const currentMidY = (currentY1 + currentY2) / 2 + const clonedMidX = (cloned.x1 + cloned.x2) / 2 + const clonedMidY = (cloned.y1 + cloned.y2) / 2 + const roof = canvas.getObjects().find((obj) => obj.id === currentObject.attributes.roofId) - if (roof.separatePolygon.length > 0) { - const separatePolygon = roof.separatePolygon - separatePolygon - .filter((polygon) => - polygon.points.some((point) => (point.x === currentX1 && point.y === currentY1) || (point.x === currentX2 && point.y === currentY2)), - ) - .forEach((polygon) => { - const newPoints = polygon.points.map((point) => { - if (point.x === currentX1 && point.y === currentY1) { - return { x: newPoint[0], y: newPoint[1] } - } else if (point.x === currentX2 && point.y === currentY2) { - return { x: newPoint[2], y: newPoint[3] } - } else { - return point - } - }) - polygon.set({ points: newPoints }) - polygon.setCoords() - polygon.addLengthText() - polygon.initLines() - const slope = (line) => (line.x2 - line.x1 === 0 ? Infinity : (line.y2 - line.y1) / (line.x2 - line.x1)) - const currentDegree = polygon.attributes.pitch > 0 ? getDegreeByChon(polygon.attributes.pitch) : polygon.attributes.degree - polygon.lines.forEach((line) => { - line.attributes.planeSize = Math.round(Math.sqrt(Math.pow(line.x2 - line.x1, 2) + Math.pow(line.y2 - line.y1, 2)) * 10) - if (currentDegree > 0 && slope(line) !== slope(currentObject)) { - const height = Math.tan(currentDegree * (Math.PI / 180)) * line.attributes.planeSize - line.attributes.actualSize = Math.round(Math.sqrt(Math.pow(line.attributes.planeSize, 2) + Math.pow(height, 2))) - } else { - line.attributes.actualSize = line.attributes.planeSize - } - }) + let isOutside = false + roof.lines.forEach((line) => { + const intersection = edgesIntersection( + { vertex1: { x: currentMidX, y: currentMidY }, vertex2: { x: clonedMidX, y: clonedMidY } }, + { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }, + ) + if (intersection && !intersection.isIntersectionOutside) { + isOutside = true + } + }) + if (isOutside) { + swalFire({ text: getMessage('modal.movement.flow.line.move.alert'), icon: 'error' }) + return + } + currentObject.set({ x1: cloned.x1, y1: cloned.y1, x2: cloned.x2, y2: cloned.y2 }) + currentObject.setCoords() + const otherRidges = roof.innerLines.filter((line) => line.name === LINE_TYPE.SUBLINE.RIDGE && line.id !== currentObject.id) + const overlapRidges = otherRidges.filter((line) => { + if (currentObject.x1 === currentObject.x2 && line.x1 === line.x2 && 0 < Math.abs(currentObject.x1 - line.x1) < 1) { + if ( + currentObject.y1 <= line.y1 <= currentObject.y2 || + currentObject.y1 >= line.y1 >= currentObject.y2 || + currentObject.y1 <= line.y2 <= currentObject.y2 || + currentObject.y1 >= line.y2 >= currentObject.y2 + ) { + return true + } + } + if (currentObject.y1 === currentObject.y2 && line.y1 === line.y2 && 0 < Math.abs(currentObject.y1 - line.y1) < 1) { + if ( + currentObject.x1 <= line.x1 <= currentObject.x2 || + currentObject.x1 >= line.x1 >= currentObject.x2 || + currentObject.x1 <= line.x2 <= currentObject.x2 || + currentObject.x1 >= line.x2 >= currentObject.x2 + ) { + return true + } + } + }) + if (overlapRidges.length > 0) { + const minX = Math.min(...overlapRidges.flatMap((line) => [line.x1, line.x2]), currentObject.x1, currentObject.x2) + const maxX = Math.max(...overlapRidges.flatMap((line) => [line.x1, line.x2]), currentObject.x1, currentObject.x2) + const minY = Math.min(...overlapRidges.flatMap((line) => [line.y1, line.y2]), currentObject.y1, currentObject.y2) + const maxY = Math.max(...overlapRidges.flatMap((line) => [line.y1, line.y2]), currentObject.y1, currentObject.y2) + const newRidge = new fabric.Line([minX, minY, maxX, maxY], { + name: LINE_TYPE.SUBLINE.RIDGE, + selectable: true, + stroke: '#1083E3', + strokeWidth: 4, + attributes: { roofId: roof.id, currentRoof: currentObject.attributes.currentRoof }, + }) + + overlapRidges.forEach((line) => + line.attributes.currentRoof.forEach((id) => { + if (!newRidge.attributes.currentRoof.includes(id)) { + newRidge.attributes.currentRoof.push(id) + } + }), + ) + canvas.add(newRidge) + newRidge.bringToFront() + roof.innerLines.push(newRidge) + + canvas.remove(currentObject) + roof.innerLines.forEach((innerLine, index) => { + if (innerLine.id === currentObject.id) { + roof.innerLines.splice(index, 1) + } + }) + + overlapRidges.forEach((line) => { + canvas.remove(line) + roof.innerLines.forEach((innerLine, index) => { + if (innerLine.id === line.id) { + roof.innerLines.splice(index, 1) + } }) + }) + } + + if (roof.separatePolygon.length > 0) { + redrawSeparatePolygon(roof) } else { roof.innerLines .filter((line) => currentObject !== line) @@ -277,6 +366,390 @@ export function useMovementSetting(id) { canvas.renderAll() canvas.discardActiveObject() + + FLOW_LINE_REF.FILLED_INPUT_REF.current.value = '' + } + + const redrawSeparatePolygon = (roof) => { + roof.separatePolygon.forEach((polygon) => canvas.remove(polygon)) + roof.separatePolygon = [] + const roofLines = roof.lines + const wallLines = roof.wall.lines + const eaves = [] + roofLines.forEach((currentRoof, index) => { + if (currentRoof.attributes?.type === LINE_TYPE.WALLLINE.EAVES) { + eaves.push({ index: index, roof: currentRoof, length: currentRoof.attributes.planeSize }) + } + }) + const ridges = roof.innerLines.filter((line) => line.name === LINE_TYPE.SUBLINE.RIDGE) + eaves.sort((a, b) => a.length - b.length) + + const ridgeCount = eaves.length - 1 + console.log('ridgeCount', ridgeCount, 'ridges', ridges.length) + + const duplicatedEaves = [] + ridges.forEach((ridge) => { + const positiveLines = [] + const negativeLines = [] + ridge.attributes.currentRoof.forEach((id) => { + console.log('id : ', id) + const eave = roofLines.find((obj) => obj.id === id) + console.log('roof : ', eave) + if (ridge.x1 === ridge.x2) { + if (eave.y1 < eave.y2) { + positiveLines.push(eave) + } else { + negativeLines.push(eave) + } + } else { + if (eave.x1 < eave.x2) { + positiveLines.push(eave) + } else { + negativeLines.push(eave) + } + } + }) + if (positiveLines.length > 1) { + duplicatedEaves.push(positiveLines) + } + if (negativeLines.length > 1) { + duplicatedEaves.push(negativeLines) + } + }) + console.log('duplicatedEaves', duplicatedEaves) + + duplicatedEaves.forEach((duplicatedEave) => { + duplicatedEave.forEach((eave) => { + const index = eaves.findIndex((item) => item.roof.id === eave.id) + eaves.splice(index, 1) + }) + }) + + // if (ridgeCount === ridges.length) { + eaves.forEach((eave, i) => { + const index = eave.index, + currentRoof = eave.roof + const currentWall = wallLines[index] + const currentRidges = ridges.filter((ridge) => ridge.attributes.currentRoof.includes(eave.roof.id)) + let points = [] + const signX = Math.sign(currentRoof.x1 - currentRoof.x2) + let currentX1 = currentRoof.x1, + currentY1 = currentRoof.y1, + currentX2 = currentRoof.x2, + currentY2 = currentRoof.y2 + let changeX1 = [Math.min(currentRoof.x1, currentRoof.x2), Math.min(currentRoof.x1, currentRoof.x2)], + changeY1 = [Math.min(currentRoof.y1, currentRoof.y2), Math.min(currentRoof.y1, currentRoof.y2)], + changeX2 = [Math.max(currentRoof.x2, currentRoof.x1), Math.max(currentRoof.x2, currentRoof.x1)], + changeY2 = [Math.max(currentRoof.y2, currentRoof.y1), Math.max(currentRoof.y2, currentRoof.y1)] + + if (signX === 0) { + currentY1 = Math.min(currentRoof.y1, currentRoof.y2, currentWall.y1, currentWall.y2) + changeY1[1] = currentY1 + currentY2 = Math.max(currentRoof.y1, currentRoof.y2, currentWall.y1, currentWall.y2) + changeY2[1] = currentY2 + } else { + currentX1 = Math.min(currentRoof.x1, currentRoof.x2, currentWall.x1, currentWall.x2) + changeX1[1] = currentX1 + currentX2 = Math.max(currentRoof.x1, currentRoof.x2, currentWall.x1, currentWall.x2) + changeX2[1] = currentX2 + } + points.push({ x: currentX1, y: currentY1 }, { x: currentX2, y: currentY2 }) + + currentRidges.forEach((ridge) => { + let ridgeX1 = ridge.x1, + ridgeY1 = ridge.y1, + ridgeX2 = ridge.x2, + ridgeY2 = ridge.y2 + if (signX === 0) { + ridgeY1 = Math.min(ridge.y1, ridge.y2) + ridgeY2 = Math.max(ridge.y1, ridge.y2) + } else { + ridgeX1 = Math.min(ridge.x1, ridge.x2) + ridgeX2 = Math.max(ridge.x1, ridge.x2) + } + points.push({ x: ridgeX1, y: ridgeY1 }, { x: ridgeX2, y: ridgeY2 }) + }) + + points.forEach((point) => { + if (point.x === changeX1[0] && changeX1[0] !== changeX1[1]) { + point.x = changeX1[1] + } + if (point.x === changeX2[0] && changeX2[0] !== changeX2[1]) { + point.x = changeX2[1] + } + if (point.y === changeY1[0] && changeY1[0] !== changeY1[1]) { + point.y = changeY1[1] + } + if (point.y === changeY2[0] && changeY2[0] !== changeY2[1]) { + point.y = changeY2[1] + } + }) + //중복된 point 제거 + points = points.filter((point, index, self) => index === self.findIndex((p) => p.x === point.x && p.y === point.y)) + //point 정렬 (가장 좌측, 최상단의 점을 기준으로 삼는다.) + const startPoint = points + .filter((point) => point.x === Math.min(...points.map((point) => point.x))) + .reduce((prev, current) => { + return prev.y < current.y ? prev : current + }) + + const sortedPoints = [] + sortedPoints.push(startPoint) + + points.forEach((p, index) => { + if (index === 0) { + //시작점 다음 점 찾기, y좌표가 startPoint.y 보다 큰 점 중 x좌표가 가까운 점 + const underStartPoint = points.filter((point) => point.y > startPoint.y) + const nextPoint = underStartPoint + .filter((point) => point.x === startPoint.x) + .reduce((prev, current) => { + if (prev === undefined) { + return current + } + return Math.abs(prev.y - startPoint.y) < Math.abs(current.y - startPoint.y) ? prev : current + }, undefined) + if (nextPoint) { + sortedPoints.push(nextPoint) + } else { + const nextPoint = underStartPoint.reduce((prev, current) => { + const prevHypos = Math.sqrt(Math.abs(Math.pow(prev.x - startPoint.x, 2)) + Math.abs(Math.pow(prev.y - startPoint.y, 2))) + const currentHypos = Math.sqrt(Math.abs(Math.pow(current.x - startPoint.x, 2)) + Math.abs(Math.pow(current.y - startPoint.y, 2))) + return prevHypos < currentHypos ? prev : current + }, undefined) + sortedPoints.push(nextPoint) + } + } else { + const lastPoint = sortedPoints[sortedPoints.length - 1] + console.log('lastPoint', lastPoint) + const prevPoint = sortedPoints[sortedPoints.length - 2] + const otherPoints = points.filter((point) => sortedPoints.includes(point) === false) + const nextPoint = otherPoints.reduce((prev, current) => { + if (prev === undefined) { + const height = Math.abs(Math.sqrt(Math.abs(Math.pow(prevPoint.x - lastPoint.x, 2)) + Math.abs(Math.pow(prevPoint.y - lastPoint.y, 2)))) + const adjacent = Math.abs(Math.sqrt(Math.abs(Math.pow(current.x - lastPoint.x, 2)) + Math.abs(Math.pow(current.y - lastPoint.y, 2)))) + const hypotenuse = Math.abs(Math.sqrt(Math.abs(Math.pow(current.x - prevPoint.x, 2)) + Math.abs(Math.pow(current.y - prevPoint.y, 2)))) + + const angle = Math.round( + Math.acos((Math.pow(adjacent, 2) + Math.pow(height, 2) - Math.pow(hypotenuse, 2)) / (2 * adjacent * height)) * (180 / Math.PI), + ) + if (angle === 90) { + return current + } + } else { + return prev + } + }, undefined) + if (nextPoint) { + sortedPoints.push(nextPoint) + } else { + const nextPoint = otherPoints.reduce((prev, current) => { + if (prev !== undefined) { + const height = Math.abs( + Math.sqrt(Math.abs(Math.pow(prevPoint.x - lastPoint.x, 2)) + Math.abs(Math.pow(prevPoint.y - lastPoint.y, 2))), + ) + const adjacentC = Math.abs(Math.sqrt(Math.abs(Math.pow(current.x - lastPoint.x, 2)) + Math.abs(Math.pow(current.y - lastPoint.y, 2)))) + const hypotenuseC = Math.abs( + Math.sqrt(Math.abs(Math.pow(current.x - prevPoint.x, 2)) + Math.abs(Math.pow(current.y - prevPoint.y, 2))), + ) + const angleC = Math.round( + Math.acos((Math.pow(adjacentC, 2) + Math.pow(height, 2) - Math.pow(hypotenuseC, 2)) / (2 * adjacentC * height)) * (180 / Math.PI), + ) + const adjacentP = Math.abs(Math.sqrt(Math.abs(Math.pow(prev.x - lastPoint.x, 2)) + Math.abs(Math.pow(prev.y - lastPoint.y, 2)))) + const hypotenuseP = Math.abs(Math.sqrt(Math.abs(Math.pow(prev.x - prevPoint.x, 2)) + Math.abs(Math.pow(prev.y - prevPoint.y, 2)))) + const angleP = Math.round( + Math.acos((Math.pow(adjacentP, 2) + Math.pow(height, 2) - Math.pow(hypotenuseP, 2)) / (2 * adjacentP * height)) * (180 / Math.PI), + ) + if (Math.abs(90 - angleC) < Math.abs(90 - angleP)) { + return current + } else { + return prev + } + } else { + return current + } + }, undefined) + if (nextPoint) { + sortedPoints.push(nextPoint) + } + } + } + }) + + if (sortedPoints.length > 0) { + const roofPolygon = new QPolygon(sortedPoints, { + fill: 'transparent', + stroke: '#000000', + strokeWidth: 1, + selectable: false, + fontSize: roof.fontSize, + name: 'roofPolygon', + attributes: { + roofId: roof.id, + currentRoof: currentRoof.id, + pitch: currentRoof.attributes.pitch, + degree: currentRoof.attributes.degree, + direction: currentRoof.direction, + }, + }) + const currentDegree = currentRoof.attributes.pitch > 0 ? getDegreeByChon(currentRoof.attributes.pitch) : currentRoof.attributes.degree + //지붕 각도에 따른 실측치 조정 + roofPolygon.lines.forEach((line) => { + line.attributes.planeSize = Math.round(Math.sqrt(Math.pow(line.x2 - line.x1, 2) + Math.pow(line.y2 - line.y1, 2)) * 10) + const slope = (line) => (line.x2 - line.x1 === 0 ? Infinity : (line.y2 - line.y1) / (line.x2 - line.x1)) + + if (currentDegree > 0 && slope(line) !== slope(currentRoof)) { + const height = Math.tan(currentDegree * (Math.PI / 180)) * line.attributes.planeSize + line.attributes.actualSize = Math.round(Math.sqrt(Math.pow(line.attributes.planeSize, 2) + Math.pow(height, 2))) + } else { + line.attributes.actualSize = line.attributes.planeSize + } + }) + roof.separatePolygon.push(roofPolygon) + canvas.add(roofPolygon) + canvas.renderAll() + } + }) + + duplicatedEaves.forEach((duplicatedEave) => { + const currentRoof = duplicatedEave[0] + let points = [] + duplicatedEave.forEach((eave) => { + points.push({ x: eave.x1, y: eave.y1 }, { x: eave.x2, y: eave.y2 }) + const currentRidges = ridges.filter((ridge) => ridge.attributes.currentRoof.includes(eave.id)) + currentRidges.forEach((ridge) => { + points.push({ x: ridge.x1, y: ridge.y1 }, { x: ridge.x2, y: ridge.y2 }) + }) + }) + console.log('points', points) + points = points.filter((point, index, self) => index === self.findIndex((p) => p.x === point.x && p.y === point.y)) + //point 정렬 (가장 좌측, 최상단의 점을 기준으로 삼는다.) + + const startPoint = points + .filter((point) => point.x === Math.min(...points.map((point) => point.x))) + .reduce((prev, current) => { + return prev.y < current.y ? prev : current + }) + + const sortedPoints = [] + sortedPoints.push(startPoint) + + points.forEach((p, index) => { + if (index === 0) { + //시작점 다음 점 찾기, y좌표가 startPoint.y 보다 큰 점 중 x좌표가 가까운 점 + const underStartPoint = points.filter((point) => point.y > startPoint.y) + const nextPoint = underStartPoint + .filter((point) => point.x === startPoint.x) + .reduce((prev, current) => { + if (prev === undefined) { + return current + } + return Math.abs(prev.y - startPoint.y) < Math.abs(current.y - startPoint.y) ? prev : current + }, undefined) + if (nextPoint) { + sortedPoints.push(nextPoint) + } else { + const nextPoint = underStartPoint.reduce((prev, current) => { + const prevHypos = Math.sqrt(Math.abs(Math.pow(prev.x - startPoint.x, 2)) + Math.abs(Math.pow(prev.y - startPoint.y, 2))) + const currentHypos = Math.sqrt(Math.abs(Math.pow(current.x - startPoint.x, 2)) + Math.abs(Math.pow(current.y - startPoint.y, 2))) + return prevHypos < currentHypos ? prev : current + }, undefined) + sortedPoints.push(nextPoint) + } + } else { + const lastPoint = sortedPoints[sortedPoints.length - 1] + console.log('lastPoint', lastPoint) + const prevPoint = sortedPoints[sortedPoints.length - 2] + const otherPoints = points.filter((point) => sortedPoints.includes(point) === false) + const nextPoint = otherPoints.reduce((prev, current) => { + if (prev === undefined) { + const height = Math.abs(Math.sqrt(Math.abs(Math.pow(prevPoint.x - lastPoint.x, 2)) + Math.abs(Math.pow(prevPoint.y - lastPoint.y, 2)))) + const adjacent = Math.abs(Math.sqrt(Math.abs(Math.pow(current.x - lastPoint.x, 2)) + Math.abs(Math.pow(current.y - lastPoint.y, 2)))) + const hypotenuse = Math.abs(Math.sqrt(Math.abs(Math.pow(current.x - prevPoint.x, 2)) + Math.abs(Math.pow(current.y - prevPoint.y, 2)))) + + const angle = Math.round( + Math.acos((Math.pow(adjacent, 2) + Math.pow(height, 2) - Math.pow(hypotenuse, 2)) / (2 * adjacent * height)) * (180 / Math.PI), + ) + if (angle === 90) { + return current + } + } else { + return prev + } + }, undefined) + if (nextPoint) { + sortedPoints.push(nextPoint) + } else { + const nextPoint = otherPoints.reduce((prev, current) => { + if (prev !== undefined) { + const height = Math.abs( + Math.sqrt(Math.abs(Math.pow(prevPoint.x - lastPoint.x, 2)) + Math.abs(Math.pow(prevPoint.y - lastPoint.y, 2))), + ) + const adjacentC = Math.abs(Math.sqrt(Math.abs(Math.pow(current.x - lastPoint.x, 2)) + Math.abs(Math.pow(current.y - lastPoint.y, 2)))) + const hypotenuseC = Math.abs( + Math.sqrt(Math.abs(Math.pow(current.x - prevPoint.x, 2)) + Math.abs(Math.pow(current.y - prevPoint.y, 2))), + ) + const angleC = Math.round( + Math.acos((Math.pow(adjacentC, 2) + Math.pow(height, 2) - Math.pow(hypotenuseC, 2)) / (2 * adjacentC * height)) * (180 / Math.PI), + ) + const adjacentP = Math.abs(Math.sqrt(Math.abs(Math.pow(prev.x - lastPoint.x, 2)) + Math.abs(Math.pow(prev.y - lastPoint.y, 2)))) + const hypotenuseP = Math.abs(Math.sqrt(Math.abs(Math.pow(prev.x - prevPoint.x, 2)) + Math.abs(Math.pow(prev.y - prevPoint.y, 2)))) + const angleP = Math.round( + Math.acos((Math.pow(adjacentP, 2) + Math.pow(height, 2) - Math.pow(hypotenuseP, 2)) / (2 * adjacentP * height)) * (180 / Math.PI), + ) + if (Math.abs(90 - angleC) < Math.abs(90 - angleP)) { + return current + } else { + return prev + } + } else { + return current + } + }, undefined) + if (nextPoint) { + sortedPoints.push(nextPoint) + } + } + } + }) + + if (sortedPoints.length > 0) { + const roofPolygon = new QPolygon(sortedPoints, { + fill: 'transparent', + stroke: '#000000', + strokeWidth: 1, + selectable: false, + fontSize: roof.fontSize, + name: 'roofPolygon', + attributes: { + roofId: roof.id, + currentRoof: currentRoof.id, + pitch: currentRoof.attributes.pitch, + degree: currentRoof.attributes.degree, + direction: currentRoof.direction, + }, + }) + const currentDegree = currentRoof.attributes.pitch > 0 ? getDegreeByChon(currentRoof.attributes.pitch) : currentRoof.attributes.degree + //지붕 각도에 따른 실측치 조정 + roofPolygon.lines.forEach((line) => { + line.attributes.planeSize = Math.round(Math.sqrt(Math.pow(line.x2 - line.x1, 2) + Math.pow(line.y2 - line.y1, 2)) * 10) + const slope = (line) => (line.x2 - line.x1 === 0 ? Infinity : (line.y2 - line.y1) / (line.x2 - line.x1)) + + if (currentDegree > 0 && slope(line) !== slope(currentRoof)) { + const height = Math.tan(currentDegree * (Math.PI / 180)) * line.attributes.planeSize + line.attributes.actualSize = Math.round(Math.sqrt(Math.pow(line.attributes.planeSize, 2) + Math.pow(height, 2))) + } else { + line.attributes.actualSize = line.attributes.planeSize + } + }) + roof.separatePolygon.push(roofPolygon) + canvas.add(roofPolygon) + canvas.renderAll() + } + }) + console.log('roof.separatePolygon : ', roof.separatePolygon) + + ridges.forEach((ridge) => ridge.bringToFront()) + console.log('ridges : ', ridges) } //형 올림내림 마우스 클릭 이벤트 @@ -295,18 +768,18 @@ export function useMovementSetting(id) { if (Math.sign(target.x1 - target.x2) !== 0) { if (targetTop > currentY) { FLOW_LINE_REF.UP_RIGHT_RADIO_REF.current.checked = true - FLOW_LINE_REF.FILLED_INPUT_REF.current.value = Math.floor((Number(Math.round(targetTop - currentY)) / 10000).toFixed(5) * 100000) + FLOW_LINE_REF.FILLED_INPUT_REF.current.value = Math.floor((Number(Math.abs(Math.round(targetTop - currentY))) / 10000).toFixed(5) * 100000) } else { FLOW_LINE_REF.DOWN_LEFT_RADIO_REF.current.checked = true - FLOW_LINE_REF.FILLED_INPUT_REF.current.value = Math.floor((Number(Math.round(targetTop - currentY)) / 10000).toFixed(5) * 100000) + FLOW_LINE_REF.FILLED_INPUT_REF.current.value = Math.floor((Number(Math.abs(Math.round(targetTop - currentY))) / 10000).toFixed(5) * 100000) } } else { - if (targetLeft > currentX) { + if (targetLeft < currentX) { FLOW_LINE_REF.UP_RIGHT_RADIO_REF.current.checked = true - FLOW_LINE_REF.FILLED_INPUT_REF.current.value = Math.floor((Number(Math.round(currentX - targetLeft)) / 10000).toFixed(5) * 100000) + FLOW_LINE_REF.FILLED_INPUT_REF.current.value = Math.floor((Number(Math.abs(Math.round(currentX - targetLeft))) / 10000).toFixed(5) * 100000) } else { FLOW_LINE_REF.DOWN_LEFT_RADIO_REF.current.checked = true - FLOW_LINE_REF.FILLED_INPUT_REF.current.value = Math.floor((Number(Math.round(currentX - targetLeft)) / 10000).toFixed(5) * 100000) + FLOW_LINE_REF.FILLED_INPUT_REF.current.value = Math.floor((Number(Math.abs(Math.round(currentX - targetLeft))) / 10000).toFixed(5) * 100000) } } diff --git a/src/hooks/roofcover/useRoofAllocationSetting.js b/src/hooks/roofcover/useRoofAllocationSetting.js index ec2d2661..0671f502 100644 --- a/src/hooks/roofcover/useRoofAllocationSetting.js +++ b/src/hooks/roofcover/useRoofAllocationSetting.js @@ -258,8 +258,6 @@ export function useRoofAllocationSetting(id) { return roof }) - newRoofList[selectedIndex].selected = true - setCurrentRoofList(newRoofList) } diff --git a/src/hooks/useContextMenu.js b/src/hooks/useContextMenu.js index 02896cb3..837c489a 100644 --- a/src/hooks/useContextMenu.js +++ b/src/hooks/useContextMenu.js @@ -1,7 +1,7 @@ import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil' import { canvasState, currentMenuState, currentObjectState } from '@/store/canvasAtom' import { useEffect, useState } from 'react' -import { MENU } from '@/common/common' +import { MENU, POLYGON_TYPE } from '@/common/common' import AuxiliarySize from '@/components/floor-plan/modal/auxiliary/AuxiliarySize' import { usePopup } from '@/hooks/usePopup' import { v4 as uuidv4 } from 'uuid' @@ -40,6 +40,7 @@ import { useCanvasSetting } from './option/useCanvasSetting' import { useGrid } from './common/useGrid' import { useAdsorptionPoint } from './useAdsorptionPoint' import { useRoofFn } from '@/hooks/common/useRoofFn' +import { MODULE_ALIGN_TYPE, useModule } from './module/useModule' export function useContextMenu() { const canvas = useRecoilValue(canvasState) @@ -66,6 +67,7 @@ export function useContextMenu() { const commonTextFont = useRecoilValue(fontSelector('commonText')) const { settingsData, setSettingsDataSave } = useCanvasSetting() const { swalFire } = useSwal() + const { alignModule } = useModule() const { removeRoofMaterial, removeAllRoofMaterial } = useRoofFn() const currentMenuSetting = () => { @@ -691,36 +693,38 @@ export function useContextMenu() { ]) break case 'moduleSetupSurface': - case 'dimensionLineText': + case 'roof': setContextMenu([ [ { id: 'moduleVerticalCenterAlign', name: getMessage('contextmenu.module.vertical.align'), + fn: () => alignModule(MODULE_ALIGN_TYPE.VERTICAL), }, { id: 'moduleHorizonCenterAlign', name: getMessage('contextmenu.module.horizon.align'), - }, - { - id: 'moduleLeftAlign', - name: getMessage('contextmenu.module.left.align'), - }, - { - id: 'moduleRightAlign', - name: getMessage('contextmenu.module.right.align'), - }, - { - id: 'moduleUpAlign', - name: getMessage('contextmenu.module.up.align'), - }, - { - id: 'moduleDownAlign', - name: getMessage('contextmenu.module.down.align'), + fn: () => alignModule(MODULE_ALIGN_TYPE.HORIZONTAL), }, { id: 'moduleRemove', name: getMessage('contextmenu.module.remove'), + fn: () => { + const moduleSetupSurface = canvas.getObjects().filter((obj) => canvas.getActiveObjects()[0].id === obj.id)[0] + const modules = canvas.getObjects().filter((obj) => obj.surfaceId === moduleSetupSurface.id && obj.name === POLYGON_TYPE.MODULE) + canvas.remove(...modules) + canvas.renderAll() + }, + }, + { + id: 'moduleMove', + name: getMessage('contextmenu.module.move'), + component: , + }, + { + id: 'moduleCopy', + name: getMessage('contextmenu.module.copy'), + component: , }, { id: 'moduleCircuitNumberEdit', diff --git a/src/hooks/useMode.js b/src/hooks/useMode.js index 99819ad6..f463447b 100644 --- a/src/hooks/useMode.js +++ b/src/hooks/useMode.js @@ -1719,6 +1719,25 @@ export function useMode() { } const drawRoofPolygon = (wall) => { + const startLine = wall.lines + .filter((line) => line.x1 === Math.min(...wall.lines.map((line) => line.x1))) + .reduce((prev, current) => { + return prev.y1 < current.y1 ? prev : current + }) + + const beforeLine = [], + afterLine = [] + let startIndex = wall.lines.findIndex((line) => line === startLine) + wall.lines.forEach((line, index) => { + if (index < startIndex) { + beforeLine.push(line) + } else { + afterLine.push(line) + } + }) + + wall.lines = afterLine.concat(beforeLine) + const polygon = createRoofPolygon(wall.points) const originPolygon = new QPolygon(wall.points, { fontSize: 0 }) originPolygon.setViewLengthText(false) diff --git a/src/locales/ja.json b/src/locales/ja.json index 27794f3e..0fc32b81 100644 --- a/src/locales/ja.json +++ b/src/locales/ja.json @@ -44,6 +44,7 @@ "plan.menu.roof.cover.eaves.kerava.edit": "처마·케라바 변경", "plan.menu.roof.cover.movement.shape.updown": "동선이동·형올림내림(JA)", "modal.movement.flow.line.move": "銅線の移動軒", + "modal.movement.flow.line.move.alert": "이동 할 수 없습니다.(JA)", "modal.movement.flow.line.updown": "型上げ・降り", "modal.movement.flow.line.updown.info": "を選択して幅を指定してください桁の異なる辺。", "modal.movement.flow.line.updown.up": "桁を上げる", @@ -631,7 +632,7 @@ "stuff.detail.header.successCopy": "商品番号がコピーされました。", "stuff.detail.header.failCopy": "存在しないものです。", "stuff.detail.header.objectNo": "商品番号のコピーに失敗しました。", - "stuff.detail.header.specificationConfirmDate": "仕様拡張日", + "stuff.detail.header.specificationConfirmDate": "仕様確認日", "stuff.detail.header.lastEditDatetime": "更新日時", "stuff.detail.header.createDatetime": "登録日", "stuff.detail.required": "必須入力項目", @@ -723,11 +724,15 @@ "stuff.search.period": "期間検索", "stuff.search.schDateTypeU": "更新日", "stuff.search.schDateTypeR": "登録日", + "stuff.search.schTempFlgT": "一時保存物", + "stuff.search.schTempFlg": "含む", + "stuff.search.schTempFlg0": "除外", + "stuff.search.schTempFlg1": "一時的なものだけを見る", "stuff.search.grid.title": "商品リスト", "stuff.search.grid.all": "全体", "stuff.search.grid.selected": "選択", "stuff.search.grid.schSortTypeR": "最近の登録日", - "stuff.search.grid.schSortTypeU": "最近の更新日", + "stuff.search.grid.schSortTypeU": "最近修正日", "stuff.windSelectPopup.title": "風速選択", "stuff.windSelectPopup.table.selected": "選択", "stuff.windSelectPopup.table.windspeed": "風速", @@ -801,6 +806,8 @@ "main.storeName": "販売店名", "main.objectNo": "物件番号", "main.faq": "FAQ", + "main.content.objectList.noData1": "登録された商品情報はありません.", + "main.content.objectList.noData2": "下のボタンをクリックして商品情報を登録してください.", "main.content.objectList": "最近の更新物件一覧", "main.content.notice": "お知らせ", "main.content.download1": "操作マニュアル", @@ -817,6 +824,7 @@ "main.popup.login.btn2": "変更しない", "main.popup.login.validate1": "入力したパスワードが異なります。", "main.popup.login.validate2": "半角10文字以内で入力してください。", + "main.popup.login.validate3": "비밀번호를 입력해주세요.", "main.popup.login.success": "パスワードが変更されました。", "common.canvas.validate.size": "寸法を入力してください.", "surface.shape.validate.size.1to2": "①길이는 ②보다 큰 값을 넣어주세요.", @@ -941,5 +949,6 @@ "can.not.move.module": "모듈을 이동할 수 없습니다.(JA)", "can.not.copy.module": "모듈을 복사할 수 없습니다.(JA)", "can.not.remove.module": "모듈을 삭제할 수 없습니다.(JA)", - "can.not.insert.module": "모듈을 삽입할 수 없습니다.(JA)" + "can.not.insert.module": "모듈을 삽입할 수 없습니다.(JA)", + "can.not.align.module": "모듈을 정렬할 수 없습니다.(JA)" } diff --git a/src/locales/ko.json b/src/locales/ko.json index b7a64b03..6b415e88 100644 --- a/src/locales/ko.json +++ b/src/locales/ko.json @@ -44,6 +44,7 @@ "plan.menu.roof.cover.eaves.kerava.edit": "처마·케라바 변경", "plan.menu.roof.cover.movement.shape.updown": "동선이동·형올림내림", "modal.movement.flow.line.move": "동선 이동", + "modal.movement.flow.line.move.alert": "이동 할 수 없습니다.", "modal.movement.flow.line.updown": "형 올림·내림", "modal.movement.flow.line.updown.info": "자릿수가 다른 변을 선택하고 폭을 지정하십시오.", "modal.movement.flow.line.updown.up": "자릿수를 올리다", @@ -641,7 +642,7 @@ "stuff.detail.header.successCopy": "물건번호가 복사되었습니다.", "stuff.detail.header.failCopy": "물건번호 복사에 실패했습니다.", "stuff.detail.header.objectNo": "물건번호", - "stuff.detail.header.specificationConfirmDate": "사양확장일", + "stuff.detail.header.specificationConfirmDate": "사양확정일", "stuff.detail.header.lastEditDatetime": "갱신일시", "stuff.detail.header.createDatetime": "등록일", "stuff.detail.required": "필수 입력항목", @@ -733,11 +734,15 @@ "stuff.search.period": "기간검색", "stuff.search.schDateTypeU": "갱신일", "stuff.search.schDateTypeR": "등록일", + "stuff.search.schTempFlgT": "임시저장 물건", + "stuff.search.schTempFlg": "포함", + "stuff.search.schTempFlg0": "제외", + "stuff.search.schTempFlg1": "임시물건만 조회", "stuff.search.grid.title": "물건목록", "stuff.search.grid.all": "전체", "stuff.search.grid.selected": "선택", "stuff.search.grid.schSortTypeR": "최근 등록일", - "stuff.search.grid.schSortTypeU": "최근 갱신일", + "stuff.search.grid.schSortTypeU": "최근 수정일", "stuff.windSelectPopup.title": "풍속선택", "stuff.windSelectPopup.table.selected": "선택", "stuff.windSelectPopup.table.windspeed": "풍속", @@ -811,6 +816,8 @@ "main.storeName": "판매점명", "main.objectNo": "물건번호", "main.faq": "FAQ", + "main.content.objectList.noData1": "등록된 물건정보가 없습니다.", + "main.content.objectList.noData2": "아래 버튼을 클릭하여 물건정보를 등록하십시오.", "main.content.objectList": "최근 갱신 물건목록", "main.content.notice": "공지사항", "main.content.download1": "조작메뉴얼", @@ -827,6 +834,7 @@ "main.popup.login.btn2": "변경안함", "main.popup.login.validate1": "입력한 패스워드가 다릅니다.", "main.popup.login.validate2": "반각 10자 이내로 입력해주세요.", + "main.popup.login.validate3": "비밀번호를 입력해주세요.", "main.popup.login.success": "비밀번호가 변경되었습니다.", "common.canvas.validate.size": "사이즈를 입력해 주세요.", "surface.shape.validate.size.1to2": "①길이는 ②보다 큰 값을 넣어주세요.", @@ -951,5 +959,6 @@ "can.not.move.module": "모듈을 이동할 수 없습니다.", "can.not.copy.module": "모듈을 복사할 수 없습니다.", "can.not.remove.module": "모듈을 삭제할 수 없습니다.", - "can.not.insert.module": "모듈을 삽입할 수 없습니다." + "can.not.insert.module": "모듈을 삽입할 수 없습니다.", + "can.not.align.module": "모듈을 정렬할 수 없습니다." } diff --git a/src/store/stuffAtom.js b/src/store/stuffAtom.js index 06593e2b..d4e9eaad 100644 --- a/src/store/stuffAtom.js +++ b/src/store/stuffAtom.js @@ -17,9 +17,10 @@ export const stuffSearchState = atom({ schOtherSelSaleStoreId: '', //1차 이외 판매대리점 선택 startRow: 1, endRow: 100, - schSortType: 'R', //정렬조건 (R:최근등록일 U:최근수정일) + schSortType: 'U', //정렬조건 (R:최근등록일 U:최근수정일) pageNo: 1, pageSize: 100, + schTempFlg: '', //임시저장여부 }, dangerouslyAllowMutability: true, }) diff --git a/src/styles/_layout.scss b/src/styles/_layout.scss index a549ad0e..858ab351 100644 --- a/src/styles/_layout.scss +++ b/src/styles/_layout.scss @@ -207,6 +207,7 @@ header{ .select-box{ min-width: 165px; margin-right: 8px; + height: 30px; >div{ width: 100%; } diff --git a/src/styles/_main.scss b/src/styles/_main.scss index 0733f285..5da1350d 100644 --- a/src/styles/_main.scss +++ b/src/styles/_main.scss @@ -155,6 +155,7 @@ .product-item-content{ margin-top: 30px; overflow: hidden; + height: 100%; .recently-list{ .recently-item{ border: 1px solid #F2F2F2; @@ -208,6 +209,25 @@ } } } + .recently-no-data{ + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 100%; + h3{ + font-size: 16px; + color: #101010; + font-weight: 600; + margin-bottom: 5px; + } + p{ + font-size: 12px; + color: #666; + font-weight: 400; + margin-bottom: 10px; + } + } .notice-box{ height: 100%; overflow-y: auto; diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js index 63c04e89..74bc1fc2 100644 --- a/src/util/qpolygon-utils.js +++ b/src/util/qpolygon-utils.js @@ -317,7 +317,7 @@ export const drawGabledRoof = (roofId, canvas) => { // 처마라인의 기본속성 입력 const eaves = [] roofLines.forEach((currentRoof, index) => { - if (currentRoof.attributes !== undefined && currentRoof.attributes.type === LINE_TYPE.WALLLINE.EAVES) { + if (currentRoof.attributes?.type === LINE_TYPE.WALLLINE.EAVES) { eaves.push({ index: index, roof: currentRoof, length: currentRoof.attributes.planeSize }) } }) @@ -325,291 +325,147 @@ export const drawGabledRoof = (roofId, canvas) => { const ridges = [] eaves.sort((a, b) => a.length - b.length) - eaves.forEach((eave) => { - if (ridges.length < ridgeCount) { - const index = eave.index, - currentRoof = eave.roof - const currentWall = wallLines[index] - - //현재 지붕의 중심 좌표 - const midX = Math.round(((currentRoof.x1 + currentRoof.x2) / 2) * 10) / 10 - const midY = Math.round(((currentRoof.y1 + currentRoof.y2) / 2) * 10) / 10 - const deltaX = currentWall.x2 - currentWall.x1 - const deltaY = currentWall.y2 - currentWall.y1 - const length = Math.sqrt(deltaX * deltaX + deltaY * deltaY) - // currentWall 과 직각인 기울기 계산 - const perpendicularDeltaX = deltaY / length - const perpendicularDeltaY = -deltaX / length - // midX와 midY를 기준으로 직각 기울기를 사용하여 midWallX와 midWallY 계산 - const midWallX = midX + perpendicularDeltaX * length - const midWallY = midY + perpendicularDeltaY * length - - const signX = Math.sign(midX - midWallX) - const signY = Math.sign(midY - midWallY) - const checkX = Math.round((midX - signX * checkLength) * 10) / 10 - const checkY = Math.round((midY - signY * checkLength) * 10) / 10 - - const intersectLines = [] - // 현재 지붕의 맞은편 지붕을 찾는다. - roofLines - .filter((line) => line !== currentRoof) - .forEach((line) => { - const intersect = edgesIntersection( - { vertex1: { x: midX, y: midY }, vertex2: { x: checkX, y: checkY } }, - { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }, - ) - if (intersect && !intersect.isIntersectionOutside) { - intersectLines.push({ x: intersect.x, y: intersect.y, line: line }) - } - }) - - // 가장 가까운 지붕을 찾는다. - const intersect = intersectLines.reduce((prev, current) => { - if (prev !== undefined) { - const prevDistance = Math.sqrt(Math.pow(prev.x - midX, 2) + Math.pow(prev.y - midY, 2)) - const currentDistance = Math.sqrt(Math.pow(current.x - midX, 2) + Math.pow(current.y - midY, 2)) - return prevDistance >= currentDistance ? current : prev - } else { - return current - } - }, undefined) - - const linesCenterX = Math.round(((midX + intersect.x) / 2) * 10) / 10 - const linesCenterY = Math.round(((midY + intersect.y) / 2) * 10) / 10 - - let addLength = currentRoof.attributes.planeSize / 2 / 10 - - let centerLineX1 = Math.sign(currentRoof.x1 - currentRoof.x2) * addLength + linesCenterX - let centerLineY1 = Math.sign(currentRoof.y1 - currentRoof.y2) * addLength + linesCenterY - let centerLineX2 = Math.sign(currentRoof.x2 - currentRoof.x1) * addLength + linesCenterX - let centerLineY2 = Math.sign(currentRoof.y2 - currentRoof.y1) * addLength + linesCenterY - - const point1Intersect = [] - const point2Intersect = [] - roofLines.forEach((line) => { - const point1 = edgesIntersection( - { vertex1: { x: linesCenterX, y: linesCenterY }, vertex2: { x: centerLineX1, y: centerLineY1 } }, - { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }, - ) - if (point1 && !point1.isIntersectionOutside) { - point1Intersect.push({ x: point1.x, y: point1.y }) - } - const point2 = edgesIntersection( - { vertex1: { x: linesCenterX, y: linesCenterY }, vertex2: { x: centerLineX2, y: centerLineY2 } }, - { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }, - ) - if (point2 && !point2.isIntersectionOutside) { - point2Intersect.push({ x: point2.x, y: point2.y }) - } - }) - point1Intersect.reduce((prev, current) => { - if (prev !== undefined) { - const prevDistance = Math.sqrt(Math.pow(prev.x - linesCenterX, 2) + Math.pow(prev.y - linesCenterY, 2)) - const currentDistance = Math.sqrt(Math.pow(current.x - linesCenterX, 2) + Math.pow(current.y - linesCenterY, 2)) - return prevDistance >= currentDistance ? current : prev - } else { - return current - } - }, undefined) - point2Intersect.reduce((prev, current) => { - if (prev !== undefined) { - const prevDistance = Math.sqrt(Math.pow(prev.x - linesCenterX, 2) + Math.pow(prev.y - linesCenterY, 2)) - const currentDistance = Math.sqrt(Math.pow(current.x - linesCenterX, 2) + Math.pow(current.y - linesCenterY, 2)) - return prevDistance >= currentDistance ? current : prev - } else { - return current - } - }, undefined) - - if (point1Intersect.length > 0) { - centerLineX1 = point1Intersect[0].x - centerLineY1 = point1Intersect[0].y - } - if (point2Intersect.length > 0) { - centerLineX2 = point2Intersect[0].x - centerLineY2 = point2Intersect[0].y - } - - const ridge = new QLine([centerLineX1, centerLineY1, centerLineX2, centerLineY2], { - fontSize: roof.fontSize, - stroke: '#1083E3', - strokeWidth: 2, - name: LINE_TYPE.SUBLINE.RIDGE, - attributes: { roofId: roof.id, currentRoof: currentRoof.id }, - }) - canvas.add(ridge) - canvas.renderAll() - ridges.push(ridge) - roof.innerLines.push(ridge) - } - }) - eaves.forEach((eave) => { + eaves.forEach((eave, i) => { const index = eave.index, currentRoof = eave.roof const currentWall = wallLines[index] - const prevRoof = index === 0 ? roofLines[wallLines.length - 1] : roofLines[index - 1] - const nextRoof = index === roofLines.length - 1 ? roofLines[0] : index === roofLines.length ? roofLines[1] : roofLines[index + 1] - const midX = Math.round(((currentRoof.x1 + currentRoof.x2) / 2) * 10) / 10 - const midY = Math.round(((currentRoof.y1 + currentRoof.y2) / 2) * 10) / 10 - const deltaX = currentWall.x2 - currentWall.x1 - const deltaY = currentWall.y2 - currentWall.y1 - const length = Math.sqrt(deltaX * deltaX + deltaY * deltaY) - // currentWall 과 직각인 기울기 계산 - const perpendicularDeltaX = deltaY / length - const perpendicularDeltaY = -deltaX / length - // midX와 midY를 기준으로 직각 기울기를 사용하여 midWallX와 midWallY 계산 - const midWallX = midX + perpendicularDeltaX * length - const midWallY = midY + perpendicularDeltaY * length - const signX = Math.sign(midX - midWallX) - const signY = Math.sign(midY - midWallY) + const oppositeLine = roofLines + .filter((line) => line !== currentRoof) // 현재 벽라인을 제외한 나머지 벽라인 + .filter((line) => { + if (currentRoof.x1 === currentRoof.x2) { + const vector = Math.sign(currentRoof.y1 - currentRoof.y2) + const vector2 = Math.sign(currentRoof.x1 - currentWall.x1) + return line.x1 === line.x2 && Math.sign(line.y1 - line.y2) === -vector && Math.sign(currentRoof.x1 - line.x1) === vector2 + } + if (currentRoof.y1 === currentRoof.y2) { + const vector = Math.sign(currentRoof.x1 - currentRoof.x2) + const vector2 = Math.sign(currentRoof.y1 - currentWall.y1) + return line.y1 === line.y2 && Math.sign(line.x1 - line.x2) === -vector && Math.sign(currentRoof.y1 - line.y1) === vector2 + } + }) // 현재 벽라인과 직교하는 벽라인 + console.log('oppositeLine', oppositeLine) + + // 현재 벽라인과 직교하는 벽라인 사이에 마루를 그린다. + oppositeLine.forEach((line) => { + let points // 마루의 시작점과 끝점 + if (currentRoof.x1 === currentRoof.x2) { + const currentRoofYRange = [Math.min(currentRoof.y1, currentRoof.y2), Math.max(currentRoof.y1, currentRoof.y2)] + const lineYRange = [Math.min(line.y1, line.y2), Math.max(line.y1, line.y2)] + const overlapYRange = [Math.max(currentRoofYRange[0], lineYRange[0]), Math.min(currentRoofYRange[1], lineYRange[1])] + if (overlapYRange[1] - overlapYRange[0] > 0) { + const centerX = Math.round(((currentRoof.x1 + line.x1) / 2) * 10) / 10 + points = [centerX, overlapYRange[0], centerX, overlapYRange[1]] + } + } + if (currentRoof.y1 === currentRoof.y2) { + const currentRoofXRange = [Math.min(currentRoof.x1, currentRoof.x2), Math.max(currentRoof.x1, currentRoof.x2)] + const lineXRange = [Math.min(line.x1, line.x2), Math.max(line.x1, line.x2)] + const overlapXRange = [Math.max(currentRoofXRange[0], lineXRange[0]), Math.min(currentRoofXRange[1], lineXRange[1])] + if (overlapXRange[1] - overlapXRange[0] > 0) { + const centerY = Math.round(((currentRoof.y1 + line.y1) / 2) * 10) / 10 + points = [overlapXRange[0], centerY, overlapXRange[1], centerY] + } + } + // 마루를 그린다. + if (points) { + const ridge = new QLine(points, { + fontSize: roof.fontSize, + stroke: '#1083E3', + strokeWidth: 1, + name: LINE_TYPE.SUBLINE.RIDGE, + attributes: { roofId: roof.id, currentRoof: [currentRoof.id] }, + visible: false, + }) + const duplicateRidge = ridges.find( + (ridge) => ridge.x1 === points[0] && ridge.y1 === points[1] && ridge.x2 === points[2] && ridge.y2 === points[3], + ) + // 중복된 마루는 제외한다. + if (duplicateRidge) { + duplicateRidge.attributes.currentRoof.push(currentRoof.id) + } else { + canvas.add(ridge) + canvas.renderAll() + ridges.push(ridge) + } + } + }) + }) + // 처마마다 지붕 polygon 을 그린다. + eaves.forEach((eave, i) => { + const index = eave.index, + currentRoof = eave.roof + const currentWall = wallLines[index] + const currentRidges = ridges.filter((ridge) => ridge.attributes.currentRoof.includes(eave.roof.id)) let points = [] - const intersectRidge = [] - // 현재 roof 가 wall 보다 작을때 이전, 다음 지붕의 offset 만큼 포인트를 조정한다. - if (currentRoof.attributes.planeSize > currentWall.attributes.planeSize) { - points.push({ x: currentRoof.x1, y: currentRoof.y1 }, { x: currentRoof.x2, y: currentRoof.y2 }) - } else if (currentRoof.attributes.planeSize === currentWall.attributes.planeSize) { - const deltaX = currentRoof.x2 - currentRoof.x1 - const deltaY = currentRoof.y2 - currentRoof.y1 - let x1 = currentRoof.x1, - y1 = currentRoof.y1, - x2 = currentRoof.x2, - y2 = currentRoof.y2 + const signX = Math.sign(currentRoof.x1 - currentRoof.x2) + let currentX1 = currentRoof.x1, + currentY1 = currentRoof.y1, + currentX2 = currentRoof.x2, + currentY2 = currentRoof.y2 + let changeX1 = [Math.min(currentRoof.x1, currentRoof.x2), Math.min(currentRoof.x1, currentRoof.x2)], + changeY1 = [Math.min(currentRoof.y1, currentRoof.y2), Math.min(currentRoof.y1, currentRoof.y2)], + changeX2 = [Math.max(currentRoof.x2, currentRoof.x1), Math.max(currentRoof.x2, currentRoof.x1)], + changeY2 = [Math.max(currentRoof.y2, currentRoof.y1), Math.max(currentRoof.y2, currentRoof.y1)] - if (deltaX !== 0) { - const minX = Math.min(currentRoof.x1, currentRoof.x2, currentWall.x1, currentWall.x2) - const maxX = Math.max(currentRoof.x1, currentRoof.x2, currentWall.x1, currentWall.x2) - if (x1 > x2) { - x1 = maxX - x2 = minX - } else { - x1 = minX - x2 = maxX - } - } - if (deltaY !== 0) { - const minY = Math.min(currentRoof.y1, currentRoof.y2, currentWall.y1, currentWall.y2) - const maxY = Math.max(currentRoof.y1, currentRoof.y2, currentWall.y1, currentWall.y2) - if (y1 > y2) { - y1 = maxY - y2 = minY - } else { - y1 = minY - y2 = maxY - } - } - points.push({ x: x1, y: y1 }, { x: x2, y: y2 }) + if (signX === 0) { + currentY1 = Math.min(currentRoof.y1, currentRoof.y2, currentWall.y1, currentWall.y2) + changeY1[1] = currentY1 + currentY2 = Math.max(currentRoof.y1, currentRoof.y2, currentWall.y1, currentWall.y2) + changeY2[1] = currentY2 } else { - const deltaX = currentRoof.x2 - currentRoof.x1 - const deltaY = currentRoof.y2 - currentRoof.y1 - points.push( - { x: currentRoof.x1 - Math.sign(deltaX) * prevRoof.attributes.offset, y: currentRoof.y1 - Math.sign(deltaY) * prevRoof.attributes.offset }, - { x: currentRoof.x2 + Math.sign(deltaX) * nextRoof.attributes.offset, y: currentRoof.y2 + Math.sign(deltaY) * nextRoof.attributes.offset }, - ) + currentX1 = Math.min(currentRoof.x1, currentRoof.x2, currentWall.x1, currentWall.x2) + changeX1[1] = currentX1 + currentX2 = Math.max(currentRoof.x1, currentRoof.x2, currentWall.x1, currentWall.x2) + changeX2[1] = currentX2 } - ridges.forEach((ridge) => { - const ridgeMidX = (ridge.x1 + ridge.x2) / 2 - const ridgeMidY = (ridge.y1 + ridge.y2) / 2 - if (midX === ridgeMidX || midY === ridgeMidY) { - const intersection = edgesIntersection( - { vertex1: { x: midX, y: midY }, vertex2: { x: ridgeMidX, y: ridgeMidY } }, - { vertex1: { x: ridge.x1, y: ridge.y1 }, vertex2: { x: ridge.x2, y: ridge.y2 } }, - ) - if (intersection && !intersection.isIntersectionOutside) { - intersectRidge.push({ line: ridge }) - } - } - if (Math.sign(midX - ridgeMidX) === signX || Math.sign(midY - ridgeMidY) === signY) { - const prevIntersect = edgesIntersection( - { vertex1: { x: prevRoof.x1, y: prevRoof.y1 }, vertex2: { x: prevRoof.x2, y: prevRoof.y2 } }, - { vertex1: { x: ridge.x1, y: ridge.y1 }, vertex2: { x: ridge.x2, y: ridge.y2 } }, - ) - const nextIntersect = edgesIntersection( - { vertex1: { x: nextRoof.x1, y: nextRoof.y1 }, vertex2: { x: nextRoof.x2, y: nextRoof.y2 } }, - { vertex1: { x: ridge.x1, y: ridge.y1 }, vertex2: { x: ridge.x2, y: ridge.y2 } }, - ) - if (prevIntersect && !prevIntersect.isIntersectionOutside) { - intersectRidge.push({ line: ridge }) - } - if (nextIntersect && !nextIntersect.isIntersectionOutside) { - intersectRidge.push({ line: ridge }) - } - } - }) + points.push({ x: currentX1, y: currentY1 }, { x: currentX2, y: currentY2 }) - intersectRidge.forEach((intersect) => { - const line = intersect.line - if (currentRoof.attributes.planeSize > currentWall.attributes.planeSize) { - points.push({ x: line.x1, y: line.y1 }, { x: line.x2, y: line.y2 }) - } else if (currentRoof.attributes.planeSize === currentWall.attributes.planeSize) { - const deltaX = currentRoof.x2 - currentRoof.x1 - const deltaY = currentRoof.y2 - currentRoof.y1 - let x1 = line.x1, - y1 = line.y1, - x2 = line.x2, - y2 = line.y2 - if (deltaX !== 0) { - const minX = Math.min(currentWall.x1, currentWall.x2, currentRoof.x1, currentRoof.x2, line.x1, line.x2) - const maxX = Math.max(currentWall.x1, currentWall.x2, currentRoof.x1, currentRoof.x2, line.x1, line.x2) - if (x1 > x2) { - x1 = maxX - x2 = minX - } else { - x1 = minX - x2 = maxX - } - } - if (deltaY !== 0) { - const minY = Math.min(currentWall.y1, currentWall.y2, currentRoof.y1, currentRoof.y2, line.y1, line.y2) - const maxY = Math.max(currentWall.y1, currentWall.y2, currentRoof.y1, currentRoof.y2, line.y1, line.y2) - if (y1 > y2) { - y1 = maxY - y2 = minY - } else { - y1 = minY - y2 = maxY - } - } - points.push({ x: x1, y: y1 }, { x: x2, y: y2 }) + currentRidges.forEach((ridge) => { + let ridgeX1 = ridge.x1, + ridgeY1 = ridge.y1, + ridgeX2 = ridge.x2, + ridgeY2 = ridge.y2 + if (signX === 0) { + ridgeY1 = Math.min(ridge.y1, ridge.y2) + ridgeY2 = Math.max(ridge.y1, ridge.y2) } else { - let lineX1 = line.x1, - lineY1 = line.y1, - lineX2 = line.x2, - lineY2 = line.y2 - const prevCheck1 = Math.sqrt(Math.pow(Math.round(lineX1 - currentRoof.x1), 2) + Math.pow(Math.round(lineY1 - currentRoof.y1), 2)) - const prevCheck2 = Math.sqrt(Math.pow(Math.round(lineX2 - currentRoof.x1), 2) + Math.pow(Math.round(lineY2 - currentRoof.y1), 2)) - const deltaX = currentRoof.x2 - currentRoof.x1 - const deltaY = currentRoof.y2 - currentRoof.y1 - if (prevCheck1 < prevCheck2) { - lineX1 = lineX1 - Math.sign(deltaX) * prevRoof.attributes.offset - lineY1 = lineY1 - Math.sign(deltaY) * prevRoof.attributes.offset - lineX2 = lineX2 + Math.sign(deltaX) * nextRoof.attributes.offset - lineY2 = lineY2 + Math.sign(deltaY) * nextRoof.attributes.offset - } else { - lineX1 = lineX1 + Math.sign(deltaX) * prevRoof.attributes.offset - lineY1 = lineY1 + Math.sign(deltaY) * prevRoof.attributes.offset - lineX2 = lineX2 - Math.sign(deltaX) * nextRoof.attributes.offset - lineY2 = lineY2 - Math.sign(deltaY) * nextRoof.attributes.offset - } - points.push({ x: lineX1, y: lineY1 }, { x: lineX2, y: lineY2 }) + ridgeX1 = Math.min(ridge.x1, ridge.x2) + ridgeX2 = Math.max(ridge.x1, ridge.x2) } - - // canvas.remove(ridge) - // canvas.renderAll() + points.push({ x: ridgeX1, y: ridgeY1 }, { x: ridgeX2, y: ridgeY2 }) }) - points = points.filter((point, index, self) => index === self.findIndex((p) => p.x === point.x && p.y === point.y)) + points.forEach((point) => { + if (point.x === changeX1[0] && changeX1[0] !== changeX1[1]) { + point.x = changeX1[1] + } + if (point.x === changeX2[0] && changeX2[0] !== changeX2[1]) { + point.x = changeX2[1] + } + if (point.y === changeY1[0] && changeY1[0] !== changeY1[1]) { + point.y = changeY1[1] + } + if (point.y === changeY2[0] && changeY2[0] !== changeY2[1]) { + point.y = changeY2[1] + } + }) + //중복된 point 제거 + points = points.filter((point, index, self) => index === self.findIndex((p) => p.x === point.x && p.y === point.y)) + //point 정렬 (가장 좌측, 최상단의 점을 기준으로 삼는다.) const startPoint = points .filter((point) => point.x === Math.min(...points.map((point) => point.x))) .reduce((prev, current) => { return prev.y < current.y ? prev : current }) - const sortedPoints = [startPoint] + + const sortedPoints = [] + sortedPoints.push(startPoint) + points.forEach((p, index) => { - const lastPoint = sortedPoints[sortedPoints.length - 1] - console.log('lastPoint', lastPoint) if (index === 0) { + //시작점 다음 점 찾기, y좌표가 startPoint.y 보다 큰 점 중 x좌표가 가까운 점 const underStartPoint = points.filter((point) => point.y > startPoint.y) const nextPoint = underStartPoint .filter((point) => point.x === startPoint.x) @@ -630,6 +486,8 @@ export const drawGabledRoof = (roofId, canvas) => { sortedPoints.push(nextPoint) } } else { + const lastPoint = sortedPoints[sortedPoints.length - 1] + console.log('lastPoint', lastPoint) const prevPoint = sortedPoints[sortedPoints.length - 2] const otherPoints = points.filter((point) => sortedPoints.includes(point) === false) const nextPoint = otherPoints.reduce((prev, current) => { @@ -656,18 +514,14 @@ export const drawGabledRoof = (roofId, canvas) => { const height = Math.abs(Math.sqrt(Math.abs(Math.pow(prevPoint.x - lastPoint.x, 2)) + Math.abs(Math.pow(prevPoint.y - lastPoint.y, 2)))) const adjacentC = Math.abs(Math.sqrt(Math.abs(Math.pow(current.x - lastPoint.x, 2)) + Math.abs(Math.pow(current.y - lastPoint.y, 2)))) const hypotenuseC = Math.abs(Math.sqrt(Math.abs(Math.pow(current.x - prevPoint.x, 2)) + Math.abs(Math.pow(current.y - prevPoint.y, 2)))) - const angleC = Math.round( Math.acos((Math.pow(adjacentC, 2) + Math.pow(height, 2) - Math.pow(hypotenuseC, 2)) / (2 * adjacentC * height)) * (180 / Math.PI), ) - const adjacentP = Math.abs(Math.sqrt(Math.abs(Math.pow(prev.x - lastPoint.x, 2)) + Math.abs(Math.pow(prev.y - lastPoint.y, 2)))) const hypotenuseP = Math.abs(Math.sqrt(Math.abs(Math.pow(prev.x - prevPoint.x, 2)) + Math.abs(Math.pow(prev.y - prevPoint.y, 2)))) - const angleP = Math.round( Math.acos((Math.pow(adjacentP, 2) + Math.pow(height, 2) - Math.pow(hypotenuseP, 2)) / (2 * adjacentP * height)) * (180 / Math.PI), ) - if (Math.abs(90 - angleC) < Math.abs(90 - angleP)) { return current } else { @@ -683,10 +537,11 @@ export const drawGabledRoof = (roofId, canvas) => { } } }) + if (sortedPoints.length > 0) { const roofPolygon = new QPolygon(sortedPoints, { fill: 'transparent', - stroke: 'blue', + stroke: '#1083E3', strokeWidth: 2, selectable: false, fontSize: roof.fontSize, @@ -717,6 +572,10 @@ export const drawGabledRoof = (roofId, canvas) => { canvas.renderAll() } }) + + if (ridges.length > 0) { + ridges.forEach((ridge) => roof.innerLines.push(ridge)) + } //기준선 제거 // ridges.forEach((ridge) => canvas.remove(ridge)) } @@ -1049,7 +908,7 @@ const isInnerLine = (prevLine, currentLine, nextLine, line) => { * @param line2 * @returns {boolean} */ -const segmentsOverlap = (line1, line2) => { +export const segmentsOverlap = (line1, line2) => { if (line1.y1 === line1.y2 && line2.y1 === line2.y2 && line1.y1 === line2.y1) { if ((line1.x1 <= line2.x1 && line1.x2 >= line2.x1) || (line1.x1 <= line2.x2 && line1.x2 >= line2.x2)) { return true