Merge branch 'dev'

This commit is contained in:
yoosangwook 2024-11-07 09:32:10 +09:00
commit be11343fb2
83 changed files with 4846 additions and 12895 deletions

4
.gitignore vendored
View File

@ -37,3 +37,7 @@ next-env.d.ts
.idea .idea
.vscode .vscode
#lock files
yarn.lock
package-lock.json

151
docs/Canvas Status.md Normal file
View File

@ -0,0 +1,151 @@
# Canvas 배포 현황
## 1. 배치면 초기 설정
## 2. 지붕덥개
### 2.1. 외벽선 그리기
### 2.2. 지붕형상 설정
### 2.3. 지붕형상 수동 설정
### 2.4. 보조선 작성
### 2.5. 처마 ·케라바 변경
### 2.6. 동선이동 ·형올림내림
### 2.7. 외벽선 편집 및 오프셋
### 2.8. 지붕면 할당
## 3. 배치면
### 3.0. Context Menu
### 3.1. 경사 설정
### 3.2. 배치면 그리기
### 3.3. 면형상 그리기
### 3.4. 오브젝트 배치
### 3.5. 배치면 전체 삭제
## 4. 모듈, 회로 구성
### 4.1. 기본 설정
### 4.2. 회로 및 가대 설정
### 4.3. 도면 방위 적용
## 0. Context menu
### (지붕덮개) 지붕재 & 보조선 우클릭
* 새로고침
* 지붕재 배치
* 지붕재 삭제
* 지붕재 전체 삭제
* 선택 · 이동
* 외벽선 삭제
* 이미지 크기 조절
* 사이즈 변경
* 보조선 이동
* 보조선 복사
* 보조선 삭제
* 보조선 수직 이등분선
* 보조선 절삭
* 보조선 전체 삭제
### 개구 우클릭
* 사이즈 변경
* 삭제
* 이동
* 복사
* 개구 오프셋
### 그림자 표시 우클릭
* 사이즈 변경
* 삭제
* 이동
* 복사
### 치수선 우클릭
* 삭제
* 이동
* 치수 보조선 변경
* 표시 변경
### 문자 우클릭
* 삭제
* 이동
* 복사
* 폰트 설정
* 편집
### (배치면) 지붕재 우클릭
* 사이즈 변경
* 삭제
* 이동
* 복사
* 이미지 크기 조절
* 지붕재 변경
* 각 변 속성 변경
* 흐름 방향 변경
### (배치면) 도머 우클릭
* 사이즈 변경
* 삭제
* 이동
* 복사
* 지붕재 변경
* 도머 오프셋
### (패널 배치된 상태) 지붕면 1면 선택 후 마우스 우클릭
* 모듈 세로 가운데 정렬
* 모듈 가로 가운데 정렬
* 모듈 왼쪽 정렬
* 모듈 오른쪽 정렬
* 모듈 위쪽 정렬
* 모듈 아래쪽 정렬
* 모듈 일괄 삭제
* 모듈 일괄 이동
* 모듈 일괄 복사
* 모듈 일괄 회로 번호 변경
*
### (패널 배치된 상태) 지붕면 2면 이상 선택 후 마우스 우클릭
* 모듈 세로 가운데 정렬
* 모듈 가로 가운데 정렬
* 모듈 왼쪽 정렬
* 모듈 오른쪽 정렬
* 모듈 위쪽 정렬
* 모듈 아래쪽 정렬
* 모듈 일괄 삭제
* 모듈 일괄 회로 번호 변경
### 패널 선택 후 마우스 우클릭
* 삭제
* 이동
* 복사
* 열 이동
* 열 복사
* 열 삭제
* 열 삽입
* 단 이동
* 단 복사
* 단 삭제
* 단 삽입

View File

@ -19,9 +19,12 @@ Allpainted : allPainted
출폭: offset 출폭: offset
폭: width 폭: width
경사(구배): pitch 경사(구배): pitch
각도: degree
이구배: doublePitch 이구배: doublePitch
소매: sleeve 소매: sleeve
개구: openSpace 개구: openSpace
도머: dormer 도머: dormer
그림자: shadow 그림자: shadow
치수선: dimensionLine 치수선: dimensionLine
복도치수: planeSize
실제치수: actualSize

10656
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,3 @@
<svg width="11" height="7" viewBox="0 0 11 7" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1 1L5.5 5.5L10 1" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 210 B

View File

@ -0,0 +1,3 @@
<svg width="11" height="7" viewBox="0 0 11 7" fill="none" xmlns="http://www.w3.org/2000/svg">
<path id="Vector 7174" d="M1 1L5.5 5.5L10 1" stroke="#C2D0DD" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 229 B

View File

@ -1,128 +0,0 @@
'use client'
import { Select, SelectItem } from '@nextui-org/react'
import { useEffect, useState } from 'react'
import { useAxios } from '@/hooks/useAxios'
export default function RoofSelect() {
const [roofMaterials, setRoofMaterials] = useState([])
const [manufacturers, setManufacturers] = useState([])
const [trestles, setTrestles] = useState([])
const [modules, setModules] = useState([])
const [originTrestles, setOriginTrestles] = useState([])
const [roofMaterialId, setRoofMaterialId] = useState(null)
const [manufacturerId, setManufacturerId] = useState(null)
const [trestleId, setTrestleId] = useState(null)
const { get } = useAxios()
useEffect(() => {
get({ url: '/api/roof-material/roof-material-infos' }).then((res) => {
//TODO: error handling
if (!res) return
setRoofMaterials(res)
})
}, [])
useEffect(() => {
if (!roofMaterialId) {
return
}
get({ url: `/api/roof-material/roof-material-infos/${roofMaterialId}/trestles` }).then((res) => {
if (res.length === 0) {
return
}
setOriginTrestles(res)
const manufactural = res.map((trestle) => {
return { id: trestle.manufacturerId, name: trestle.manufacturerName }
})
// Remove duplicates
const uniqueManufactural = Array.from(new Set(manufactural.map((a) => a.id))).map((id) => {
return manufactural.find((a) => a.id === id)
})
setManufacturers(uniqueManufactural)
})
}, [roofMaterialId])
useEffect(() => {
if (!manufacturerId) {
return
}
const trestles = originTrestles.filter((trestle) => trestle.manufacturerId === manufacturerId)
setTrestles(trestles)
}, [manufacturerId])
useEffect(() => {
if (!trestleId) {
return
}
get({ url: `/api/module/module-infos?roofMaterialId=${roofMaterialId}&trestleId=${trestleId}` }).then((res) => {
if (res.length === 0) {
return
}
setModules(res)
})
}, [trestleId])
const handleRoofMaterialOnChange = (e) => {
const roofMaterialId = e.target.value
setRoofMaterialId(roofMaterialId)
setManufacturers([])
setManufacturerId(null)
setTrestleId(null)
setTrestles([])
setModules([])
}
const handleManufacturersOnChange = (e) => {
const manufacturerId = Number(e.target.value)
setTrestles([])
setManufacturerId(manufacturerId)
setTrestleId(null)
setModules([])
}
const handleTrestlesOnChange = (e) => {
const trestleId = Number(e.target.value)
setTrestleId(trestleId)
setModules([])
}
return (
<div className="flex w-full flex-wrap md:flex-nowrap gap-4">
{roofMaterials.length > 0 && (
<Select label="지붕재" className="max-w-xs" onChange={handleRoofMaterialOnChange}>
{roofMaterials.map((roofMaterial) => (
<SelectItem key={roofMaterial.id}>{roofMaterial.name}</SelectItem>
))}
</Select>
)}
{manufacturers.length > 0 && (
<Select label="제조 회사" className="max-w-xs" onChange={handleManufacturersOnChange}>
{manufacturers.map((manufacturer) => (
<SelectItem key={manufacturer.id}>{manufacturer.name}</SelectItem>
))}
</Select>
)}
{trestles.length > 0 && (
<Select label="가대" className="max-w-xs" onChange={handleTrestlesOnChange}>
{trestles.map((trestle) => (
<SelectItem key={trestle.id}>{trestle.name}</SelectItem>
))}
</Select>
)}
{modules.length > 0 && (
<Select label="설치가능 모듈" className="max-w-xs">
{modules.map((module) => (
<SelectItem key={module.id}>{module.name}</SelectItem>
))}
</Select>
)}
</div>
)
}

View File

@ -1,25 +1,10 @@
import Roof2 from '@/components/Roof2' import Roof2 from '@/components/Roof2'
import { initCheck } from '@/util/session-util'
import RoofSelect from '@/app/roof2/RoofSelect'
export default async function Roof2Page() { export default async function Roof2Page() {
const session = await initCheck()
const roof2Props = {
name: session.name || '',
userId: session.userId || '',
email: session.email || '',
isLoggedIn: session.isLoggedIn,
}
return ( return (
<> <>
<div>
<div className="m-2">
<RoofSelect />
</div>
</div>
<div className="flex flex-col justify-center my-8 pt-20"> <div className="flex flex-col justify-center my-8 pt-20">
<Roof2 {...roof2Props} /> <Roof2 />
</div> </div>
</> </>
) )

View File

@ -65,6 +65,7 @@ export const LINE_TYPE = {
HIPANDGABLE: 'hipAndGable', HIPANDGABLE: 'hipAndGable',
JERKINHEAD: 'jerkinhead', JERKINHEAD: 'jerkinhead',
SHED: 'shed', SHED: 'shed',
ETC: 'etc',
}, },
SUBLINE: { SUBLINE: {
/** /**
@ -116,6 +117,7 @@ export const INPUT_TYPE = {
export const POLYGON_TYPE = { export const POLYGON_TYPE = {
ROOF: 'roof', ROOF: 'roof',
WALL: 'wall',
TRESTLE: 'trestle', TRESTLE: 'trestle',
} }
@ -156,6 +158,7 @@ export const SAVE_KEY = [
'groupYn', 'groupYn',
'groupName', 'groupName',
'lineDirection', 'lineDirection',
'groupId',
] ]
export const OBJECT_PROTOTYPE = [fabric.Line.prototype, fabric.Polygon.prototype, fabric.Triangle.prototype] export const OBJECT_PROTOTYPE = [fabric.Line.prototype, fabric.Polygon.prototype, fabric.Triangle.prototype]

View File

@ -29,13 +29,6 @@ export default function InitSettingsModal(props) {
//const { get, post } = useAxios() //const { get, post } = useAxios()
useEffect(() => { useEffect(() => {
get({ url: '/api/roof-material/roof-material-infos' }).then((res) => {
//TODO: error handling
if (!res) return
setRoofMaterials(res)
})
get({ url: `/api/canvas-management/canvas-basic-settings/by-object/${objectNo}` }).then((res) => { get({ url: `/api/canvas-management/canvas-basic-settings/by-object/${objectNo}` }).then((res) => {
if (res.length == 0) return if (res.length == 0) return

View File

@ -39,6 +39,7 @@ export default function Playground() {
const [color, setColor] = useState('#ff0000') const [color, setColor] = useState('#ff0000')
const [textInput, setTextInput] = useState('') const [textInput, setTextInput] = useState('')
const [numberInput, setNumberInput] = useState('')
const [radioInput, setRadioInput] = useState('') const [radioInput, setRadioInput] = useState('')
const [checkboxInput, setCheckboxInput] = useState([]) const [checkboxInput, setCheckboxInput] = useState([])
const [selectedValue, setSelectedValue] = useState('') const [selectedValue, setSelectedValue] = useState('')
@ -48,6 +49,9 @@ export default function Playground() {
useEffect(() => { useEffect(() => {
console.log('textInput:', textInput) console.log('textInput:', textInput)
}, [textInput]) }, [textInput])
useEffect(() => {
console.log('numberInput:', numberInput)
}, [numberInput])
useEffect(() => { useEffect(() => {
console.log('radioInput:', radioInput) console.log('radioInput:', radioInput)
}, [radioInput]) }, [radioInput])
@ -161,8 +165,19 @@ export default function Playground() {
> >
QInput TextInput DATA RESET QInput TextInput DATA RESET
</button> </button>
<QInput type="text" value={textInput} onChange={setTextInput} /> <QInput type="text" placeholder="placeholder" value={textInput} onChange={setTextInput} />
<QInput type="text" value={textInput} onChange={setTextInput} readOnly="true" /> <QInput type="text" placeholder="read only" value={textInput} onChange={setTextInput} readOnly="true" />
<br />
<button
className="btn-frame deepgray"
onClick={() => {
setNumberInput('')
}}
>
QInput NumberInput DATA RESET
</button>
<QInput type="number" placeholder="placeholder" value={numberInput} onChange={setNumberInput} />
<QInput type="number" placeholder="read only" value={numberInput} onChange={setNumberInput} readOnly="true" />
<br /> <br />
<button <button
className="btn-frame deepgray" className="btn-frame deepgray"
@ -185,7 +200,7 @@ export default function Playground() {
<button <button
className="btn-frame deepgray" className="btn-frame deepgray"
onClick={() => { onClick={() => {
setCheckboxInput('') setCheckboxInput([])
}} }}
> >
QInput Checkbox DATA RESET QInput Checkbox DATA RESET

View File

@ -4,7 +4,7 @@ import { useCanvas } from '@/hooks/useCanvas'
import { useEffect, useRef, useState } from 'react' import { useEffect, useRef, useState } from 'react'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
import { useMode } from '@/hooks/useMode' import { useMode } from '@/hooks/useMode'
import { Mode } from '@/common/common' import { LINE_TYPE, Mode } from '@/common/common'
import { Button, Input } from '@nextui-org/react' import { Button, Input } from '@nextui-org/react'
import RangeSlider from './ui/RangeSlider' import RangeSlider from './ui/RangeSlider'
import { useRecoilState, useRecoilValue } from 'recoil' import { useRecoilState, useRecoilValue } from 'recoil'
@ -39,7 +39,7 @@ import QEmptyContextMenu from '@/components/common/context-menu/QEmptyContextMen
import InitSettingsModal from './InitSettingsModal' import InitSettingsModal from './InitSettingsModal'
import GridSettingsModal from './GridSettingsModal' import GridSettingsModal from './GridSettingsModal'
import { SurfaceShapeModal } from '@/components/ui/SurfaceShape' import { SurfaceShapeModal } from '@/components/ui/SurfaceShape'
import { drawDirectionStringToArrow } from '@/util/qpolygon-utils' import { changeCurrentRoof, drawDirectionStringToArrow } from '@/util/qpolygon-utils'
import ThumbnailList from '@/components/ui/ThumbnailLIst' import ThumbnailList from '@/components/ui/ThumbnailLIst'
import ObjectPlacement from '@/components/ui/ObjectPlacement' import ObjectPlacement from '@/components/ui/ObjectPlacement'
import { globalLocaleStore } from '@/store/localeAtom' import { globalLocaleStore } from '@/store/localeAtom'
@ -409,6 +409,20 @@ export default function Roof2(props) {
{ x: 600, y: 100 }, { x: 600, y: 100 },
] ]
const rectangleType1 = [
{ x: 100, y: 100 },
{ x: 100, y: 600 },
{ x: 300, y: 600 },
{ x: 300, y: 100 },
]
const rectangleType2 = [
{ x: 100, y: 100 },
{ x: 100, y: 300 },
{ x: 600, y: 300 },
{ x: 600, y: 100 },
]
const types = [type1, type2, type3, type4, type1A, type1B, eightPoint, eightPoint2, eightPoint3, eightPoint4, twelvePoint] const types = [type1, type2, type3, type4, type1A, type1B, eightPoint, eightPoint2, eightPoint3, eightPoint4, twelvePoint]
const newP = [ const newP = [
{ x: 450, y: 450 }, { x: 450, y: 450 },
@ -417,7 +431,7 @@ export default function Roof2(props) {
{ x: 450, y: 850 }, { x: 450, y: 850 },
] ]
const polygon = new QPolygon(twelvePoint, { const polygon = new QPolygon(rectangleType2, {
fill: 'transparent', fill: 'transparent',
stroke: 'green', stroke: 'green',
strokeWidth: 1, strokeWidth: 1,
@ -658,17 +672,52 @@ export default function Roof2(props) {
canvas?.renderAll() canvas?.renderAll()
} }
const setAllGableRoof = () => { const setHipRoof = () => {
let offset = Number(prompt('gable roof offset', '50')) const polygon = canvas?.getObjects().find((obj) => obj.name === 'roof')
const currentRoof = polygon.lines[2]
currentRoof.attributes.type = LINE_TYPE.WALLLINE.EAVES
currentRoof.attributes.offset = 50
changeCurrentRoof(currentRoof, canvas)
}
const setGableRoof = () => {
const polygon = canvas?.getObjects().find((obj) => obj.name === 'roof')
const currentRoof = polygon.lines[2]
currentRoof.attributes.type = LINE_TYPE.WALLLINE.GABLE
currentRoof.attributes.offset = 30
changeCurrentRoof(currentRoof, canvas)
}
const setHipAndGableRoof = () => {
let offset = Number(prompt('팔작지붕 폭', '50'))
if (!isNaN(offset) && offset > 0) { if (!isNaN(offset) && offset > 0) {
const polygon = canvas?.getObjects() const polygon = canvas?.getObjects().find((obj) => obj.name === 'roof')
console.log('gable roof offset : ', offset) const currentRoof = polygon.lines[2]
console.log('polygon : ', polygon) currentRoof.attributes.type = LINE_TYPE.WALLLINE.HIPANDGABLE
changeAllGableRoof(polygon, offset, canvas) currentRoof.attributes.width = offset
changeCurrentRoof(currentRoof, canvas)
} else { } else {
alert('offset 은 0 보다 커야 함') alert('은 0 보다 커야 함')
} }
} }
const setJerkInHeadRoof = () => {
let offset = Number(prompt('팔작지붕 폭', '50'))
if (!isNaN(offset) && offset > 0) {
const polygon = canvas?.getObjects().find((obj) => obj.name === 'roof')
const currentRoof = polygon.lines[2]
currentRoof.attributes.type = LINE_TYPE.WALLLINE.JERKINHEAD
currentRoof.attributes.width = offset
changeCurrentRoof(currentRoof, canvas)
} else {
alert('폭은 0 보다 커야 함')
}
}
const setWallRoof = () => {
let offset = Number(prompt('소매 폭', '0'))
const polygon = canvas?.getObjects().find((obj) => obj.name === 'roof')
const currentRoof = polygon.lines[2]
currentRoof.attributes.type = LINE_TYPE.WALLLINE.WALL
currentRoof.attributes.width = offset
changeCurrentRoof(currentRoof, canvas)
}
return ( return (
<> <>
{canvas && ( {canvas && (
@ -788,16 +837,28 @@ export default function Roof2(props) {
<Button className="m-1 p-2" onClick={makePolygon}> <Button className="m-1 p-2" onClick={makePolygon}>
다각형 추가 다각형 추가
</Button> </Button>
{templateType === 0 && ( {/*{templateType === 0 && (*/}
<> {/* <>*/}
<Button className="m-1 p-2" onClick={makeQPolygon}> <Button className="m-1 p-2" onClick={makeQPolygon}>
QPolygon QPolygon
</Button> </Button>
</> {/* </>*/}
)} {/*)}*/}
<Button className={'m-1 p-2'} onClick={setAllGableRoof}> <Button className={'m-1 p-2'} onClick={setHipRoof}>
용마루지붕
</Button>
<Button className={'m-1 p-2'} onClick={setHipAndGableRoof}>
팔작지붕 팔작지붕
</Button> </Button>
<Button className={'m-1 p-2'} onClick={setGableRoof}>
박공지붕
</Button>
<Button className={'m-1 p-2'} onClick={setJerkInHeadRoof}>
반절처지붕
</Button>
<Button className={'m-1 p-2'} onClick={setWallRoof}>
벽지붕
</Button>
<Button className="m-1 p-2" onClick={() => saveImage(uuidv4(), userId, setThumbnails)}> <Button className="m-1 p-2" onClick={() => saveImage(uuidv4(), userId, setThumbnails)}>
저장 저장
</Button> </Button>

View File

@ -8,7 +8,7 @@ import { contextPopupPositionState } from '@/store/popupAtom'
export default function ColorPickerModal(props) { export default function ColorPickerModal(props) {
const contextPopupPosition = useRecoilValue(contextPopupPositionState) // const contextPopupPosition = useRecoilValue(contextPopupPositionState) //
const { isShow, setIsShow, pos = contextPopupPosition, color = '#ff0000', setColor, id } = props const { isShow, setIsShow, pos = contextPopupPosition, color = '#ff0000', setColor, id, isConfig = false } = props
const { getMessage } = useMessage() const { getMessage } = useMessage()
const [originColor, setOriginColor] = useState(color) const [originColor, setOriginColor] = useState(color)
const { closePopup } = usePopup() const { closePopup } = usePopup()
@ -29,7 +29,7 @@ export default function ColorPickerModal(props) {
setIsShow(false) setIsShow(false)
} }
console.log(id) console.log(id)
closePopup(id) closePopup(id, isConfig)
}} }}
> >
닫기 닫기
@ -49,10 +49,10 @@ export default function ColorPickerModal(props) {
if (setColor) setColor(originColor) if (setColor) setColor(originColor)
if (setIsShow) setIsShow(false) if (setIsShow) setIsShow(false)
closePopup(id) closePopup(id, isConfig)
}} }}
> >
{getMessage('common.message.save')} {getMessage('modal.common.save')}
</button> </button>
</div> </div>
</div> </div>

View File

@ -1,14 +1,20 @@
'use client' 'use client'
import { useEffect } from 'react' import { useEffect } from 'react'
import '@/styles/contents.scss' import '@/styles/contents.scss'
import { useRecoilState } from 'recoil' import { useRecoilState, useRecoilValue } from 'recoil'
import { contextMenuListState, contextMenuState } from '@/store/contextMenu' import { contextMenuListState, contextMenuState } from '@/store/contextMenu'
import { useTempGrid } from '@/hooks/useTempGrid'
import { useContextMenu } from '@/hooks/useContextMenu'
import { useEvent } from '@/hooks/useEvent'
export default function QContextMenu(props) { export default function QContextMenu(props) {
const { contextRef, canvasProps, handleKeyup } = props const { contextRef, canvasProps } = props
const [contextMenu, setContextMenu] = useRecoilState(contextMenuState) const [contextMenu, setContextMenu] = useRecoilState(contextMenuState)
const [contextMenuList, setContextMenuList] = useRecoilState(contextMenuListState) const contextMenuList = useRecoilValue(contextMenuListState)
const activeObject = canvasProps?.getActiveObject() // const activeObject = canvasProps?.getActiveObject() //
const { tempGridMode, setTempGridMode } = useTempGrid()
const { handleKeyup } = useContextMenu()
const { addDocumentEventListener, removeDocumentEvent } = useEvent()
let contextType = '' let contextType = ''
@ -20,24 +26,25 @@ export default function QContextMenu(props) {
} }
} }
} }
const getYPosition = (e) => { const getYPosition = (e) => {
const contextLength = contextMenuList.reduce((acc, cur, index) => { const contextLength = contextMenuList.reduce((acc, cur, index) => {
return acc + cur.length return acc + cur.length
}, 0) }, 0)
return e?.clientY - (contextLength * 25 + contextMenuList.length * 2 * 17) return e?.clientY - (contextLength * 25 + contextMenuList.length * 2 * 17)
} }
useEffect(() => { useEffect(() => {
if (!contextRef.current) return if (!contextRef.current) return
const handleContextMenu = (e) => { const handleContextMenu = (e) => {
// e.preventDefault() // contextmenu // e.preventDefault() // contextmenu
if (tempGridMode) return
const position = { const position = {
x: window.innerWidth / 2 < e.pageX ? e.pageX - 240 : e.pageX, x: window.innerWidth / 2 < e.pageX ? e.pageX - 240 : e.pageX,
y: window.innerHeight / 2 < e.pageY ? getYPosition(e) : e.pageY, y: window.innerHeight / 2 < e.pageY ? getYPosition(e) : e.pageY,
} }
setContextMenu({ visible: true, ...position }) setContextMenu({ visible: true, ...position })
document.addEventListener('keyup', (e) => handleKeyup(e)) addDocumentEventListener('keyup', document, handleKeyup)
canvasProps?.upperCanvasEl.removeEventListener('contextmenu', handleContextMenu) // canvasProps?.upperCanvasEl.removeEventListener('contextmenu', handleContextMenu) //
} }
@ -48,8 +55,9 @@ export default function QContextMenu(props) {
const handleOutsideClick = (e) => { const handleOutsideClick = (e) => {
// e.preventDefault() // e.preventDefault()
if (contextMenu.visible && !ref.current.contains(e.target)) { if (contextMenu.visible) {
setContextMenu({ ...contextMenu, visible: false }) setContextMenu({ ...contextMenu, visible: false })
removeDocumentEvent('keyup')
} }
} }
@ -58,10 +66,11 @@ export default function QContextMenu(props) {
document.addEventListener('click', handleOutsideClick) document.addEventListener('click', handleOutsideClick)
return () => { return () => {
removeDocumentEvent('keyup')
document.removeEventListener('click', handleClick) document.removeEventListener('click', handleClick)
document.removeEventListener('click', handleOutsideClick) document.removeEventListener('click', handleOutsideClick)
} }
}, [contextRef, contextMenu]) }, [contextRef, contextMenuList])
const handleObjectMove = () => { const handleObjectMove = () => {
activeObject.set({ activeObject.set({

View File

@ -1,18 +1,18 @@
'use client' 'use client'
import { useEffect, useState } from 'react' import { useState } from 'react'
import Draggable from 'react-draggable' import Draggable from 'react-draggable'
export default function WithDraggable({ isShow, children, pos, handle = '' }) { export default function WithDraggable({ isShow, children, pos = { x: 0, y: 0 }, handle = '' }) {
const [position, setPosition] = useState({ x: 0, y: 0 }) const [position, setPosition] = useState(pos)
const handleOnDrag = (e, data) => { const handleOnDrag = (e, data) => {
e.stopPropagation() e.stopPropagation()
setPosition({ x: data.x, y: data.y }) setPosition({ x: data.x, y: data.y })
} }
useEffect(() => { // useEffect(() => {
setPosition({ ...pos }) // setPosition({ ...pos })
}, []) // }, [])
return ( return (
<> <>

View File

@ -14,15 +14,7 @@ const fonts = [
{ name: '@Yu Gothic UI', value: '@Yu Gothic UI' }, { name: '@Yu Gothic UI', value: '@Yu Gothic UI' },
{ name: 'Yu Gothic UI', value: 'Yu Gothic UI' }, { name: 'Yu Gothic UI', value: 'Yu Gothic UI' },
] ]
const fontOptions = [
{ name: '보통', value: 'normal' },
{ name: '기울임꼴', value: 'italic' },
{
name: '굵게',
value: 'bold',
},
{ name: '굵은 기울임꼴', value: 'boldAndItalic' },
]
const fontSizes = [ const fontSizes = [
...Array.from({ length: 4 }).map((_, index) => { ...Array.from({ length: 4 }).map((_, index) => {
return { name: index + 8, value: index + 8 } return { name: index + 8, value: index + 8 }
@ -34,20 +26,10 @@ const fontSizes = [
{ name: 48, value: 48 }, { name: 48, value: 48 },
{ name: 72, value: 72 }, { name: 72, value: 72 },
] ]
const fontColors = [
{ name: '검정색', value: 'black' },
{ name: '빨강색', value: 'red' },
{ name: '파랑색', value: 'blue' },
{ name: '회색', value: 'gray' },
{ name: '황색', value: 'yellow' },
{ name: '녹색', value: 'green' },
{ name: '분홍색', value: 'pink' },
{ name: '황금색', value: 'gold' },
{ name: '남색', value: 'darkblue' },
]
export default function FontSetting(props) { export default function FontSetting(props) {
const contextPopupPosition = useRecoilValue(contextPopupPositionState) const contextPopupPosition = useRecoilValue(contextPopupPositionState)
const { id, setIsShow, pos = contextPopupPosition, type } = props const { id, setIsShow, pos = contextPopupPosition, type, isConfig = false } = props
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { closePopup } = usePopup() const { closePopup } = usePopup()
const [globalFont, setGlobalFont] = useRecoilState(globalFontAtom) const [globalFont, setGlobalFont] = useRecoilState(globalFontAtom)
@ -57,7 +39,26 @@ export default function FontSetting(props) {
const [selectedFontWeight, setSelectedFontWeight] = useState(currentFont.fontWeight) const [selectedFontWeight, setSelectedFontWeight] = useState(currentFont.fontWeight)
const [selectedFontSize, setSelectedFontSize] = useState(currentFont.fontSize) const [selectedFontSize, setSelectedFontSize] = useState(currentFont.fontSize)
const [selectedFontColor, setSelectedFontColor] = useState(currentFont.fontColor) const [selectedFontColor, setSelectedFontColor] = useState(currentFont.fontColor)
const fontOptions = [
{ name: getMessage('font.style.normal'), value: 'normal' },
{ name: getMessage('font.style.italic'), value: 'italic' },
{
name: getMessage('font.style.bold'),
value: 'bold',
},
{ name: getMessage('font.style.bold.italic'), value: 'boldAndItalic' },
]
const fontColors = [
{ name: getMessage('color.black'), value: 'black' },
{ name: getMessage('color.red'), value: 'red' },
{ name: getMessage('color.blue'), value: 'blue' },
{ name: getMessage('color.gray'), value: 'gray' },
{ name: getMessage('color.yellow'), value: 'yellow' },
{ name: getMessage('color.green'), value: 'green' },
{ name: getMessage('color.pink'), value: 'pink' },
{ name: getMessage('color.gold'), value: 'gold' },
{ name: getMessage('color.darkblue'), value: 'darkblue' },
]
const handleSaveBtn = () => { const handleSaveBtn = () => {
setGlobalFont((prev) => { setGlobalFont((prev) => {
return { return {
@ -83,7 +84,7 @@ export default function FontSetting(props) {
className="modal-close" className="modal-close"
onClick={() => { onClick={() => {
if (setIsShow) setIsShow(false) if (setIsShow) setIsShow(false)
closePopup(id) closePopup(id, isConfig)
}} }}
> >
닫기 닫기

View File

@ -1,8 +1,9 @@
'use client' 'use client'
import { useCallback } from 'react' import { useCallback, useState } from 'react'
export default function QInput({ type, readOnly = false, options = [], value, onChange }) { export default function QInput({ type, className, ref, id, readOnly = false, options = [], placeholder, value, onChange }) {
const [prevNum, setPrevNum] = useState('')
// options = options || [ // options = options || [
// { // {
// id: 'one', // id: 'one',
@ -21,11 +22,11 @@ export default function QInput({ type, readOnly = false, options = [], value, on
// }, // },
// ] // ]
const handleChange = useCallback( const handleRadioCheckboxChange = useCallback(
(e, optionValue) => { (e, optionValue) => {
if (type === 'radio') { if (type === 'radio') {
onChange(e.target.value) onChange(e.target.value)
} else { } else if (type === 'checkbox') {
const newValue = value.includes(optionValue) ? value.filter((v) => v !== optionValue) : [...value, optionValue] const newValue = value.includes(optionValue) ? value.filter((v) => v !== optionValue) : [...value, optionValue]
onChange(newValue) onChange(newValue)
} }
@ -33,36 +34,85 @@ export default function QInput({ type, readOnly = false, options = [], value, on
[type, value, onChange], [type, value, onChange],
) )
const handleTextChange = useCallback( const handleTextNumberChange = useCallback(
(e) => { (e) => {
onChange(e.target.value) onChange(e.target.value)
}, },
[onChange], [onChange],
) )
return ( // type=number , ,
<> // const checkInputNumber = (e) => {
{type === 'text' ? ( // const value = e.target.value
<div className="mb5"> // const key = e.key
<input type={type} className="input-light" readOnly={readOnly ? true : false} value={value} onChange={handleTextChange} /> // const allowKeys = ['Backspace', 'Delete', 'ArrowLeft', 'ArrowRight', 'Home', 'End', 'Tab', 'Enter', 'Control'] // 'ArrowUp', 'ArrowDown',
</div> // if (key >= '0' && key <= '9') {
) : type === 'radio' || type === 'checkbox' ? ( // return
<div className="flx mb5"> // }
{options?.map((option) => ( // if (key === '.' && !value.includes('.') && value.length > 0 && !isNaN(Number(value))) {
<div key={option.name} className={`d-${type}-radio light mr5`}> // return
<input // }
type={type} // if (key === '-' && !value.includes('-') && value.length > 0) {
name={type === 'radio' ? 'radioGroup' : 'checkboxGroup'} // return
value={option.value} // }
id={option.id} // if (allowKeys.includes(key)) {
checked={type === 'radio' ? value === option.value : value.includes(option.value)} // return
onChange={(e) => handleChange(e, option.value)} // }
/> // if (key === 'a' || key === 'c' || key === 'v' || key === 'x' || key === 'z') {
<label htmlFor={option.id}>{option.name}</label> // return
</div> // }
))} // e.preventDefault()
</div> // }
) : null} // type=number ,
</> const checkInputNumber = (e) => {
) const value = e.target.defaultValue
if (value === '') return
const regex = /^-?([1-9]\d*|\d)(\.\d*)?$/
if (regex.test(value)) {
setPrevNum(value)
} else {
onChange(prevNum)
}
}
// input type : text, number
const inputTextNumber = () => {
return (
<input
type="text"
className={`input-light ${className ? className : ''}`}
ref={ref}
id={id}
readOnly={readOnly ? true : false}
placeholder={placeholder}
value={value}
onChange={handleTextNumberChange}
onKeyUp={type === 'number' ? checkInputNumber : undefined}
/>
)
}
// input type : radio, checkbox
const inputRadioCheckbox = () => {
return (
<div className="flx">
{options?.map((option) => (
<div key={option.name} className={`d-${type}-radio light mr5`}>
<input
type={type}
name={type === 'radio' ? 'radioGroup' : 'checkboxGroup'}
value={option.value}
id={option.id}
checked={type === 'radio' ? value === option.value : value.includes(option.value)}
onChange={(e) => handleRadioCheckboxChange(e, option.value)}
/>
<label htmlFor={option.id}>{option.name}</label>
</div>
))}
</div>
)
}
return <>{type === 'text' || type === 'number' ? inputTextNumber() : type === 'radio' || type === 'checkbox' ? inputRadioCheckbox() : null}</>
} }

View File

@ -1,9 +1,13 @@
'use client' 'use client'
import { useRecoilState } from 'recoil' import { useRecoilValue } from 'recoil'
import { popupState } from '@/store/popupAtom' import { popupState } from '@/store/popupAtom'
import { Fragment } from 'react' import { Fragment } from 'react'
export default function PopupManager() { export default function PopupManager() {
const [popup, setPopup] = useRecoilState(popupState) const popup = useRecoilValue(popupState)
return popup.children?.map((child) => <Fragment key={child.id}>{child.component}</Fragment>)
return [
...popup?.config.map((child) => <Fragment key={child.id}>{child.component}</Fragment>),
...popup?.other.map((child) => <Fragment key={child.id}>{child.component}</Fragment>),
]
} }

View File

@ -1,20 +1,55 @@
'use client' 'use client'
import { useEffect, useState, useRef } from 'react' import { useEffect, useState, useContext } from 'react'
import { useRecoilValue } from 'recoil' import { useRecoilValue } from 'recoil'
import { floorPlanObjectState } from '@/store/floorPlanObjectAtom' import { floorPlanObjectState } from '@/store/floorPlanObjectAtom'
import { sessionStore } from '@/store/commonAtom' import { SessionContext } from '@/app/SessionProvider'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { useCanvasMenu } from '@/hooks/common/useCanvasMenu' import { useCanvasMenu } from '@/hooks/common/useCanvasMenu'
import SingleDatePicker from '../common/datepicker/SingleDatePicker' import SingleDatePicker from '../common/datepicker/SingleDatePicker'
import EstimateFileUploader from './EstimateFileUploader' import EstimateFileUploader from './EstimateFileUploader'
import { useAxios } from '@/hooks/useAxios'
import { globalLocaleStore } from '@/store/localeAtom'
import { isNotEmptyArray, isObjectNotEmpty } from '@/util/common-utils'
import dayjs from 'dayjs'
import { useCommonCode } from '@/hooks/common/useCommonCode'
import Select from 'react-select'
import { useEstimateController } from '@/hooks/floorPlan/estimate/useEstimateController'
export default function Estimate({ params }) { export default function Estimate({ params }) {
const [objectNo, setObjectNo] = useState('') const [objectNo, setObjectNo] = useState('') //
const [files, setFiles] = useState([]) // const [planNo, setPlanNo] = useState('') //
const [files, setFiles] = useState([]) //
const sessionState = useRecoilValue(sessionStore) const [showContentCode, setShowContentCode] = useState('ATTR001')
//
const [hidden, setHidden] = useState(false)
//
const { findCommonCode } = useCommonCode()
const [honorificCodeList, setHonorificCodeList] = useState([]) //
const [startDate, setStartDate] = useState(new Date())
const singleDatePickerProps = {
startDate,
setStartDate,
}
const { session } = useContext(SessionContext)
const objectRecoil = useRecoilValue(floorPlanObjectState) const objectRecoil = useRecoilValue(floorPlanObjectState)
//
const { state, setState } = useEstimateController(params.pid)
// LIST
// List
const [specialNoteList, setSpecialNoteList] = useState([])
const globalLocaleState = useRecoilValue(globalLocaleStore)
const { get, post } = useAxios(globalLocaleState)
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { setMenuNumber } = useCanvasMenu() const { setMenuNumber } = useCanvasMenu()
@ -27,46 +62,107 @@ export default function Estimate({ params }) {
setUploadFiles: setFiles, setUploadFiles: setFiles,
} }
useEffect(() => {
setObjectNo(objectRecoil.floorPlanObjectNo)
}, [objectRecoil])
useEffect(() => {
if (objectNo) {
//Q101X278191023001
console.log('세션정보::::', sessionState)
//API
}
}, [objectNo])
useEffect(() => { useEffect(() => {
setMenuNumber(5) setMenuNumber(5)
setObjectNo(objectRecoil.floorPlanObjectNo)
setPlanNo(params.pid)
//
const code1 = findCommonCode(200800)
if (code1 != null) {
setHonorificCodeList(code1)
}
}, []) }, [])
useEffect(() => {
// API
// ()
let url = `/api/estimate/special-note-list`
get({ url: url }).then((res) => {
if (isNotEmptyArray(res)) {
if (state?.estimateOption) {
res.map((row) => {
let estimateOption = state?.estimateOption?.split('、')
row.text = false
estimateOption.map((row2) => {
if (row2 === row.code) {
row.text = true
}
})
})
setSpecialNoteList(res)
}
}
})
}, [state?.estimateOption])
// set
useEffect(() => {
let estimateDate = dayjs(startDate).format('YYYY-MM-DD')
setState({ estimateDate: estimateDate })
}, [startDate])
useEffect(() => {
// setState
if (isNotEmptyArray(specialNoteList)) {
const liveCheckedData = specialNoteList.filter((row) => row.text === true)
const data = []
for (let ele of liveCheckedData) {
data.push(ele.code)
}
const newData = data.join('、')
setState({ estimateOption: newData })
}
}, [specialNoteList])
// remark
const settingShowContent = (code, event) => {
setShowContentCode(code)
event.stopPropagation()
}
// state
useEffect(() => {
// console.log(files)
if (files.length > 0) {
files.map((row) => {
setState({ fileList: row.data })
})
} else {
console.log('첨부파일 없음')
setState({ fileList: [] })
}
}, [files])
return ( return (
<div className="sub-content estimate"> <div className="sub-content estimate">
<div className="sub-content-inner"> <div className="sub-content-inner">
{/* 물건번호, 견적서번호, 등록일, 변경일시 시작 */} {/* 물건번호, 견적서번호, 등록일, 변경일시 시작 */}
{/* <form onSubmit={handleSubmit(onValid)}> */}
<div className="sub-content-box"> <div className="sub-content-box">
<div className="sub-table-box"> <div className="sub-table-box">
<div className="estimate-list-wrap one"> <div className="estimate-list-wrap one">
<div className="estimate-box"> <div className="estimate-box">
<div className="estimate-tit">{getMessage('estimate.detail.objectNo')}</div> <div className="estimate-tit">{getMessage('estimate.detail.objectNo')}</div>
<div className="estimate-name"> <div className="estimate-name">
{objectNo} (Plan No: {params.pid}) {objectNo} (Plan No: {planNo})
</div> </div>
</div> </div>
<div className="estimate-box"> <div className="estimate-box">
<div className="estimate-tit">{getMessage('estimate.detail.estimateNo')}</div> <div className="estimate-tit">{getMessage('estimate.detail.docNo')}</div>
<div className="estimate-name">5242310200065242</div> <div className="estimate-name">{state.docNo}</div>
</div> </div>
<div className="estimate-box"> <div className="estimate-box">
<div className="estimate-tit">{getMessage('estimate.detail.createDatetime')}</div> <div className="estimate-tit">{getMessage('estimate.detail.drawingEstimateCreateDate')}</div>
<div className="estimate-name">9999.09.28</div> <div className="estimate-name">
{state?.drawingEstimateCreateDate ? `${dayjs(state.drawingEstimateCreateDate).format('YYYY.MM.DD')}` : ''}
</div>
</div> </div>
<div className="estimate-box"> <div className="estimate-box">
<div className="estimate-tit">{getMessage('estimate.detail.lastEditDatetime')}</div> <div className="estimate-tit">{getMessage('estimate.detail.lastEditDatetime')}</div>
<div className="estimate-name">9999.09.28 06:36</div> <div className="estimate-name">{state?.lastEditDatetime ? `${dayjs(state.lastEditDatetime).format('YYYY.MM.DD HH:mm')}` : ''}</div>
</div> </div>
</div> </div>
</div> </div>
@ -99,7 +195,7 @@ export default function Estimate({ params }) {
</th> </th>
<td> <td>
<div className="date-picker" style={{ width: '350px' }}> <div className="date-picker" style={{ width: '350px' }}>
<SingleDatePicker /> <SingleDatePicker {...singleDatePickerProps} />
</div> </div>
</td> </td>
</tr> </tr>
@ -113,64 +209,148 @@ export default function Estimate({ params }) {
</th> </th>
<td> <td>
<div className="input-wrap" style={{ width: '350px' }}> <div className="input-wrap" style={{ width: '350px' }}>
<input type="text" className="input-light" defaultValue={'물건정보에서 입력한 담당자명 표시'} /> <input
type="text"
className="input-light"
defaultValue={state?.charger}
onChange={(e) => {
// charger
setState({ charger: e.target.value })
}}
/>
</div> </div>
</td> </td>
</tr> </tr>
<tr> <tr>
{/* 안건명 */} {/* 안건명 */}
<th> <th>
{getMessage('estimate.detail.title')} <span className="important">*</span> {getMessage('estimate.detail.objectName')} <span className="important">*</span>
</th> </th>
<td colSpan={3}> <td colSpan={3}>
<div className="form-flex-wrap"> <div className="form-flex-wrap">
<div className="input-wrap mr5" style={{ width: '610px' }}> <div className="input-wrap mr5" style={{ width: '610px' }}>
<input type="text" className="input-light" defaultValue={'안건명:::'} /> <input
type="text"
className="input-light"
defaultValue={state?.objectName}
onChange={(e) => {
// objectName
setState({ objectName: e.target.value })
}}
/>
</div> </div>
<div className="input-wrap" style={{ width: '200px' }}> <div className="select-wrap" style={{ width: '200px' }}>
<input type="text" className="input-light" defaultValue={'경칭?'} /> <Select
id="objectNameOmit"
instanceId="objectNameOmit"
className="react-select-custom"
classNamePrefix="custom"
placeholder="Select"
options={honorificCodeList}
onChange={(e) => {
if (isObjectNotEmpty(e)) {
setState({ objectNameOmit: e.clCodeNm })
} else {
setState({ objectNameOmit: '' })
}
}}
getOptionLabel={(x) => x.clCodeNm}
getOptionValue={(x) => x.clCode}
isClearable={true}
isSearchable={false}
value={honorificCodeList.filter(function (option) {
return option.clCodeNm === state.objectNameOmit
})}
/>
</div> </div>
</div> </div>
</td> </td>
</tr> </tr>
<tr> <tr>
{/* 메모 */} {/* 물건정보에서 입력한 메모 */}
<th>{getMessage('estimate.detail.remarks')}</th> <th>{getMessage('estimate.detail.objectRemarks')}</th>
<td colSpan={3}>물건정보에서 입력한 메모 표시</td> <td colSpan={3}>{state?.objectRemarks}</td>
</tr> </tr>
<tr> <tr>
{/* 주문분류 */} {/* 주문분류 */}
<th> <th>
{getMessage('estimate.detail.orderType')} <span className="important">*</span> {getMessage('estimate.detail.estimateType')} <span className="important">*</span>
</th> </th>
<td colSpan={3}> <td colSpan={3}>
<div className="radio-wrap"></div> <div className="radio-wrap">
<div className="d-check-radio light mr10">
<input
type="radio"
name="estimateType"
id="YJSS"
value={'YJSS'}
checked={state?.estimateType === 'YJSS' ? true : false}
onChange={(e) => {
setState({ estimateType: e.target.value })
}}
/>
<label htmlFor="YJSS">住宅PKG</label>
</div>
<div className="d-check-radio light">
<input
type="radio"
name="estimateType"
id="YJOD"
value={'YJOD'}
checked={state?.estimateType === 'YJOD' ? true : false}
onChange={(e) => {
setState({ estimateType: e.target.value })
}}
/>
<label htmlFor="YJOD">積上げ( YJOD )</label>
</div>
</div>
</td> </td>
</tr> </tr>
<tr> <tr>
{/* 지붕재・사양시공 최대4개*/} {/* 지붕재・사양시공 최대4개*/}
<th>{getMessage('estimate.detail.roofCns')}</th> <th>{getMessage('estimate.detail.roofCns')}</th>
<td colSpan={3}> <td colSpan={3}>
<div className="form-flex-wrap mb5"> {state?.roofMaterialIdMulti?.split('、').map((row, index) => {
<div className="input-wrap mr5" style={{ width: '610px' }}> //
<input type="text" className="input-light" defaultValue={'ハゼ式折板(角ハゼ)'} readOnly /> let roofList = row
</div> let roofListLength = state?.roofMaterialIdMulti?.split('、').length
<div className="input-wrap" style={{ width: '200px' }}> let style = 'mb5'
<input type="text" className="input-light" defaultValue={'標準施工'} readOnly /> if (roofListLength == index + 1) {
</div> style = ''
</div> }
<div className="form-flex-wrap mb5"></div> //
<div className="form-flex-wrap mb5"></div> let constructSpecificationMulti = state?.constructSpecificationMulti?.split('、')
{/* 마지막div엔 mb5 제외 */}
<div className="form-flex-wrap"></div> return (
<>
<div className={`form-flex-wrap ${style}`}>
<div className="input-wrap mr5" style={{ width: '610px' }}>
<input type="text" className="input-light" defaultValue={roofList} readOnly />
</div>
<div className="input-wrap" style={{ width: '200px' }}>
<input type="text" className="input-light" defaultValue={constructSpecificationMulti[index]} readOnly />
</div>
</div>
</>
)
})}
</td> </td>
</tr> </tr>
<tr> <tr>
{/* 비고 */} {/* 비고 */}
<th>{getMessage('estimate.detail.note')}</th> <th>{getMessage('estimate.detail.remarks')}</th>
<td colSpan={3}> <td colSpan={3}>
<div className="input-wrap"> <div className="input-wrap">
<input type="text" className="input-light" /> <input
type="text"
className="input-light"
defaultValue={state?.remarks}
onChange={(e) => {
//
setState({ remarks: e.target.value })
}}
/>
</div> </div>
</td> </td>
</tr> </tr>
@ -200,25 +380,6 @@ export default function Estimate({ params }) {
<th>{getMessage('estimate.detail.header.fileList1')}</th> <th>{getMessage('estimate.detail.header.fileList1')}</th>
<td> <td>
<EstimateFileUploader {...fileUploadProps} /> <EstimateFileUploader {...fileUploadProps} />
{/* <div className="drag-file-box">
<div className="btn-area">
<Button type="button" className="btn-origin grey" onClick={handleButtonClick}>
{getMessage('estimate.detail.fileList.btn')}
</Button>
<input
type="file"
id="fileUpload"
name="fileUpload"
ref={fileInputRef}
onChange={onChangeFiles}
style={{ display: 'none' }}
/>
</div>
<div className="drag-file-area">
<p>Drag file here</p>
<ul className="file-list"></ul>
</div>
</div> */}
</td> </td>
</tr> </tr>
</tbody> </tbody>
@ -246,21 +407,70 @@ export default function Estimate({ params }) {
<h3 className="product">{getMessage('estimate.detail.header.specialEstimate')}</h3> <h3 className="product">{getMessage('estimate.detail.header.specialEstimate')}</h3>
<div className="product_tit">{getMessage('estimate.detail.header.specialEstimateProductInfo')}</div> <div className="product_tit">{getMessage('estimate.detail.header.specialEstimateProductInfo')}</div>
</div> </div>
<div className="left-unit-box">
<button className={`estimate-arr-btn down mr5 ${hidden ? '' : 'on'}`} onClick={() => setHidden(false)}></button>
<button className={`estimate-arr-btn up ${hidden ? 'on' : ''}`} onClick={() => setHidden(true)}></button>
</div>
</div> </div>
{/* 공통코드영역시작 */} {/* 견적 특이사항 코드영역시작 */}
<div className="special-note-check-wrap"></div> <div className={`estimate-check-wrap ${hidden ? 'hide' : ''}`}>
{/* 공통코드영역끝 */} <div className="estimate-check-inner">
{/* 견적특이사항 내용영역시작 */} <div className="special-note-check-wrap">
<div className="calculation-estimate"></div> {/* SpecialNoteList반복문 */}
{/* 견적특이사항 내용영역끝 */} {specialNoteList.map((row) => {
{/* 견적특이사항 끝 */} return (
<div
className="special-note-check-item"
onClick={(event) => {
settingShowContent(row.code, event)
}}
>
<div className="d-check-box light">
<input
type="checkbox"
id={row.code}
checked={!!row.text}
disabled={row.code === 'ATTR001' ? true : false}
onChange={(event) => {
setSpecialNoteList((specialNote) =>
specialNote.map((temp) => (temp.code === row.code ? { ...temp, text: !temp.text } : temp)),
)
settingShowContent(row.code, event)
}}
/>
<label htmlFor={row.code}>{row.codeNm}</label>
</div>
</div>
)
})}
</div>
{/* 견적특이사항 선택한 내용?영역시작 */}
<div className="calculation-estimate">
{specialNoteList.map((row) => {
if (row.code === showContentCode) {
return (
<dl>
<dt>{row.codeNm}</dt>
<dd>{row.remarks}</dd>
</dl>
)
}
})}
</div>
{/* 견적특이사항 선택한 내용?영역끝 */}
</div>
</div>
{/* 견적 특이사항 코드영역 끝 */}
{/* 견적특이사항 영역끝 */}
{/* 제품정보 시작 */} {/* 제품정보 시작 */}
<div className="table-box-title-wrap"> <div className="table-box-title-wrap">
<div className="title-wrap"> <div className="title-wrap">
<h3>{getMessage('estimate.detail.header.specialEstimateProductInfo')}</h3> <h3>{getMessage('estimate.detail.header.specialEstimateProductInfo')}</h3>
</div> </div>
</div> </div>
<div className="estimate-wrap"> <div className="esimate-wrap">
<div className="estimate-list-wrap one"> <div className="estimate-list-wrap one">
<div className="estimate-box"> <div className="estimate-box">
<div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.totPcs')}</div> <div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.totPcs')}</div>
@ -285,7 +495,7 @@ export default function Estimate({ params }) {
</div> </div>
</div> </div>
{/* YJOD면 아래영역 숨김 */} {/* YJOD면 아래영역 숨김 */}
<div className="common-table bt-able"> <div className="common-table bt-able" style={{ display: state?.estimateType === 'YJSS' ? '' : 'none' }}>
<table> <table>
<colgroup> <colgroup>
<col style={{ width: '160px' }} /> <col style={{ width: '160px' }} />
@ -319,18 +529,35 @@ export default function Estimate({ params }) {
<div className="estimate-product-option"> <div className="estimate-product-option">
<div className="product-price-wrap"> <div className="product-price-wrap">
<div className="product-price-tit">{getMessage('estimate.detail.header.showPrice')}</div> <div className="product-price-tit">{getMessage('estimate.detail.header.showPrice')}</div>
<div className="select-wrap"></div> <div className="select-wrap">
<select className="select-light" name="" id="">
<option value="">111</option>
<option value="">222</option>
</select>
</div>
<button className="btn-origin grey ml5">{getMessage('estimate.detail.showPrice.btn1')}</button> <button className="btn-origin grey ml5">{getMessage('estimate.detail.showPrice.btn1')}</button>
</div> </div>
<div className="product-edit-wrap"> <div className="product-edit-wrap">
<div className="product-edit-explane"> <ul className="product-edit-explane">
<div className="click-check"> <li className="explane-item item01">
<span className="ico"></span> <span className="ico"></span>
{getMessage('estimate.detail.showPrice.description')} {getMessage('estimate.detail.showPrice.description1')}
</div> </li>
</div> <li className="explane-item item02">
<span className="ico"></span>
{getMessage('estimate.detail.showPrice.description2')}
</li>
<li className="explane-item item03">
<span className="ico"></span>
{getMessage('estimate.detail.showPrice.description3')}
</li>
<li className="explane-item item04">
<span className="ico"></span>
{getMessage('estimate.detail.showPrice.description4')}
</li>
</ul>
<div className="product-edit-btn"> <div className="product-edit-btn">
<button className="btn-origin navy mr5"> <button className="btn-origin navy mr5" type="submit">
<span className="plus"></span> <span className="plus"></span>
{getMessage('estimate.detail.showPrice.btn2')} {getMessage('estimate.detail.showPrice.btn2')}
</button> </button>
@ -348,6 +575,7 @@ export default function Estimate({ params }) {
</div> </div>
</div> </div>
{/* 기본정보끝 */} {/* 기본정보끝 */}
{/* </form> */}
</div> </div>
</div> </div>
) )

View File

@ -13,6 +13,9 @@ export const QLine = fabric.util.createClass(fabric.Line, {
area: 0, area: 0,
children: [], children: [],
initialize: function (points, options, length = 0) { initialize: function (points, options, length = 0) {
// 소수점 전부 제거
points = points.map((point) => Number(point.toFixed(1)))
this.callSuper('initialize', points, { ...options, selectable: options.selectable ?? true }) this.callSuper('initialize', points, { ...options, selectable: options.selectable ?? true })
if (options.id) { if (options.id) {
this.id = options.id this.id = options.id
@ -20,8 +23,6 @@ export const QLine = fabric.util.createClass(fabric.Line, {
this.id = uuidv4() this.id = uuidv4()
} }
this.line = this this.line = this
// 소수점 전부 제거
points = points.map((point) => Math.round(point))
this.idx = options.idx ?? 0 this.idx = options.idx ?? 0
this.direction = options.direction ?? getDirectionByPoint({ x: this.x1, y: this.y1 }, { x: this.x2, y: this.y2 }) this.direction = options.direction ?? getDirectionByPoint({ x: this.x1, y: this.y1 }, { x: this.x2, y: this.y2 })

View File

@ -2,26 +2,36 @@ import { fabric } from 'fabric'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
import { QLine } from '@/components/fabric/QLine' import { QLine } from '@/components/fabric/QLine'
import { distanceBetweenPoints, findTopTwoIndexesByDistance, getDirectionByPoint, sortedPointLessEightPoint, sortedPoints } from '@/util/canvas-util' import { distanceBetweenPoints, findTopTwoIndexesByDistance, getDirectionByPoint, sortedPointLessEightPoint, sortedPoints } from '@/util/canvas-util'
import { calculateAngle, drawHippedRoof, inPolygon, toGeoJSON } from '@/util/qpolygon-utils' import { calculateAngle, drawRidgeRoof, drawHippedRoof, inPolygon, toGeoJSON } from '@/util/qpolygon-utils'
import * as turf from '@turf/turf' import * as turf from '@turf/turf'
import { LINE_TYPE } from '@/common/common'
export const QPolygon = fabric.util.createClass(fabric.Polygon, { export const QPolygon = fabric.util.createClass(fabric.Polygon, {
type: 'QPolygon', type: 'QPolygon',
lines: [], // lines: [],
texts: [], // texts: [],
id: null, id: null,
length: 0, length: 0,
hips: [], // hips: [],
ridges: [], // ridges: [],
connectRidges: [], // connectRidges: [],
cells: [], // cells: [],
parentId: null, parentId: null,
innerLines: [], // innerLines: [],
children: [], // children: [],
initOptions: null, initOptions: null,
direction: null, direction: null,
arrow: null, arrow: null,
initialize: function (points, options, canvas) { initialize: function (points, options, canvas) {
this.lines = []
this.texts = []
this.hips = []
this.ridges = []
this.connectRidges = []
this.cells = []
this.innerLines = []
this.children = []
// 소수점 전부 제거 // 소수점 전부 제거
points.forEach((point) => { points.forEach((point) => {
point.x = Number(point.x.toFixed(1)) point.x = Number(point.x.toFixed(1))
@ -127,6 +137,22 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
}) })
}) })
this.on('polygonMoved', () => {
//폴리곤일때만 사용
let matrix = this.calcTransformMatrix()
let transformedPoints = this.get('points')
.map((p) => {
return new fabric.Point(p.x - this.pathOffset.x, p.y - this.pathOffset.y)
})
.map((p) => {
return fabric.util.transformPoint(p, matrix)
})
this.set('points', transformedPoints)
this.set('pathOffset', { x: this.left, y: this.top })
this.setCoords()
})
// polygon.fillCell({ width: 50, height: 30, padding: 10 }) // polygon.fillCell({ width: 50, height: 30, padding: 10 })
}, },
@ -153,9 +179,42 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
}, },
// 보조선 그리기 // 보조선 그리기
drawHelpLine(chon = 4) { drawHelpLine() {
// drawHelpLineInHexagon(this, chon) // drawHelpLineInHexagon(this, pitch)
drawHippedRoof(this, chon) const types = []
this.lines.forEach((line) => types.push(line.attributes.type))
const eavesType = [LINE_TYPE.WALLLINE.EAVES, LINE_TYPE.WALLLINE.HIPANDGABLE]
const gableType = [LINE_TYPE.WALLLINE.GABLE, LINE_TYPE.WALLLINE.JERKINHEAD]
const isEaves = types.every((type) => eavesType.includes(type))
const gableOdd = types.filter((type, i) => i % 2 === 0)
const gableEven = types.filter((type, i) => i % 2 === 1)
const hasShed = types.includes(LINE_TYPE.WALLLINE.SHED)
// A형, B형 박공 지붕
if (
(gableOdd.every((type) => type === LINE_TYPE.WALLLINE.EAVES) && gableEven.every((type) => gableType.includes(type))) ||
(gableEven.every((type) => type === LINE_TYPE.WALLLINE.EAVES) && gableOdd.every((type) => gableType.includes(type)))
) {
console.log('박공 지붕')
} else if (hasShed) {
//편류지붕
let shedIndex = 0
types.forEach((type, i) => {
if (type === LINE_TYPE.WALLLINE.SHED) {
shedIndex = i
}
})
const shedOdd = types.filter((type, i) => i % 2 === shedIndex % 2).filter((type) => type !== LINE_TYPE.WALLLINE.SHED)
const shedEven = types.filter((type, i) => i % 2 !== shedIndex % 2)
types.forEach((type, i) => console.log(type, i, i % 2, shedIndex % 2, i % 2 === shedIndex % 2))
if (shedOdd.every((type) => type === LINE_TYPE.WALLLINE.EAVES) && shedEven.every((type) => type === LINE_TYPE.WALLLINE.GABLE)) {
console.log('편류지붕')
}
} else {
drawRidgeRoof(this.id, this.canvas)
}
}, },
addLengthText() { addLengthText() {
@ -168,6 +227,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
let points = this.getCurrentPoints() let points = this.getCurrentPoints()
this.texts = []
points.forEach((start, i) => { points.forEach((start, i) => {
const end = points[(i + 1) % points.length] const end = points[(i + 1) % points.length]
const dx = end.x - start.x const dx = end.x - start.x

View File

@ -19,7 +19,7 @@ export default function CanvasFrame() {
const { canvas } = useCanvas('canvas') const { canvas } = useCanvas('canvas')
const { canvasLoadInit, gridInit } = useCanvasConfigInitialize() const { canvasLoadInit, gridInit } = useCanvasConfigInitialize()
const currentMenu = useRecoilValue(currentMenuState) const currentMenu = useRecoilValue(currentMenuState)
const { contextMenu, handleClick, handleKeyup } = useContextMenu() const { contextMenu, handleClick } = useContextMenu()
const { selectedPlan, modifiedPlanFlag, checkCanvasObjectEvent, resetModifiedPlans } = usePlan() const { selectedPlan, modifiedPlanFlag, checkCanvasObjectEvent, resetModifiedPlans } = usePlan()
useEvent() useEvent()
@ -57,7 +57,7 @@ export default function CanvasFrame() {
<div className="canvas-frame"> <div className="canvas-frame">
<canvas ref={canvasRef} id="canvas" style={{ position: 'relative' }}></canvas> <canvas ref={canvasRef} id="canvas" style={{ position: 'relative' }}></canvas>
<QContextMenu contextRef={canvasRef} canvasProps={canvas} handleKeyup={handleKeyup}> <QContextMenu contextRef={canvasRef} canvasProps={canvas}>
{contextMenu?.map((menus, index) => ( {contextMenu?.map((menus, index) => (
<ul key={index}> <ul key={index}>
{menus.map((menu) => ( {menus.map((menu) => (

View File

@ -32,6 +32,8 @@ import { menusState, menuTypeState } from '@/store/menuAtom'
import useMenu from '@/hooks/common/useMenu' import useMenu from '@/hooks/common/useMenu'
import { MENU } from '@/common/common' import { MENU } from '@/common/common'
import { useEstimateController } from '@/hooks/floorPlan/estimate/useEstimateController'
export default function CanvasMenu(props) { export default function CanvasMenu(props) {
const { menuNumber, setMenuNumber } = props const { menuNumber, setMenuNumber } = props
const pathname = usePathname() const pathname = usePathname()
@ -49,8 +51,9 @@ export default function CanvasMenu(props) {
const sessionState = useRecoilValue(sessionStore) const sessionState = useRecoilValue(sessionStore)
const globalLocale = useRecoilValue(globalLocaleStore) const globalLocale = useRecoilValue(globalLocaleStore)
const canvas = useRecoilValue(canvasState) const canvas = useRecoilValue(canvasState)
const { handleZoomClear } = useCanvasEvent() const { handleZoomClear, handleZoom } = useCanvasEvent()
const { handleMenu } = useMenu() const { handleMenu } = useMenu()
const { handleEstimateSubmit } = useEstimateController()
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { currentCanvasPlan, saveCanvas } = usePlan() const { currentCanvasPlan, saveCanvas } = usePlan()
@ -129,7 +132,7 @@ export default function CanvasMenu(props) {
const handlePopup = () => { const handlePopup = () => {
const id = uuidv4() const id = uuidv4()
addPopup(id, 0, <SettingModal01 id={id} />) addPopup(id, 1, <SettingModal01 id={id} />, true)
} }
useEffect(() => { useEffect(() => {
@ -201,14 +204,18 @@ export default function CanvasMenu(props) {
<button <button
className="control-btn minus" className="control-btn minus"
onClick={() => { onClick={() => {
canvas.setZoom(canvas.getZoom() - 0.1) handleZoom(false)
}} }}
></button> ></button>
<span>{canvasZoom}%</span> <span>{canvasZoom}%</span>
<button className="control-btn plus" onClick={handleZoomClear}></button> <button
className="control-btn plus"
onClick={() => {
handleZoom(true)
}}
></button>
</div> </div>
<div className="btn-from"> <div className="btn-from">
<button className="btn07" onClick={handleClear}></button>
<button className="btn08" onClick={handleSaveCanvas}></button> <button className="btn08" onClick={handleSaveCanvas}></button>
<button className="btn09"></button> <button className="btn09"></button>
</div> </div>
@ -220,9 +227,9 @@ export default function CanvasMenu(props) {
<div className="ico-btn-from"> <div className="ico-btn-from">
<button className="btn-frame gray ico-flx act"> <button className="btn-frame gray ico-flx act">
<span className="ico ico01"></span> <span className="ico ico01"></span>
<span>{getMessage('plan.menu.estimate.roof.alloc')}</span> <span>{getMessage('plan.menu.estimate.docDown')}</span>
</button> </button>
<button className="btn-frame gray ico-flx"> <button className="btn-frame gray ico-flx" onClick={handleEstimateSubmit}>
<span className="ico ico02"></span> <span className="ico ico02"></span>
<span>{getMessage('plan.menu.estimate.save')}</span> <span>{getMessage('plan.menu.estimate.save')}</span>
</button> </button>

View File

@ -50,7 +50,11 @@ export default function CircuitTrestleSetting({ id }) {
Next Next
</button> </button>
)} )}
{tabNum === 3 && <button className="btn-frame modal act">保存 (仮割り当て)</button>} {tabNum === 3 && (
<button className="btn-frame modal act">
{`${getMessage('modal.common.save')} (${getMessage('modal.circuit.trestle.setting.alloc.trestle')})`}
</button>
)}
</div> </div>
</div> </div>
</div> </div>

View File

@ -114,7 +114,7 @@ export default function PowerConditionalSelect({ setTabNum }) {
</div> </div>
</div> </div>
<div className="circuit-right-wrap mb10"> <div className="circuit-right-wrap mb10">
<button className="btn-frame self mr5">追加</button> <button className="btn-frame self mr5">{getMessage('modal.common.add')}</button>
</div> </div>
<div className="circuit-data-form"> <div className="circuit-data-form">
<span className="normal-font"> <span className="normal-font">
@ -132,12 +132,12 @@ export default function PowerConditionalSelect({ setTabNum }) {
<div className="slope-wrap"> <div className="slope-wrap">
<div className="d-check-box pop mb15"> <div className="d-check-box pop mb15">
<input type="checkbox" id="ch03" /> <input type="checkbox" id="ch03" />
<label htmlFor="ch03">同一傾斜同一方面の面積の場合同じ面として回路を分ける</label> <label htmlFor="ch03"> {getMessage('modal.circuit.trestle.setting.power.conditional.select.check1')}</label>
</div> </div>
<div className="d-check-box pop"> <div className="d-check-box pop">
<input type="checkbox" id="ch04" /> <input type="checkbox" id="ch04" />
<label className="red" htmlFor="ch04"> <label className="red" htmlFor="ch04">
MAX接続過積で回路を分ける {getMessage('modal.circuit.trestle.setting.power.conditional.select.check2')}
</label> </label>
</div> </div>
</div> </div>

View File

@ -18,8 +18,8 @@ export default function StepUp({}) {
<table> <table>
<thead> <thead>
<tr> <tr>
<th>シリアル枚数</th> <th>{getMessage('modal.circuit.trestle.setting.step.up.allocation.serial.amount')}</th>
<th>総回路数</th> <th>{getMessage('modal.circuit.trestle.setting.step.up.allocation.total.amount')}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -49,14 +49,14 @@ export default function StepUp({}) {
</div> </div>
<div className="circuit-table-flx-wrap"> <div className="circuit-table-flx-wrap">
<div className="circuit-table-flx-box"> <div className="circuit-table-flx-box">
<div className="bold-font mb10">接続する</div> <div className="bold-font mb10">{getMessage('modal.circuit.trestle.setting.step.up.allocation.connected')}</div>
<div className="roof-module-table mb10"> <div className="roof-module-table mb10">
<table> <table>
<thead> <thead>
<tr> <tr>
<th style={{ width: '140px' }}>名称</th> <th style={{ width: '140px' }}>{getMessage('modal.circuit.trestle.setting.power.conditional.select.name')}</th>
<th>回路数</th> <th>{getMessage('modal.circuit.trestle.setting.power.conditional.select.circuit.amount')}</th>
<th>昇圧回路数</th> <th>{getMessage('modal.circuit.trestle.setting.step.up.allocation.circuit.amount')}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -80,7 +80,7 @@ export default function StepUp({}) {
</div> </div>
<div className="bottom-wrap"> <div className="bottom-wrap">
<div className="circuit-right-wrap mb10"> <div className="circuit-right-wrap mb10">
<button className="btn-frame self mr5">追加</button> <button className="btn-frame self mr5">{getMessage('modal.common.add')}</button>
</div> </div>
<div className="circuit-data-form"> <div className="circuit-data-form">
<span className="normal-font"> <span className="normal-font">
@ -90,13 +90,13 @@ export default function StepUp({}) {
</div> </div>
</div> </div>
<div className="circuit-table-flx-box"> <div className="circuit-table-flx-box">
<div className="bold-font mb10">昇圧オプション</div> <div className="bold-font mb10">{getMessage('modal.circuit.trestle.setting.step.up.allocation.option')}</div>
<div className="roof-module-table mb10"> <div className="roof-module-table mb10">
<table> <table>
<thead> <thead>
<tr> <tr>
<th>名称</th> <th>{getMessage('modal.circuit.trestle.setting.power.conditional.select.name')}</th>
<th>昇圧回路数</th> <th>{getMessage('modal.circuit.trestle.setting.step.up.allocation.circuit.amount')}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -113,7 +113,7 @@ export default function StepUp({}) {
</div> </div>
<div className="bottom-wrap"> <div className="bottom-wrap">
<div className="circuit-right-wrap mb10"> <div className="circuit-right-wrap mb10">
<button className="btn-frame self mr5">追加</button> <button className="btn-frame self mr5">{getMessage('modal.common.add')}</button>
</div> </div>
<div className="circuit-data-form"> <div className="circuit-data-form">
<span className="normal-font"> <span className="normal-font">
@ -124,7 +124,7 @@ export default function StepUp({}) {
</div> </div>
</div> </div>
<div className="circuit-count-input"> <div className="circuit-count-input">
<span className="normal-font">綿調道区分</span> <span className="normal-font">{getMessage('modal.module.basic.setting.module.cotton.classification')}</span>
<div className="input-grid mr5" style={{ width: '40px' }}> <div className="input-grid mr5" style={{ width: '40px' }}>
<input type="text" className="input-origin block" /> <input type="text" className="input-origin block" />
</div> </div>
@ -395,7 +395,7 @@ export default function StepUp({}) {
<div className="slope-wrap"> <div className="slope-wrap">
<div className="outline-form"> <div className="outline-form">
<span className="mr10" style={{ width: 'auto' }}> <span className="mr10" style={{ width: 'auto' }}>
モニターの選択 {getMessage('modal.circuit.trestle.setting.step.up.allocation.select.monitor')}
</span> </span>
<div className="grid-select mr10"> <div className="grid-select mr10">
<QSelectBox title={'電力検出ユニット (モニター付き)'} option={SelectOption01} /> <QSelectBox title={'電力検出ユニット (モニター付き)'} option={SelectOption01} />

View File

@ -4,13 +4,13 @@ export default function PassivityCircuitAllocation() {
const { getMessage } = useMessage() const { getMessage } = useMessage()
const moduleData = { const moduleData = {
header: [ header: [
{ name: getMessage('屋根面'), prop: 'roofShape' }, { name: getMessage('modal.panel.batch.statistic.roof.shape'), prop: 'roofShape' },
{ {
name: getMessage('Q.TRON M-G2'), name: getMessage('Q.TRON M-G2'),
prop: 'moduleName', prop: 'moduleName',
}, },
{ {
name: getMessage('発電量 (kW)'), name: `${getMessage('modal.panel.batch.statistic.power.generation.amount')}(kW)`,
prop: 'powerGeneration', prop: 'powerGeneration',
}, },
], ],

View File

@ -49,9 +49,8 @@ export default function DimensionLineSetting(props) {
resultText = calculateLength(basicLength, slopeInput1.angleValue).toFixed(0) resultText = calculateLength(basicLength, slopeInput1.angleValue).toFixed(0)
if (slopeInput2) { if (slopeInput2) {
const angle = slopeInput1 + slopeInput2 const length = calculateLength(basicLength, slopeInput1.angleValue, slopeInput2.angleValue)
const length = calculateLength(basicLength, angle) resultText = length.toFixed(0)
resultText = length.toFixed(2)
} }
} }
} }
@ -71,11 +70,18 @@ export default function DimensionLineSetting(props) {
} }
function calculateLength(originalLength, angle) { function calculateLength(originalLength, angle) {
const angleInRadians = angle * (Math.PI / 180) // const angleInRadians = angle * (Math.PI / 180)
const result = Math.sqrt(Math.pow(originalLength * Math.tan(angleInRadians), 2) + Math.pow(originalLength, 2)) const result = Math.sqrt(Math.pow(originalLength * Math.tan(angleInRadians), 2) + Math.pow(originalLength, 2))
return result return result
} }
function calculateLength(originalLength, angle1, angle2) {
const numerator = Math.sqrt(Math.pow(angle1, 2) + 100 + Math.pow((10 * angle1) / angle2, 2)) * originalLength
const denominator = Math.sqrt(Math.pow((10 * angle1) / angle2, 2) + 100)
const result = numerator / denominator
return result
}
return ( return (
<WithDraggable isShow={true} pos={pos}> <WithDraggable isShow={true} pos={pos}>
<div className={`modal-pop-wrap xm mount`}> <div className={`modal-pop-wrap xm mount`}>

View File

@ -1,4 +1,4 @@
import WithDraggable from '@/components/common/draggable/withDraggable' import WithDraggable from '@/components/common/draggable/WithDraggable'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import QSelectBox from '@/components/common/select/QSelectBox' import QSelectBox from '@/components/common/select/QSelectBox'
import { useRecoilValue } from 'recoil' import { useRecoilValue } from 'recoil'
@ -60,7 +60,7 @@ export default function FlowDirectionSetting(props) {
}, [compasDeg]) }, [compasDeg])
return ( return (
<WithDraggable isShow={true} pos={pos}> <WithDraggable isShow={true} pos={pos}>
<div className={`modal-pop-wrap lx mount`}> <div className={`modal-pop-wrap ml mount`}>
<div className="modal-head"> <div className="modal-head">
<h1 className="title">{getMessage('modal.shape.flow.direction.setting')} </h1> <h1 className="title">{getMessage('modal.shape.flow.direction.setting')} </h1>
<button className="modal-close" onClick={() => closePopup(id)}> <button className="modal-close" onClick={() => closePopup(id)}>
@ -103,28 +103,30 @@ export default function FlowDirectionSetting(props) {
<label htmlFor="ra02">{getMessage('modal.shape.flow.direction.setting.orientation.24')}</label> <label htmlFor="ra02">{getMessage('modal.shape.flow.direction.setting.orientation.24')}</label>
</div> </div>
</div> </div>
<div className="compas-box"> <div className="draw-flow-wrap">
<div className="compas-box-inner"> <div className="compas-box">
{Array.from({ length: 180 / 15 + 1 }).map((dot, index) => ( <div className="compas-box-inner">
<div {Array.from({ length: 180 / 15 + 1 }).map((dot, index) => (
key={index} <div
className={`circle ${compasDeg === 15 * (12 + index) ? 'act' : ''}`} key={index}
onClick={() => setCompasDeg(15 * (12 + index))} className={`circle ${compasDeg === 15 * (12 + index) ? 'act' : ''}`}
> onClick={() => setCompasDeg(15 * (12 + index))}
<i>{13 - index}</i> >
<i>{13 - index}</i>
</div>
))}
{Array.from({ length: 180 / 15 - 1 }).map((dot, index) => (
<div
key={index}
className={`circle ${compasDeg === 15 * (index + 1) ? 'act' : ''}`}
onClick={() => setCompasDeg(15 * (index + 1))}
>
<i>{24 - index}</i>
</div>
))}
<div className="compas">
<div className="compas-arr" style={{ transform: `rotate(${compasDeg}deg)` }}></div>
</div> </div>
))}
{Array.from({ length: 180 / 15 - 1 }).map((dot, index) => (
<div
key={index}
className={`circle ${compasDeg === 15 * (index + 1) ? 'act' : ''}`}
onClick={() => setCompasDeg(15 * (index + 1))}
>
<i>{24 - index}</i>
</div>
))}
<div className="compas">
<div className="compas-arr" style={{ transform: `rotate(${compasDeg}deg)` }}></div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -5,9 +5,7 @@ import { useMessage } from '@/hooks/useMessage'
import { canvasState, dotLineGridSettingState, dotLineIntervalSelector } from '@/store/canvasAtom' import { canvasState, dotLineGridSettingState, dotLineIntervalSelector } from '@/store/canvasAtom'
import { useRecoilState, useRecoilValue, useResetRecoilState, useSetRecoilState } from 'recoil' import { useRecoilState, useRecoilValue, useResetRecoilState, useSetRecoilState } from 'recoil'
import { onlyNumberInputChange } from '@/util/input-utils' import { onlyNumberInputChange } from '@/util/input-utils'
import { fabric } from 'fabric' import { settingModalGridOptionsState } from '@/store/settingAtom'
import { gridColorState } from '@/store/gridAtom'
import { gridDisplaySelector, settingModalGridOptionsState } from '@/store/settingAtom'
import { useAxios } from '@/hooks/useAxios' import { useAxios } from '@/hooks/useAxios'
import { useSwal } from '@/hooks/useSwal' import { useSwal } from '@/hooks/useSwal'
import { usePopup } from '@/hooks/usePopup' import { usePopup } from '@/hooks/usePopup'
@ -33,7 +31,7 @@ export default function DotLineGrid(props) {
// const [modalOption, setModalOption] = useRecoilState(modalState); //modal state // const [modalOption, setModalOption] = useRecoilState(modalState); //modal state
const [objectNo, setObjectNo] = useState('test123240912001') // const [objectNo, setObjectNo] = useState('test123240912001') //
const [close, setClose] = useState(false) const [close, setClose] = useState(false)
const { id, setIsShow, pos = { x: 840, y: -815 } } = props const { id, setIsShow, pos = { x: 840, y: -815 }, isConfig = false } = props
const setSettingModalGridOptions = useSetRecoilState(settingModalGridOptionsState) const setSettingModalGridOptions = useSetRecoilState(settingModalGridOptionsState)
const canvas = useRecoilValue(canvasState) const canvas = useRecoilValue(canvasState)
@ -142,7 +140,7 @@ export default function DotLineGrid(props) {
await post({ url: `/api/canvas-management/canvas-grid-settings`, data: patternData }).then((res) => { await post({ url: `/api/canvas-management/canvas-grid-settings`, data: patternData }).then((res) => {
swalFire({ text: getMessage(res.returnMessage) }) swalFire({ text: getMessage(res.returnMessage) })
setDotLineGridSettingState({ ...currentSetting }) setDotLineGridSettingState({ ...currentSetting })
closePopup(id) closePopup(id, isConfig)
}) })
} catch (error) { } catch (error) {
swalFire({ text: getMessage(res.returnMessage), icon: 'error' }) swalFire({ text: getMessage(res.returnMessage), icon: 'error' })
@ -213,7 +211,7 @@ export default function DotLineGrid(props) {
className="modal-close" className="modal-close"
onClick={() => { onClick={() => {
setIsShow(false) setIsShow(false)
closePopup(id) closePopup(id, isConfig)
}} }}
> >
닫기 닫기

View File

@ -1,4 +1,4 @@
import WithDraggable from '@/components/common/draggable/withDraggable' import WithDraggable from '@/components/common/draggable/WithDraggable'
import { useRecoilValue } from 'recoil' import { useRecoilValue } from 'recoil'
import { contextPopupPositionState } from '@/store/popupAtom' import { contextPopupPositionState } from '@/store/popupAtom'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'

View File

@ -1,4 +1,4 @@
import WithDraggable from '@/components/common/draggable/withDraggable' import WithDraggable from '@/components/common/draggable/WithDraggable'
import { useRecoilValue } from 'recoil' import { useRecoilValue } from 'recoil'
import { contextPopupPositionState } from '@/store/popupAtom' import { contextPopupPositionState } from '@/store/popupAtom'
import { usePopup } from '@/hooks/usePopup' import { usePopup } from '@/hooks/usePopup'

View File

@ -1,4 +1,4 @@
import WithDraggable from '@/components/common/draggable/withDraggable' import WithDraggable from '@/components/common/draggable/WithDraggable'
import Image from 'next/image' import Image from 'next/image'
import { useState } from 'react' import { useState } from 'react'
import { useRecoilValue } from 'recoil' import { useRecoilValue } from 'recoil'

View File

@ -1,4 +1,4 @@
import WithDraggable from '@/components/common/draggable/withDraggable' import WithDraggable from '@/components/common/draggable/WithDraggable'
import Image from 'next/image' import Image from 'next/image'
import { useState } from 'react' import { useState } from 'react'
import { useRecoilValue } from 'recoil' import { useRecoilValue } from 'recoil'

View File

@ -1,22 +1,49 @@
import { useState, useEffect, useRef } from 'react'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import WithDraggable from '@/components/common/draggable/WithDraggable' import WithDraggable from '@/components/common/draggable/WithDraggable'
import { useRecoilValue } from 'recoil' import { useRecoilValue } from 'recoil'
import { contextPopupPositionState } from '@/store/popupAtom' import { contextPopupPositionState } from '@/store/popupAtom'
import { usePopup } from '@/hooks/usePopup' import { usePopup } from '@/hooks/usePopup'
import { useState } from 'react' import { useObjectBatch } from '@/hooks/object/useObjectBatch'
import { canvasState } from '@/store/canvasAtom'
export default function DormerOffset(props) { export default function DormerOffset(props) {
const contextPopupPosition = useRecoilValue(contextPopupPositionState) const contextPopupPosition = useRecoilValue(contextPopupPositionState)
const { id, pos = contextPopupPosition } = props const { id, pos = contextPopupPosition, title } = props
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { closePopup } = usePopup() const { closePopup } = usePopup()
const [arrow1, setArrow1] = useState(null) const [arrow1, setArrow1] = useState(null)
const [arrow2, setArrow2] = useState(null) const [arrow2, setArrow2] = useState(null)
const arrow1LengthRef = useRef()
const arrow2LengthRef = useRef()
const canvas = useRecoilValue(canvasState)
const { dormerOffsetKeyEvent, dormerOffset } = useObjectBatch({})
useEffect(() => {
if (canvas) {
dormerOffsetKeyEvent(setArrow1, setArrow2)
}
}, [])
const handleOffsetDormer = () => {
const length1 = arrow1LengthRef.current.value
const length2 = arrow2LengthRef.current.value
dormerOffset(arrow1, arrow2, length1, length2)
setArrow1(null)
setArrow2(null)
arrow1LengthRef.current.value = ''
arrow2LengthRef.current.value = ''
// closePopup(id)
}
return ( return (
<WithDraggable isShow={true} pos={pos}> <WithDraggable isShow={true} pos={pos}>
<div className={`modal-pop-wrap xm mount`}> <div className={`modal-pop-wrap xm mount`}>
<div className="modal-head"> <div className="modal-head">
<h1 className="title">{getMessage('contextmenu.dormer.offset')}</h1> <h1 className="title">{title}</h1>
<button className="modal-close" onClick={() => closePopup(id)}> <button className="modal-close" onClick={() => closePopup(id)}>
닫기 닫기
</button> </button>
@ -29,44 +56,40 @@ export default function DormerOffset(props) {
<p className="mb5">{getMessage('length')}</p> <p className="mb5">{getMessage('length')}</p>
<div className="input-move-wrap mb5"> <div className="input-move-wrap mb5">
<div className="input-move"> <div className="input-move">
<input type="text" className="input-origin" defaultValue={0} /> <input type="text" className="input-origin" ref={arrow1LengthRef} placeholder="0" />
</div> </div>
<span>mm</span> <span>mm</span>
<div className="direction-move-wrap"> <div className="direction-move-wrap">
<button <button
className={`direction up ${arrow1 === '' ? 'act' : ''}`} className={`direction up ${arrow1 === 'up' ? 'act' : ''}`}
onClick={() => { onClick={() => {
setArrow1('↑') setArrow1('up')
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' }))
}} }}
></button> ></button>
<button <button
className={`direction down ${arrow1 === '' ? 'act' : ''}`} className={`direction down ${arrow1 === 'down' ? 'act' : ''}`}
onClick={() => { onClick={() => {
setArrow1('↓') setArrow1('down')
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' }))
}} }}
></button> ></button>
</div> </div>
</div> </div>
<div className="input-move-wrap"> <div className="input-move-wrap">
<div className="input-move"> <div className="input-move">
<input type="text" className="input-origin" defaultValue={0} /> <input type="text" className="input-origin" ref={arrow2LengthRef} placeholder="0" />
</div> </div>
<span>mm</span> <span>mm</span>
<div className="direction-move-wrap"> <div className="direction-move-wrap">
<button <button
className={`direction left ${arrow2 === '' ? 'act' : ''}`} className={`direction left ${arrow2 === 'left' ? 'act' : ''}`}
onClick={() => { onClick={() => {
setArrow2('←') setArrow2('left')
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowLeft' }))
}} }}
></button> ></button>
<button <button
className={`direction right ${arrow2 === '' ? 'act' : ''}`} className={`direction right ${arrow2 === 'right' ? 'act' : ''}`}
onClick={() => { onClick={() => {
setArrow2('→') setArrow2('right')
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowRight' }))
}} }}
></button> ></button>
</div> </div>
@ -75,7 +98,9 @@ export default function DormerOffset(props) {
</div> </div>
</div> </div>
<div className="grid-btn-wrap"> <div className="grid-btn-wrap">
<button className="btn-frame modal act">{getMessage('modal.common.save')}</button> <button className="btn-frame modal act" onClick={handleOffsetDormer}>
{getMessage('modal.common.save')}
</button>
</div> </div>
</div> </div>
</div> </div>

View File

@ -5,7 +5,11 @@ import { useMessage } from '@/hooks/useMessage'
import WithDraggable from '@/components/common/draggable/WithDraggable' import WithDraggable from '@/components/common/draggable/WithDraggable'
import { usePopup } from '@/hooks/usePopup' import { usePopup } from '@/hooks/usePopup'
import { contextPopupPositionState } from '@/store/popupAtom' import { contextPopupPositionState } from '@/store/popupAtom'
import { useState } from 'react' import { useRef, useState, useEffect } from 'react'
import { useObjectBatch } from '@/hooks/object/useObjectBatch'
import { useEvent } from '@/hooks/useEvent'
import { BATCH_TYPE, POLYGON_TYPE } from '@/common/common'
import { useSurfaceShapeBatch } from '@/hooks/surface/useSurfaceShapeBatch'
export default function SizeSetting(props) { export default function SizeSetting(props) {
const contextPopupPosition = useRecoilValue(contextPopupPositionState) const contextPopupPosition = useRecoilValue(contextPopupPositionState)
@ -13,6 +17,31 @@ export default function SizeSetting(props) {
const { id, pos = contextPopupPosition, target } = props const { id, pos = contextPopupPosition, target } = props
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { closePopup } = usePopup() const { closePopup } = usePopup()
const { resizeObjectBatch } = useObjectBatch({})
const { reSizePolygon } = useSurfaceShapeBatch()
const widthRef = useRef(null)
const heightRef = useRef(null)
const { initEvent } = useEvent()
useEffect(() => {
initEvent()
}, [])
const handleReSizeObject = () => {
const width = widthRef.current.value
const height = heightRef.current.value
if (
target.name === BATCH_TYPE.OPENING ||
target.name === BATCH_TYPE.SHADOW ||
target.name === BATCH_TYPE.TRIANGLE_DORMER ||
target.name === BATCH_TYPE.PENTAGON_DORMER ||
target.name === POLYGON_TYPE.ROOF
) {
resizeObjectBatch(settingTarget, target, width, height)
}
}
return ( return (
<WithDraggable isShow={true} pos={pos}> <WithDraggable isShow={true} pos={pos}>
@ -28,11 +57,11 @@ export default function SizeSetting(props) {
<div className="size-option-top"> <div className="size-option-top">
<div className="size-option-wrap"> <div className="size-option-wrap">
<div className="size-option mb5"> <div className="size-option mb5">
<input type="text" className="input-origin mr5" defaultValue={1000} readOnly value={target?.width * 10 * 2} /> <input type="text" className="input-origin mr5" value={target?.width.toFixed(0) * 10} readOnly={true} />
<span className="normal-font">mm</span> <span className="normal-font">mm</span>
</div> </div>
<div className="size-option"> <div className="size-option">
<input type="text" className="input-origin mr5" defaultValue={1000} value={target?.width * 10 * 2} /> <input type="text" className="input-origin mr5" defaultValue={target?.width.toFixed(0) * 10} ref={widthRef} />
<span className="normal-font">mm</span> <span className="normal-font">mm</span>
</div> </div>
</div> </div>
@ -41,11 +70,11 @@ export default function SizeSetting(props) {
<div className="size-option-side"> <div className="size-option-side">
<div className="size-option-wrap"> <div className="size-option-wrap">
<div className="size-option mb5"> <div className="size-option mb5">
<input type="text" className="input-origin mr5" defaultValue={1000} readOnly value={target?.height * 10} /> <input type="text" className="input-origin mr5" value={target?.height.toFixed(0) * 10} readOnly={true} />
<span className="normal-font">mm</span> <span className="normal-font">mm</span>
</div> </div>
<div className="size-option"> <div className="size-option">
<input type="text" className="input-origin mr5" defaultValue={1000} value={target?.height * 10} /> <input type="text" className="input-origin mr5" defaultValue={target?.height.toFixed(0) * 10} ref={heightRef} />
<span className="normal-font">mm</span> <span className="normal-font">mm</span>
</div> </div>
</div> </div>
@ -60,7 +89,9 @@ export default function SizeSetting(props) {
</div> </div>
</div> </div>
<div className="grid-btn-wrap"> <div className="grid-btn-wrap">
<button className="btn-frame modal act">{getMessage('write')}</button> <button className="btn-frame modal act" onClick={() => handleReSizeObject()}>
{getMessage('write')}
</button>
</div> </div>
</div> </div>
</div> </div>

View File

@ -38,7 +38,7 @@ const PentagonDormer = forwardRef((props, refs) => {
<div className="eaves-keraba-td"> <div className="eaves-keraba-td">
<div className="outline-form"> <div className="outline-form">
<div className="input-grid mr5" style={{ width: '60px' }}> <div className="input-grid mr5" style={{ width: '60px' }}>
<input type="text" className="input-origin block" placeholder={0} ref={refs.offsetRef} /> <input type="text" className="input-origin block" placeholder={0} ref={refs.offsetRef} defaultValue={400} />
</div> </div>
<span className="thin">mm</span> <span className="thin">mm</span>
</div> </div>
@ -60,7 +60,7 @@ const PentagonDormer = forwardRef((props, refs) => {
<div className="eaves-keraba-td"> <div className="eaves-keraba-td">
<div className="outline-form"> <div className="outline-form">
<div className="input-grid mr5" style={{ width: '60px' }}> <div className="input-grid mr5" style={{ width: '60px' }}>
<input type="text" className="input-origin block" placeholder={0} ref={refs.offsetWidthRef} /> <input type="text" className="input-origin block" placeholder={0} ref={refs.offsetWidthRef} defaultValue={300} />
</div> </div>
<span className="thin">mm</span> <span className="thin">mm</span>
</div> </div>

View File

@ -38,7 +38,7 @@ const TriangleDormer = forwardRef((props, refs) => {
<div className="eaves-keraba-td"> <div className="eaves-keraba-td">
<div className="outline-form"> <div className="outline-form">
<div className="input-grid mr5" style={{ width: '60px' }}> <div className="input-grid mr5" style={{ width: '60px' }}>
<input type="text" className="input-origin block" placeholder={0} ref={refs.offsetRef} /> <input type="text" className="input-origin block" placeholder={0} ref={refs.offsetRef} defaultValue={400} />
</div> </div>
<span className="thin">mm</span> <span className="thin">mm</span>
</div> </div>

View File

@ -2,7 +2,7 @@
import { useState } from 'react' import { useState } from 'react'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import WithDraggable from '@/components/common/draggable/withDraggable' import WithDraggable from '@/components/common/draggable/WithDraggable'
export default function PanelBatchStatistics() { export default function PanelBatchStatistics() {
const { getMessage } = useMessage() const { getMessage } = useMessage()

View File

@ -180,8 +180,10 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, set
</tr> </tr>
<tr> <tr>
<th> <th>
{getMessage('modal.placement.initial.setting.size')} <div className="tip-wrap">
<button className="tooltip" onClick={() => setShowSizeGuidModal(true)}></button> {getMessage('modal.placement.initial.setting.size')}
<button className="tooltip" onClick={() => setShowSizeGuidModal(true)}></button>
</div>
</th> </th>
<td> <td>
<div className="pop-form-radio"> <div className="pop-form-radio">
@ -252,8 +254,10 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, set
</tr> </tr>
<tr> <tr>
<th> <th>
{getMessage('modal.placement.initial.setting.roof.material')} <div className="tip-wrap">
<button className="tooltip" onClick={() => setShowMaterialGuidModal(true)}></button> {getMessage('modal.placement.initial.setting.roof.material')}
<button className="tooltip" onClick={() => setShowMaterialGuidModal(true)}></button>
</div>
</th> </th>
<td> <td>
<div className="placement-option"> <div className="placement-option">

View File

@ -240,7 +240,7 @@ export default function PlacementSurfaceSetting({ id, pos = { x: 50, y: 230 } })
}, []) }, [])
return ( return (
<WithDraggable isShow={true} pos={{ x: 50, y: 230 }}> <WithDraggable isShow={true} pos={pos}>
<div className={`modal-pop-wrap lr-2`}> <div className={`modal-pop-wrap lr-2`}>
<div className="modal-head"> <div className="modal-head">
<h1 className="title">{getMessage('plan.menu.placement.surface.arrangement')} </h1> <h1 className="title">{getMessage('plan.menu.placement.surface.arrangement')} </h1>

View File

@ -78,14 +78,14 @@ export default function GridOption() {
// //
setShowDotLineGridModal(selectedOption.selected) setShowDotLineGridModal(selectedOption.selected)
addPopup(dotLineId, 2, <DotLineGrid {...dotLineGridProps} />) addPopup(dotLineId, 2, <DotLineGrid {...dotLineGridProps} />, true)
} else if (selectedOption.id === 3) { } else if (selectedOption.id === 3) {
// //
setAdsorptionPointAddMode(selectedOption.selected) setAdsorptionPointAddMode(selectedOption.selected)
} else if (selectedOption.id === 4) { } else if (selectedOption.id === 4) {
// //
setShowColorPickerModal(selectedOption.selected) setShowColorPickerModal(selectedOption.selected)
addPopup(colorId, 2, <ColorPickerModal {...colorPickerProps} />) addPopup(colorId, 2, <ColorPickerModal {...colorPickerProps} />, true)
} }
setGridOptions(newGridOptions) setGridOptions(newGridOptions)
@ -94,6 +94,7 @@ export default function GridOption() {
const dotLineGridProps = { const dotLineGridProps = {
id: dotLineId, id: dotLineId,
setIsShow: setShowDotLineGridModal, setIsShow: setShowDotLineGridModal,
isConfig: true,
pos: { pos: {
x: 845, x: 845,
y: 180, y: 180,
@ -106,6 +107,7 @@ export default function GridOption() {
setColor: setGridColor, setColor: setGridColor,
isShow: showColorPickerModal, isShow: showColorPickerModal,
setIsShow: setShowColorPickerModal, setIsShow: setShowColorPickerModal,
isConfig: true,
pos: { pos: {
x: 785, x: 785,
y: 180, y: 180,

View File

@ -1,4 +1,4 @@
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil' import { useRecoilValue } from 'recoil'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import React, { useEffect, useState } from 'react' import React, { useEffect, useState } from 'react'
import DimensionLineSetting from '@/components/floor-plan/modal/setting01/dimensionLine/DimensionLineSetting' import DimensionLineSetting from '@/components/floor-plan/modal/setting01/dimensionLine/DimensionLineSetting'
@ -67,6 +67,7 @@ export default function SecondOption() {
id: fontId, id: fontId,
pos: { x: 745, y: 180 }, pos: { x: 745, y: 180 },
setIsShow: setShowFontSettingModal, setIsShow: setShowFontSettingModal,
isConfig: true,
} }
const planSizeProps = { const planSizeProps = {
id: planSizeId, id: planSizeId,
@ -86,36 +87,41 @@ export default function SecondOption() {
case 'font1': { case 'font1': {
// //
setShowFontSettingModal(true) setShowFontSettingModal(true)
setShowDimensionLineSettingModal(false)
fontProps.type = 'commonText' fontProps.type = 'commonText'
fontProps.id = fontId + 1 fontProps.id = fontId + 1
addPopup(fontId + 1, 2, <FontSetting {...fontProps} />) addPopup(fontId + 1, 2, <FontSetting {...fontProps} />, true)
break break
} }
case 'font2': { case 'font2': {
// //
setShowFontSettingModal(true) setShowFontSettingModal(true)
setShowDimensionLineSettingModal(false)
fontProps.type = 'flowText' fontProps.type = 'flowText'
fontProps.id = fontId + 2 fontProps.id = fontId + 2
addPopup(fontId + 2, 2, <FontSetting {...fontProps} />) addPopup(fontId + 2, 2, <FontSetting {...fontProps} />, true)
break break
} }
case 'font3': { case 'font3': {
// //
setShowFontSettingModal(true) setShowFontSettingModal(true)
setShowDimensionLineSettingModal(false)
fontProps.type = 'lengthText' fontProps.type = 'lengthText'
fontProps.id = fontId + 3 fontProps.id = fontId + 3
addPopup(fontId + 3, 2, <FontSetting {...fontProps} />) addPopup(fontId + 3, 2, <FontSetting {...fontProps} />, true)
break break
} }
case 'font4': { case 'font4': {
// //
setShowFontSettingModal(true) setShowFontSettingModal(true)
setShowDimensionLineSettingModal(false)
fontProps.type = 'circuitNumberText' fontProps.type = 'circuitNumberText'
fontProps.id = fontId fontProps.id = fontId
addPopup(fontId, 2, <FontSetting {...fontProps} />) addPopup(fontId, 2, <FontSetting {...fontProps} />, true)
break break
} }
@ -123,7 +129,7 @@ export default function SecondOption() {
// //
if (!showDimensionLineSettingModal) { if (!showDimensionLineSettingModal) {
setShowDimensionLineSettingModal(true) setShowDimensionLineSettingModal(true)
addPopup(dimensionId, 2, <DimensionLineSetting {...dimensionProps} />) addPopup(dimensionId, 2, <DimensionLineSetting {...dimensionProps} />, true)
} else { } else {
setShowDimensionLineSettingModal(false) setShowDimensionLineSettingModal(false)
closePopup(dimensionId) closePopup(dimensionId)
@ -134,7 +140,8 @@ export default function SecondOption() {
case 'planSize': { case 'planSize': {
// //
setShowPlanSizeSettingModal(true) setShowPlanSizeSettingModal(true)
addPopup(planSizeId, 2, <PlanSizeSetting {...planSizeProps} />) setShowDimensionLineSettingModal(false)
addPopup(planSizeId, 2, <PlanSizeSetting {...planSizeProps} />, true)
break break
} }
} }

View File

@ -11,12 +11,12 @@ import { useRecoilValue } from 'recoil'
import { usePopup } from '@/hooks/usePopup' import { usePopup } from '@/hooks/usePopup'
export default function SettingModal01(props) { export default function SettingModal01(props) {
const { setShowDotLineGridModal, setShowFontSettingModal, id } = props const { id } = props
console.log(props)
const [buttonAct, setButtonAct] = useState(1) const [buttonAct, setButtonAct] = useState(1)
const { getMessage } = useMessage() const { getMessage } = useMessage()
const canGridOptionSeletorValue = useRecoilValue(canGridOptionSeletor) const canGridOptionSeletorValue = useRecoilValue(canGridOptionSeletor)
const { addPopup, closePopup } = usePopup() const { closePopup } = usePopup()
const handleBtnClick = (num) => { const handleBtnClick = (num) => {
setButtonAct(num) setButtonAct(num)
} }
@ -27,7 +27,7 @@ export default function SettingModal01(props) {
<div className={`modal-pop-wrap sm mount`}> <div className={`modal-pop-wrap sm mount`}>
<div className="modal-head"> <div className="modal-head">
<h1 className="title">{getMessage('modal.canvas.setting')}</h1> <h1 className="title">{getMessage('modal.canvas.setting')}</h1>
<button className="modal-close" onClick={() => closePopup(id)}> <button className="modal-close" onClick={() => closePopup(id, true)}>
닫기 닫기
</button> </button>
</div> </div>

View File

@ -87,6 +87,7 @@ export default function DimensionLineSetting(props) {
setFontColor: setOriginFontColor, setFontColor: setOriginFontColor,
fontSize: originFontSize, fontSize: originFontSize,
setFontSize: setOriginFontSize, setFontSize: setOriginFontSize,
isConfig: true,
id: fontModalId, id: fontModalId,
pos: { pos: {
x: 455, x: 455,
@ -97,17 +98,17 @@ export default function DimensionLineSetting(props) {
const popupHandle = (type) => { const popupHandle = (type) => {
switch (type) { switch (type) {
case 'color': case 'color':
addPopup(colorModalId, 3, <ColorPickerModal {...colorPickerProps} />) addPopup(colorModalId, 3, <ColorPickerModal {...colorPickerProps} />, true)
break break
case 'font': case 'font':
addPopup(fontModalId, 3, <FontSetting {...fontProps} />) addPopup(fontModalId, 3, <FontSetting {...fontProps} />, true)
break break
} }
} }
return ( return (
<WithDraggable isShow={true} pos={pos}> <WithDraggable isShow={true} pos={pos}>
<div className={`modal-pop-wrap xxxm`}> <div className={`modal-pop-wrap xxxm mount`}>
<div className="modal-head"> <div className="modal-head">
<h1 className="title">{getMessage('modal.canvas.setting.font.plan.absorption.dimension.line')} </h1> <h1 className="title">{getMessage('modal.canvas.setting.font.plan.absorption.dimension.line')} </h1>
<button <button

View File

@ -15,14 +15,14 @@ export default function PlanSizeSetting(props) {
return ( return (
<WithDraggable isShow={true} pos={pos}> <WithDraggable isShow={true} pos={pos}>
<div className={`modal-pop-wrap xsm`}> <div className={`modal-pop-wrap xsm mount`}>
<div className="modal-head"> <div className="modal-head">
<h1 className="title">{getMessage('modal.canvas.setting.font.plan.absorption.plan.size.setting')}</h1> <h1 className="title">{getMessage('modal.canvas.setting.font.plan.absorption.plan.size.setting')}</h1>
<button <button
className="modal-close" className="modal-close"
onClick={() => { onClick={() => {
setIsShow(false) setIsShow(false)
closePopup(id) closePopup(id, true)
}} }}
> >
닫기 닫기

View File

@ -5,7 +5,7 @@ import { useRouter, usePathname } from 'next/navigation'
import { useAxios } from '@/hooks/useAxios' import { useAxios } from '@/hooks/useAxios'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import StuffQGrid from './StuffQGrid' import StuffQGrid from './StuffQGrid'
import { useRecoilValue, useRecoilState, useSetRecoilState } from 'recoil' import { useRecoilValue, useRecoilState, useSetRecoilState, useResetRecoilState } from 'recoil'
import { stuffSearchState } from '@/store/stuffAtom' import { stuffSearchState } from '@/store/stuffAtom'
import { queryStringFormatter, isEmptyArray } from '@/util/common-utils' import { queryStringFormatter, isEmptyArray } from '@/util/common-utils'
import dayjs from 'dayjs' import dayjs from 'dayjs'
@ -19,7 +19,7 @@ import { sessionStore } from '@/store/commonAtom'
import { SessionContext } from '@/app/SessionProvider' import { SessionContext } from '@/app/SessionProvider'
export default function Stuff() { export default function Stuff() {
const sessionState = useRecoilValue(sessionStore) const resetStuffRecoil = useResetRecoilState(stuffSearchState)
const { session } = useContext(SessionContext) const { session } = useContext(SessionContext)
const setAppMessageState = useSetRecoilState(appMessageStore) const setAppMessageState = useSetRecoilState(appMessageStore)
const stuffSearchParams = useRecoilValue(stuffSearchState) const stuffSearchParams = useRecoilValue(stuffSearchState)
@ -34,9 +34,6 @@ export default function Stuff() {
const { get } = useAxios(globalLocaleState) const { get } = useAxios(globalLocaleState)
const gridRef = useRef() const gridRef = useRef()
// const [selectedRowData, setSelectedRowData] = useState([])
// const [selectedRowDataCount, setSelectedRowDataCount] = useState(0)
const router = useRouter() const router = useRouter()
const pathname = usePathname() const pathname = usePathname()
@ -67,10 +64,6 @@ export default function Stuff() {
field: 'lastEditDatetime', field: 'lastEditDatetime',
minWidth: 200, minWidth: 200,
headerName: getMessage('stuff.gridHeader.lastEditDatetime'), headerName: getMessage('stuff.gridHeader.lastEditDatetime'),
// headerCheckboxSelection: true,
// headerCheckboxSelectionCurrentPageOnly: true, //
// checkboxSelection: true,
// showDisabledCheckboxes: true,
cellStyle: { justifyContent: 'center' }, cellStyle: { justifyContent: 'center' },
valueFormatter: function (params) { valueFormatter: function (params) {
if (params.value) { if (params.value) {
@ -167,188 +160,70 @@ export default function Stuff() {
} }
} }
//
// const getSelectedRowdata = (data) => {
// setSelectedRowData(data)
// setSelectedRowDataCount(data.length)
// }
//
// const fnDeleteRowData = (data) => {
// if (data.length === 0) {
// return alert(' ')
// }
// let errCount = 0
// data.forEach((cell) => {
// if (!cell.objectNo) {
// if (errCount === 0) {
// alert(' ')
// }
// errCount++
// }
// })
// }
//
// let newCount = 0
// const addRowItems = () => {
// // console.log('girdRef::::::', gridRef.current.api)
// const newItems = [
// {
// mission: newCount + 1,
// successful: true,
// },
// ]
// gridRef.current.api.applyTransaction({
// add: newItems,
// addIndex: newCount,
// })
// newCount++
// }
//
// const removeRowItems = () => {
// // console.log('selectedRowData::', selectedRowData)
// let errCount = 0
// selectedRowData.forEach((cell) => {
// if (!cell.company) {
// let newSelectedRowData = selectedRowData.filter((item) => item.company == null)
// gridRef.current.api.applyTransaction({ remove: newSelectedRowData })
// } else {
// if (errCount === 0) {
// alert(' .')
// }
// errCount++
// }
// })
// }
// //
useEffect(() => { useEffect(() => {
// if (isObjectNotEmpty(sessionState)) { if (stuffSearchParams?.code === 'S') {
if (isObjectNotEmpty(session)) { const params = {
// saleStoreId: session.storeId,
if (stuffSearchParams?.code === 'S') { schObjectNo: stuffSearchParams?.schObjectNo,
// const params = { schAddress: stuffSearchParams?.schAddress,
// saleStoreId: sessionState?.storeId, schObjectName: stuffSearchParams?.schObjectName,
// schObjectNo: stuffSearchParams?.schObjectNo, schSaleStoreName: stuffSearchParams?.schSaleStoreName,
// schAddress: stuffSearchParams?.schAddress, schReceiveUser: stuffSearchParams?.schReceiveUser,
// schObjectName: stuffSearchParams?.schObjectName, schDispCompanyName: stuffSearchParams?.schDispCompanyName,
// schSaleStoreName: stuffSearchParams?.schSaleStoreName, schDateType: stuffSearchParams.schDateType,
// schReceiveUser: stuffSearchParams?.schReceiveUser, schFromDt: dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD'),
// schDispCompanyName: stuffSearchParams?.schDispCompanyName, schToDt: dayjs(new Date()).format('YYYY-MM-DD'),
// schDateType: stuffSearchParams.schDateType, startRow: (pageNo - 1) * pageSize + 1,
// schFromDt: dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD'), endRow: pageNo * pageSize,
// schToDt: dayjs(new Date()).format('YYYY-MM-DD'), schSelSaleStoreId: stuffSearchParams?.schOtherSelSaleStoreId ? stuffSearchParams.schOtherSelSaleStoreId : stuffSearchParams.schSelSaleStoreId,
// startRow: (pageNo - 1) * pageSize + 1, schSortType: stuffSearchParams.schSortType,
// endRow: pageNo * pageSize,
// schSelSaleStoreId: stuffSearchParams?.schOtherSelSaleStoreId
// ? stuffSearchParams.schOtherSelSaleStoreId
// : stuffSearchParams.schSelSaleStoreId,
// schSortType: stuffSearchParams.schSortType,
// }
const params = {
saleStoreId: session?.storeId,
schObjectNo: stuffSearchParams?.schObjectNo,
schAddress: stuffSearchParams?.schAddress,
schObjectName: stuffSearchParams?.schObjectName,
schSaleStoreName: stuffSearchParams?.schSaleStoreName,
schReceiveUser: stuffSearchParams?.schReceiveUser,
schDispCompanyName: stuffSearchParams?.schDispCompanyName,
schDateType: stuffSearchParams.schDateType,
schFromDt: dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD'),
schToDt: dayjs(new Date()).format('YYYY-MM-DD'),
startRow: (pageNo - 1) * pageSize + 1,
endRow: pageNo * pageSize,
schSelSaleStoreId: stuffSearchParams?.schOtherSelSaleStoreId
? stuffSearchParams.schOtherSelSaleStoreId
: stuffSearchParams.schSelSaleStoreId,
schSortType: stuffSearchParams.schSortType,
}
async function fetchData() {
const apiUrl = `/api/object/list?${queryStringFormatter(params)}`
await get({
url: apiUrl,
}).then((res) => {
if (!isEmptyArray(res)) {
setGridProps({ ...gridProps, gridData: res, count: res[0].totCnt })
setTotalCount(res[0].totCnt)
}
})
}
fetchData()
} else if (stuffSearchParams?.code === 'M') {
//
// const params = {
// saleStoreId: sessionState?.storeId,
// schObjectNo: stuffSearchParams.schObjectNo,
// schAddress: 'dfdfdfdfdf',
// schObjectName: '',
// schSaleStoreName: '',
// schReceiveUser: '',
// schDispCompanyName: '',
// schDateType: 'U',
// 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',
// }
const params = {
saleStoreId: session?.storeId,
schObjectNo: stuffSearchParams.schObjectNo,
schAddress: 'dfdfdfdfdf',
schObjectName: '',
schSaleStoreName: '',
schReceiveUser: '',
schDispCompanyName: '',
schDateType: 'U',
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',
}
setStuffSearch({
...params,
})
// async function fetchData() {
// const apiUrl = `/api/object/list?${queryStringFormatter(params)}`
// await get({
// url: apiUrl,
// }).then((res) => {
// if (!isEmptyArray(res)) {
// setGridProps({ ...gridProps, gridData: res, count: res[0].totCnt })
// setTotalCount(res[0].totCnt)
// }
// })
// }
// fetchData()
} }
}
// }, [pageNo, sessionState, stuffSearchParams])
}, [pageNo, stuffSearchParams])
useEffect(() => { async function fetchData() {
if (stuffSearchParams?.code === 'E') { const apiUrl = `/api/object/list?${queryStringFormatter(params)}`
await get({
url: apiUrl,
}).then((res) => {
if (!isEmptyArray(res)) {
setGridProps({ ...gridProps, gridData: res, count: res[0].totCnt })
setTotalCount(res[0].totCnt)
}
})
}
fetchData()
} else if (stuffSearchParams?.code === 'M') {
const params = {
saleStoreId: session?.storeId,
schObjectNo: stuffSearchParams.schObjectNo,
schAddress: '',
schObjectName: '',
schSaleStoreName: '',
schReceiveUser: '',
schDispCompanyName: '',
schDateType: 'U',
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',
}
setStuffSearch({
...params,
})
} else if (stuffSearchParams?.code === 'E') {
stuffSearchParams.startRow = 1 stuffSearchParams.startRow = 1
stuffSearchParams.endRow = 1 * pageSize stuffSearchParams.endRow = 1 * pageSize
stuffSearchParams.schSortType = defaultSortType stuffSearchParams.schSortType = defaultSortType
setPageNo(1) setPageNo(1)
setStuffSearch({
...stuffSearch,
code: 'FINISH',
})
//
async function fetchData() { async function fetchData() {
// const apiUrl = `/api/object/list?saleStoreId=${sessionState?.storeId}&${queryStringFormatter(stuffSearchParams)}`
const apiUrl = `/api/object/list?saleStoreId=${session?.storeId}&${queryStringFormatter(stuffSearchParams)}` const apiUrl = `/api/object/list?saleStoreId=${session?.storeId}&${queryStringFormatter(stuffSearchParams)}`
await get({ url: apiUrl }).then((res) => { await get({ url: apiUrl }).then((res) => {
if (!isEmptyArray(res)) { if (!isEmptyArray(res)) {
@ -360,7 +235,10 @@ export default function Stuff() {
} }
}) })
} }
fetchData() fetchData()
} else if (stuffSearchParams?.code === 'C') {
resetStuffRecoil()
} }
}, [stuffSearchParams]) }, [stuffSearchParams])
@ -375,13 +253,11 @@ export default function Stuff() {
setPageSize(e.target.value) setPageSize(e.target.value)
setStuffSearch({ setStuffSearch({
...stuffSearchParams, ...stuffSearchParams,
// code: 'P',
startRow: startRow, startRow: startRow,
endRow: 1 * e.target.value, endRow: 1 * e.target.value,
}) })
setPageNo(1) setPageNo(1)
// const apiUrl = `/api/object/list?saleStoreId=${sessionState?.storeId}&${queryStringFormatter(stuffSearchParams)}`
const apiUrl = `/api/object/list?saleStoreId=${session?.storeId}&${queryStringFormatter(stuffSearchParams)}` const apiUrl = `/api/object/list?saleStoreId=${session?.storeId}&${queryStringFormatter(stuffSearchParams)}`
get({ url: apiUrl }).then((res) => { get({ url: apiUrl }).then((res) => {
if (!isEmptyArray(res)) { if (!isEmptyArray(res)) {
@ -414,7 +290,6 @@ export default function Stuff() {
}) })
setPageNo(1) setPageNo(1)
// const apiUrl = `/api/object/list?saleStoreId=${sessionState?.storeId}&${queryStringFormatter(stuffSearchParams)}`
const apiUrl = `/api/object/list?saleStoreId=${session?.storeId}&${queryStringFormatter(stuffSearchParams)}` const apiUrl = `/api/object/list?saleStoreId=${session?.storeId}&${queryStringFormatter(stuffSearchParams)}`
get({ url: apiUrl }).then((res) => { get({ url: apiUrl }).then((res) => {
if (!isEmptyArray(res)) { if (!isEmptyArray(res)) {

View File

@ -11,7 +11,6 @@ import { isEmptyArray, isNotEmptyArray, isObjectNotEmpty } from '@/util/common-u
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { useForm } from 'react-hook-form' import { useForm } from 'react-hook-form'
import { useRecoilValue, useSetRecoilState } from 'recoil' import { useRecoilValue, useSetRecoilState } from 'recoil'
import { sessionStore } from '@/store/commonAtom'
import { SessionContext } from '@/app/SessionProvider' import { SessionContext } from '@/app/SessionProvider'
import FindAddressPop from './popup/FindAddressPop' import FindAddressPop from './popup/FindAddressPop'
import PlanRequestPop from './popup/PlanRequestPop' import PlanRequestPop from './popup/PlanRequestPop'
@ -28,7 +27,6 @@ export default function StuffDetail() {
const [selOptions, setSelOptions] = useState('') // 1 const [selOptions, setSelOptions] = useState('') // 1
const [otherSelOptions, setOtherSelOptions] = useState('') // 1 const [otherSelOptions, setOtherSelOptions] = useState('') // 1
const sessionState = useRecoilValue(sessionStore)
const { session } = useContext(SessionContext) const { session } = useContext(SessionContext)
const router = useRouter() const router = useRouter()
@ -320,12 +318,11 @@ export default function StuffDetail() {
let firstList let firstList
let otherList let otherList
let favList let favList
// if (sessionState?.storeId === 'T01') {
if (session?.storeId === 'T01') { if (session?.storeId === 'T01') {
url = `/api/object/saleStore/${session?.storeId}/firstList?userId=${session?.userId}` url = `/api/object/saleStore/${session?.storeId}/firstList?userId=${session?.userId}`
} else { } else {
if (session.storeLvl === '1') { if (session.storeLvl === '1') {
url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=1&userId=${sessionState?.userId}` url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=1&userId=${session?.userId}`
} else { } else {
url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=1&userId=${session?.userId}` url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=1&userId=${session?.userId}`
} }
@ -400,7 +397,6 @@ export default function StuffDetail() {
} }
}) })
} }
// }, [objectNo, sessionState])
}, [objectNo, session]) }, [objectNo, session])
useEffect(() => { useEffect(() => {
@ -420,7 +416,6 @@ export default function StuffDetail() {
useEffect(() => { useEffect(() => {
if (isObjectNotEmpty(detailData)) { if (isObjectNotEmpty(detailData)) {
// console.log(':::::', detailData)
// API // API
get({ url: '/api/object/prefecture/list' }).then((res) => { get({ url: '/api/object/prefecture/list' }).then((res) => {
if (!isEmptyArray(res)) { if (!isEmptyArray(res)) {
@ -434,23 +429,18 @@ export default function StuffDetail() {
let firstList let firstList
let otherList let otherList
let favList let favList
// if (sessionState?.storeId === 'T01') {
if (session?.storeId === 'T01') { if (session?.storeId === 'T01') {
// url = `/api/object/saleStore/${sessionState?.storeId}/firstList?userId=${sessionState?.userId}`
url = `/api/object/saleStore/${session?.storeId}/firstList?userId=${session?.userId}` url = `/api/object/saleStore/${session?.storeId}/firstList?userId=${session?.userId}`
} else { } else {
// if (sessionState.storeLvl === '1') {
if (session.storeLvl === '1') { if (session.storeLvl === '1') {
// url = `/api/object/saleStore/${sessionState?.storeId}/list?firstFlg=1&userId=${sessionState?.userId}`
url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=1&userId=${session?.userId}` url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=1&userId=${session?.userId}`
} else { } else {
// url = `/api/object/saleStore/${sessionState?.storeId}/list?firstFlg=1&userId=${sessionState?.userId}`
url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=1&userId=${session?.userId}` url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=1&userId=${session?.userId}`
} }
} }
get({ url: url }).then((res) => { get({ url: url }).then((res) => {
if (!isEmptyArray(res)) { if (!isEmptyArray(res)) {
// if (sessionState?.storeId === 'T01') {
if (session?.storeId === 'T01') { if (session?.storeId === 'T01') {
firstList = res.filter((row) => row.saleStoreLevel === '1') firstList = res.filter((row) => row.saleStoreLevel === '1')
firstList.sort((a, b) => (a.saleStoreId !== 'T01') - (b.saleStoreId !== 'T01') || a.saleStoreId - b.saleStoreId) firstList.sort((a, b) => (a.saleStoreId !== 'T01') - (b.saleStoreId !== 'T01') || a.saleStoreId - b.saleStoreId)
@ -459,14 +449,19 @@ export default function StuffDetail() {
setFavoriteStoreList(favList) setFavoriteStoreList(favList)
setShowSaleStoreList(favList) setShowSaleStoreList(favList)
form.setValue('saleStoreId', firstList[0].saleStoreId) if (detailData.firstAgentId != null) {
form.setValue('saleStoreName', firstList[0].saleStoreName) form.setValue('saleStoreId', detailData.firstAgentId)
form.setValue('saleStoreLevel', firstList[0].saleStoreLevel) setSelOptions(detailData.firstAgentId)
setSelOptions(firstList[0].saleStoreId) } else {
form.setValue('saleStoreId', detailData.saleStoreId)
setSelOptions(detailData.saleStoreId)
}
// 1 2 // 1 2
// url = `/api/object/saleStore/${detailData?.saleStoreId}/list?firstFlg=0&userId=${sessionState?.userId}`
url = `/api/object/saleStore/${detailData?.saleStoreId}/list?firstFlg=0&userId=${session?.userId}` let data = detailData?.firstAgentId ? detailData.firstAgentId : detailData.saleStoreId
// url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=0&userId=${session?.userId}`
url = `/api/object/saleStore/${data}/list?firstFlg=0&userId=${session?.userId}`
get({ url: url }).then((res) => { get({ url: url }).then((res) => {
if (!isEmptyArray(res)) { if (!isEmptyArray(res)) {
@ -482,7 +477,6 @@ export default function StuffDetail() {
}) })
} else { } else {
//1 //1
// if (sessionState?.storeLvl === '1') {
if (session?.storeLvl === '1') { if (session?.storeLvl === '1') {
firstList = res firstList = res
favList = res.filter((row) => row.priority !== 'B') favList = res.filter((row) => row.priority !== 'B')
@ -572,7 +566,6 @@ export default function StuffDetail() {
form.setValue('remarks', detailData.remarks) form.setValue('remarks', detailData.remarks)
}) })
} }
// }, [detailData, sessionState])
}, [detailData, session]) }, [detailData, session])
// //
@ -1282,9 +1275,7 @@ export default function StuffDetail() {
//1 or 2 //1 or 2
if (params.saleStoreId == '') { if (params.saleStoreId == '') {
// params.saleStoreId = sessionState.storeId
params.saleStoreId = session.storeId params.saleStoreId = session.storeId
// params.saleStoreLevel = sessionState.storeLvl
params.saleStoreLevel = session.storeLvl params.saleStoreLevel = session.storeLvl
} }
@ -1448,7 +1439,6 @@ export default function StuffDetail() {
</th> </th>
<td> <td>
<div className="flx-box"> <div className="flx-box">
{/* {sessionState?.storeId === 'T01' && ( */}
{session?.storeId === 'T01' && ( {session?.storeId === 'T01' && (
<> <>
<div className="select-wrap mr5" style={{ width: '567px' }}> <div className="select-wrap mr5" style={{ width: '567px' }}>
@ -1482,7 +1472,6 @@ export default function StuffDetail() {
</> </>
)} )}
{/* {sessionState?.storeId !== 'T01' && sessionState?.storeLvl === '1' && ( */}
{session?.storeId !== 'T01' && session?.storeLvl === '1' && ( {session?.storeId !== 'T01' && session?.storeLvl === '1' && (
<> <>
<div className="select-wrap mr5" style={{ width: '567px' }}> <div className="select-wrap mr5" style={{ width: '567px' }}>
@ -1514,7 +1503,6 @@ export default function StuffDetail() {
</div> </div>
</> </>
)} )}
{/* {sessionState?.storeId !== 'T01' && sessionState?.storeLvl !== '1' && ( */}
{session?.storeId !== 'T01' && session?.storeLvl !== '1' && ( {session?.storeId !== 'T01' && session?.storeLvl !== '1' && (
<> <>
<div className="select-wrap mr5" style={{ width: '567px' }}> <div className="select-wrap mr5" style={{ width: '567px' }}>
@ -1931,7 +1919,6 @@ export default function StuffDetail() {
</th> </th>
<td> <td>
<div className="flx-box"> <div className="flx-box">
{/* {sessionState?.storeId === 'T01' && ( */}
{session?.storeId === 'T01' && ( {session?.storeId === 'T01' && (
<> <>
<div className="select-wrap mr5" style={{ width: '567px' }}> <div className="select-wrap mr5" style={{ width: '567px' }}>
@ -1947,10 +1934,8 @@ export default function StuffDetail() {
onChange={onSelectionChange} onChange={onSelectionChange}
getOptionLabel={(x) => x.saleStoreName} getOptionLabel={(x) => x.saleStoreName}
getOptionValue={(x) => x.saleStoreId} getOptionValue={(x) => x.saleStoreId}
// isClearable={sessionState?.storeLvl === '1' ? true : false} isClearable={detailData.tempFlg === '0' ? false : session?.storeLvl === '1' ? true : false}
isClearable={session?.storeLvl === '1' ? true : false} isDisabled={detailData.tempFlg === '0' ? true : session?.storeLvl !== '1' ? true : false}
// isDisabled={sessionState?.storeLvl !== '1' ? true : false}
isDisabled={session?.storeLvl !== '1' ? true : false}
value={saleStoreList.filter(function (option) { value={saleStoreList.filter(function (option) {
return option.saleStoreId === selOptions return option.saleStoreId === selOptions
})} })}
@ -1967,7 +1952,6 @@ export default function StuffDetail() {
</div> </div>
</> </>
)} )}
{/* {sessionState?.storeId !== 'T01' && sessionState?.storeLvl === '1' && ( */}
{session?.storeId !== 'T01' && session?.storeLvl === '1' && ( {session?.storeId !== 'T01' && session?.storeLvl === '1' && (
<> <>
<div className="select-wrap mr5" style={{ width: '567px' }}> <div className="select-wrap mr5" style={{ width: '567px' }}>
@ -1983,8 +1967,9 @@ export default function StuffDetail() {
getOptionLabel={(x) => x.saleStoreName} getOptionLabel={(x) => x.saleStoreName}
getOptionValue={(x) => x.saleStoreId} getOptionValue={(x) => x.saleStoreId}
isClearable={false} isClearable={false}
// isDisabled={sessionState?.storeLvl !== '1' ? true : sessionState?.storeId !== 'T01' ? true : false} isDisabled={
isDisabled={session?.storeLvl !== '1' ? true : session?.storeId !== 'T01' ? true : false} detailData.tempFlg === '0' ? true : session?.storeLvl !== '1' ? true : session?.storeId !== 'T01' ? true : false
}
value={showSaleStoreList.filter(function (option) { value={showSaleStoreList.filter(function (option) {
return option.saleStoreId === selOptions return option.saleStoreId === selOptions
})} })}
@ -2001,7 +1986,6 @@ export default function StuffDetail() {
</div> </div>
</> </>
)} )}
{/* {sessionState?.storeId !== 'T01' && sessionState?.storeLvl !== '1' && ( */}
{session?.storeId !== 'T01' && session?.storeLvl !== '1' && ( {session?.storeId !== 'T01' && session?.storeLvl !== '1' && (
<> <>
<div className="select-wrap mr5" style={{ width: '567px' }}> <div className="select-wrap mr5" style={{ width: '567px' }}>
@ -2062,10 +2046,10 @@ export default function StuffDetail() {
onChange={onSelectionChange2} onChange={onSelectionChange2}
getOptionLabel={(x) => x.saleStoreName} getOptionLabel={(x) => x.saleStoreName}
getOptionValue={(x) => x.saleStoreId} getOptionValue={(x) => x.saleStoreId}
// isDisabled={sessionState?.storeLvl === '1' && form.watch('saleStoreId') != '' ? false : true} isDisabled={
isDisabled={session?.storeLvl === '1' && form.watch('saleStoreId') != '' ? false : true} detailData.tempFlg === '0' ? true : session?.storeLvl === '1' && form.watch('saleStoreId') != '' ? false : true
// isClearable={sessionState?.storeLvl === '1' ? true : false} }
isClearable={session?.storeLvl === '1' ? true : false} isClearable={detailData.tempFlg === '0' ? false : session?.storeLvl === '1' ? true : false}
value={otherSaleStoreList.filter(function (option) { value={otherSaleStoreList.filter(function (option) {
return option.saleStoreId === otherSelOptions return option.saleStoreId === otherSelOptions
})} })}

View File

@ -77,14 +77,14 @@ export default function StuffSearchCondition() {
if (stuffSearch.code === 'S') { if (stuffSearch.code === 'S') {
setStuffSearch({ setStuffSearch({
schObjectNo: objectNo ? objectNo : stuffSearch?.schObjectNo, schObjectNo: objectNo ? objectNo : stuffSearch.schObjectNo,
schSaleStoreName: stuffSearch?.schSaleStoreName ? stuffSearch?.schSaleStoreName : saleStoreName, schSaleStoreName: saleStoreName ? saleStoreName : '',
schAddress: address ? address : stuffSearch?.schAddress, schAddress: address ? address : '',
schObjectName: objectName ? objectName : stuffSearch?.schObjectName, schObjectName: objectName ? objectName : '',
schDispCompanyName: dispCompanyName ? dispCompanyName : stuffSearch?.schDispCompanyName, schDispCompanyName: dispCompanyName ? dispCompanyName : '',
schSelSaleStoreId: stuffSearch?.schOtherSelSaleStoreId ? stuffSearch.schOtherSelSaleStoreId : stuffSearch.schSelSaleStoreId, schSelSaleStoreId: stuffSearch?.schOtherSelSaleStoreId ? stuffSearch.schOtherSelSaleStoreId : stuffSearch.schSelSaleStoreId,
schReceiveUser: receiveUser ? receiveUser : stuffSearch?.schReceiveUser, schReceiveUser: receiveUser ? receiveUser : '',
schDateType: stuffSearch?.schDateType ? stuffSearch.schDateType : dateType, schDateType: dateType,
schFromDt: dayjs(startDate).format('YYYY-MM-DD'), schFromDt: dayjs(startDate).format('YYYY-MM-DD'),
schToDt: dayjs(endDate).format('YYYY-MM-DD'), schToDt: dayjs(endDate).format('YYYY-MM-DD'),
code: 'E', code: 'E',
@ -94,13 +94,13 @@ export default function StuffSearchCondition() {
}) })
} else { } else {
setStuffSearch({ setStuffSearch({
schObjectNo: objectNo ? objectNo : '', schObjectNo: objectNo,
schSaleStoreName: saleStoreName ? saleStoreName : '', schSaleStoreName: saleStoreName,
schAddress: address ? address : '', schAddress: address,
schObjectName: objectName ? objectName : '', schObjectName: objectName,
schDispCompanyName: dispCompanyName ? dispCompanyName : '', schDispCompanyName: dispCompanyName,
schSelSaleStoreId: stuffSearch?.schOtherSelSaleStoreId ? stuffSearch.schOtherSelSaleStoreId : stuffSearch.schSelSaleStoreId, schSelSaleStoreId: stuffSearch?.schOtherSelSaleStoreId ? stuffSearch.schOtherSelSaleStoreId : stuffSearch.schSelSaleStoreId,
schReceiveUser: receiveUser ? receiveUser : '', schReceiveUser: receiveUser,
schDateType: dateType, schDateType: dateType,
schFromDt: dayjs(startDate).format('YYYY-MM-DD'), schFromDt: dayjs(startDate).format('YYYY-MM-DD'),
schToDt: dayjs(endDate).format('YYYY-MM-DD'), schToDt: dayjs(endDate).format('YYYY-MM-DD'),
@ -131,14 +131,12 @@ export default function StuffSearchCondition() {
setDateType('U') setDateType('U')
setStartDate(dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD')) setStartDate(dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD'))
setEndDate(dayjs(new Date()).format('YYYY-MM-DD')) setEndDate(dayjs(new Date()).format('YYYY-MM-DD'))
// if (sessionState?.storeId === 'T01') {
if (session?.storeId === 'T01') { if (session?.storeId === 'T01') {
setSchSelSaleStoreId('') setSchSelSaleStoreId('')
handleClear1() // handleClear1() //
resetStuffRecoil() resetStuffRecoil()
setStuffSearch({ setStuffSearch({
...stuffSearch, ...stuffSearch,
code: 'C',
schSelSaleStoreId: '', schSelSaleStoreId: '',
schOtherSelSaleStoreId: '', schOtherSelSaleStoreId: '',
}) })
@ -156,23 +154,17 @@ export default function StuffSearchCondition() {
} }
useEffect(() => { useEffect(() => {
// if (isObjectNotEmpty(sessionState)) {
if (isObjectNotEmpty(session)) { if (isObjectNotEmpty(session)) {
// storeId T01 storeLvl 1 // storeId T01 storeLvl 1
let url let url
// if (sessionState?.storeId === 'T01') {
if (session?.storeId === 'T01') { if (session?.storeId === 'T01') {
//T01 //T01
// url = `/api/object/saleStore/${sessionState?.storeId}/firstList?userId=${sessionState?.userId}`
url = `/api/object/saleStore/${session?.storeId}/firstList?userId=${session?.userId}` url = `/api/object/saleStore/${session?.storeId}/firstList?userId=${session?.userId}`
} else { } else {
// if (sessionState.storeLvl === '1') {
if (session.storeLvl === '1') { if (session.storeLvl === '1') {
//T01 1 //T01 1
// url = `/api/object/saleStore/${sessionState?.storeId}/list?firstFlg=1&userId=${sessionState?.userId}`
url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=1&userId=${sessionState?.userId}` url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=1&userId=${sessionState?.userId}`
} else { } else {
// url = `/api/object/saleStore/${sessionState?.storeId}/list?firstFlg=1&userId=${sessionState?.userId}`
url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=1&userId=${session?.userId}` url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=1&userId=${session?.userId}`
} }
} }
@ -187,7 +179,6 @@ export default function StuffSearchCondition() {
let allList let allList
let favList let favList
let otherList let otherList
// if (sessionState?.storeId === 'T01') {
if (session?.storeId === 'T01') { if (session?.storeId === 'T01') {
allList = res allList = res
allList.sort((a, b) => (a.saleStoreId !== 'T01') - (b.saleStoreId !== 'T01') || a.saleStoreId - b.saleStoreId) allList.sort((a, b) => (a.saleStoreId !== 'T01') - (b.saleStoreId !== 'T01') || a.saleStoreId - b.saleStoreId)
@ -195,17 +186,14 @@ export default function StuffSearchCondition() {
setSchSelSaleStoreList(allList) setSchSelSaleStoreList(allList)
setFavoriteStoreList(favList) setFavoriteStoreList(favList)
setShowSaleStoreList(favList) setShowSaleStoreList(favList)
// setSchSelSaleStoreId(sessionState?.storeId)
setSchSelSaleStoreId(session?.storeId) setSchSelSaleStoreId(session?.storeId)
setStuffSearch({ setStuffSearch({
...stuffSearch, ...stuffSearch,
code: 'S', code: 'S',
// schSelSaleStoreId: sessionState?.storeId,
schSelSaleStoreId: session?.storeId, schSelSaleStoreId: session?.storeId,
}) })
//T01 2 1 storeId //T01 2 1 storeId
// url = `/api/object/saleStore/${sessionState?.storeId}/list?firstFlg=0&userId=${sessionState?.userId}`
url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=0&userId=${session?.userId}` url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=0&userId=${session?.userId}`
get({ url: url }).then((res) => { get({ url: url }).then((res) => {
@ -222,7 +210,6 @@ export default function StuffSearchCondition() {
} }
}) })
} else { } else {
// if (sessionState?.storeLvl === '1') {
if (session?.storeLvl === '1') { if (session?.storeLvl === '1') {
allList = res allList = res
favList = res.filter((row) => row.priority !== 'B') favList = res.filter((row) => row.priority !== 'B')
@ -250,7 +237,6 @@ export default function StuffSearchCondition() {
setOtherSaleStoreList(otherList) setOtherSaleStoreList(otherList)
// 2 // 2
// setOtherSaleStoreId(sessionState?.storeId)
setOtherSaleStoreId(session?.storeId) setOtherSaleStoreId(session?.storeId)
setStuffSearch({ setStuffSearch({
...stuffSearch, ...stuffSearch,
@ -262,7 +248,6 @@ export default function StuffSearchCondition() {
} }
}) })
} }
// }, [sessionState])
}, [session]) }, [session])
// 1 .. // 1 ..
@ -296,7 +281,6 @@ export default function StuffSearchCondition() {
stuffSearch.schSelSaleStoreId = key.saleStoreId stuffSearch.schSelSaleStoreId = key.saleStoreId
//T01 1 //T01 1
// 1 saleStoreId 2 API // 1 saleStoreId 2 API
// let url = `/api/object/saleStore/${key.saleStoreId}/list?firstFlg=0&userId=${sessionState?.userId}`
let url = `/api/object/saleStore/${key.saleStoreId}/list?firstFlg=0&userId=${session?.userId}` let url = `/api/object/saleStore/${key.saleStoreId}/list?firstFlg=0&userId=${session?.userId}`
let otherList let otherList
get({ url: url }).then((res) => { get({ url: url }).then((res) => {
@ -352,6 +336,7 @@ export default function StuffSearchCondition() {
// //
const handleByOnKeyUp = (e) => { const handleByOnKeyUp = (e) => {
if (e.key === 'Enter') { if (e.key === 'Enter') {
stuffSearch.code = 'E'
onSubmit() onSubmit()
} }
} }
@ -399,7 +384,7 @@ export default function StuffSearchCondition() {
type="text" type="text"
ref={objectNoRef} ref={objectNoRef}
className="input-light" className="input-light"
defaultValue={stuffSearch.code === 'E' || stuffSearch.code === 'M' ? stuffSearch?.schObjectNo : objectNo} defaultValue={stuffSearch?.schObjectNo ? stuffSearch.schObjectNo : objectNo}
onChange={(e) => { onChange={(e) => {
setObjectNo(objectNoRef.current.value) setObjectNo(objectNoRef.current.value)
}} }}
@ -414,7 +399,7 @@ export default function StuffSearchCondition() {
type="text" type="text"
ref={saleStoreNameRef} ref={saleStoreNameRef}
className="input-light" className="input-light"
defaultValue={stuffSearch.code === 'E' ? stuffSearch?.schSaleStoreName : saleStoreName} defaultValue={stuffSearch?.schSaleStoreName ? stuffSearch.schSaleStoreName : saleStoreName}
onChange={(e) => { onChange={(e) => {
setSaleStoreName(saleStoreNameRef.current.value) setSaleStoreName(saleStoreNameRef.current.value)
}} }}
@ -429,7 +414,7 @@ export default function StuffSearchCondition() {
type="text" type="text"
ref={addressRef} ref={addressRef}
className="input-light" className="input-light"
defaultValue={stuffSearch.code === 'E' ? stuffSearch?.schAddress : address} defaultValue={stuffSearch?.schAddress ? stuffSearch.schAddress : address}
onChange={(e) => { onChange={(e) => {
setAddress(addressRef.current.value) setAddress(addressRef.current.value)
}} }}
@ -444,7 +429,7 @@ export default function StuffSearchCondition() {
type="text" type="text"
ref={dispCompanyNameRef} ref={dispCompanyNameRef}
className="input-light" className="input-light"
defaultValue={stuffSearch.code === 'E' ? stuffSearch?.schDispCompanyName : dispCompanyName} defaultValue={stuffSearch?.schDispCompanyName ? stuffSearch.schDispCompanyName : dispCompanyName}
onChange={(e) => { onChange={(e) => {
setDispCompanyName(dispCompanyNameRef.current.value) setDispCompanyName(dispCompanyNameRef.current.value)
}} }}
@ -461,7 +446,7 @@ export default function StuffSearchCondition() {
type="text" type="text"
ref={objectNameRef} ref={objectNameRef}
className="input-light" className="input-light"
defaultValue={stuffSearch.code === 'E' ? stuffSearch?.schObjectName : objectName} defaultValue={stuffSearch?.schObjectName ? stuffSearch.schObjectName : objectName}
onChange={(e) => { onChange={(e) => {
setobjectName(objectNameRef.current.value) setobjectName(objectNameRef.current.value)
}} }}
@ -476,7 +461,7 @@ export default function StuffSearchCondition() {
type="text" type="text"
className="input-light" className="input-light"
ref={receiveUserRef} ref={receiveUserRef}
defaultValue={stuffSearch.code === 'E' ? stuffSearch?.schReceiveUser : receiveUser} defaultValue={stuffSearch?.schReceiveUser ? stuffSearch.schReceiveUser : receiveUser}
onChange={(e) => { onChange={(e) => {
setReceiveUser(receiveUserRef.current.value) setReceiveUser(receiveUserRef.current.value)
}} }}
@ -488,7 +473,6 @@ export default function StuffSearchCondition() {
<td colSpan={3}> <td colSpan={3}>
<div className="form-flex-wrap"> <div className="form-flex-wrap">
<div className="select-wrap mr5" style={{ flex: 1 }}> <div className="select-wrap mr5" style={{ flex: 1 }}>
{/* {sessionState?.storeId === 'T01' && ( */}
{session?.storeId === 'T01' && ( {session?.storeId === 'T01' && (
<Select <Select
id="long-value-select1" id="long-value-select1"
@ -517,12 +501,10 @@ export default function StuffSearchCondition() {
} }
} }
})} })}
// isDisabled={sessionState?.storeLvl !== '1' ? true : sessionState?.storeId !== 'T01' ? true : false}
isDisabled={session?.storeLvl !== '1' ? true : session?.storeId !== 'T01' ? true : false} isDisabled={session?.storeLvl !== '1' ? true : session?.storeId !== 'T01' ? true : false}
isClearable={true} isClearable={true}
/> />
)} )}
{/* {sessionState?.storeId !== 'T01' && sessionState?.storeLvl === '1' && ( */}
{session?.storeId !== 'T01' && session?.storeLvl === '1' && ( {session?.storeId !== 'T01' && session?.storeLvl === '1' && (
<Select <Select
id="long-value-select1" id="long-value-select1"
@ -550,12 +532,10 @@ export default function StuffSearchCondition() {
} }
} }
})} })}
// isDisabled={sessionState?.storeLvl !== '1' ? true : sessionState?.storeId !== 'T01' ? true : false}
isDisabled={session?.storeLvl !== '1' ? true : session?.storeId !== 'T01' ? true : false} isDisabled={session?.storeLvl !== '1' ? true : session?.storeId !== 'T01' ? true : false}
isClearable={false} isClearable={false}
/> />
)} )}
{/* {sessionState?.storeId !== 'T01' && sessionState?.storeLvl !== '1' && ( */}
{session?.storeId !== 'T01' && session?.storeLvl !== '1' && ( {session?.storeId !== 'T01' && session?.storeLvl !== '1' && (
<Select <Select
id="long-value-select1" id="long-value-select1"
@ -620,11 +600,10 @@ export default function StuffSearchCondition() {
type="radio" type="radio"
name="radio_ptype" name="radio_ptype"
id="radio_u" id="radio_u"
checked={stuffSearch?.schDateType === 'U' ? true : false} checked={dateType === 'U' ? true : false}
value={'U'} value={'U'}
onChange={(e) => { onChange={(e) => {
setDateType(e.target.value) setDateType(e.target.value)
setStuffSearch({ ...stuffSearch, code: 'S', schDateType: e.target.value })
}} }}
/> />
<label htmlFor="radio_u">{getMessage('stuff.search.schDateTypeU')}</label> <label htmlFor="radio_u">{getMessage('stuff.search.schDateTypeU')}</label>
@ -634,11 +613,10 @@ export default function StuffSearchCondition() {
type="radio" type="radio"
name="radio_ptype" name="radio_ptype"
id="radio_r" id="radio_r"
checked={stuffSearch?.schDateType === 'R' ? true : false} checked={dateType === 'R' ? true : false}
value={'R'} value={'R'}
onChange={(e) => { onChange={(e) => {
setDateType(e.target.value) setDateType(e.target.value)
setStuffSearch({ ...stuffSearch, code: 'S', schDateType: e.target.value })
}} }}
/> />
<label htmlFor="radio_r">{getMessage('stuff.search.schDateTypeR')}</label> <label htmlFor="radio_r">{getMessage('stuff.search.schDateTypeR')}</label>

View File

@ -7,7 +7,7 @@ import { useMessage } from '@/hooks/useMessage'
import { useRouter, useSearchParams } from 'next/navigation' import { useRouter, useSearchParams } from 'next/navigation'
import { floorPlanObjectState } from '@/store/floorPlanObjectAtom' import { floorPlanObjectState } from '@/store/floorPlanObjectAtom'
import { useSetRecoilState } from 'recoil' import { useSetRecoilState } from 'recoil'
import { queryStringFormatter } from '@/util/common-utils'
export default function StuffSubHeader({ type }) { export default function StuffSubHeader({ type }) {
const { getMessage } = useMessage() const { getMessage } = useMessage()
const router = useRouter() const router = useRouter()
@ -25,8 +25,13 @@ export default function StuffSubHeader({ type }) {
const moveFloorPlan = () => { const moveFloorPlan = () => {
setFloorPlanObjectNo({ floorPlanObjectNo: objectNo }) setFloorPlanObjectNo({ floorPlanObjectNo: objectNo })
// const param = {
router.push('/floor-plan/estimate/5/1') pid: '1',
}
//
const url = `/floor-plan?${queryStringFormatter(param)}`
console.log(url)
router.push(url)
} }
return ( return (

View File

@ -59,7 +59,8 @@ export function useCanvasConfigInitialize() {
const canvasLoadInit = () => { const canvasLoadInit = () => {
roofInit() //화면표시 초기화 roofInit() //화면표시 초기화
groupInit() groupDimensionInit()
reGroupInit() //그룹 객체 재그룹
} }
const gridInit = () => { const gridInit = () => {
@ -95,8 +96,8 @@ export function useCanvasConfigInitialize() {
}) })
} }
const groupInit = () => { const groupDimensionInit = () => {
const groups = canvas.getObjects().filter((obj) => obj.groupYn) const groups = canvas.getObjects().filter((obj) => obj.groupYn && obj.name === 'dimensionGroup')
const groupIds = [] const groupIds = []
groups.forEach((group) => { groups.forEach((group) => {
@ -138,5 +139,57 @@ export function useCanvasConfigInitialize() {
}) })
} }
const reGroupInit = () => {
const excludeObjects = ['dimensionGroup', 'dimensionLineText']
const groups = canvas.getObjects().filter((obj) => obj.groupYn && !obj.name.includes(excludeObjects))
const groupIds = []
groups.forEach((group) => {
if (!groupIds.includes(group.groupId)) {
groupIds.push(group.groupId)
}
})
groupIds.forEach((id) => {
//그룹아이디로 캔버스의 객체를 조회함
const groupObjects = canvas.getObjects().filter((obj) => obj.groupId === id || obj.id === id)
const objectsName = canvas.getObjects().filter((obj) => obj.groupId === id || obj.id === id)[0].groupName
const objectsParentId = canvas.getObjects().filter((obj) => obj.groupId === id || obj.id === id)[0].parentId
let objectArray = []
//그룹객체가 있으면 배열에 추가함
if (groupObjects) {
groupObjects.forEach((obj) => {
objectArray.push(obj)
})
}
//그룹객체를 캔버스에서 제거함
objectArray.forEach((obj) => {
canvas?.remove(obj)
})
//그룹 객체로 다시 만든다 (좌표때문에)
const group = new fabric.Group(objectArray, {
groupId: id,
name: objectsName,
selectable: true,
lockMovementX: true,
lockMovementY: true,
originX: 'center',
originY: 'center',
parentId: objectsParentId,
})
canvas.add(group)
//그룹 객체 재그룹 완료
group.getObjects().forEach((obj) => {
obj.fire('modified')
})
})
}
return { canvasLoadInit, gridInit } return { canvasLoadInit, gridInit }
} }

View File

@ -20,7 +20,6 @@ export function useCommonUtils() {
const dimensionLineTextFont = useRecoilValue(fontSelector('dimensionLineText')) const dimensionLineTextFont = useRecoilValue(fontSelector('dimensionLineText'))
const commonTextFont = useRecoilValue(fontSelector('commonText')) const commonTextFont = useRecoilValue(fontSelector('commonText'))
const [commonUtils, setCommonUtilsState] = useRecoilState(commonUtilsState) const [commonUtils, setCommonUtilsState] = useRecoilState(commonUtilsState)
const { addPopup } = usePopup() const { addPopup } = usePopup()
useEffect(() => { useEffect(() => {
@ -278,7 +277,7 @@ export function useCommonUtils() {
groupObjects.push(distanceText) groupObjects.push(distanceText)
const group = new fabric.Group(groupObjects, { const group = new fabric.Group(groupObjects, {
name: 'dimensionLine', name: 'dimensionGroup',
selectable: true, selectable: true,
originX: 'center', originX: 'center',
originY: 'center', originY: 'center',
@ -522,10 +521,10 @@ export function useCommonUtils() {
if (object) { if (object) {
canvas?.remove(object) canvas?.remove(object)
if (object.id) { // if (object.id) {
const group = canvas.getObjects().filter((obj) => obj.id === object.id) // const group = canvas.getObjects().filter((obj) => obj.id === object.id)
group.forEach((obj) => canvas?.remove(obj)) // group.forEach((obj) => canvas?.remove(obj))
} // }
} }
} }
@ -547,7 +546,7 @@ export function useCommonUtils() {
initEvent() initEvent()
obj.setCoords() obj.setCoords()
updateGroupObjectCoords(obj, originLeft, originTop) updateGroupObjectCoords(obj, originLeft, originTop)
// canvas?.renderAll() canvas?.renderAll()
}) })
} }
} }
@ -585,6 +584,10 @@ export function useCommonUtils() {
editable: false, editable: false,
id: uuidv4(), //복사된 객체라 새로 따준다 id: uuidv4(), //복사된 객체라 새로 따준다
}) })
//객체가 그룹일 경우에는 그룹 아이디를 따로 넣어준다
if (clonedObj.type === 'group') clonedObj.set({ groupId: uuidv4() })
initEvent() initEvent()
}) })
} }
@ -720,41 +723,74 @@ export function useCommonUtils() {
} }
// 그룹 이동 시 라인 및 각 객체의 좌표를 절대 좌표로 업데이트하는 함수 // 그룹 이동 시 라인 및 각 객체의 좌표를 절대 좌표로 업데이트하는 함수
function updateGroupObjectCoords(group, originLeft, originTop) { function updateGroupObjectCoords(targetObj, originLeft, originTop) {
const diffrenceLeft = group.left - originLeft const diffrenceLeft = targetObj.left - originLeft
const diffrenceTop = group.top - originTop const diffrenceTop = targetObj.top - originTop
group.getObjects().forEach((obj) => { if (targetObj.type === 'group') {
// 그룹 내 객체의 절대 좌표를 계산 targetObj.getObjects().forEach((obj) => {
// 그룹 내 객체의 절대 좌표를 계산
const originObjLeft = obj.left const originObjLeft = obj.left
const originObjTop = obj.top const originObjTop = obj.top
if (obj.type === 'line') { if (obj.type === 'line') {
// Line 객체의 경우, x1, y1, x2, y2 절대 좌표 계산 // Line 객체의 경우, x1, y1, x2, y2 절대 좌표 계산
obj.set({ obj.set({
x1: obj.x1 + diffrenceLeft, x1: obj.x1 + diffrenceLeft,
y1: obj.y1 + diffrenceTop, y1: obj.y1 + diffrenceTop,
x2: obj.x2 + diffrenceLeft, x2: obj.x2 + diffrenceLeft,
y2: obj.y2 + diffrenceTop, y2: obj.y2 + diffrenceTop,
}) })
obj.set({ obj.set({
left: originObjLeft, left: originObjLeft,
top: originObjTop, top: originObjTop,
}) })
} else { obj.fire('modified')
// 다른 객체의 경우 left, top 절대 좌표 설정 } else {
obj.set({ // 다른 객체의 경우 left, top 절대 좌표 설정
...obj, obj.set({
left: obj.left, left: obj.left,
top: obj.top, top: obj.top,
}) })
obj.fire('modified')
}
obj.setCoords() // 좌표 반영
})
} else {
if (targetObj.type === 'line') {
const originObjLeft = obj.left
const originObjTop = obj.top
if (obj.type === 'line') {
// Line 객체의 경우, x1, y1, x2, y2 절대 좌표 계산
obj.set({
x1: obj.x1 + diffrenceLeft,
y1: obj.y1 + diffrenceTop,
x2: obj.x2 + diffrenceLeft,
y2: obj.y2 + diffrenceTop,
})
obj.set({
left: originObjLeft,
top: originObjTop,
})
obj.fire('modified')
} else {
targetObj.set({
...targetObj,
left: targetObj.left,
top: targetObj.top,
})
obj.fire('modified')
}
targetObj.setCoords()
} }
obj.setCoords() // 좌표 반영
canvas?.renderAll() canvas?.renderAll()
}) }
} }
const deleteOuterLineObject = () => { const deleteOuterLineObject = () => {

View File

@ -0,0 +1,176 @@
import { useAxios } from '@/hooks/useAxios'
import { useContext, useEffect, useReducer, useState } from 'react'
import { useRecoilState, useRecoilValue } from 'recoil'
import { globalLocaleStore } from '@/store/localeAtom'
import { estimateState, floorPlanObjectState } from '@/store/floorPlanObjectAtom'
import { isObjectNotEmpty } from '@/util/common-utils'
import { SessionContext } from '@/app/SessionProvider'
const reducer = (prevState, nextState) => {
return { ...prevState, ...nextState }
}
// Constants
const ESTIMATE_API_ENDPOINT = '/api/estimates' // API 엔드포인트 정의
const defaultEstimateData = {
estimateDate: new Date(), //견적일
charger: '', //담당자
objectName: '', //안건명
objectNameOmit: '', //경칭코드
estimateType: 'YJOD', //주문분류
remarks: '', //비고
estimateOption: '', //견적특이사항
// itemList: [{ id: 1, name: '' }],
//아이템에 필요없는거 빼기
itemList: [
{
amount: '',
fileUploadFlg: '',
itemChangeFlg: '',
itemGroup: '',
itemId: '', //키값??
itemName: '',
itemNo: '',
moduleFlg: '',
objectNo: '',
pkgMaterialFlg: '',
planNo: '',
pnowW: '',
salePrice: '',
saleTotPrice: '',
specification: '',
unit: '',
},
],
fileList: [],
}
// Helper functions
// const updateItemInList = (itemList, id, updates) => {
const updateItemInList = (itemList, itemId, updates) => {
// return itemList.map((item) => (item.id === id ? { ...item, ...updates } : item))
return itemList.map((item) => (item.itemId === itemId ? { ...item, ...updates } : item))
}
export const useEstimateController = (planNo) => {
const { session } = useContext(SessionContext)
const globalLocaleState = useRecoilValue(globalLocaleStore)
const objectRecoil = useRecoilValue(floorPlanObjectState)
const [estimateData, setEstimateData] = useRecoilState(estimateState)
const { get, post, promisePost } = useAxios(globalLocaleState)
const [isLoading, setIsLoading] = useState(false)
const [state, setState] = useReducer(reducer, defaultEstimateData)
useEffect(() => {
if (!isLoading) {
if (objectRecoil.floorPlanObjectNo && planNo) {
fetchSetting()
}
}
}, [])
// 상세 조회
const fetchSetting = async () => {
try {
await get({ url: `/api/estimate/${objectRecoil.floorPlanObjectNo}/${planNo}/detail` }).then((res) => {
if (isObjectNotEmpty(res)) {
setState(res)
}
})
setIsLoading(true)
} catch (error) {
console.error('견적서 상세조회 Error: ', error)
setIsLoading(true)
}
}
// const updateItem = (id, updates) => {
const updateItem = (itemId, updates) => {
setState({
// itemList: updateItemInList(state.itemList, id, updates),
itemList: updateItemInList(state.itemList, itemId, updates),
})
}
const addItem = () => {
// const newId = Math.max(...state.itemList.map((item) => item.id)) + 1
const newItemId = Math.max(...state.itemList.map((item) => item.itemId)) + 1
setState({
// itemList: [...state.itemList, { id: newId, name: '' }],
//셋팅할필요없는거 빼기
itemList: [
...state.itemList,
{
itemId: newItemId,
amount: '',
fileUploadFlg: '',
itemChangeFlg: '',
itemGroup: '',
itemName: '',
itemNo: '',
moduleFlg: '',
objectNo: '',
pkgMaterialFlg: '',
planNo: '',
pnowW: '',
salePrice: '',
saleTotPrice: '',
specification: '',
unit: '',
},
],
})
}
useEffect(() => {
setEstimateData({ ...state, userId: session.userId })
//sapSalesStoreCd 추가예정 필수값
// setEstimateData({ ...state, userId: session.userId, sapSalesStoreCd : session.sapSalesStoreCd })
}, [state])
//견적서 저장
const handleEstimateSubmit = async () => {
console.log('::담긴 estimateData:::', estimateData)
//1. 첨부파일 저장
const formData = new FormData()
formData.append('file', estimateData.fileList)
formData.append('objectNo', estimateData.objectNo)
formData.append('planNo', estimateData.planNo)
formData.append('category', '10')
formData.append('userId', estimateData.userId)
for (const value of formData.values()) {
console.log('formData::', value)
}
await promisePost({ url: '/api/file/fileUpload', data: formData }).then((res) => {
console.log('파일저장::::::::::', res)
})
//2. 상세데이터 저장
console.log('상세저장시작!!')
return
try {
const result = await promisePost({
url: ESTIMATE_API_ENDPOINT,
data: estimateData,
})
return result
} catch (error) {
console.error('Failed to submit estimate:', error)
throw error
}
}
return {
state,
setState,
updateItem,
addItem,
handleEstimateSubmit,
fetchSetting,
}
}

View File

@ -1,4 +1,5 @@
'use client' 'use client'
import { useEffect } from 'react'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { useRecoilValue } from 'recoil' import { useRecoilValue } from 'recoil'
import { canvasState } from '@/store/canvasAtom' import { canvasState } from '@/store/canvasAtom'
@ -9,13 +10,63 @@ import { useSwal } from '@/hooks/useSwal'
import * as turf from '@turf/turf' import * as turf from '@turf/turf'
import { usePolygon } from '@/hooks/usePolygon' import { usePolygon } from '@/hooks/usePolygon'
import { QPolygon } from '@/components/fabric/QPolygon' import { QPolygon } from '@/components/fabric/QPolygon'
import { v4 as uuidv4 } from 'uuid'
import { fontSelector } from '@/store/fontAtom'
export function useObjectBatch({ isHidden, setIsHidden }) { export function useObjectBatch({ isHidden, setIsHidden }) {
const { getMessage } = useMessage() const { getMessage } = useMessage()
const canvas = useRecoilValue(canvasState) const canvas = useRecoilValue(canvasState)
const { addCanvasMouseEventListener, initEvent } = useEvent() const { addCanvasMouseEventListener, initEvent, addDocumentEventListener } = useEvent()
const { swalFire } = useSwal() const { swalFire } = useSwal()
const { drawDirectionArrow } = usePolygon() const { drawDirectionArrow } = usePolygon()
const lengthTextFont = useRecoilValue(fontSelector('lengthText'))
useEffect(() => {
if (canvas) {
dbClickEvent()
}
return () => {
initEvent()
if (canvas) canvas.off('mouse:dblclick')
}
}, [])
const dbClickEvent = () => {
console.log('dbClickEvent 실행')
const dormerObject = canvas.getObjects().filter((obj) => obj.name === BATCH_TYPE.TRIANGLE_DORMER || obj.name === BATCH_TYPE.PENTAGON_DORMER)
console.log('dormerObject', dormerObject)
if (dormerObject) {
canvas.off('mouse:dblclick')
canvas.on('mouse:dblclick', (e) => {
console.log('event', e)
if (e.target && e.target instanceof fabric.Group) {
const pointer = canvas.getPointer(e.e)
const objects = e.target._objects
// 클릭한 위치에 있는 객체 찾기
const clickedObject = objects.find((obj) => {
if (obj.type === 'QPolygon') {
const polygon = pointsToTurfPolygon(obj.getCurrentPoints())
const turfPointer = turf.point([pointer.x, pointer.y])
return turf.booleanPointInPolygon(turfPointer, polygon)
} else {
return obj.containsPoint(pointer)
}
})
if (clickedObject) {
// 클릭된 객체 선택
canvas.setActiveObject(clickedObject)
canvas.renderAll()
}
}
})
}
}
const applyOpeningAndShadow = (objectPlacement, buttonAct, surfaceShapePolygons) => { const applyOpeningAndShadow = (objectPlacement, buttonAct, surfaceShapePolygons) => {
const selectedType = objectPlacement.typeRef.current.find((radio) => radio.checked).value const selectedType = objectPlacement.typeRef.current.find((radio) => radio.checked).value
@ -26,7 +77,7 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
let rect, isDown, origX, origY let rect, isDown, origX, origY
let selectedSurface let selectedSurface
//프리입력 //프리입력
console.log('useObjectBatch', isHidden)
if (selectedType === INPUT_TYPE.FREE) { if (selectedType === INPUT_TYPE.FREE) {
addCanvasMouseEventListener('mouse:down', (e) => { addCanvasMouseEventListener('mouse:down', (e) => {
isDown = true isDown = true
@ -115,7 +166,7 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
} }
isDown = false isDown = false
rect.set({ name: objName }) rect.set({ name: objName, parentId: selectedSurface.id })
rect.setCoords() rect.setCoords()
initEvent() initEvent()
@ -159,6 +210,7 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
lockRotation: true, lockRotation: true,
lockScalingX: true, lockScalingX: true,
lockScalingY: true, lockScalingY: true,
parentId: selectedSurface.id,
}) })
//개구냐 그림자냐에 따라 변경 //개구냐 그림자냐에 따라 변경
@ -196,7 +248,7 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
} }
isDown = false isDown = false
rect.set({ name: objName }) rect.set({ name: objName, parentId: selectedSurface.id })
rect.setCoords() rect.setCoords()
initEvent() initEvent()
if (setIsHidden) setIsHidden(false) if (setIsHidden) setIsHidden(false)
@ -232,6 +284,7 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
: 0 : 0
const directionRef = dormerPlacement.directionRef.current const directionRef = dormerPlacement.directionRef.current
let dormer, dormerOffset, isDown, selectedSurface, pentagonPoints, pentagonOffsetPoints let dormer, dormerOffset, isDown, selectedSurface, pentagonPoints, pentagonOffsetPoints
const id = uuidv4()
if (height === '' || pitch === '' || height <= 0 || pitch <= 0) { if (height === '' || pitch === '' || height <= 0 || pitch <= 0) {
swalFire({ text: getMessage('common.canvas.validate.size'), icon: 'error' }) swalFire({ text: getMessage('common.canvas.validate.size'), icon: 'error' })
@ -310,6 +363,7 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
originX: 'center', originX: 'center',
originY: 'top', originY: 'top',
angle: angle, angle: angle,
objectId: id,
}) })
canvas?.add(dormerOffset) canvas?.add(dormerOffset)
} }
@ -322,7 +376,7 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
//지붕 밖으로 그렸을때 //지붕 밖으로 그렸을때
if (!turf.booleanWithin(trianglePolygon, selectedSurfacePolygon)) { if (!turf.booleanWithin(trianglePolygon, selectedSurfacePolygon)) {
swalFire({ text: '개구를 배치할 수 없습니다.', icon: 'error' }) swalFire({ text: '도머를 배치할 수 없습니다.', icon: 'error' })
//일단 지워 //일단 지워
deleteTempObjects() deleteTempObjects()
return return
@ -358,7 +412,7 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
}) //오프셋이 있을땐 같이 도머로 만든다 }) //오프셋이 있을땐 같이 도머로 만든다
const leftTriangle = new QPolygon(splitedTriangle[0], { const leftTriangle = new QPolygon(splitedTriangle[0], {
fill: 'transparent', fill: 'white',
stroke: 'black', stroke: 'black',
strokeWidth: 1, strokeWidth: 1,
selectable: true, selectable: true,
@ -366,16 +420,18 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
lockMovementY: true, // Y 축 이동 잠금 lockMovementY: true, // Y 축 이동 잠금
lockRotation: true, // 회전 잠금 lockRotation: true, // 회전 잠금
viewLengthText: true, viewLengthText: true,
fontSize: 14,
direction: direction, direction: direction,
originX: 'center', originX: 'center',
originY: 'center', originY: 'center',
name: dormerName, name: dormerName,
pitch: pitch, pitch: pitch,
fontSize: lengthTextFont.fontSize.value,
fontStyle: lengthTextFont.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal',
fontWeight: lengthTextFont.fontWeight.value,
}) })
const rightTriangle = new QPolygon(splitedTriangle[1], { const rightTriangle = new QPolygon(splitedTriangle[1], {
fill: 'transparent', fill: 'white',
stroke: 'black', stroke: 'black',
strokeWidth: 1, strokeWidth: 1,
selectable: true, selectable: true,
@ -383,26 +439,66 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
lockMovementY: true, // Y 축 이동 잠금 lockMovementY: true, // Y 축 이동 잠금
lockRotation: true, // 회전 잠금 lockRotation: true, // 회전 잠금
viewLengthText: true, viewLengthText: true,
fontSize: 14,
direction: direction, direction: direction,
originX: 'center', originX: 'center',
originY: 'center', originY: 'center',
name: dormerName, name: dormerName,
pitch: pitch, pitch: pitch,
fontSize: lengthTextFont.fontSize.value,
fontStyle: lengthTextFont.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal',
fontWeight: lengthTextFont.fontWeight.value,
}) })
canvas?.add(leftTriangle) // canvas?.add(leftTriangle)
canvas?.add(rightTriangle) // canvas?.add(rightTriangle)
//패턴 //패턴
setSurfaceShapePattern(leftTriangle) setSurfaceShapePattern(leftTriangle)
setSurfaceShapePattern(rightTriangle) setSurfaceShapePattern(rightTriangle)
//방향 //방향
drawDirectionArrow(leftTriangle) drawDirectionArrow(leftTriangle)
drawDirectionArrow(rightTriangle) drawDirectionArrow(rightTriangle)
let offsetPolygon
if (offsetRef > 0) {
canvas?.remove(dormer)
offsetPolygon = new QPolygon(triangleToPolygon(dormer), {
selectable: true,
lockMovementX: true, // X 축 이동 잠금
lockMovementY: true, // Y 축 이동 잠금
lockRotation: true, // 회전 잠금
viewLengthText: true,
name: 'triangleDormerOffset',
id: id,
fill: 'rgba(255, 255, 255, 0.6)',
stroke: 'black',
strokeWidth: 1,
originX: 'center',
originY: 'center',
fontSize: lengthTextFont.fontSize.value,
fontStyle: lengthTextFont.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal',
fontWeight: lengthTextFont.fontWeight.value,
})
}
const groupPolygon = offsetPolygon ? [leftTriangle, rightTriangle, offsetPolygon] : [leftTriangle, rightTriangle]
const objectGroup = new fabric.Group(groupPolygon, {
subTargetCheck: true,
name: dormerName,
id: id,
parentId: selectedSurface.id,
originX: 'center',
originY: 'center',
})
canvas?.add(objectGroup)
isDown = false isDown = false
initEvent() initEvent()
dbClickEvent()
if (setIsHidden) setIsHidden(false) if (setIsHidden) setIsHidden(false)
} }
}) })
@ -498,8 +594,7 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
//지붕 밖으로 그렸을때 //지붕 밖으로 그렸을때
if (!turf.booleanWithin(pentagonPolygon, selectedSurfacePolygon)) { if (!turf.booleanWithin(pentagonPolygon, selectedSurfacePolygon)) {
swalFire({ text: '개구를 배치할 수 없습니다.', icon: 'error' }) swalFire({ text: '도머를 배치할 수 없습니다.', icon: 'error' })
//일단 지워 //일단 지워
deleteTempObjects() deleteTempObjects()
return return
@ -568,8 +663,8 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
pitch: pitch, pitch: pitch,
}) })
canvas?.add(leftPentagon) // canvas?.add(leftPentagon)
canvas?.add(rightPentagon) // canvas?.add(rightPentagon)
//패턴 //패턴
setSurfaceShapePattern(leftPentagon) setSurfaceShapePattern(leftPentagon)
@ -578,8 +673,47 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
drawDirectionArrow(leftPentagon) drawDirectionArrow(leftPentagon)
drawDirectionArrow(rightPentagon) drawDirectionArrow(rightPentagon)
let offsetPolygon
if (offsetRef > 0) {
canvas?.remove(dormer)
offsetPolygon = new QPolygon(dormer.points, {
selectable: true,
lockMovementX: true, // X 축 이동 잠금
lockMovementY: true, // Y 축 이동 잠금
lockRotation: true, // 회전 잠금
viewLengthText: true,
name: 'pentagonDormerOffset',
id: id,
fill: 'rgba(255, 255, 255, 0.6)',
stroke: 'black',
strokeWidth: 1,
originX: 'center',
originY: 'center',
fontSize: lengthTextFont.fontSize.value,
fontStyle: lengthTextFont.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal',
fontWeight: lengthTextFont.fontWeight.value,
})
}
const groupPolygon = offsetPolygon ? [leftPentagon, rightPentagon, offsetPolygon] : [leftPentagon, rightPentagon]
const objectGroup = new fabric.Group(groupPolygon, {
subTargetCheck: true,
name: dormerName,
id: id,
parentId: selectedSurface.id,
groupYn: true,
originX: 'center',
originY: 'center',
})
canvas?.add(objectGroup)
isDown = false isDown = false
initEvent() initEvent()
dbClickEvent()
if (setIsHidden) setIsHidden(false) if (setIsHidden) setIsHidden(false)
} }
}) })
@ -656,7 +790,6 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
{ x: triangle.left + triangle.height, y: triangle.top - triangle.height }, { x: triangle.left + triangle.height, y: triangle.top - triangle.height },
] ]
} }
return [leftPoints, rightPoints] return [leftPoints, rightPoints]
} }
@ -739,10 +872,157 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
return [leftPoints, rightPoints] return [leftPoints, rightPoints]
} }
const resizeObjectBatch = (side, target, width, height) => {
const objectWidth = target.width
const objectHeight = target.height
const changeWidth = (width / 10 / objectWidth).toFixed(2)
const changeHeight = (height / 10 / objectHeight).toFixed(2)
let sideX = 'left'
let sideY = 'top'
//그룹 중심점 변경
if (side === 2) {
sideX = 'right'
sideY = 'top'
} else if (side === 3) {
sideX = 'left'
sideY = 'bottom'
} else if (side === 4) {
sideX = 'right'
sideY = 'bottom'
}
//변경 전 좌표
const newCoords = target.getPointByOrigin(sideX, sideY)
target.set({
originX: sideX,
originY: sideY,
left: newCoords.x,
top: newCoords.y,
})
target.setCoords()
canvas?.renderAll() //변경 좌표를 한번 적용
target.scaleX = changeWidth || 1
target.scaleY = changeHeight || 1
//크기 변경후 좌표를 재 적용
const changedCoords = target.getPointByOrigin('center', 'center')
target.set({
...target,
originX: 'center',
originY: 'center',
left: changedCoords.x,
top: changedCoords.y,
})
if (target.name === 'roof') {
//얘는 일단 도머에 적용함
if (target.type === 'group') {
target._objects.forEach((obj) => setSurfaceShapePattern(obj))
} else {
setSurfaceShapePattern(target)
target.fire('modified')
}
}
// target.setCoords()
canvas.renderAll()
if (target.type === 'group') reGroupObject(target)
}
const reGroupObject = (groupObj) => {
groupObj._restoreObjectsState() //이건 실행만 되도 그룹이 변경됨
const reGroupObjects = []
groupObj._objects.forEach((obj) => {
const newObj = new QPolygon(obj.getCurrentPoints(), {
...obj,
points: obj.getCurrentPoints(),
scaleX: 1,
scaleY: 1,
texts: [],
})
reGroupObjects.push(newObj)
canvas.remove(obj)
if (obj.direction) {
drawDirectionArrow(obj)
}
newObj.fire('modified')
})
const reGroup = new fabric.Group(reGroupObjects, {
subTargetCheck: true,
name: groupObj.name,
id: groupObj.id,
groupYn: true,
parentId: groupObj.parentId,
originX: 'center',
originY: 'center',
})
canvas?.add(reGroup)
canvas?.remove(groupObj)
return reGroup
}
const moveObjectBatch = () => {
const obj = canvas.getActiveObject()
if (obj) {
obj.set({
lockMovementX: false,
lockMovementY: false,
})
addCanvasMouseEventListener('mouse:up', (e) => {
obj.set({
lockMovementX: true,
lockMovementY: true,
})
initEvent()
obj.setCoords()
if (obj.type === 'group') reGroupObject(obj)
})
}
}
const dormerOffsetKeyEvent = (setArrow1, setArrow2) => {
addDocumentEventListener('keydown', document, (e) => {
if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
const keyEvent = e.key === 'ArrowDown' ? 'down' : 'up'
setArrow1(keyEvent)
} else if (e.key === 'ArrowLeft' || e.key === 'ArrowRight') {
const keyEvent = e.key === 'ArrowLeft' ? 'left' : 'right'
setArrow2(keyEvent)
}
})
}
const dormerOffset = (arrow1, arrow2, length1, length2) => {
length1 = parseInt(length1) / 10
length2 = parseInt(length2) / 10
const dormer = canvas.getActiveObject()
if (length1) dormer.top = arrow1 === 'down' ? dormer.top + length1 : dormer.top - length1
if (length2) dormer.left = arrow2 === 'left' ? dormer.left - length2 : dormer.left + length2
if (dormer.type === 'group') {
const newDormer = reGroupObject(dormer)
canvas?.setActiveObject(newDormer)
}
canvas.renderAll()
}
return { return {
applyOpeningAndShadow, applyOpeningAndShadow,
applyDormers, applyDormers,
splitDormerTriangle, splitDormerTriangle,
splitDormerPentagon, splitDormerPentagon,
resizeObjectBatch,
moveObjectBatch,
dormerOffsetKeyEvent,
dormerOffset,
} }
} }

View File

@ -1,6 +1,6 @@
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil' import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
import { canvasState } from '@/store/canvasAtom' import { adsorptionPointModeState, adsorptionRangeState, canvasState } from '@/store/canvasAtom'
import { globalLocaleStore } from '@/store/localeAtom' import { globalLocaleStore } from '@/store/localeAtom'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { useAxios } from '@/hooks/useAxios' import { useAxios } from '@/hooks/useAxios'
@ -9,8 +9,6 @@ import { settingModalFirstOptionsState, settingModalSecondOptionsState } from '@
import { setSurfaceShapePattern } from '@/util/canvas-util' import { setSurfaceShapePattern } from '@/util/canvas-util'
import { POLYGON_TYPE } from '@/common/common' import { POLYGON_TYPE } from '@/common/common'
import { adsorptionPointModeState, adsorptionRangeState } from '@/store/canvasAtom'
export function useCanvasSetting() { export function useCanvasSetting() {
const canvas = useRecoilValue(canvasState) const canvas = useRecoilValue(canvasState)
@ -225,9 +223,9 @@ export function useCanvasSetting() {
const option1 = settingModalFirstOptions.option1 const option1 = settingModalFirstOptions.option1
// 'allocDisplay' 할당 표시 // 'allocDisplay' 할당 표시
// 'outlineDisplay' 외벽선 표시 'outerLine', 'wallLine' // 'outlineDisplay' 외벽선 표시 'outerLine', POLYGON_TYPE.WALL
// 'gridDisplay' 그리드 표시 'lindGrid', 'dotGrid' // 'gridDisplay' 그리드 표시 'lindGrid', 'dotGrid'
// 'lineDisplay' 지붕선 표시 'roof', 'roofBase' // 'lineDisplay' 지붕선 표시 'roof', POLYGON_TYPE.ROOF
// 'wordDisplay' 문자 표시 // 'wordDisplay' 문자 표시
// 'circuitNumDisplay' 회로번호 표시 // 'circuitNumDisplay' 회로번호 표시
// 'flowDisplay' 흐름방향 표시 'arrow' // 'flowDisplay' 흐름방향 표시 'arrow'
@ -244,13 +242,13 @@ export function useCanvasSetting() {
optionName = ['1'] optionName = ['1']
break break
case 'outlineDisplay': //외벽선 표시 case 'outlineDisplay': //외벽선 표시
optionName = ['outerLine', 'wallLine'] optionName = ['outerLine', POLYGON_TYPE.WALL]
break break
case 'gridDisplay': //그리드 표시 case 'gridDisplay': //그리드 표시
optionName = ['lindGrid', 'dotGrid'] optionName = ['lindGrid', 'dotGrid']
break break
case 'lineDisplay': //지붕선 표시 case 'lineDisplay': //지붕선 표시
optionName = ['roof', 'roofBase'] optionName = ['roof', POLYGON_TYPE.ROOF]
break break
case 'wordDisplay': //문자 표시 case 'wordDisplay': //문자 표시
optionName = ['6'] optionName = ['6']

View File

@ -2,6 +2,7 @@ import { useRecoilState, useRecoilValue } from 'recoil'
import { canvasState } from '@/store/canvasAtom' import { canvasState } from '@/store/canvasAtom'
import { useEffect } from 'react' import { useEffect } from 'react'
import { settingModalFirstOptionsState } from '@/store/settingAtom' import { settingModalFirstOptionsState } from '@/store/settingAtom'
import { POLYGON_TYPE } from '@/common/common'
export function useFirstOption() { export function useFirstOption() {
const canvas = useRecoilValue(canvasState) const canvas = useRecoilValue(canvasState)
@ -12,9 +13,9 @@ export function useFirstOption() {
const option1 = settingModalFirstOptions.option1 const option1 = settingModalFirstOptions.option1
// 'allocDisplay' 할당 표시 // 'allocDisplay' 할당 표시
// 'outlineDisplay' 외벽선 표시 'outerLine', 'wallLine' // 'outlineDisplay' 외벽선 표시 'outerLine', POLYGON_TYPE.WALL
// 'gridDisplay' 그리드 표시 'lindGrid', 'dotGrid' // 'gridDisplay' 그리드 표시 'lindGrid', 'dotGrid'
// 'lineDisplay' 지붕선 표시 'roof', 'roofBase' // 'lineDisplay' 지붕선 표시 'roof', POLYGON_TYPE.ROOF
// 'wordDisplay' 문자 표시 // 'wordDisplay' 문자 표시
// 'circuitNumDisplay' 회로번호 표시 // 'circuitNumDisplay' 회로번호 표시
// 'flowDisplay' 흐름방향 표시 'arrow' // 'flowDisplay' 흐름방향 표시 'arrow'
@ -30,13 +31,13 @@ export function useFirstOption() {
optionName = ['1'] optionName = ['1']
break break
case 'outlineDisplay': //외벽선 표시 case 'outlineDisplay': //외벽선 표시
optionName = ['outerLine', 'wallLine'] optionName = ['outerLine', POLYGON_TYPE.WALL]
break break
case 'gridDisplay': //그리드 표시 case 'gridDisplay': //그리드 표시
optionName = ['lineGrid', 'dotGrid', 'adsorptionPoint', 'tempGrid'] optionName = ['lineGrid', 'dotGrid', 'adsorptionPoint', 'tempGrid']
break break
case 'lineDisplay': //지붕선 표시 case 'lineDisplay': //지붕선 표시
optionName = ['roof', 'roofBase'] optionName = ['roof', POLYGON_TYPE.ROOF]
break break
case 'wordDisplay': //문자 표시 case 'wordDisplay': //문자 표시
optionName = ['6'] optionName = ['6']

View File

@ -15,14 +15,15 @@ import {
outerLineLength2State, outerLineLength2State,
outerLineTypeState, outerLineTypeState,
} from '@/store/outerLineAtom' } from '@/store/outerLineAtom'
import { calculateIntersection, distanceBetweenPoints, findClosestPoint, polygonToTurfPolygon } from '@/util/canvas-util' import { calculateIntersection, distanceBetweenPoints, findClosestPoint, isPointOnLine, polygonToTurfPolygon } from '@/util/canvas-util'
import { fabric } from 'fabric' import { fabric } from 'fabric'
import { useAdsorptionPoint } from '@/hooks/useAdsorptionPoint' import { useAdsorptionPoint } from '@/hooks/useAdsorptionPoint'
import { useSwal } from '@/hooks/useSwal' import { useSwal } from '@/hooks/useSwal'
import { booleanPointInPolygon } from '@turf/turf' import { booleanPointInPolygon } from '@turf/turf'
import { usePopup } from '@/hooks/usePopup' import { usePopup } from '@/hooks/usePopup'
import { calculateAngle } from '@/util/qpolygon-utils' import { calculateAngle, isSamePoint } from '@/util/qpolygon-utils'
import { QPolygon } from '@/components/fabric/QPolygon' import { QPolygon } from '@/components/fabric/QPolygon'
import { POLYGON_TYPE } from '@/common/common'
// 보조선 작성 // 보조선 작성
export function useAuxiliaryDrawing(id) { export function useAuxiliaryDrawing(id) {
@ -80,7 +81,7 @@ export function useAuxiliaryDrawing(id) {
useEffect(() => { useEffect(() => {
// innerLines가 있을경우 삭제 // innerLines가 있을경우 삭제
const roofs = canvas?.getObjects().filter((obj) => obj.name === 'roofBase') const roofs = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
if (roofs.length === 0) { if (roofs.length === 0) {
swalFire({ text: '지붕형상이 없습니다.' }) swalFire({ text: '지붕형상이 없습니다.' })
closePopup(id) closePopup(id)
@ -561,7 +562,7 @@ export function useAuxiliaryDrawing(id) {
return return
} }
const roofBases = canvas.getObjects().filter((obj) => obj.name === 'roofBase') const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
/*const allLines = [...auxiliaryLines] /*const allLines = [...auxiliaryLines]
roofBases.forEach((roofBase) => { roofBases.forEach((roofBase) => {
@ -586,7 +587,7 @@ export function useAuxiliaryDrawing(id) {
return return
} }
// 기존 점과 겹치는지 확인 // 기존 점과 겹치는지 확인
if (interSectionPointsWithRoofLines.some((point) => point.x === intersectionPoint.x && point.y === intersectionPoint.y)) { if (interSectionPointsWithRoofLines.some((point) => isSamePoint(point, intersectionPoint))) {
return return
} }
@ -611,9 +612,41 @@ export function useAuxiliaryDrawing(id) {
}, },
) )
lineHistory.current.push(newLine) lineHistory.current.push(newLine)
lineHistory.current = lineHistory.current.filter((history) => history !== line1)
removeLine(line1) removeLine(line1)
intersectionPoints.current.push(...interSectionPointsWithRoofLines) intersectionPoints.current.push(...interSectionPointsWithRoofLines)
return return
} else if (interSectionPointsWithRoofLines.length === 1) {
//지붕선과 만나는 점이 하나일 경우
const distance1 = distanceBetweenPoints({ x: line1.x1, y: line1.y1 }, interSectionPointsWithRoofLines[0])
const distance2 = distanceBetweenPoints({ x: line1.x2, y: line1.y2 }, interSectionPointsWithRoofLines[0])
if (!(distance1 === 0 || distance2 === 0)) {
if (distance1 >= distance2) {
const newLine = addLine([line1.x1, line1.y1, interSectionPointsWithRoofLines[0].x, interSectionPointsWithRoofLines[0].y], {
stroke: 'black',
strokeWidth: 1,
selectable: false,
name: 'auxiliaryLine',
isFixed: true,
})
lineHistory.current.push(newLine)
lineHistory.current = lineHistory.current.filter((history) => history !== line1)
removeLine(line1)
} else {
const newLine = addLine([line1.x2, line1.y2, interSectionPointsWithRoofLines[0].x, interSectionPointsWithRoofLines[0].y], {
stroke: 'black',
strokeWidth: 1,
selectable: false,
name: 'auxiliaryLine',
isFixed: true,
})
lineHistory.current.push(newLine)
lineHistory.current = lineHistory.current.filter((history) => history !== line1)
removeLine(line1)
}
intersectionPoints.current.push(interSectionPointsWithRoofLines[0])
}
} }
//보조선과 만나는 점을 찾는다. //보조선과 만나는 점을 찾는다.
@ -659,6 +692,7 @@ export function useAuxiliaryDrawing(id) {
}) })
} }
lineHistory.current.push(newLine) lineHistory.current.push(newLine)
lineHistory.current = lineHistory.current.filter((history) => history !== line1)
removeLine(line1) removeLine(line1)
}) })
@ -742,7 +776,7 @@ export function useAuxiliaryDrawing(id) {
return return
} }
const roofBases = canvas.getObjects().filter((obj) => obj.name === 'roofBase') const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
//lineHistory.current에 있는 선들 중 startPoint와 endPoint가 겹치는 line은 제거 //lineHistory.current에 있는 선들 중 startPoint와 endPoint가 겹치는 line은 제거
// 겹치는 선 하나는 canvas에서 제거한다. // 겹치는 선 하나는 canvas에서 제거한다.
@ -772,9 +806,13 @@ export function useAuxiliaryDrawing(id) {
}) })
const roofInnerLines = innerLines.filter((line) => { const roofInnerLines = innerLines.filter((line) => {
const inPolygon1 = const inPolygon1 =
tempPolygonPoints.some((point) => point.x === line.x1 && point.y === line.y1) || roofBase.inPolygon({ x: line.x1, y: line.y1 }) tempPolygonPoints.some((point) => point.x === line.x1 && point.y === line.y1) ||
roofBase.inPolygon({ x: line.x1, y: line.y1 }) ||
roofBase.lines.some((line) => isPointOnLine(line, { x: line.x1, y: line.y1 }))
const inPolygon2 = const inPolygon2 =
tempPolygonPoints.some((point) => point.x === line.x2 && point.y === line.y2) || roofBase.inPolygon({ x: line.x2, y: line.y2 }) tempPolygonPoints.some((point) => point.x === line.x2 && point.y === line.y2) ||
roofBase.inPolygon({ x: line.x2, y: line.y2 }) ||
roofBase.lines.some((line) => isPointOnLine(line, { x: line.x2, y: line.y2 }))
if (inPolygon1 && inPolygon2) { if (inPolygon1 && inPolygon2) {
line.attributes = { ...line.attributes, roofId: roofBase.id } line.attributes = { ...line.attributes, roofId: roofBase.id }

View File

@ -3,7 +3,7 @@ import { useRecoilValue } from 'recoil'
import { ANGLE_TYPE, canvasState, currentAngleTypeSelector, pitchTextSelector } from '@/store/canvasAtom' import { ANGLE_TYPE, canvasState, currentAngleTypeSelector, pitchTextSelector } from '@/store/canvasAtom'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { useEvent } from '@/hooks/useEvent' import { useEvent } from '@/hooks/useEvent'
import { LINE_TYPE } from '@/common/common' import { LINE_TYPE, POLYGON_TYPE } from '@/common/common'
import { useLine } from '@/hooks/useLine' import { useLine } from '@/hooks/useLine'
import { useMode } from '@/hooks/useMode' import { useMode } from '@/hooks/useMode'
import { outerLineFixState } from '@/store/outerLineAtom' import { outerLineFixState } from '@/store/outerLineAtom'
@ -54,7 +54,7 @@ export function useEavesGableEdit(id) {
}, []) }, [])
useEffect(() => { useEffect(() => {
const wallLines = canvas.getObjects().filter((obj) => obj.name === 'wallLine') const wallLines = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.WALL)
wallLines.forEach((wallLine) => { wallLines.forEach((wallLine) => {
convertPolygonToLines(wallLine) convertPolygonToLines(wallLine)
}) })
@ -160,7 +160,7 @@ export function useEavesGableEdit(id) {
attributes, attributes,
}) })
const roofBases = canvas?.getObjects().filter((obj) => obj.name === 'roofBase') const roofBases = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
roofBases.forEach((roof) => { roofBases.forEach((roof) => {
roof.innerLines.forEach((line) => { roof.innerLines.forEach((line) => {
@ -169,7 +169,7 @@ export function useEavesGableEdit(id) {
canvas.remove(roof) canvas.remove(roof)
}) })
const wallLines = canvas.getObjects().filter((obj) => obj.name === 'wallLine') const wallLines = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.WALL)
const removeTargets = canvas.getObjects().filter((obj) => obj.name === 'pitchText') const removeTargets = canvas.getObjects().filter((obj) => obj.name === 'pitchText')
removeTargets.forEach((obj) => { removeTargets.forEach((obj) => {
canvas.remove(obj) canvas.remove(obj)

View File

@ -1,9 +1,12 @@
import { useRecoilValue } from 'recoil' import { useRecoilValue } from 'recoil'
import { canvasState } from '@/store/canvasAtom' import { canvasState, currentObjectState } from '@/store/canvasAtom'
import { usePopup } from '@/hooks/usePopup' import { usePopup } from '@/hooks/usePopup'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { useEffect, useRef, useState } from 'react' import { useEffect, useRef, useState } from 'react'
import { useEvent } from '@/hooks/useEvent' import { useEvent } from '@/hooks/useEvent'
import { POLYGON_TYPE } from '@/common/common'
import { OUTER_LINE_TYPE } from '@/store/outerLineAtom'
import { QLine } from '@/components/fabric/QLine'
//동선이동 형 올림 내림 //동선이동 형 올림 내림
export function useMovementSetting(id) { export function useMovementSetting(id) {
@ -15,6 +18,8 @@ export function useMovementSetting(id) {
const { initEvent, addCanvasMouseEventListener } = useEvent() const { initEvent, addCanvasMouseEventListener } = useEvent()
const { closePopup } = usePopup() const { closePopup } = usePopup()
const { getMessage } = useMessage() const { getMessage } = useMessage()
const currentObject = useRecoilValue(currentObjectState)
const selectedObject = useRef(null)
const buttonType = [ const buttonType = [
{ id: 1, name: getMessage('modal.movement.flow.line.move'), type: TYPE.FLOW_LINE }, { id: 1, name: getMessage('modal.movement.flow.line.move'), type: TYPE.FLOW_LINE },
{ id: 2, name: getMessage('modal.movement.flow.line.updown'), type: TYPE.UP_DOWN }, { id: 2, name: getMessage('modal.movement.flow.line.updown'), type: TYPE.UP_DOWN },
@ -37,42 +42,231 @@ export function useMovementSetting(id) {
} }
useEffect(() => { useEffect(() => {
removeFlowLine()
typeRef.current = type typeRef.current = type
const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') // 기존 outerLine의 selectable true
outerLines.forEach((line) => {
line.set({ stroke: 'black' })
line.set({ visible: false })
})
canvas.getObjects().forEach((obj) => {
obj.set({ selectable: false })
})
const roofs = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF) // 기존 wallLine의 visible false
roofs.forEach((roof) => {
roof.innerLines.forEach((line) => {
line.bringToFront()
line.set({ selectable: false })
line.set({ strokeWidth: 1 })
})
})
if (type === TYPE.FLOW_LINE) {
roofs.forEach((roof) => {
roof.innerLines.forEach((line) => {
line.bringToFront()
line.set({ selectable: true })
line.set({ strokeWidth: 4 })
})
})
} else if (type === TYPE.UP_DOWN) {
const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') // 기존 outerLine의 selectable true
outerLines.forEach((line) => {
line.set({ stroke: 'black' })
line.set({ visible: true })
line.bringToFront()
line.set({ selectable: true })
})
}
canvas.renderAll()
}, [type]) }, [type])
useEffect(() => { useEffect(() => {
const wallLines = canvas.getObjects().filter((obj) => obj.name === 'wallLine') // 기존 wallLine의 visible false /*const wallLines = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.WALL) // 기존 wallLine의 visible false
wallLines.forEach((line) => { wallLines.forEach((line) => {
line.set({ visible: false }) line.set({ visible: false })
}) })*/
const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') // 기존 outerLine의 selectable true
outerLines.forEach((line) => {
line.bringToFront()
line.set({ selectable: true })
})
canvas.renderAll() canvas.renderAll()
addCanvasMouseEventListener('mouse:move', mouseMoveEvent) addCanvasMouseEventListener('mouse:move', mouseMoveEvent)
addCanvasMouseEventListener('mouse:down', mouseDownEvent)
return () => { return () => {
initEvent() initEvent()
const wallLines = canvas.getObjects().filter((obj) => obj.name === 'wallLine') removeFlowLine()
const wallLines = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.WALL)
wallLines.forEach((line) => { wallLines.forEach((line) => {
line.set({ visible: true }) line.set({ visible: true })
}) })
const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') // 기존 outerLine의 selectable true
outerLines.forEach((line) => {
line.set({ stroke: 'black' })
line.set({ visible: false })
})
canvas.renderAll() canvas.renderAll()
} }
}, []) }, [])
const mouseMoveEvent = (e) => { useEffect(() => {
if (typeRef.current === TYPE.FLOW_LINE) { const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') // 기존 outerLine의 selectable true
flowLineEvent(e) outerLines.forEach((line) => {
} else { line.set({ stroke: 'black' })
updownEvent(e) })
selectedObject.current = null
if (!currentObject) {
return
}
clearRef()
selectedObject.current = currentObject
if (currentObject.name === OUTER_LINE_TYPE.OUTER_LINE) {
currentObject.set({ stroke: '#EA10AC' })
currentObject.bringToFront()
}
canvas.renderAll()
}, [currentObject])
const clearRef = () => {
if (type === TYPE.FLOW_LINE) {
FLOW_LINE_REF.DOWN_LEFT_INPUT_REF.current.value = ''
FLOW_LINE_REF.UP_RIGHT_INPUT_REF.current.value = ''
FLOW_LINE_REF.DOWN_LEFT_RADIO_REF.current.checked = false
FLOW_LINE_REF.UP_RIGHT_RADIO_REF.current.checked = false
}
if (type === TYPE.UP_DOWN) {
UP_DOWN_REF.UP_INPUT_REF.current.value = ''
UP_DOWN_REF.DOWN_INPUT_REF.current.value = ''
UP_DOWN_REF.UP_RADIO_REF.current.checked = false
UP_DOWN_REF.DOWN_RADIO_REF.current.checked = false
} }
} }
const flowLineEvent = (e) => {}
const updownEvent = (e) => { const mouseDownEvent = (e) => {
if (typeRef.current === TYPE.FLOW_LINE) {
flowLineDownEvent(e)
} else {
updownDownEvent(e)
}
}
const removeFlowLine = () => {
const flowLine = canvas.getObjects().filter((obj) => obj.name === 'flowLine')
flowLine.forEach((line) => {
canvas.remove(line)
})
}
const mouseMoveEvent = (e) => {
if (typeRef.current === TYPE.FLOW_LINE) {
flowLineMoveEvent(e)
} else {
updownMoveEvent(e)
}
}
//동선 이동 마우스 클릭 이벤트
const flowLineDownEvent = (e) => {
const target = selectedObject.current
if (!target) {
return
}
const direction = target.direction
removeFlowLine()
let newPoint = []
if (direction === 'left' || direction === 'right') {
if (FLOW_LINE_REF.DOWN_LEFT_RADIO_REF.current.checked) {
newPoint = [
target.x1,
target.y1 + Number(FLOW_LINE_REF.DOWN_LEFT_INPUT_REF.current.value / 10),
target.x2,
target.y2 + Number(FLOW_LINE_REF.DOWN_LEFT_INPUT_REF.current.value / 10),
]
} else {
newPoint = [
target.x1,
target.y1 - Number(FLOW_LINE_REF.UP_RIGHT_INPUT_REF.current.value / 10),
target.x2,
target.y2 - Number(FLOW_LINE_REF.UP_RIGHT_INPUT_REF.current.value / 10),
]
}
} else {
if (FLOW_LINE_REF.DOWN_LEFT_RADIO_REF.current.checked) {
newPoint = [
target.x1 - Number(FLOW_LINE_REF.DOWN_LEFT_INPUT_REF.current.value / 10),
target.y1,
target.x2 - Number(FLOW_LINE_REF.DOWN_LEFT_INPUT_REF.current.value / 10),
target.y2,
]
} else {
newPoint = [
target.x1 + Number(FLOW_LINE_REF.UP_RIGHT_INPUT_REF.current.value / 10),
target.y1,
target.x2 + Number(FLOW_LINE_REF.UP_RIGHT_INPUT_REF.current.value / 10),
target.y2,
]
}
}
const cloned = new fabric.Line(newPoint, {
stroke: 'red',
strokeWidth: 4,
name: 'flowLine',
currentLine: target,
})
canvas.add(cloned)
canvas.renderAll()
canvas.discardActiveObject()
}
//형 올림내림 마우스 클릭 이벤트
const updownDownEvent = (e) => {
console.log('updownDownEvent')
}
const flowLineMoveEvent = (e) => {
const target = canvas.getActiveObject()
if (!target) {
return
}
const direction = target.direction
const { top: targetTop, left: targetLeft } = target
const currentX = canvas.getPointer(e.e).x
const currentY = Math.floor(canvas.getPointer(e.e).y)
if (direction === 'left' || direction === 'right') {
if (targetTop > currentY) {
console.log('targetTop > currentY')
FLOW_LINE_REF.UP_RIGHT_RADIO_REF.current.checked = true
FLOW_LINE_REF.DOWN_LEFT_INPUT_REF.current.value = ''
FLOW_LINE_REF.UP_RIGHT_INPUT_REF.current.value = Math.floor((Number(Math.abs(targetTop - currentY)) / 10000).toFixed(5) * 100000)
} else {
FLOW_LINE_REF.DOWN_LEFT_RADIO_REF.current.checked = true
FLOW_LINE_REF.UP_RIGHT_INPUT_REF.current.value = ''
FLOW_LINE_REF.DOWN_LEFT_INPUT_REF.current.value = Math.floor((Number(Math.abs(targetTop - currentY)) / 10000).toFixed(5) * 100000)
}
} else {
if (targetLeft > currentX) {
FLOW_LINE_REF.DOWN_LEFT_RADIO_REF.current.checked = true
FLOW_LINE_REF.UP_RIGHT_INPUT_REF.current.value = ''
FLOW_LINE_REF.DOWN_LEFT_INPUT_REF.current.value = Math.floor((Number(Math.abs(targetLeft - currentX)) / 10000).toFixed(5) * 100000)
} else {
FLOW_LINE_REF.UP_RIGHT_RADIO_REF.current.checked = true
FLOW_LINE_REF.DOWN_LEFT_INPUT_REF.current.value = ''
FLOW_LINE_REF.UP_RIGHT_INPUT_REF.current.value = Math.floor((Number(Math.abs(targetLeft - currentX)) / 10000).toFixed(5) * 100000)
}
}
canvas?.renderAll()
}
const updownMoveEvent = (e) => {
const target = canvas.getActiveObject() const target = canvas.getActiveObject()
if (!target) { if (!target) {
return return
@ -113,12 +307,24 @@ export function useMovementSetting(id) {
const handleSave = () => { const handleSave = () => {
if (type === TYPE.FLOW_LINE) { if (type === TYPE.FLOW_LINE) {
// 동선이동 const flowLine = canvas.getObjects().find((obj) => obj.name === 'flowLine')
if (FLOW_LINE_REF.DOWN_LEFT_RADIO_REF.current.checked) {
// 높이 변경: 아래, 왼쪽 체크 const currentLine = flowLine.currentLine
} else { if (!flowLine || !currentLine) {
// 높이 변경: 위, 오른쪽 체크 return
} }
currentLine.set({
x1: flowLine.x1,
y1: flowLine.y1,
x2: flowLine.x2,
y2: flowLine.y2,
})
currentLine.startPoint = { x: flowLine.x1, y: flowLine.y1 }
currentLine.endPoint = { x: flowLine.x2, y: flowLine.y2 }
canvas.remove(flowLine)
canvas.renderAll()
} else { } else {
// 형 올림내림 // 형 올림내림
if (UP_DOWN_REF.UP_RADIO_REF.current.checked) { if (UP_DOWN_REF.UP_RADIO_REF.current.checked) {

View File

@ -1,5 +1,5 @@
import { useEffect, useRef } from 'react' import { useEffect, useRef } from 'react'
import { LINE_TYPE } from '@/common/common' import { LINE_TYPE, POLYGON_TYPE } from '@/common/common'
import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil' import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil'
import { canvasState, currentObjectState } from '@/store/canvasAtom' import { canvasState, currentObjectState } from '@/store/canvasAtom'
import { useMode } from '@/hooks/useMode' import { useMode } from '@/hooks/useMode'
@ -135,7 +135,7 @@ export function usePropertiesSetting(id) {
hideLine(line) hideLine(line)
}) })
const wall = addPolygonByLines(lines, { name: 'wallLine', fill: 'transparent', stroke: 'black' }) const wall = addPolygonByLines(lines, { name: POLYGON_TYPE.WALL, fill: 'transparent', stroke: 'black' })
wall.lines = [...lines] wall.lines = [...lines]

View File

@ -7,6 +7,7 @@ import { useSwal } from '@/hooks/useSwal'
import { usePolygon } from '@/hooks/usePolygon' import { usePolygon } from '@/hooks/usePolygon'
import { roofDisplaySelector } from '@/store/settingAtom' import { roofDisplaySelector } from '@/store/settingAtom'
import { usePopup } from '@/hooks/usePopup' import { usePopup } from '@/hooks/usePopup'
import { POLYGON_TYPE } from '@/common/common'
// 지붕면 할당 // 지붕면 할당
export function useRoofAllocationSetting(id) { export function useRoofAllocationSetting(id) {
@ -81,12 +82,12 @@ export function useRoofAllocationSetting(id) {
const [selectedRoofMaterial, setSelectedRoofMaterial] = useState(roofMaterials[0]) const [selectedRoofMaterial, setSelectedRoofMaterial] = useState(roofMaterials[0])
useEffect(() => { useEffect(() => {
const roofBases = canvas.getObjects().filter((obj) => obj.name === 'roofBase') const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
if (roofBases.length === 0) { if (roofBases.length === 0) {
swalFire({ text: '할당할 지붕이 없습니다.' }) swalFire({ text: '할당할 지붕이 없습니다.' })
closePopup(id) closePopup(id)
} }
// if (type === 'roofBase') { // if (type === POLYGON_TYPE.ROOF) {
// // 지붕면 할당 // // 지붕면 할당
// //
// } else if ('roof') { // } else if ('roof') {
@ -104,8 +105,8 @@ export function useRoofAllocationSetting(id) {
// 선택한 지붕재로 할당 // 선택한 지붕재로 할당
const handleSave = () => { const handleSave = () => {
const roofBases = canvas.getObjects().filter((obj) => obj.name === 'roofBase') const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
const wallLines = canvas.getObjects().filter((obj) => obj.name === 'wallLine') const wallLines = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.WALL)
roofBases.forEach((roofBase) => { roofBases.forEach((roofBase) => {
try { try {
splitPolygonWithLines(roofBase) splitPolygonWithLines(roofBase)
@ -117,7 +118,7 @@ export function useRoofAllocationSetting(id) {
canvas.remove(line) canvas.remove(line)
}) })
canvas.remove(roofBase) // canvas.remove(roofBase)
}) })
wallLines.forEach((wallLine) => { wallLines.forEach((wallLine) => {

View File

@ -4,7 +4,7 @@ import { useEffect, useRef, useState } from 'react'
import { useLine } from '@/hooks/useLine' import { useLine } from '@/hooks/useLine'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { useEvent } from '@/hooks/useEvent' import { useEvent } from '@/hooks/useEvent'
import { LINE_TYPE } from '@/common/common' import { LINE_TYPE, POLYGON_TYPE } from '@/common/common'
import { useMode } from '@/hooks/useMode' import { useMode } from '@/hooks/useMode'
import { usePolygon } from '@/hooks/usePolygon' import { usePolygon } from '@/hooks/usePolygon'
import { outerLineFixState } from '@/store/outerLineAtom' import { outerLineFixState } from '@/store/outerLineAtom'
@ -60,7 +60,7 @@ export function useRoofShapePassivitySetting(id) {
useEffect(() => { useEffect(() => {
if (!isLoading) return if (!isLoading) return
addCanvasMouseEventListener('mouse:down', mouseDown) addCanvasMouseEventListener('mouse:down', mouseDown)
const wallLines = canvas.getObjects().filter((obj) => obj.name === 'wallLine') const wallLines = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.WALL)
canvas?.remove(...wallLines) canvas?.remove(...wallLines)
@ -185,7 +185,7 @@ export function useRoofShapePassivitySetting(id) {
} }
const handleLineToPolygon = () => { const handleLineToPolygon = () => {
const roofBases = canvas.getObjects().filter((obj) => obj.name === 'roofBase') const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
const exceptObjs = canvas.getObjects().filter((obj) => obj.name !== 'outerLine' && obj.parent?.name !== 'outerLine') const exceptObjs = canvas.getObjects().filter((obj) => obj.name !== 'outerLine' && obj.parent?.name !== 'outerLine')
const lines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') const lines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
exceptObjs.forEach((obj) => { exceptObjs.forEach((obj) => {
@ -199,10 +199,10 @@ export function useRoofShapePassivitySetting(id) {
let wall let wall
if (isFix.current) { if (isFix.current) {
wall = addPolygonByLines(lines, { name: 'wallLine', fill: 'transparent', stroke: 'black' }) wall = addPolygonByLines(lines, { name: POLYGON_TYPE.WALL, fill: 'transparent', stroke: 'black' })
} else { } else {
// 그냥 닫을 경우 처리 // 그냥 닫을 경우 처리
wall = addPolygonByLines([...initLines.current], { name: 'wallLine', fill: 'transparent', stroke: 'black' }) wall = addPolygonByLines([...initLines.current], { name: POLYGON_TYPE.WALL, fill: 'transparent', stroke: 'black' })
lines.forEach((line, idx) => { lines.forEach((line, idx) => {
line.attributes = initLines.current[idx].attributes line.attributes = initLines.current[idx].attributes
}) })

View File

@ -2,7 +2,7 @@ import { useEffect, useRef, useState } from 'react'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { useRecoilValue, useSetRecoilState } from 'recoil' import { useRecoilValue, useSetRecoilState } from 'recoil'
import { ANGLE_TYPE, canvasState, currentAngleTypeSelector, currentMenuState, currentObjectState, pitchTextSelector } from '@/store/canvasAtom' import { ANGLE_TYPE, canvasState, currentAngleTypeSelector, currentMenuState, currentObjectState, pitchTextSelector } from '@/store/canvasAtom'
import { LINE_TYPE } from '@/common/common' import { LINE_TYPE, POLYGON_TYPE } from '@/common/common'
import { usePolygon } from '@/hooks/usePolygon' import { usePolygon } from '@/hooks/usePolygon'
import { useMode } from '@/hooks/useMode' import { useMode } from '@/hooks/useMode'
import { useLine } from '@/hooks/useLine' import { useLine } from '@/hooks/useLine'
@ -129,7 +129,7 @@ export function useRoofShapeSetting(id) {
useEffect(() => { useEffect(() => {
if (shapeNum === 4) { if (shapeNum === 4) {
canvas?.remove(canvas.getObjects().find((obj) => obj.name === 'wallLine')) canvas?.remove(canvas.getObjects().find((obj) => obj.name === POLYGON_TYPE.WALL))
const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
outerLines.forEach((line) => { outerLines.forEach((line) => {
showLine(line) showLine(line)
@ -376,20 +376,20 @@ export function useRoofShapeSetting(id) {
// 기존 wallLine, roofBase 제거 // 기존 wallLine, roofBase 제거
canvas canvas
.getObjects() .getObjects()
.filter((obj) => obj.name === 'wallLine') .filter((obj) => obj.name === POLYGON_TYPE.WALL)
.forEach((line) => { .forEach((line) => {
canvas.remove(line) canvas.remove(line)
}) })
canvas canvas
.getObjects() .getObjects()
.filter((obj) => obj.name === 'roofBase') .filter((obj) => obj.name === POLYGON_TYPE.ROOF)
.forEach((obj) => { .forEach((obj) => {
canvas.remove(...obj.innerLines) canvas.remove(...obj.innerLines)
canvas.remove(obj) canvas.remove(obj)
}) })
const polygon = addPolygonByLines(outerLines, { name: 'wallLine' }) const polygon = addPolygonByLines(outerLines, { name: POLYGON_TYPE.WALL })
polygon.lines = [...outerLines] polygon.lines = [...outerLines]
addPitchTextsByOuterLines() addPitchTextsByOuterLines()

View File

@ -12,10 +12,12 @@ import { useEvent } from '@/hooks/useEvent'
import { usePopup } from '@/hooks/usePopup' import { usePopup } from '@/hooks/usePopup'
import { roofDisplaySelector } from '@/store/settingAtom' import { roofDisplaySelector } from '@/store/settingAtom'
import { usePolygon } from '@/hooks/usePolygon' import { usePolygon } from '@/hooks/usePolygon'
import { fontSelector } from '@/store/fontAtom'
export function useSurfaceShapeBatch() { export function useSurfaceShapeBatch() {
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { drawDirectionArrow } = usePolygon() const { drawDirectionArrow } = usePolygon()
const lengthTextFont = useRecoilValue(fontSelector('lengthText'))
const canvas = useRecoilValue(canvasState) const canvas = useRecoilValue(canvasState)
const globalPitch = useRecoilValue(globalPitchState) const globalPitch = useRecoilValue(globalPitchState)
@ -598,8 +600,214 @@ export function useSurfaceShapeBatch() {
}) })
} }
const findAllChildren = (parentId) => {
let allChildren = []
// 직계 자식 객체들 찾기
const directChildren = canvas.getObjects().filter((obj) => obj.parentId === parentId)
directChildren.forEach((child) => {
allChildren.push(child) // 현재 자식 추가
// 자식이 그룹인 경우
if (child.type === 'group') {
// 그룹 내부의 객체들 추가
child.getObjects().forEach((groupItem) => {
allChildren.push(groupItem)
// 그룹 내부 객체의 자식들도 찾기
const nestedChildren = findAllChildren(groupItem.id)
allChildren.push(...nestedChildren)
})
}
// 현재 자식의 하위 자식들 찾기
const childrenOfChild = findAllChildren(child.id)
allChildren.push(...childrenOfChild)
})
// 중복 제거하여 반환
return [...new Set(allChildren)]
}
const findGroupObjects = (parentId) => {
let groupObjectsArray = []
// 직계 자식 객체들 찾기
const directChildren = canvas.getObjects().filter((obj) => obj.parentId === parentId)
// 각 자식 객체에 대해 처리
directChildren.forEach((child) => {
groupObjectsArray.push(child) // 현재 자식 추가
// 자식이 그룹인 경우 그룹 내부 객체들도 처리
if (child.type === 'group') {
child.getObjects().forEach((groupItem) => {
// 그룹 내부 각 아이템의 하위 객체들 찾기
const nestedObjects = findGroupObjects(groupItem.id)
groupObjectsArray.push(...nestedObjects)
})
}
// 일반 자식의 하위 객체들 찾기
const childObjects = findGroupObjects(child.id)
groupObjectsArray.push(...childObjects)
})
return groupObjectsArray
}
function getAllRelatedObjects(id) {
const result = []
const map = new Map()
// Create a map of objects by their id
canvas.getObjects().forEach((obj) => {
map.set(obj.id, obj)
})
// Helper function to recursively find all related objects
function findRelatedObjects(id) {
const obj = map.get(id)
if (obj) {
result.push(obj)
canvas.getObjects().forEach((o) => {
if (o.parentId === id) {
findRelatedObjects(o.id)
}
})
}
}
// Start the search with the given parentId
findRelatedObjects(id)
return result
}
const moveSurfaceShapeBatch = () => {
const roof = canvas.getActiveObject()
if (roof) {
let isDragging = false
const childrenObjects = canvas.getObjects().filter((obj) => obj.parentId === roof.id)
console.log('childrenObjects', childrenObjects)
// const groupObjects = canvas.getObjects().filter((obj) => obj.parentId === roof.id && obj.type === 'group')
// const ungroupObjects = [] // 그룹 해제된 객체들
// const groupChildObjects = []
// groupObjects.forEach((obj) => {
// obj._restoreObjectsState()
// obj.getObjects().forEach((o) => {
// o.set({
// ungroupYn: true,
// })
// canvas.add(o)
// ungroupObjects.push(o)
// })
// canvas.remove(obj)
// })
// const childObjects = findAllChildren(roof.id)
// groupObjects.forEach((obj) => {
// groupChildObjects.push(...obj.getObjects())
// })
// console.log('ungroupObjects', ungroupObjects)
// console.log('childObjects', childObjects)
// console.log('groupChildObjects', groupChildObjects)
// const children = canvas.getObjects().filter((obj) => obj.parentId === roof.id)
// let grandChildren = []
// children.forEach((child) => {
// if (child.type === 'group') {
// child.getObjects().forEach((grandChild) => {
// const groupObjects = canvas.getObjects().filter((obj) => obj.parentId === grandChild.id)
// grandChildren.push(...groupObjects)
// })
// } else {
// grandChildren.push(...canvas.getObjects().filter((obj) => obj.parentId === child.id))
// }
// })
const selectionArray = [roof, ...childrenObjects]
const selection = new fabric.ActiveSelection(selectionArray, {
canvas: canvas,
draggable: true,
lockMovementX: false, // X축 이동 허용
lockMovementY: false, // Y축 이동 허용
originX: 'center',
originY: 'center',
})
canvas.setActiveObject(selection)
addCanvasMouseEventListener('mouse:up', (e) => {
isDragging = false
canvas.selection = true
canvas.discardActiveObject() // 모든 선택 해제
canvas.requestRenderAll() // 화면 업데이트
selection.getObjects().forEach((obj) => {
obj.set({
lockMovementX: true,
lockMovementY: true,
})
obj.setCoords()
if (obj.type === 'group') {
reGroupObject(obj)
}
})
canvas.renderAll()
roof.fire('polygonMoved')
if (roof.type === 'group') reGroupObject(obj)
drawDirectionArrow(roof)
initEvent()
})
}
}
const reGroupObject = (groupObj) => {
groupObj._restoreObjectsState() //이건 실행만 되도 그룹이 변경됨
const reGroupObjects = []
groupObj._objects.forEach((obj) => {
const newObj = new QPolygon(obj.getCurrentPoints(), {
...obj,
points: obj.getCurrentPoints(),
scaleX: 1,
scaleY: 1,
})
reGroupObjects.push(newObj)
canvas.remove(obj)
if (obj.direction) {
drawDirectionArrow(obj)
}
})
const reGroup = new fabric.Group(reGroupObjects, {
subTargetCheck: true,
name: groupObj.name,
id: groupObj.id,
groupYn: true,
parentId: groupObj.parentId,
})
canvas?.add(reGroup)
canvas?.remove(groupObj)
}
return { return {
applySurfaceShape, applySurfaceShape,
deleteAllSurfacesAndObjects, deleteAllSurfacesAndObjects,
moveSurfaceShapeBatch,
} }
} }

View File

@ -1,4 +1,4 @@
import { useState } from 'react' import { useEffect, useState } from 'react'
import { useRecoilState, useRecoilValue } from 'recoil' import { useRecoilState, useRecoilValue } from 'recoil'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
import { canvasSizeState, canvasState, canvasZoomState, currentObjectState, fontFamilyState, fontSizeState } from '@/store/canvasAtom' import { canvasSizeState, canvasState, canvasZoomState, currentObjectState, fontFamilyState, fontSizeState } from '@/store/canvasAtom'
@ -18,6 +18,10 @@ export function useCanvasEvent() {
const lengthTextOption = useRecoilValue(fontSelector('lengthText')) const lengthTextOption = useRecoilValue(fontSelector('lengthText'))
const { modifiedPlanFlag, setModifiedPlanFlag } = usePlan() const { modifiedPlanFlag, setModifiedPlanFlag } = usePlan()
useEffect(() => {
canvas?.setZoom(canvasZoom / 100)
}, [canvasZoom])
// 기본적인 이벤트 필요시 추가 // 기본적인 이벤트 필요시 추가
const attachDefaultEventOnCanvas = () => { const attachDefaultEventOnCanvas = () => {
removeEventOnCanvas() removeEventOnCanvas()
@ -205,13 +209,52 @@ export function useCanvasEvent() {
created: (e) => { created: (e) => {
const target = e.selected[0] const target = e.selected[0]
setCurrentObject(target) setCurrentObject(target)
const { selected } = e
if (selected.length > 0) {
selected.forEach((obj) => {
if (obj.type === 'QPolygon') {
obj.set({ stroke: 'red' })
}
})
canvas.renderAll()
}
}, },
cleared: (e) => { cleared: (e) => {
setCurrentObject(null) setCurrentObject(null)
const { deselected } = e
if (deselected.length > 0) {
deselected.forEach((obj) => {
if (obj.type === 'QPolygon') {
obj.set({ stroke: 'black' })
}
})
}
canvas.renderAll()
}, },
updated: (e) => { updated: (e) => {
const target = e.selected[0] const target = e.selected[0]
setCurrentObject(target) setCurrentObject(target)
const { selected, deselected } = e
if (deselected.length > 0) {
deselected.forEach((obj) => {
if (obj.type === 'QPolygon') {
obj.set({ stroke: 'black' })
}
})
}
if (selected.length > 0) {
selected.forEach((obj) => {
if (obj.type === 'QPolygon') {
obj.set({ stroke: 'red' })
}
})
}
canvas.renderAll()
}, },
} }
@ -365,6 +408,14 @@ export function useCanvasEvent() {
}) })
} }
const handleZoom = (isZoom) => {
if (isZoom) {
setCanvasZoom(canvasZoom + 10)
} else {
setCanvasZoom(canvasZoom - 10)
}
}
const handleZoomClear = () => { const handleZoomClear = () => {
setCanvasZoom(100) setCanvasZoom(100)
canvas.set({ zoom: 1 }) canvas.set({ zoom: 1 })
@ -376,5 +427,6 @@ export function useCanvasEvent() {
setCanvasForEvent, setCanvasForEvent,
attachDefaultEventOnCanvas, attachDefaultEventOnCanvas,
handleZoomClear, handleZoomClear,
handleZoom,
} }
} }

View File

@ -32,6 +32,8 @@ import ColumnInsert from '@/components/floor-plan/modal/module/column/ColumnInse
import RowRemove from '@/components/floor-plan/modal/module/row/RowRemove' import RowRemove from '@/components/floor-plan/modal/module/row/RowRemove'
import RowInsert from '@/components/floor-plan/modal/module/row/RowInsert' import RowInsert from '@/components/floor-plan/modal/module/row/RowInsert'
import CircuitNumberEdit from '@/components/floor-plan/modal/module/CircuitNumberEdit' import CircuitNumberEdit from '@/components/floor-plan/modal/module/CircuitNumberEdit'
import { useObjectBatch } from '@/hooks/object/useObjectBatch'
import { useSurfaceShapeBatch } from '@/hooks/surface/useSurfaceShapeBatch'
export function useContextMenu() { export function useContextMenu() {
const currentMenu = useRecoilValue(currentMenuState) // 현재 메뉴 const currentMenu = useRecoilValue(currentMenuState) // 현재 메뉴
@ -48,7 +50,8 @@ export function useContextMenu() {
const [cell, setCell] = useState(null) const [cell, setCell] = useState(null)
const [column, setColumn] = useState(null) const [column, setColumn] = useState(null)
const { handleZoomClear } = useCanvasEvent() const { handleZoomClear } = useCanvasEvent()
const { moveObjectBatch } = useObjectBatch({})
const { moveSurfaceShapeBatch } = useSurfaceShapeBatch()
const currentMenuSetting = () => { const currentMenuSetting = () => {
switch (currentMenu) { switch (currentMenu) {
case MENU.PLAN_DRAWING: case MENU.PLAN_DRAWING:
@ -228,21 +231,21 @@ export function useContextMenu() {
y: 180, y: 180,
}) })
setCurrentContextMenu(menu) setCurrentContextMenu(menu)
currentMenuSetting()
setQContextMenu({ ...qContextMenu, visible: false }) setQContextMenu({ ...qContextMenu, visible: false })
} }
const handleKeyup = (e) => { const handleKeyup = (e) => {
let menu = null let menu = null
for (let i = 0; i < contextMenu.length; i++) { for (let i = 0; i < contextMenu.length; i++) {
const temp = contextMenu[i].filter((menu) => { const temp = contextMenu[i].filter((menu) => {
return menu.shortcut?.includes(e.key) return menu.shortcut?.includes(e.key)
}) })
if (temp.length > 0) menu = temp if (temp.length > 0) menu = temp[0]
} }
handleClick(null, menu) if (menu) handleClick(null, menu)
} }
useEffect(() => { useEffect(() => {
@ -254,8 +257,8 @@ export function useContextMenu() {
}, [currentContextMenu]) }, [currentContextMenu])
useEffect(() => { useEffect(() => {
console.log('currentObject', currentObject)
if (currentObject?.name) { if (currentObject?.name) {
console.log(currentObject?.name)
switch (currentObject.name) { switch (currentObject.name) {
case 'triangleDormer': case 'triangleDormer':
case 'pentagonDormer': case 'pentagonDormer':
@ -270,16 +273,19 @@ export function useContextMenu() {
id: 'dormerRemove', id: 'dormerRemove',
shortcut: ['d', 'D'], shortcut: ['d', 'D'],
name: `${getMessage('contextmenu.remove')}(D)`, name: `${getMessage('contextmenu.remove')}(D)`,
fn: () => deleteObject(),
}, },
{ {
id: 'dormerMove', id: 'dormerMove',
shortcut: ['m', 'M'], shortcut: ['m', 'M'],
name: `${getMessage('contextmenu.move')}(M)`, name: `${getMessage('contextmenu.move')}(M)`,
fn: () => moveObjectBatch(),
}, },
{ {
id: 'dormerCopy', id: 'dormerCopy',
shortcut: ['c', 'C'], shortcut: ['c', 'C'],
name: `${getMessage('contextmenu.copy')}(C)`, name: `${getMessage('contextmenu.copy')}(C)`,
fn: () => copyObject(),
}, },
{ {
id: 'roofMaterialEdit', id: 'roofMaterialEdit',
@ -289,7 +295,7 @@ export function useContextMenu() {
{ {
id: 'dormerOffset', id: 'dormerOffset',
name: getMessage('contextmenu.dormer.offset'), name: getMessage('contextmenu.dormer.offset'),
component: <DormerOffset id={popupId} />, component: <DormerOffset id={popupId} title={getMessage('contextmenu.dormer.offset')} />,
}, },
], ],
]) ])
@ -300,22 +306,25 @@ export function useContextMenu() {
{ {
id: 'sizeEdit', id: 'sizeEdit',
name: '사이즈 변경', name: '사이즈 변경',
component: <SizeSetting id={popupId} />, component: <SizeSetting id={popupId} target={currentObject} />,
}, },
{ {
id: 'roofMaterialRemove', id: 'roofMaterialRemove',
shortcut: ['d', 'D'], shortcut: ['d', 'D'],
name: `${getMessage('contextmenu.remove')}(D)`, name: `${getMessage('contextmenu.remove')}(D)`,
fn: () => deleteObject(),
}, },
{ {
id: 'roofMaterialMove', id: 'roofMaterialMove',
shortcut: ['m', 'M'], shortcut: ['m', 'M'],
name: `${getMessage('contextmenu.move')}(M)`, name: `${getMessage('contextmenu.move')}(M)`,
fn: () => moveSurfaceShapeBatch(),
}, },
{ {
id: 'roofMaterialCopy', id: 'roofMaterialCopy',
shortcut: ['c', 'C'], shortcut: ['c', 'C'],
name: `${getMessage('contextmenu.copy')}(C)`, name: `${getMessage('contextmenu.copy')}(C)`,
fn: () => copyObject(),
}, },
], ],
[ [
@ -343,25 +352,30 @@ export function useContextMenu() {
{ {
id: 'sizeEdit', id: 'sizeEdit',
name: getMessage('contextmenu.size.edit'), name: getMessage('contextmenu.size.edit'),
component: <SizeSetting id={popupId} target={currentObject} />,
}, },
{ {
id: 'openingRemove', id: 'openingRemove',
shortcut: ['d', 'D'], shortcut: ['d', 'D'],
name: `${getMessage('contextmenu.remove')}(D)`, name: `${getMessage('contextmenu.remove')}(D)`,
fn: () => deleteObject(),
}, },
{ {
id: 'openingMove', id: 'openingMove',
shortcut: ['m', 'M'], shortcut: ['m', 'M'],
name: `${getMessage('contextmenu.move')}(M)`, name: `${getMessage('contextmenu.move')}(M)`,
fn: () => moveObject(),
}, },
{ {
id: 'openingCopy', id: 'openingCopy',
shortcut: ['c', 'C'], shortcut: ['c', 'C'],
name: `${getMessage('contextmenu.copy')}(C)`, name: `${getMessage('contextmenu.copy')}(C)`,
fn: () => copyObject(),
}, },
{ {
id: 'openingOffset', id: 'openingOffset',
name: getMessage('contextmenu.opening.offset'), name: getMessage('contextmenu.opening.offset'),
component: <DormerOffset id={popupId} title={getMessage('contextmenu.opening.offset')} />,
}, },
], ],
]) ])
@ -445,7 +459,7 @@ export function useContextMenu() {
], ],
]) ])
break break
case 'dimensionLine': case 'dimensionGroup':
setContextMenu([ setContextMenu([
[ [
{ {
@ -466,7 +480,7 @@ export function useContextMenu() {
{ {
id: 'dimensionLineDisplayEdit', id: 'dimensionLineDisplayEdit',
name: getMessage('contextmenu.display.edit'), name: getMessage('contextmenu.display.edit'),
component: <DimensionLineSetting id={popupId} />, component: <DimensionLineSetting id={popupId} isConfig={false} />,
}, },
], ],
]) ])
@ -477,22 +491,25 @@ export function useContextMenu() {
{ {
id: 'sizeEdit', id: 'sizeEdit',
name: getMessage('contextmenu.size.edit'), name: getMessage('contextmenu.size.edit'),
component: <SizeSetting id={popupId} />, component: <SizeSetting id={popupId} target={currentObject} />,
}, },
{ {
id: 'remove', id: 'remove',
shortcut: ['d', 'D'], shortcut: ['d', 'D'],
name: `${getMessage('contextmenu.remove')}(D)`, name: `${getMessage('contextmenu.remove')}(D)`,
fn: () => deleteObject(),
}, },
{ {
id: 'move', id: 'move',
shortcut: ['m', 'M'], shortcut: ['m', 'M'],
name: `${getMessage('contextmenu.move')}(M)`, name: `${getMessage('contextmenu.move')}(M)`,
fn: () => moveObject(),
}, },
{ {
id: 'copy', id: 'copy',
shortcut: ['c', 'C'], shortcut: ['c', 'C'],
name: `${getMessage('contextmenu.copy')}(C)`, name: `${getMessage('contextmenu.copy')}(C)`,
fn: () => copyObject(),
}, },
], ],
]) ])

View File

@ -1,12 +1,11 @@
import { useEffect, useRef } from 'react' import { useEffect, useRef } from 'react'
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil' import { useRecoilValue, useSetRecoilState } from 'recoil'
import { canvasState, canvasZoomState, currentMenuState, textModeState } from '@/store/canvasAtom' import { canvasState, canvasZoomState, currentMenuState, textModeState } from '@/store/canvasAtom'
import { fabric } from 'fabric' import { fabric } from 'fabric'
import { calculateDistance, calculateIntersection, distanceBetweenPoints, findClosestPoint, polygonToTurfPolygon } from '@/util/canvas-util' import { calculateDistance, calculateIntersection, distanceBetweenPoints, findClosestPoint } from '@/util/canvas-util'
import { useAdsorptionPoint } from '@/hooks/useAdsorptionPoint' import { useAdsorptionPoint } from '@/hooks/useAdsorptionPoint'
import { useDotLineGrid } from '@/hooks/useDotLineGrid' import { useDotLineGrid } from '@/hooks/useDotLineGrid'
import { useTempGrid } from '@/hooks/useTempGrid' import { useTempGrid } from '@/hooks/useTempGrid'
import { gridDisplaySelector } from '@/store/settingAtom'
export function useEvent() { export function useEvent() {
const canvas = useRecoilValue(canvasState) const canvas = useRecoilValue(canvasState)
@ -100,7 +99,13 @@ export function useEvent() {
const distance = calculateDistance(pointer, closestLine) const distance = calculateDistance(pointer, closestLine)
if (distance < adsorptionRange) { if (distance < adsorptionRange) {
arrivalPoint = closestLine.direction === 'vertical' ? { x: closestLine.x1, y: pointer.y } : { x: pointer.x, y: closestLine.y1 } arrivalPoint =
closestLine.direction === 'vertical'
? { x: closestLine.x1, y: pointer.y }
: {
x: pointer.x,
y: closestLine.y1,
}
} }
} }
} }
@ -261,6 +266,7 @@ export function useEvent() {
addCanvasMouseEventListener, addCanvasMouseEventListener,
removeAllMouseEventListeners, removeAllMouseEventListeners,
removeAllDocumentEventListeners, removeAllDocumentEventListeners,
removeDocumentEvent,
removeMouseEvent, removeMouseEvent,
removeMouseLine, removeMouseLine,
initEvent, initEvent,

View File

@ -36,10 +36,7 @@ import { QPolygon } from '@/components/fabric/QPolygon'
import offsetPolygon from '@/util/qpolygon-utils' import offsetPolygon from '@/util/qpolygon-utils'
import { isObjectNotEmpty } from '@/util/common-utils' import { isObjectNotEmpty } from '@/util/common-utils'
import * as turf from '@turf/turf' import * as turf from '@turf/turf'
import { INPUT_TYPE, Mode } from '@/common/common' import { INPUT_TYPE, LINE_TYPE, Mode, POLYGON_TYPE } from '@/common/common'
import { m } from 'framer-motion'
import { set } from 'react-hook-form'
import { FaWineGlassEmpty } from 'react-icons/fa6'
export function useMode() { export function useMode() {
const [mode, setMode] = useRecoilState(modeState) const [mode, setMode] = useRecoilState(modeState)
@ -1509,6 +1506,59 @@ export function useMode() {
* 지붕 외곽선 생성 polygon을 입력받아 만들기 * 지붕 외곽선 생성 polygon을 입력받아 만들기
*/ */
const handleOuterlinesTest2 = (polygon, offset = 50) => { const handleOuterlinesTest2 = (polygon, offset = 50) => {
// TODO [ljyoung] : offset 입력 처리 후 제거 해야함.
polygon.lines.forEach((line, index) => {
line.attributes = {
type: LINE_TYPE.WALLLINE.EAVES,
offset: 40,
width: 50,
pitch: 4,
sleeve: true,
}
/*if (index === 1 || index === 3) {
line.attributes = {
type: LINE_TYPE.WALLLINE.WALL,
offset: 50, //출폭
width: 30, //폭
pitch: 4, //구배
sleeve: true, //소매
}
} else {
line.attributes = {
type: LINE_TYPE.WALLLINE.EAVES,
offset: 40,
width: 50,
pitch: 4,
sleeve: true,
}
}*/
/* if (index === 1) {
line.attributes = {
type: LINE_TYPE.WALLLINE.SHED,
offset: 20, //출폭
width: 30, //폭
pitch: 4, //구배
sleeve: true, //소매
}
} else if (index === 3) {
line.attributes = {
type: LINE_TYPE.WALLLINE.EAVES,
offset: 50, //출폭
width: 30, //폭
pitch: 4, //구배
sleeve: true, //소매
}
} else {
line.attributes = {
type: LINE_TYPE.WALLLINE.GABLE,
offset: 30,
width: 50,
pitch: 4,
sleeve: true,
}
}*/
})
const roof = drawRoofPolygon(polygon) //지붕 그리기 const roof = drawRoofPolygon(polygon) //지붕 그리기
roof.drawHelpLine() roof.drawHelpLine()
// roof.divideLine() // roof.divideLine()
@ -1677,21 +1727,6 @@ export function useMode() {
} }
const drawRoofPolygon = (wall) => { const drawRoofPolygon = (wall) => {
// TODO [ljyoung] : offset 입력 처리 후 제거 해야함.
/*wall.lines.forEach((line, index) => {
if (index === wall.lines.length - 1 || index === 3) {
line.attributes = {
type: 'gable',
offset: 30,
}
} else {
line.attributes = {
type: 'hip',
offset: 50,
}
}
})*/
const polygon = createRoofPolygon(wall.points) const polygon = createRoofPolygon(wall.points)
const originPolygon = new QPolygon(wall.points, { fontSize: 0 }) const originPolygon = new QPolygon(wall.points, { fontSize: 0 })
originPolygon.setViewLengthText(false) originPolygon.setViewLengthText(false)
@ -1711,13 +1746,33 @@ export function useMode() {
return { x1: point.x, y1: point.y } return { x1: point.x, y1: point.y }
}), }),
) )
roof.name = 'roofBase' roof.name = POLYGON_TYPE.ROOF
roof.setWall(wall) roof.setWall(wall)
roof.lines.forEach((line, index) => { roof.lines.forEach((line, index) => {
line.attributes = wall.lines[index].attributes line.attributes = {
roofId: roof.id,
wallLine: wall.lines[index].id,
type: wall.lines[index].attributes.type,
offset: wall.lines[index].attributes.offset,
width: wall.lines[index].attributes.width,
pitch: wall.lines[index].attributes.pitch,
sleeve: wall.lines[index].attributes.sleeve || false,
}
}) })
wall.attributes = {
roofId: roof.id,
}
wall.lines.forEach((line, index) => {
line.attributes.roofId = roof.id
line.attributes.currentRoof = roof.lines[index].id
})
console.log('drawRoofPolygon roof : ', roof)
console.log('drawRoofPolygon wall : ', wall)
setRoof(roof) setRoof(roof)
setWall(wall) setWall(wall)

View File

@ -55,31 +55,38 @@ export function usePlan() {
/** /**
* 현재 캔버스에 그려진 데이터를 추출 * 현재 캔버스에 그려진 데이터를 추출
*/ */
const currentCanvasData = () => { const currentCanvasData = (mode = '') => {
removeMouseLines() removeMouseLines()
const groups = canvas.getObjects().filter((obj) => obj.type === 'group') if (mode === 'save') {
const groups = canvas.getObjects().filter((obj) => obj.type === 'group')
if (groups.length > 0) { if (groups.length > 0) {
groups.forEach((group) => { groups.forEach((group) => {
canvas?.remove(group) canvas?.remove(group)
canvas?.renderAll()
const restore = group._restoreObjectsState()
restore._objects.forEach((obj) => {
obj.set({
...obj,
groupYn: true,
groupName: group.name,
lineDirection: group.lineDirection,
})
canvas?.add(obj)
obj.setCoords()
canvas?.requestRenderAll()
canvas?.renderAll() canvas?.renderAll()
const restore = group._restoreObjectsState() //그룹 좌표 복구
//그룹시 좌표가 틀어지는 이슈
restore._objects.forEach((obj) => {
obj.set({
...obj,
groupYn: true,
groupName: group.name,
groupId: group.id,
})
//디렉션이 있는 경우에만
if (group.lineDirection) obj.set({ lineDirection: group.lineDirection })
//부모객체가 있으면 (면형상 위에 도머등..)
if (group.parentId) obj.set({ parentId: group.parentId })
canvas?.add(obj)
obj.setCoords()
canvas?.renderAll()
})
}) })
}) }
} }
return addCanvas() return addCanvas()
@ -163,7 +170,7 @@ export function usePlan() {
* 페이지 캔버스를 저장 * 페이지 캔버스를 저장
*/ */
const saveCanvas = async (userId) => { const saveCanvas = async (userId) => {
const canvasStatus = currentCanvasData() const canvasStatus = currentCanvasData('save')
initCanvasPlans.some((plan) => plan.id === currentCanvasPlan.id) initCanvasPlans.some((plan) => plan.id === currentCanvasPlan.id)
? await putCanvasStatus(canvasStatus) ? await putCanvasStatus(canvasStatus)
: await postCanvasStatus(userId, canvasStatus) : await postCanvasStatus(userId, canvasStatus)

View File

@ -113,6 +113,12 @@ export const usePolygon = () => {
return return
} }
//동일 아이디가 있으면 일단 지우고 다시 그린다
const existArrow = polygon.canvas.getObjects().filter((obj) => obj.name === 'arrow' && obj.parentId === polygon.id)
if (existArrow.length > 0) {
polygon.canvas.remove(...existArrow)
}
polygon.canvas polygon.canvas
.getObjects() .getObjects()
.filter((obj) => obj.name === 'flowText' && obj.parent === polygon.arrow) .filter((obj) => obj.name === 'flowText' && obj.parent === polygon.arrow)

View File

@ -4,47 +4,90 @@ import { contextPopupState, popupState } from '@/store/popupAtom'
export function usePopup() { export function usePopup() {
const [popup, setPopup] = useRecoilState(popupState) const [popup, setPopup] = useRecoilState(popupState)
const [contextMenuPopup, setContextMenuPopup] = useRecoilState(contextPopupState) const [contextMenuPopup, setContextMenuPopup] = useRecoilState(contextPopupState)
const addPopup = (id, depth, component) => {
setPopup({ children: [...filterDepth(depth), { id: id, depth: depth, component: component }] }) const addPopup = (id, depth, component, isConfig = false) => {
setPopup({
config: isConfig ? [...filterDepth(depth, isConfig), { id, depth, component, isConfig }] : [...popup.config],
other: !isConfig ? [...filterDepth(depth, isConfig), { id, depth, component, isConfig }] : [...popup.other],
})
} }
const closePopup = (id) => { const closePopup = (id, isConfig = false) => {
if (contextMenuPopup) setContextMenuPopup(null) if (contextMenuPopup) setContextMenuPopup(null)
setPopup({ children: [...filterChildrenPopup(id).filter((child) => child.id !== id)] }) if (isConfig) {
setPopup({
config: [...filterChildrenPopup(id, isConfig).filter((child) => child.id !== id)],
other: popup.other,
})
} else {
setPopup({
config: popup.config,
other: [...filterChildrenPopup(id, isConfig).filter((child) => child.id !== id)],
})
}
} }
const filterPopup = (depth) => { const filterPopup = (depth) => {
setPopup({ children: [...filterDepth(depth)] }) setPopup({
config: [...filterDepth(depth)],
other: [],
})
} }
const filterChildrenPopup = (id) => { const filterChildrenPopup = (id, isConfig) => {
const target = popup.children.filter((child) => child.id === id) let target = []
if (target.length !== 0) { if (isConfig) {
return popup.children.filter((child) => child.depth <= target[0].depth) target = popup?.config.filter((child) => child.id === id)
} else {
target = popup?.other.filter((child) => child.id === id)
} }
return popup.children if (target.length !== 0) {
if (isConfig) {
return popup?.config.filter((child) => child.depth <= target[0].depth)
} else {
return popup?.other.filter((child) => child.depth <= target[0].depth)
}
} else {
if (isConfig) {
return popup.config
} else {
return popup.other
}
}
} }
const closePopups = (ids) => { const closePopups = (ids) => {
setPopup({ children: [...popup.children.filter((child) => !ids.includes(child.id))] }) setPopup({
config: [...popup?.config.filter((child) => !ids.includes(child.id))],
other: [...popup?.other.filter((child) => !ids.includes(child.id))],
})
} }
const closeAll = () => { const closeAll = () => {
setPopup({ children: [] }) setPopup({
other: [],
config: [],
})
} }
const closePrevPopup = () => { const closePrevPopup = () => {
setPopup({ children: [...popup.children.slice(popup.children.length - 1)] }) setPopup({
config: [...popup?.slice(popup?.length - 1)],
other: [],
})
} }
const filterDepth = (depth) => { const filterDepth = (depth, isConfig) => {
return [...popup.children.filter((child) => child.depth !== depth)] if (isConfig) {
return [...popup?.config.filter((child) => child.depth < depth)]
} else {
return [...popup?.other.filter((child) => child.depth < depth)]
}
} }
return { return {
popup, popup,
setPopup,
addPopup, addPopup,
closePopup, closePopup,
closePopups, closePopups,

View File

@ -123,6 +123,7 @@
"modal.module.basic.setting.auto.placement": "設定値に自動配置", "modal.module.basic.setting.auto.placement": "設定値に自動配置",
"plan.menu.module.circuit.setting.circuit.trestle.setting": "回路と架台の設定", "plan.menu.module.circuit.setting.circuit.trestle.setting": "回路と架台の設定",
"modal.circuit.trestle.setting": "回路と架台設定", "modal.circuit.trestle.setting": "回路と架台設定",
"modal.circuit.trestle.setting.alloc.trestle": "仮割り当て",
"modal.circuit.trestle.setting.power.conditional.select": "パワーコンディショナーを選択", "modal.circuit.trestle.setting.power.conditional.select": "パワーコンディショナーを選択",
"modal.circuit.trestle.setting.power.conditional.select.name": "名称", "modal.circuit.trestle.setting.power.conditional.select.name": "名称",
"modal.circuit.trestle.setting.power.conditional.select.rated.output": "定格出力", "modal.circuit.trestle.setting.power.conditional.select.rated.output": "定格出力",
@ -130,6 +131,8 @@
"modal.circuit.trestle.setting.power.conditional.select.max.connection": "最大接続枚数", "modal.circuit.trestle.setting.power.conditional.select.max.connection": "最大接続枚数",
"modal.circuit.trestle.setting.power.conditional.select.max.overload": "過積最大枚数", "modal.circuit.trestle.setting.power.conditional.select.max.overload": "過積最大枚数",
"modal.circuit.trestle.setting.power.conditional.select.output.current": "出力電流", "modal.circuit.trestle.setting.power.conditional.select.output.current": "出力電流",
"modal.circuit.trestle.setting.power.conditional.select.check1": "同一傾斜同一方面の面積の場合、同じ面として回路を分ける。",
"modal.circuit.trestle.setting.power.conditional.select.check2": "MAX接続過積で回路を分ける。",
"modal.circuit.trestle.setting.circuit.allocation": "回路割り当て", "modal.circuit.trestle.setting.circuit.allocation": "回路割り当て",
"modal.circuit.trestle.setting.circuit.allocation.auto": "自動回路割り当て", "modal.circuit.trestle.setting.circuit.allocation.auto": "自動回路割り当て",
"modal.circuit.trestle.setting.circuit.allocation.passivity": "手動回路割当", "modal.circuit.trestle.setting.circuit.allocation.passivity": "手動回路割当",
@ -140,6 +143,12 @@
"modal.circuit.trestle.setting.circuit.allocation.passivity.all.power.conditional.reset": "すべての回路番号の初期化", "modal.circuit.trestle.setting.circuit.allocation.passivity.all.power.conditional.reset": "すべての回路番号の初期化",
"modal.circuit.trestle.setting.circuit.allocation.passivity.circuit.num.fix": "番号確定", "modal.circuit.trestle.setting.circuit.allocation.passivity.circuit.num.fix": "番号確定",
"modal.circuit.trestle.setting.step.up.allocation": "昇圧設定", "modal.circuit.trestle.setting.step.up.allocation": "昇圧設定",
"modal.circuit.trestle.setting.step.up.allocation.serial.amount": "シリアル枚数",
"modal.circuit.trestle.setting.step.up.allocation.total.amount": "総回路数",
"modal.circuit.trestle.setting.step.up.allocation.connected": "接続する",
"modal.circuit.trestle.setting.step.up.allocation.circuit.amount": "昇圧回路数",
"modal.circuit.trestle.setting.step.up.allocation.option": "昇圧オプション",
"modal.circuit.trestle.setting.step.up.allocation.select.monitor": "モニターの選択",
"plan.menu.module.circuit.setting.plan.orientation": "図面方位の適用", "plan.menu.module.circuit.setting.plan.orientation": "図面方位の適用",
"plan.menu.estimate": "見積", "plan.menu.estimate": "見積",
"plan.menu.estimate.roof.alloc": "屋根面の割り当て", "plan.menu.estimate.roof.alloc": "屋根面の割り当て",
@ -148,6 +157,7 @@
"modal.roof.alloc.select.parallel": "並列式", "modal.roof.alloc.select.parallel": "並列式",
"modal.roof.alloc.select.stairs": "カスケード", "modal.roof.alloc.select.stairs": "カスケード",
"modal.roof.alloc.apply": "選択した屋根材として割り当て", "modal.roof.alloc.apply": "選択した屋根材として割り当て",
"plan.menu.estimate.docDown": "文書のダウンロード",
"plan.menu.estimate.save": "保存", "plan.menu.estimate.save": "保存",
"plan.menu.estimate.reset": "初期化", "plan.menu.estimate.reset": "初期化",
"plan.menu.estimate.copy": "コピー", "plan.menu.estimate.copy": "コピー",
@ -473,6 +483,19 @@
"commons.east": "ドン", "commons.east": "ドン",
"commons.south": "南", "commons.south": "南",
"commons.north": "北", "commons.north": "北",
"font.style.normal": "보통(JA)",
"font.style.italic": "기울임꼴(JA)",
"font.style.bold": "굵게(JA)",
"font.style.bold.italic": "굵은 기울임꼴(JA)",
"color.black": "검정색(JA)",
"color.red": "빨강색(JA)",
"color.blue": "파랑색(JA)",
"color.gray": "회색(JA)",
"color.yellow": "황색(JA)",
"color.green": "녹색(JA)",
"color.pink": "분홍색(JA)",
"color.gold": "황금색(JA)",
"color.darkblue": "남색(JA)",
"site.name": "Q.CAST III", "site.name": "Q.CAST III",
"site.sub_name": "太陽光発電システム図面管理サイト", "site.sub_name": "太陽光発電システム図面管理サイト",
"board.notice.title": "お知らせ", "board.notice.title": "お知らせ",
@ -783,18 +806,18 @@
"surface.shape.validate.size.4to5": "④길이는 ⑤보다 큰 값을 넣어주세요.", "surface.shape.validate.size.4to5": "④길이는 ⑤보다 큰 값을 넣어주세요.",
"estimate.detail.header.title": "基本情報", "estimate.detail.header.title": "基本情報",
"estimate.detail.objectNo": "品番", "estimate.detail.objectNo": "品番",
"estimate.detail.estimateNo": "見積書番号", "estimate.detail.docNo": "見積書番号",
"estimate.detail.createDatetime": "登録日", "estimate.detail.drawingEstimateCreateDate": "登録日",
"estimate.detail.lastEditDatetime": "変更日時", "estimate.detail.lastEditDatetime": "変更日時",
"estimate.detail.saleStoreId": "一次販売店名", "estimate.detail.saleStoreId": "一次販売店名",
"estimate.detail.estimateDate": "見積日", "estimate.detail.estimateDate": "見積日",
"estimate.detail.otherSaleStoreId": "二次販売店名", "estimate.detail.otherSaleStoreId": "二次販売店名",
"estimate.detail.receiveUser": "担当者 ", "estimate.detail.receiveUser": "担当者 ",
"estimate.detail.title": "案件名", "estimate.detail.objectName": "案件名",
"estimate.detail.remarks": "メモ", "estimate.detail.objectRemarks": "メモ",
"estimate.detail.orderType": "注文分類", "estimate.detail.estimateType": "注文分類",
"estimate.detail.roofCns": "屋根材・仕様施工", "estimate.detail.roofCns": "屋根材・仕様施工",
"estimate.detail.note": "備考", "estimate.detail.remarks": "備考",
"estimate.detail.nextSubmit": "後日資料提出", "estimate.detail.nextSubmit": "後日資料提出",
"estimate.detail.header.fileList1": "ファイル添付", "estimate.detail.header.fileList1": "ファイル添付",
"estimate.detail.fileList.btn": "ファイル選択", "estimate.detail.fileList.btn": "ファイル選択",
@ -813,7 +836,10 @@
"estimate.detail.sepcialEstimateProductInfo.calcFormula2": "PKG単価 (W)×PKG容量(W)", "estimate.detail.sepcialEstimateProductInfo.calcFormula2": "PKG単価 (W)×PKG容量(W)",
"estimate.detail.header.showPrice": "価格表示", "estimate.detail.header.showPrice": "価格表示",
"estimate.detail.showPrice.btn1": "Pricing", "estimate.detail.showPrice.btn1": "Pricing",
"estimate.detail.showPrice.description": "クリックして製品の特異性を確認する", "estimate.detail.showPrice.description1": "製品価格 OPEN",
"estimate.detail.showPrice.description2": "追加, 変更資材",
"estimate.detail.showPrice.description3": "添付必須",
"estimate.detail.showPrice.description4": "クリックして製品の特異性を確認する",
"estimate.detail.showPrice.btn2": "製品を追加", "estimate.detail.showPrice.btn2": "製品を追加",
"estimate.detail.showPrice.btn3": "製品削除" "estimate.detail.showPrice.btn3": "製品削除"
} }

View File

@ -127,6 +127,7 @@
"modal.module.basic.setting.auto.placement": "설정값으로 자동 배치", "modal.module.basic.setting.auto.placement": "설정값으로 자동 배치",
"plan.menu.module.circuit.setting.circuit.trestle.setting": "회로 및 가대 설정", "plan.menu.module.circuit.setting.circuit.trestle.setting": "회로 및 가대 설정",
"modal.circuit.trestle.setting": "회로 및 가대설정", "modal.circuit.trestle.setting": "회로 및 가대설정",
"modal.circuit.trestle.setting.alloc.trestle": "가대할당",
"modal.circuit.trestle.setting.power.conditional.select": "파워컨디셔너 선택", "modal.circuit.trestle.setting.power.conditional.select": "파워컨디셔너 선택",
"modal.circuit.trestle.setting.power.conditional.select.name": "명칭", "modal.circuit.trestle.setting.power.conditional.select.name": "명칭",
"modal.circuit.trestle.setting.power.conditional.select.rated.output": "정격출력", "modal.circuit.trestle.setting.power.conditional.select.rated.output": "정격출력",
@ -134,6 +135,8 @@
"modal.circuit.trestle.setting.power.conditional.select.max.connection": "최대접속매수", "modal.circuit.trestle.setting.power.conditional.select.max.connection": "최대접속매수",
"modal.circuit.trestle.setting.power.conditional.select.max.overload": "과적최대매수", "modal.circuit.trestle.setting.power.conditional.select.max.overload": "과적최대매수",
"modal.circuit.trestle.setting.power.conditional.select.output.current": "출력전류", "modal.circuit.trestle.setting.power.conditional.select.output.current": "출력전류",
"modal.circuit.trestle.setting.power.conditional.select.check1": "동일경사 동일 방면의 면적인 경우, 같은 면으로서 회로를 나눈다.",
"modal.circuit.trestle.setting.power.conditional.select.check2": "MAX 접속(과적)으로 회로를 나눈다.",
"modal.circuit.trestle.setting.circuit.allocation": "회로 할당", "modal.circuit.trestle.setting.circuit.allocation": "회로 할당",
"modal.circuit.trestle.setting.circuit.allocation.auto": "자동 회로 할당", "modal.circuit.trestle.setting.circuit.allocation.auto": "자동 회로 할당",
"modal.circuit.trestle.setting.circuit.allocation.passivity": "수동 회로 할당", "modal.circuit.trestle.setting.circuit.allocation.passivity": "수동 회로 할당",
@ -144,6 +147,12 @@
"modal.circuit.trestle.setting.circuit.allocation.passivity.all.power.conditional.reset": "모든 회로번호 초기화", "modal.circuit.trestle.setting.circuit.allocation.passivity.all.power.conditional.reset": "모든 회로번호 초기화",
"modal.circuit.trestle.setting.circuit.allocation.passivity.circuit.num.fix": "번호 확정", "modal.circuit.trestle.setting.circuit.allocation.passivity.circuit.num.fix": "번호 확정",
"modal.circuit.trestle.setting.step.up.allocation": "승압 설정", "modal.circuit.trestle.setting.step.up.allocation": "승압 설정",
"modal.circuit.trestle.setting.step.up.allocation.serial.amount": "직렬매수",
"modal.circuit.trestle.setting.step.up.allocation.total.amount": "총 회로수",
"modal.circuit.trestle.setting.step.up.allocation.connected": "연결함",
"modal.circuit.trestle.setting.step.up.allocation.circuit.amount": "승압회로수",
"modal.circuit.trestle.setting.step.up.allocation.option": "승압옵션",
"modal.circuit.trestle.setting.step.up.allocation.select.monitor": "모니터 선택",
"plan.menu.module.circuit.setting.plan.orientation": "도면 방위 적용", "plan.menu.module.circuit.setting.plan.orientation": "도면 방위 적용",
"plan.menu.estimate": "견적서", "plan.menu.estimate": "견적서",
"plan.menu.estimate.roof.alloc": "지붕면 할당", "plan.menu.estimate.roof.alloc": "지붕면 할당",
@ -152,6 +161,7 @@
"modal.roof.alloc.select.parallel": "병렬식", "modal.roof.alloc.select.parallel": "병렬식",
"modal.roof.alloc.select.stairs": "계단식", "modal.roof.alloc.select.stairs": "계단식",
"modal.roof.alloc.apply": "선택한 지붕재로 할당", "modal.roof.alloc.apply": "선택한 지붕재로 할당",
"plan.menu.estimate.docDown": "문서 다운로드",
"plan.menu.estimate.save": "저장", "plan.menu.estimate.save": "저장",
"plan.menu.estimate.reset": "초기화", "plan.menu.estimate.reset": "초기화",
"plan.menu.estimate.copy": "복사", "plan.menu.estimate.copy": "복사",
@ -479,6 +489,19 @@
"commons.east": "동", "commons.east": "동",
"commons.south": "남", "commons.south": "남",
"commons.north": "북", "commons.north": "북",
"font.style.normal": "보통",
"font.style.italic": "기울임꼴",
"font.style.bold": "굵게",
"font.style.bold.italic": "굵은 기울임꼴",
"color.black": "검정색",
"color.red": "빨강색",
"color.blue": "파랑색",
"color.gray": "회색",
"color.yellow": "황색",
"color.green": "녹색",
"color.pink": "분홍색",
"color.gold": "황금색",
"color.darkblue": "남색",
"site.name": "Q.CAST III", "site.name": "Q.CAST III",
"site.sub_name": "태양광 발전 시스템 도면관리 사이트", "site.sub_name": "태양광 발전 시스템 도면관리 사이트",
"board.notice.title": "공지사항", "board.notice.title": "공지사항",
@ -789,18 +812,18 @@
"surface.shape.validate.size.4to5": "④길이는 ⑤보다 큰 값을 넣어주세요.", "surface.shape.validate.size.4to5": "④길이는 ⑤보다 큰 값을 넣어주세요.",
"estimate.detail.header.title": "기본정보", "estimate.detail.header.title": "기본정보",
"estimate.detail.objectNo": "물건번호", "estimate.detail.objectNo": "물건번호",
"estimate.detail.estimateNo": "견적서 번호", "estimate.detail.docNo": "견적서 번호",
"estimate.detail.createDatetime": "등록일", "estimate.detail.drawingEstimateCreateDate": "등록일",
"estimate.detail.lastEditDatetime": "변경일시", "estimate.detail.lastEditDatetime": "변경일시",
"estimate.detail.saleStoreId": "1차 판매점명", "estimate.detail.saleStoreId": "1차 판매점명",
"estimate.detail.estimateDate": "견적일", "estimate.detail.estimateDate": "견적일",
"estimate.detail.otherSaleStoreId": "2차 판매점명", "estimate.detail.otherSaleStoreId": "2차 판매점명",
"estimate.detail.receiveUser": "담당자", "estimate.detail.receiveUser": "담당자",
"estimate.detail.title": "안건명", "estimate.detail.objectName": "안건명",
"estimate.detail.remarks": "메모", "estimate.detail.objectRemarks": "메모",
"estimate.detail.orderType": "주문분류", "estimate.detail.estimateType": "주문분류",
"estimate.detail.roofCns": "지붕재・사양시공", "estimate.detail.roofCns": "지붕재・사양시공",
"estimate.detail.note": "비고", "estimate.detail.remarks": "비고",
"estimate.detail.nextSubmit": "후일자료제출", "estimate.detail.nextSubmit": "후일자료제출",
"estimate.detail.header.fileList1": "파일첨부", "estimate.detail.header.fileList1": "파일첨부",
"estimate.detail.fileList.btn": "파일선택", "estimate.detail.fileList.btn": "파일선택",
@ -819,7 +842,10 @@
"estimate.detail.sepcialEstimateProductInfo.calcFormula2": "PKG단가(W) * PKG용량(W)", "estimate.detail.sepcialEstimateProductInfo.calcFormula2": "PKG단가(W) * PKG용량(W)",
"estimate.detail.header.showPrice": "가격표시", "estimate.detail.header.showPrice": "가격표시",
"estimate.detail.showPrice.btn1": "Pricing", "estimate.detail.showPrice.btn1": "Pricing",
"estimate.detail.showPrice.description": "클릭하여 제품 특이사항 확인", "estimate.detail.showPrice.description1": "제품 가격 OPEN",
"estimate.detail.showPrice.description2": "추가, 변경 자재",
"estimate.detail.showPrice.description3": "첨부필수",
"estimate.detail.showPrice.description4": "클릭하여 제품 특이사항 확인",
"estimate.detail.showPrice.btn2": "제품추가", "estimate.detail.showPrice.btn2": "제품추가",
"estimate.detail.showPrice.btn3": "제품삭제" "estimate.detail.showPrice.btn3": "제품삭제"
} }

View File

@ -7,3 +7,9 @@ export const floorPlanObjectState = atom({
}, },
dangerouslyAllowMutability: true, dangerouslyAllowMutability: true,
}) })
export const estimateState = atom({
key: `estimateState`,
default: {},
dangerouslyAllowMutability: true,
})

View File

@ -7,7 +7,8 @@ import { atom } from 'recoil'
export const popupState = atom({ export const popupState = atom({
key: 'popupState', key: 'popupState',
default: { default: {
children: [], config: [],
other: [],
}, },
dangerouslyAllowMutability: true, dangerouslyAllowMutability: true,
}) })

View File

@ -5,7 +5,7 @@ export const settingModalFirstOptionsState = atom({
default: { default: {
option1: [ option1: [
{ id: 1, column: 'allocDisplay', name: 'modal.canvas.setting.first.option.alloc', selected: false }, { id: 1, column: 'allocDisplay', name: 'modal.canvas.setting.first.option.alloc', selected: false },
{ id: 2, column: 'outlineDisplay', name: 'modal.canvas.setting.first.option.outline', selected: false }, { id: 2, column: 'outlineDisplay', name: 'modal.canvas.setting.first.option.outline', selected: true },
{ id: 3, column: 'gridDisplay', name: 'modal.canvas.setting.first.option.grid', selected: false }, { id: 3, column: 'gridDisplay', name: 'modal.canvas.setting.first.option.grid', selected: false },
{ id: 4, column: 'lineDisplay', name: 'modal.canvas.setting.first.option.roof.line', selected: false }, { id: 4, column: 'lineDisplay', name: 'modal.canvas.setting.first.option.roof.line', selected: false },
{ id: 5, column: 'wordDisplay', name: 'modal.canvas.setting.first.option.word', selected: false }, { id: 5, column: 'wordDisplay', name: 'modal.canvas.setting.first.option.word', selected: false },

View File

@ -828,22 +828,49 @@
} }
} }
.estimate-arr-btn{
display: block;
width: 20px;
height: 20px;
background-color: #94A0AD;
border: 1px solid #94A0AD;
background-position: center;
background-repeat: no-repeat;
background-image: url(../../public/static/images/canvas/estiment_arr.svg);
background-size: 11px 7px;
border-radius: 2px;
&.up{
rotate: 180deg;
}
&.on{
background-color: #fff;
border-color: #C2D0DD;
background-image: url(../../public/static/images/canvas/estiment_arr_color.svg)
}
}
.estimate-check-wrap{
.estimate-check-inner{
display: block;
}
&.hide{
border-bottom: 1px solid #ECF0F4;
margin-bottom: 15px;
.estimate-check-inner{
display: none;
}
}
}
.special-note-check-wrap{ .special-note-check-wrap{
display: grid; display: grid;
grid-template-columns: repeat(5, 1fr); grid-template-columns: repeat(5, 1fr);
border: 1px solid #ECF0F4;
border-radius: 3px; border-radius: 3px;
margin-bottom: 30px; margin-bottom: 30px;
.special-note-check-item{ .special-note-check-item{
padding: 14px 10px; padding: 14px 10px;
border-right: 1px solid #ECF0F4; border: 1px solid #ECF0F4;
border-top: 1px solid #ECF0F4; margin-top: -1px;
&:nth-child(5n){ margin-right: -1px;
border-right: none;
}
&:nth-child(-n+5){
border-top: none;
}
&.act{ &.act{
background-color: #F7F9FA; background-color: #F7F9FA;
} }
@ -1312,6 +1339,7 @@
font-weight: 400; font-weight: 400;
color: #999999; color: #999999;
padding: 0; padding: 0;
width: 100%;
height: 100%; height: 100%;
flex: 1 ; flex: 1 ;
background-color: inherit; background-color: inherit;

View File

@ -239,6 +239,7 @@ footer{
nav{ nav{
.nav-list{ .nav-list{
.nav-item{ .nav-item{
a,
button{ button{
font-size: 13px; font-size: 13px;
} }

View File

@ -37,9 +37,10 @@
top: 50%; top: 50%;
left: 0; left: 0;
transform: translateY(-50%); transform: translateY(-50%);
width: 20px; width: 22px;
height: 20px; height: 22px;
background: url(../../public/static/images/main/id_icon.svg)no-repeat center; background: url(../../public/static/images/main/id_icon.svg)no-repeat center;
background-size: cover;
} }
} }
.store-arr{ .store-arr{

View File

@ -391,19 +391,22 @@ $alert-color: #101010;
table-layout: fixed; table-layout: fixed;
tr{ tr{
th{ th{
display: flex;
align-items: center;
font-size: $pop-normal-size; font-size: $pop-normal-size;
color: $pop-color; color: $pop-color;
font-weight: $pop-bold-weight; font-weight: $pop-bold-weight;
padding: 18px 0; padding: 18px 0;
border-bottom: 1px solid #424242; border-bottom: 1px solid #424242;
vertical-align: middle;
.tip-wrap{
display: flex;
align-items: center;
}
} }
td{ td{
font-size: $pop-normal-size; font-size: $pop-normal-size;
color: $pop-color; color: $pop-color;
border-bottom: 1px solid #424242; border-bottom: 1px solid #424242;
padding-left: 20px; padding: 18px 0 18px 20px;
vertical-align: middle; vertical-align: middle;
.flex-box{ .flex-box{
display: flex; display: flex;
@ -479,6 +482,7 @@ $alert-color: #101010;
color: $pop-color; color: $pop-color;
font-weight: $pop-normal-weight; font-weight: $pop-normal-weight;
} }
} }
.img-edit-wrap{ .img-edit-wrap{
@ -528,6 +532,23 @@ $alert-color: #101010;
} }
} }
.for-address{
input{
flex: 1;
}
.check-address{
flex: none;
width: 18px;
height: 18px;
margin-left: 5px;
background-repeat: no-repeat;
background-position: center;
background-size: cover;
&.fail{background-image: url(../../public/static/images/canvas/img_check_fail.svg);}
&.success{background-image: url(../../public/static/images/canvas/img_check_success.svg);}
}
}
// 외벽선 그리기 // 외벽선 그리기
.outline-wrap{ .outline-wrap{
padding: 24px 0; padding: 24px 0;
@ -1136,6 +1157,10 @@ $alert-color: #101010;
.object-direction-wrap{ .object-direction-wrap{
flex: 1; flex: 1;
} }
&:first-child{
flex: none;
width: 195px;
}
} }
} }
@ -1208,6 +1233,7 @@ $alert-color: #101010;
width: 5px; width: 5px;
height: 5px; height: 5px;
background-color: #fff; background-color: #fff;
border-radius: 50%;
} }
i{ i{
color: #fff; color: #fff;
@ -1232,6 +1258,9 @@ $alert-color: #101010;
} }
} }
.draw-flow-wrap{
margin: 10px 0;
}
// 지붕모듈선택 // 지붕모듈선택
.roof-module-tab{ .roof-module-tab{

View File

@ -263,12 +263,12 @@ export const getDegreeByChon = (chon) => {
} }
/** /**
* * 각도를 입력받아 촌을 반환
* @param degree
* @returns {number}
*/ */
export const getChonByDegree = (degree) => { export const getChonByDegree = (degree) => {
// tan(theta) = height / base return Number((Math.tan((degree * Math.PI) / 180) * 10).toFixed(2))
const radians = (degree * Math.PI) / 180
return Number(Number(Math.tan(radians) * 10).toFixed(2))
} }
/** /**
@ -521,9 +521,11 @@ export function isPointOnLine(line, point) {
const a = line.y2 - line.y1 const a = line.y2 - line.y1
const b = line.x1 - line.x2 const b = line.x1 - line.x2
const c = line.x2 * line.y1 - line.x1 * line.y2 const c = line.x2 * line.y1 - line.x1 * line.y2
return a * point.x + b * point.y + c === 0 const result = Math.abs(a * point.x + b * point.y + c) / 100
}
// 점이 선 위에 있는지 확인
return result <= 10
}
/** /**
* 점과 가까운 line 찾기 * 점과 가까운 line 찾기
* @param point * @param point
@ -766,9 +768,9 @@ export const triangleToPolygon = (triangle) => {
const halfWidth = triangle.width / 2 const halfWidth = triangle.width / 2
const height = triangle.height const height = triangle.height
points.push({ x: triangle.left + halfWidth, y: triangle.top }) points.push({ x: triangle.left, y: triangle.top })
points.push({ x: triangle.left, y: triangle.top + height }) points.push({ x: triangle.left - halfWidth, y: triangle.top + height })
points.push({ x: triangle.left + triangle.width, y: triangle.top + height }) points.push({ x: triangle.left + halfWidth, y: triangle.top + height })
return points return points
} }

File diff suppressed because it is too large Load Diff

595
yarn.lock

File diff suppressed because it is too large Load Diff