Merge branch 'dev'
This commit is contained in:
commit
be11343fb2
4
.gitignore
vendored
4
.gitignore
vendored
@ -37,3 +37,7 @@ next-env.d.ts
|
||||
|
||||
.idea
|
||||
.vscode
|
||||
|
||||
#lock files
|
||||
yarn.lock
|
||||
package-lock.json
|
||||
151
docs/Canvas Status.md
Normal file
151
docs/Canvas Status.md
Normal 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면 이상 선택 후 마우스 우클릭
|
||||
|
||||
* 모듈 세로 가운데 정렬
|
||||
* 모듈 가로 가운데 정렬
|
||||
* 모듈 왼쪽 정렬
|
||||
* 모듈 오른쪽 정렬
|
||||
* 모듈 위쪽 정렬
|
||||
* 모듈 아래쪽 정렬
|
||||
* 모듈 일괄 삭제
|
||||
* 모듈 일괄 회로 번호 변경
|
||||
|
||||
### 패널 선택 후 마우스 우클릭
|
||||
|
||||
* 삭제
|
||||
* 이동
|
||||
* 복사
|
||||
* 열 이동
|
||||
* 열 복사
|
||||
* 열 삭제
|
||||
* 열 삽입
|
||||
* 단 이동
|
||||
* 단 복사
|
||||
* 단 삭제
|
||||
* 단 삽입
|
||||
@ -19,9 +19,12 @@ Allpainted : allPainted
|
||||
출폭: offset
|
||||
폭: width
|
||||
경사(구배): pitch
|
||||
각도: degree
|
||||
이구배: doublePitch
|
||||
소매: sleeve
|
||||
개구: openSpace
|
||||
도머: dormer
|
||||
그림자: shadow
|
||||
치수선: dimensionLine
|
||||
치수선: dimensionLine
|
||||
복도치수: planeSize
|
||||
실제치수: actualSize
|
||||
|
||||
10656
package-lock.json
generated
10656
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
3
public/static/images/canvas/estiment_arr.svg
Normal file
3
public/static/images/canvas/estiment_arr.svg
Normal 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 |
3
public/static/images/canvas/estiment_arr_color.svg
Normal file
3
public/static/images/canvas/estiment_arr_color.svg
Normal 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 |
@ -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>
|
||||
)
|
||||
}
|
||||
@ -1,25 +1,10 @@
|
||||
import Roof2 from '@/components/Roof2'
|
||||
import { initCheck } from '@/util/session-util'
|
||||
import RoofSelect from '@/app/roof2/RoofSelect'
|
||||
|
||||
export default async function Roof2Page() {
|
||||
const session = await initCheck()
|
||||
const roof2Props = {
|
||||
name: session.name || '',
|
||||
userId: session.userId || '',
|
||||
email: session.email || '',
|
||||
isLoggedIn: session.isLoggedIn,
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<div className="m-2">
|
||||
<RoofSelect />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col justify-center my-8 pt-20">
|
||||
<Roof2 {...roof2Props} />
|
||||
<Roof2 />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
|
||||
@ -65,6 +65,7 @@ export const LINE_TYPE = {
|
||||
HIPANDGABLE: 'hipAndGable',
|
||||
JERKINHEAD: 'jerkinhead',
|
||||
SHED: 'shed',
|
||||
ETC: 'etc',
|
||||
},
|
||||
SUBLINE: {
|
||||
/**
|
||||
@ -116,6 +117,7 @@ export const INPUT_TYPE = {
|
||||
|
||||
export const POLYGON_TYPE = {
|
||||
ROOF: 'roof',
|
||||
WALL: 'wall',
|
||||
TRESTLE: 'trestle',
|
||||
}
|
||||
|
||||
@ -156,6 +158,7 @@ export const SAVE_KEY = [
|
||||
'groupYn',
|
||||
'groupName',
|
||||
'lineDirection',
|
||||
'groupId',
|
||||
]
|
||||
|
||||
export const OBJECT_PROTOTYPE = [fabric.Line.prototype, fabric.Polygon.prototype, fabric.Triangle.prototype]
|
||||
|
||||
@ -29,13 +29,6 @@ export default function InitSettingsModal(props) {
|
||||
//const { get, post } = useAxios()
|
||||
|
||||
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) => {
|
||||
if (res.length == 0) return
|
||||
|
||||
|
||||
@ -39,6 +39,7 @@ export default function Playground() {
|
||||
const [color, setColor] = useState('#ff0000')
|
||||
|
||||
const [textInput, setTextInput] = useState('')
|
||||
const [numberInput, setNumberInput] = useState('')
|
||||
const [radioInput, setRadioInput] = useState('')
|
||||
const [checkboxInput, setCheckboxInput] = useState([])
|
||||
const [selectedValue, setSelectedValue] = useState('')
|
||||
@ -48,6 +49,9 @@ export default function Playground() {
|
||||
useEffect(() => {
|
||||
console.log('textInput:', textInput)
|
||||
}, [textInput])
|
||||
useEffect(() => {
|
||||
console.log('numberInput:', numberInput)
|
||||
}, [numberInput])
|
||||
useEffect(() => {
|
||||
console.log('radioInput:', radioInput)
|
||||
}, [radioInput])
|
||||
@ -161,8 +165,19 @@ export default function Playground() {
|
||||
>
|
||||
QInput TextInput DATA RESET
|
||||
</button>
|
||||
<QInput type="text" value={textInput} onChange={setTextInput} />
|
||||
<QInput type="text" value={textInput} onChange={setTextInput} readOnly="true" />
|
||||
<QInput type="text" placeholder="placeholder" value={textInput} onChange={setTextInput} />
|
||||
<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 />
|
||||
<button
|
||||
className="btn-frame deepgray"
|
||||
@ -185,7 +200,7 @@ export default function Playground() {
|
||||
<button
|
||||
className="btn-frame deepgray"
|
||||
onClick={() => {
|
||||
setCheckboxInput('')
|
||||
setCheckboxInput([])
|
||||
}}
|
||||
>
|
||||
QInput Checkbox DATA RESET
|
||||
|
||||
@ -4,7 +4,7 @@ import { useCanvas } from '@/hooks/useCanvas'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
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 RangeSlider from './ui/RangeSlider'
|
||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||
@ -39,7 +39,7 @@ import QEmptyContextMenu from '@/components/common/context-menu/QEmptyContextMen
|
||||
import InitSettingsModal from './InitSettingsModal'
|
||||
import GridSettingsModal from './GridSettingsModal'
|
||||
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 ObjectPlacement from '@/components/ui/ObjectPlacement'
|
||||
import { globalLocaleStore } from '@/store/localeAtom'
|
||||
@ -409,6 +409,20 @@ export default function Roof2(props) {
|
||||
{ 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 newP = [
|
||||
{ x: 450, y: 450 },
|
||||
@ -417,7 +431,7 @@ export default function Roof2(props) {
|
||||
{ x: 450, y: 850 },
|
||||
]
|
||||
|
||||
const polygon = new QPolygon(twelvePoint, {
|
||||
const polygon = new QPolygon(rectangleType2, {
|
||||
fill: 'transparent',
|
||||
stroke: 'green',
|
||||
strokeWidth: 1,
|
||||
@ -658,17 +672,52 @@ export default function Roof2(props) {
|
||||
canvas?.renderAll()
|
||||
}
|
||||
|
||||
const setAllGableRoof = () => {
|
||||
let offset = Number(prompt('gable roof offset', '50'))
|
||||
const setHipRoof = () => {
|
||||
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) {
|
||||
const polygon = canvas?.getObjects()
|
||||
console.log('gable roof offset : ', offset)
|
||||
console.log('polygon : ', polygon)
|
||||
changeAllGableRoof(polygon, offset, canvas)
|
||||
const polygon = canvas?.getObjects().find((obj) => obj.name === 'roof')
|
||||
const currentRoof = polygon.lines[2]
|
||||
currentRoof.attributes.type = LINE_TYPE.WALLLINE.HIPANDGABLE
|
||||
currentRoof.attributes.width = offset
|
||||
changeCurrentRoof(currentRoof, canvas)
|
||||
} 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 (
|
||||
<>
|
||||
{canvas && (
|
||||
@ -788,16 +837,28 @@ export default function Roof2(props) {
|
||||
<Button className="m-1 p-2" onClick={makePolygon}>
|
||||
다각형 추가
|
||||
</Button>
|
||||
{templateType === 0 && (
|
||||
<>
|
||||
<Button className="m-1 p-2" onClick={makeQPolygon}>
|
||||
QPolygon
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
<Button className={'m-1 p-2'} onClick={setAllGableRoof}>
|
||||
{/*{templateType === 0 && (*/}
|
||||
{/* <>*/}
|
||||
<Button className="m-1 p-2" onClick={makeQPolygon}>
|
||||
QPolygon
|
||||
</Button>
|
||||
{/* </>*/}
|
||||
{/*)}*/}
|
||||
<Button className={'m-1 p-2'} onClick={setHipRoof}>
|
||||
용마루지붕
|
||||
</Button>
|
||||
<Button className={'m-1 p-2'} onClick={setHipAndGableRoof}>
|
||||
팔작지붕
|
||||
</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>
|
||||
|
||||
@ -8,7 +8,7 @@ import { contextPopupPositionState } from '@/store/popupAtom'
|
||||
|
||||
export default function ColorPickerModal(props) {
|
||||
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 [originColor, setOriginColor] = useState(color)
|
||||
const { closePopup } = usePopup()
|
||||
@ -29,7 +29,7 @@ export default function ColorPickerModal(props) {
|
||||
setIsShow(false)
|
||||
}
|
||||
console.log(id)
|
||||
closePopup(id)
|
||||
closePopup(id, isConfig)
|
||||
}}
|
||||
>
|
||||
닫기
|
||||
@ -49,10 +49,10 @@ export default function ColorPickerModal(props) {
|
||||
if (setColor) setColor(originColor)
|
||||
if (setIsShow) setIsShow(false)
|
||||
|
||||
closePopup(id)
|
||||
closePopup(id, isConfig)
|
||||
}}
|
||||
>
|
||||
{getMessage('common.message.save')}
|
||||
{getMessage('modal.common.save')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,14 +1,20 @@
|
||||
'use client'
|
||||
import { useEffect } from 'react'
|
||||
import '@/styles/contents.scss'
|
||||
import { useRecoilState } from 'recoil'
|
||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||
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) {
|
||||
const { contextRef, canvasProps, handleKeyup } = props
|
||||
const { contextRef, canvasProps } = props
|
||||
const [contextMenu, setContextMenu] = useRecoilState(contextMenuState)
|
||||
const [contextMenuList, setContextMenuList] = useRecoilState(contextMenuListState)
|
||||
const contextMenuList = useRecoilValue(contextMenuListState)
|
||||
const activeObject = canvasProps?.getActiveObject() //액티브된 객체를 가져옴
|
||||
const { tempGridMode, setTempGridMode } = useTempGrid()
|
||||
const { handleKeyup } = useContextMenu()
|
||||
const { addDocumentEventListener, removeDocumentEvent } = useEvent()
|
||||
|
||||
let contextType = ''
|
||||
|
||||
@ -20,24 +26,25 @@ export default function QContextMenu(props) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const getYPosition = (e) => {
|
||||
const contextLength = contextMenuList.reduce((acc, cur, index) => {
|
||||
return acc + cur.length
|
||||
}, 0)
|
||||
return e?.clientY - (contextLength * 25 + contextMenuList.length * 2 * 17)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!contextRef.current) return
|
||||
|
||||
const handleContextMenu = (e) => {
|
||||
// e.preventDefault() //기존 contextmenu 막고
|
||||
if (tempGridMode) return
|
||||
const position = {
|
||||
x: window.innerWidth / 2 < e.pageX ? e.pageX - 240 : e.pageX,
|
||||
y: window.innerHeight / 2 < e.pageY ? getYPosition(e) : e.pageY,
|
||||
}
|
||||
setContextMenu({ visible: true, ...position })
|
||||
document.addEventListener('keyup', (e) => handleKeyup(e))
|
||||
addDocumentEventListener('keyup', document, handleKeyup)
|
||||
canvasProps?.upperCanvasEl.removeEventListener('contextmenu', handleContextMenu) //한번 노출 후 이벤트 삭제
|
||||
}
|
||||
|
||||
@ -48,8 +55,9 @@ export default function QContextMenu(props) {
|
||||
|
||||
const handleOutsideClick = (e) => {
|
||||
// e.preventDefault()
|
||||
if (contextMenu.visible && !ref.current.contains(e.target)) {
|
||||
if (contextMenu.visible) {
|
||||
setContextMenu({ ...contextMenu, visible: false })
|
||||
removeDocumentEvent('keyup')
|
||||
}
|
||||
}
|
||||
|
||||
@ -58,10 +66,11 @@ export default function QContextMenu(props) {
|
||||
document.addEventListener('click', handleOutsideClick)
|
||||
|
||||
return () => {
|
||||
removeDocumentEvent('keyup')
|
||||
document.removeEventListener('click', handleClick)
|
||||
document.removeEventListener('click', handleOutsideClick)
|
||||
}
|
||||
}, [contextRef, contextMenu])
|
||||
}, [contextRef, contextMenuList])
|
||||
|
||||
const handleObjectMove = () => {
|
||||
activeObject.set({
|
||||
|
||||
@ -1,18 +1,18 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useState } from 'react'
|
||||
import Draggable from 'react-draggable'
|
||||
|
||||
export default function WithDraggable({ isShow, children, pos, handle = '' }) {
|
||||
const [position, setPosition] = useState({ x: 0, y: 0 })
|
||||
export default function WithDraggable({ isShow, children, pos = { x: 0, y: 0 }, handle = '' }) {
|
||||
const [position, setPosition] = useState(pos)
|
||||
|
||||
const handleOnDrag = (e, data) => {
|
||||
e.stopPropagation()
|
||||
setPosition({ x: data.x, y: data.y })
|
||||
}
|
||||
useEffect(() => {
|
||||
setPosition({ ...pos })
|
||||
}, [])
|
||||
// useEffect(() => {
|
||||
// setPosition({ ...pos })
|
||||
// }, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@ -14,15 +14,7 @@ const fonts = [
|
||||
{ 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 = [
|
||||
...Array.from({ length: 4 }).map((_, index) => {
|
||||
return { name: index + 8, value: index + 8 }
|
||||
@ -34,20 +26,10 @@ const fontSizes = [
|
||||
{ name: 48, value: 48 },
|
||||
{ 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) {
|
||||
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
||||
const { id, setIsShow, pos = contextPopupPosition, type } = props
|
||||
const { id, setIsShow, pos = contextPopupPosition, type, isConfig = false } = props
|
||||
const { getMessage } = useMessage()
|
||||
const { closePopup } = usePopup()
|
||||
const [globalFont, setGlobalFont] = useRecoilState(globalFontAtom)
|
||||
@ -57,7 +39,26 @@ export default function FontSetting(props) {
|
||||
const [selectedFontWeight, setSelectedFontWeight] = useState(currentFont.fontWeight)
|
||||
const [selectedFontSize, setSelectedFontSize] = useState(currentFont.fontSize)
|
||||
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 = () => {
|
||||
setGlobalFont((prev) => {
|
||||
return {
|
||||
@ -83,7 +84,7 @@ export default function FontSetting(props) {
|
||||
className="modal-close"
|
||||
onClick={() => {
|
||||
if (setIsShow) setIsShow(false)
|
||||
closePopup(id)
|
||||
closePopup(id, isConfig)
|
||||
}}
|
||||
>
|
||||
닫기
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
'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 || [
|
||||
// {
|
||||
// id: 'one',
|
||||
@ -21,11 +22,11 @@ export default function QInput({ type, readOnly = false, options = [], value, on
|
||||
// },
|
||||
// ]
|
||||
|
||||
const handleChange = useCallback(
|
||||
const handleRadioCheckboxChange = useCallback(
|
||||
(e, optionValue) => {
|
||||
if (type === 'radio') {
|
||||
onChange(e.target.value)
|
||||
} else {
|
||||
} else if (type === 'checkbox') {
|
||||
const newValue = value.includes(optionValue) ? value.filter((v) => v !== optionValue) : [...value, optionValue]
|
||||
onChange(newValue)
|
||||
}
|
||||
@ -33,36 +34,85 @@ export default function QInput({ type, readOnly = false, options = [], value, on
|
||||
[type, value, onChange],
|
||||
)
|
||||
|
||||
const handleTextChange = useCallback(
|
||||
const handleTextNumberChange = useCallback(
|
||||
(e) => {
|
||||
onChange(e.target.value)
|
||||
},
|
||||
[onChange],
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
{type === 'text' ? (
|
||||
<div className="mb5">
|
||||
<input type={type} className="input-light" readOnly={readOnly ? true : false} value={value} onChange={handleTextChange} />
|
||||
</div>
|
||||
) : type === 'radio' || type === 'checkbox' ? (
|
||||
<div className="flx mb5">
|
||||
{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) => handleChange(e, option.value)}
|
||||
/>
|
||||
<label htmlFor={option.id}>{option.name}</label>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : null}
|
||||
</>
|
||||
)
|
||||
// type=number 정수 부호, 소수점 검사, 일부 키 허용
|
||||
// const checkInputNumber = (e) => {
|
||||
// const value = e.target.value
|
||||
// const key = e.key
|
||||
// const allowKeys = ['Backspace', 'Delete', 'ArrowLeft', 'ArrowRight', 'Home', 'End', 'Tab', 'Enter', 'Control'] // 'ArrowUp', 'ArrowDown',
|
||||
// if (key >= '0' && key <= '9') {
|
||||
// return
|
||||
// }
|
||||
// if (key === '.' && !value.includes('.') && value.length > 0 && !isNaN(Number(value))) {
|
||||
// return
|
||||
// }
|
||||
// if (key === '-' && !value.includes('-') && value.length > 0) {
|
||||
// return
|
||||
// }
|
||||
// if (allowKeys.includes(key)) {
|
||||
// return
|
||||
// }
|
||||
// if (key === 'a' || key === 'c' || key === 'v' || key === 'x' || key === 'z') {
|
||||
// return
|
||||
// }
|
||||
// e.preventDefault()
|
||||
// }
|
||||
// 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}</>
|
||||
}
|
||||
|
||||
@ -1,9 +1,13 @@
|
||||
'use client'
|
||||
import { useRecoilState } from 'recoil'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { popupState } from '@/store/popupAtom'
|
||||
import { Fragment } from 'react'
|
||||
|
||||
export default function PopupManager() {
|
||||
const [popup, setPopup] = useRecoilState(popupState)
|
||||
return popup.children?.map((child) => <Fragment key={child.id}>{child.component}</Fragment>)
|
||||
const popup = useRecoilValue(popupState)
|
||||
|
||||
return [
|
||||
...popup?.config.map((child) => <Fragment key={child.id}>{child.component}</Fragment>),
|
||||
...popup?.other.map((child) => <Fragment key={child.id}>{child.component}</Fragment>),
|
||||
]
|
||||
}
|
||||
|
||||
@ -1,20 +1,55 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useState, useRef } from 'react'
|
||||
import { useEffect, useState, useContext } from 'react'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { floorPlanObjectState } from '@/store/floorPlanObjectAtom'
|
||||
import { sessionStore } from '@/store/commonAtom'
|
||||
import { SessionContext } from '@/app/SessionProvider'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { useCanvasMenu } from '@/hooks/common/useCanvasMenu'
|
||||
import SingleDatePicker from '../common/datepicker/SingleDatePicker'
|
||||
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 }) {
|
||||
const [objectNo, setObjectNo] = useState('')
|
||||
const [files, setFiles] = useState([]) //첨부파일
|
||||
const [objectNo, setObjectNo] = 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 { state, setState } = useEstimateController(params.pid)
|
||||
|
||||
//견적특이사항 상세 데이터 LIST
|
||||
|
||||
//견적특이사항 List
|
||||
const [specialNoteList, setSpecialNoteList] = useState([])
|
||||
|
||||
const globalLocaleState = useRecoilValue(globalLocaleStore)
|
||||
const { get, post } = useAxios(globalLocaleState)
|
||||
|
||||
const { getMessage } = useMessage()
|
||||
|
||||
const { setMenuNumber } = useCanvasMenu()
|
||||
@ -27,46 +62,107 @@ export default function Estimate({ params }) {
|
||||
setUploadFiles: setFiles,
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setObjectNo(objectRecoil.floorPlanObjectNo)
|
||||
}, [objectRecoil])
|
||||
|
||||
useEffect(() => {
|
||||
if (objectNo) {
|
||||
//Q101X278191023001
|
||||
console.log('세션정보::::', sessionState)
|
||||
//상세API호출
|
||||
}
|
||||
}, [objectNo])
|
||||
|
||||
useEffect(() => {
|
||||
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 (
|
||||
<div className="sub-content estimate">
|
||||
<div className="sub-content-inner">
|
||||
{/* 물건번호, 견적서번호, 등록일, 변경일시 시작 */}
|
||||
{/* <form onSubmit={handleSubmit(onValid)}> */}
|
||||
<div className="sub-content-box">
|
||||
<div className="sub-table-box">
|
||||
<div className="estimate-list-wrap one">
|
||||
<div className="estimate-box">
|
||||
<div className="estimate-tit">{getMessage('estimate.detail.objectNo')}</div>
|
||||
<div className="estimate-name">
|
||||
{objectNo} (Plan No: {params.pid})
|
||||
{objectNo} (Plan No: {planNo})
|
||||
</div>
|
||||
</div>
|
||||
<div className="estimate-box">
|
||||
<div className="estimate-tit">{getMessage('estimate.detail.estimateNo')}</div>
|
||||
<div className="estimate-name">5242310200065242</div>
|
||||
<div className="estimate-tit">{getMessage('estimate.detail.docNo')}</div>
|
||||
<div className="estimate-name">{state.docNo}</div>
|
||||
</div>
|
||||
<div className="estimate-box">
|
||||
<div className="estimate-tit">{getMessage('estimate.detail.createDatetime')}</div>
|
||||
<div className="estimate-name">9999.09.28</div>
|
||||
<div className="estimate-tit">{getMessage('estimate.detail.drawingEstimateCreateDate')}</div>
|
||||
<div className="estimate-name">
|
||||
{state?.drawingEstimateCreateDate ? `${dayjs(state.drawingEstimateCreateDate).format('YYYY.MM.DD')}` : ''}
|
||||
</div>
|
||||
</div>
|
||||
<div className="estimate-box">
|
||||
<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>
|
||||
@ -99,7 +195,7 @@ export default function Estimate({ params }) {
|
||||
</th>
|
||||
<td>
|
||||
<div className="date-picker" style={{ width: '350px' }}>
|
||||
<SingleDatePicker />
|
||||
<SingleDatePicker {...singleDatePickerProps} />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@ -113,64 +209,148 @@ export default function Estimate({ params }) {
|
||||
</th>
|
||||
<td>
|
||||
<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>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
{/* 안건명 */}
|
||||
<th>
|
||||
{getMessage('estimate.detail.title')} <span className="important">*</span>
|
||||
{getMessage('estimate.detail.objectName')} <span className="important">*</span>
|
||||
</th>
|
||||
<td colSpan={3}>
|
||||
<div className="form-flex-wrap">
|
||||
<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 className="input-wrap" style={{ width: '200px' }}>
|
||||
<input type="text" className="input-light" defaultValue={'경칭?'} />
|
||||
<div className="select-wrap" style={{ width: '200px' }}>
|
||||
<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>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
{/* 메모 */}
|
||||
<th>{getMessage('estimate.detail.remarks')}</th>
|
||||
<td colSpan={3}>물건정보에서 입력한 메모 표시</td>
|
||||
{/* 물건정보에서 입력한 메모 */}
|
||||
<th>{getMessage('estimate.detail.objectRemarks')}</th>
|
||||
<td colSpan={3}>{state?.objectRemarks}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
{/* 주문분류 */}
|
||||
<th>
|
||||
{getMessage('estimate.detail.orderType')} <span className="important">*</span>
|
||||
{getMessage('estimate.detail.estimateType')} <span className="important">*</span>
|
||||
</th>
|
||||
<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>
|
||||
</tr>
|
||||
<tr>
|
||||
{/* 지붕재・사양시공 최대4개*/}
|
||||
<th>{getMessage('estimate.detail.roofCns')}</th>
|
||||
<td colSpan={3}>
|
||||
<div className="form-flex-wrap mb5">
|
||||
<div className="input-wrap mr5" style={{ width: '610px' }}>
|
||||
<input type="text" className="input-light" defaultValue={'ハゼ式折板(角ハゼ)'} readOnly />
|
||||
</div>
|
||||
<div className="input-wrap" style={{ width: '200px' }}>
|
||||
<input type="text" className="input-light" defaultValue={'標準施工'} readOnly />
|
||||
</div>
|
||||
</div>
|
||||
<div className="form-flex-wrap mb5"></div>
|
||||
<div className="form-flex-wrap mb5"></div>
|
||||
{/* 마지막div엔 mb5 제외 */}
|
||||
<div className="form-flex-wrap"></div>
|
||||
{state?.roofMaterialIdMulti?.split('、').map((row, index) => {
|
||||
//지붕재
|
||||
let roofList = row
|
||||
let roofListLength = state?.roofMaterialIdMulti?.split('、').length
|
||||
let style = 'mb5'
|
||||
if (roofListLength == index + 1) {
|
||||
style = ''
|
||||
}
|
||||
//사양시공
|
||||
let constructSpecificationMulti = state?.constructSpecificationMulti?.split('、')
|
||||
|
||||
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>
|
||||
</tr>
|
||||
<tr>
|
||||
{/* 비고 */}
|
||||
<th>{getMessage('estimate.detail.note')}</th>
|
||||
<th>{getMessage('estimate.detail.remarks')}</th>
|
||||
<td colSpan={3}>
|
||||
<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>
|
||||
</td>
|
||||
</tr>
|
||||
@ -200,25 +380,6 @@ export default function Estimate({ params }) {
|
||||
<th>{getMessage('estimate.detail.header.fileList1')}</th>
|
||||
<td>
|
||||
<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>
|
||||
</tr>
|
||||
</tbody>
|
||||
@ -246,21 +407,70 @@ export default function Estimate({ params }) {
|
||||
<h3 className="product">{getMessage('estimate.detail.header.specialEstimate')}</h3>
|
||||
<div className="product_tit">{getMessage('estimate.detail.header.specialEstimateProductInfo')}</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 className="special-note-check-wrap"></div>
|
||||
{/* 공통코드영역끝 */}
|
||||
{/* 견적특이사항 내용영역시작 */}
|
||||
<div className="calculation-estimate"></div>
|
||||
{/* 견적특이사항 내용영역끝 */}
|
||||
{/* 견적특이사항 끝 */}
|
||||
{/* 견적 특이사항 코드영역시작 */}
|
||||
<div className={`estimate-check-wrap ${hidden ? 'hide' : ''}`}>
|
||||
<div className="estimate-check-inner">
|
||||
<div className="special-note-check-wrap">
|
||||
{/* 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="title-wrap">
|
||||
<h3>{getMessage('estimate.detail.header.specialEstimateProductInfo')}</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div className="estimate-wrap">
|
||||
<div className="esimate-wrap">
|
||||
<div className="estimate-list-wrap one">
|
||||
<div className="estimate-box">
|
||||
<div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.totPcs')}</div>
|
||||
@ -285,7 +495,7 @@ export default function Estimate({ params }) {
|
||||
</div>
|
||||
</div>
|
||||
{/* YJOD면 아래영역 숨김 */}
|
||||
<div className="common-table bt-able">
|
||||
<div className="common-table bt-able" style={{ display: state?.estimateType === 'YJSS' ? '' : 'none' }}>
|
||||
<table>
|
||||
<colgroup>
|
||||
<col style={{ width: '160px' }} />
|
||||
@ -319,18 +529,35 @@ export default function Estimate({ params }) {
|
||||
<div className="estimate-product-option">
|
||||
<div className="product-price-wrap">
|
||||
<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>
|
||||
</div>
|
||||
<div className="product-edit-wrap">
|
||||
<div className="product-edit-explane">
|
||||
<div className="click-check">
|
||||
<ul className="product-edit-explane">
|
||||
<li className="explane-item item01">
|
||||
<span className="ico"></span>
|
||||
{getMessage('estimate.detail.showPrice.description')}
|
||||
</div>
|
||||
</div>
|
||||
{getMessage('estimate.detail.showPrice.description1')}
|
||||
</li>
|
||||
<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">
|
||||
<button className="btn-origin navy mr5">
|
||||
<button className="btn-origin navy mr5" type="submit">
|
||||
<span className="plus"></span>
|
||||
{getMessage('estimate.detail.showPrice.btn2')}
|
||||
</button>
|
||||
@ -348,6 +575,7 @@ export default function Estimate({ params }) {
|
||||
</div>
|
||||
</div>
|
||||
{/* 기본정보끝 */}
|
||||
{/* </form> */}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -13,6 +13,9 @@ export const QLine = fabric.util.createClass(fabric.Line, {
|
||||
area: 0,
|
||||
children: [],
|
||||
initialize: function (points, options, length = 0) {
|
||||
// 소수점 전부 제거
|
||||
points = points.map((point) => Number(point.toFixed(1)))
|
||||
|
||||
this.callSuper('initialize', points, { ...options, selectable: options.selectable ?? true })
|
||||
if (options.id) {
|
||||
this.id = options.id
|
||||
@ -20,8 +23,6 @@ export const QLine = fabric.util.createClass(fabric.Line, {
|
||||
this.id = uuidv4()
|
||||
}
|
||||
this.line = this
|
||||
// 소수점 전부 제거
|
||||
points = points.map((point) => Math.round(point))
|
||||
|
||||
this.idx = options.idx ?? 0
|
||||
this.direction = options.direction ?? getDirectionByPoint({ x: this.x1, y: this.y1 }, { x: this.x2, y: this.y2 })
|
||||
|
||||
@ -2,26 +2,36 @@ import { fabric } from 'fabric'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { QLine } from '@/components/fabric/QLine'
|
||||
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 { LINE_TYPE } from '@/common/common'
|
||||
|
||||
export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
||||
type: 'QPolygon',
|
||||
lines: [],
|
||||
texts: [],
|
||||
// lines: [],
|
||||
// texts: [],
|
||||
id: null,
|
||||
length: 0,
|
||||
hips: [],
|
||||
ridges: [],
|
||||
connectRidges: [],
|
||||
cells: [],
|
||||
// hips: [],
|
||||
// ridges: [],
|
||||
// connectRidges: [],
|
||||
// cells: [],
|
||||
parentId: null,
|
||||
innerLines: [],
|
||||
children: [],
|
||||
// innerLines: [],
|
||||
// children: [],
|
||||
initOptions: null,
|
||||
direction: null,
|
||||
arrow: null,
|
||||
initialize: function (points, options, canvas) {
|
||||
this.lines = []
|
||||
this.texts = []
|
||||
this.hips = []
|
||||
this.ridges = []
|
||||
this.connectRidges = []
|
||||
this.cells = []
|
||||
this.innerLines = []
|
||||
this.children = []
|
||||
|
||||
// 소수점 전부 제거
|
||||
points.forEach((point) => {
|
||||
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 })
|
||||
},
|
||||
|
||||
@ -153,9 +179,42 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
||||
},
|
||||
|
||||
// 보조선 그리기
|
||||
drawHelpLine(chon = 4) {
|
||||
// drawHelpLineInHexagon(this, chon)
|
||||
drawHippedRoof(this, chon)
|
||||
drawHelpLine() {
|
||||
// drawHelpLineInHexagon(this, pitch)
|
||||
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() {
|
||||
@ -168,6 +227,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
||||
|
||||
let points = this.getCurrentPoints()
|
||||
|
||||
this.texts = []
|
||||
points.forEach((start, i) => {
|
||||
const end = points[(i + 1) % points.length]
|
||||
const dx = end.x - start.x
|
||||
|
||||
@ -19,7 +19,7 @@ export default function CanvasFrame() {
|
||||
const { canvas } = useCanvas('canvas')
|
||||
const { canvasLoadInit, gridInit } = useCanvasConfigInitialize()
|
||||
const currentMenu = useRecoilValue(currentMenuState)
|
||||
const { contextMenu, handleClick, handleKeyup } = useContextMenu()
|
||||
const { contextMenu, handleClick } = useContextMenu()
|
||||
const { selectedPlan, modifiedPlanFlag, checkCanvasObjectEvent, resetModifiedPlans } = usePlan()
|
||||
useEvent()
|
||||
|
||||
@ -57,7 +57,7 @@ export default function CanvasFrame() {
|
||||
<div className="canvas-frame">
|
||||
<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) => (
|
||||
<ul key={index}>
|
||||
{menus.map((menu) => (
|
||||
|
||||
@ -32,6 +32,8 @@ import { menusState, menuTypeState } from '@/store/menuAtom'
|
||||
import useMenu from '@/hooks/common/useMenu'
|
||||
import { MENU } from '@/common/common'
|
||||
|
||||
import { useEstimateController } from '@/hooks/floorPlan/estimate/useEstimateController'
|
||||
|
||||
export default function CanvasMenu(props) {
|
||||
const { menuNumber, setMenuNumber } = props
|
||||
const pathname = usePathname()
|
||||
@ -49,8 +51,9 @@ export default function CanvasMenu(props) {
|
||||
const sessionState = useRecoilValue(sessionStore)
|
||||
const globalLocale = useRecoilValue(globalLocaleStore)
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
const { handleZoomClear } = useCanvasEvent()
|
||||
const { handleZoomClear, handleZoom } = useCanvasEvent()
|
||||
const { handleMenu } = useMenu()
|
||||
const { handleEstimateSubmit } = useEstimateController()
|
||||
|
||||
const { getMessage } = useMessage()
|
||||
const { currentCanvasPlan, saveCanvas } = usePlan()
|
||||
@ -129,7 +132,7 @@ export default function CanvasMenu(props) {
|
||||
|
||||
const handlePopup = () => {
|
||||
const id = uuidv4()
|
||||
addPopup(id, 0, <SettingModal01 id={id} />)
|
||||
addPopup(id, 1, <SettingModal01 id={id} />, true)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
@ -201,14 +204,18 @@ export default function CanvasMenu(props) {
|
||||
<button
|
||||
className="control-btn minus"
|
||||
onClick={() => {
|
||||
canvas.setZoom(canvas.getZoom() - 0.1)
|
||||
handleZoom(false)
|
||||
}}
|
||||
></button>
|
||||
<span>{canvasZoom}%</span>
|
||||
<button className="control-btn plus" onClick={handleZoomClear}></button>
|
||||
<button
|
||||
className="control-btn plus"
|
||||
onClick={() => {
|
||||
handleZoom(true)
|
||||
}}
|
||||
></button>
|
||||
</div>
|
||||
<div className="btn-from">
|
||||
<button className="btn07" onClick={handleClear}></button>
|
||||
<button className="btn08" onClick={handleSaveCanvas}></button>
|
||||
<button className="btn09"></button>
|
||||
</div>
|
||||
@ -220,9 +227,9 @@ export default function CanvasMenu(props) {
|
||||
<div className="ico-btn-from">
|
||||
<button className="btn-frame gray ico-flx act">
|
||||
<span className="ico ico01"></span>
|
||||
<span>{getMessage('plan.menu.estimate.roof.alloc')}</span>
|
||||
<span>{getMessage('plan.menu.estimate.docDown')}</span>
|
||||
</button>
|
||||
<button className="btn-frame gray ico-flx">
|
||||
<button className="btn-frame gray ico-flx" onClick={handleEstimateSubmit}>
|
||||
<span className="ico ico02"></span>
|
||||
<span>{getMessage('plan.menu.estimate.save')}</span>
|
||||
</button>
|
||||
|
||||
@ -50,7 +50,11 @@ export default function CircuitTrestleSetting({ id }) {
|
||||
Next
|
||||
</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>
|
||||
|
||||
@ -114,7 +114,7 @@ export default function PowerConditionalSelect({ setTabNum }) {
|
||||
</div>
|
||||
</div>
|
||||
<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 className="circuit-data-form">
|
||||
<span className="normal-font">
|
||||
@ -132,12 +132,12 @@ export default function PowerConditionalSelect({ setTabNum }) {
|
||||
<div className="slope-wrap">
|
||||
<div className="d-check-box pop mb15">
|
||||
<input type="checkbox" id="ch03" />
|
||||
<label htmlFor="ch03">同一傾斜同一方面の面積の場合、同じ面として回路を分ける。</label>
|
||||
<label htmlFor="ch03"> {getMessage('modal.circuit.trestle.setting.power.conditional.select.check1')}</label>
|
||||
</div>
|
||||
<div className="d-check-box pop">
|
||||
<input type="checkbox" id="ch04" />
|
||||
<label className="red" htmlFor="ch04">
|
||||
MAX接続(過積)で回路を分ける。
|
||||
{getMessage('modal.circuit.trestle.setting.power.conditional.select.check2')}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -18,8 +18,8 @@ export default function StepUp({}) {
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>シリアル枚数</th>
|
||||
<th>総回路数</th>
|
||||
<th>{getMessage('modal.circuit.trestle.setting.step.up.allocation.serial.amount')}</th>
|
||||
<th>{getMessage('modal.circuit.trestle.setting.step.up.allocation.total.amount')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@ -49,14 +49,14 @@ export default function StepUp({}) {
|
||||
</div>
|
||||
<div className="circuit-table-flx-wrap">
|
||||
<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">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th style={{ width: '140px' }}>名称</th>
|
||||
<th>回路数</th>
|
||||
<th>昇圧回路数</th>
|
||||
<th style={{ width: '140px' }}>{getMessage('modal.circuit.trestle.setting.power.conditional.select.name')}</th>
|
||||
<th>{getMessage('modal.circuit.trestle.setting.power.conditional.select.circuit.amount')}</th>
|
||||
<th>{getMessage('modal.circuit.trestle.setting.step.up.allocation.circuit.amount')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@ -80,7 +80,7 @@ export default function StepUp({}) {
|
||||
</div>
|
||||
<div className="bottom-wrap">
|
||||
<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 className="circuit-data-form">
|
||||
<span className="normal-font">
|
||||
@ -90,13 +90,13 @@ export default function StepUp({}) {
|
||||
</div>
|
||||
</div>
|
||||
<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">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>名称</th>
|
||||
<th>昇圧回路数</th>
|
||||
<th>{getMessage('modal.circuit.trestle.setting.power.conditional.select.name')}</th>
|
||||
<th>{getMessage('modal.circuit.trestle.setting.step.up.allocation.circuit.amount')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@ -113,7 +113,7 @@ export default function StepUp({}) {
|
||||
</div>
|
||||
<div className="bottom-wrap">
|
||||
<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 className="circuit-data-form">
|
||||
<span className="normal-font">
|
||||
@ -124,7 +124,7 @@ export default function StepUp({}) {
|
||||
</div>
|
||||
</div>
|
||||
<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' }}>
|
||||
<input type="text" className="input-origin block" />
|
||||
</div>
|
||||
@ -395,7 +395,7 @@ export default function StepUp({}) {
|
||||
<div className="slope-wrap">
|
||||
<div className="outline-form">
|
||||
<span className="mr10" style={{ width: 'auto' }}>
|
||||
モニターの選択
|
||||
{getMessage('modal.circuit.trestle.setting.step.up.allocation.select.monitor')}
|
||||
</span>
|
||||
<div className="grid-select mr10">
|
||||
<QSelectBox title={'電力検出ユニット (モニター付き)'} option={SelectOption01} />
|
||||
|
||||
@ -4,13 +4,13 @@ export default function PassivityCircuitAllocation() {
|
||||
const { getMessage } = useMessage()
|
||||
const moduleData = {
|
||||
header: [
|
||||
{ name: getMessage('屋根面'), prop: 'roofShape' },
|
||||
{ name: getMessage('modal.panel.batch.statistic.roof.shape'), prop: 'roofShape' },
|
||||
{
|
||||
name: getMessage('Q.TRON M-G2'),
|
||||
prop: 'moduleName',
|
||||
},
|
||||
{
|
||||
name: getMessage('発電量 (kW)'),
|
||||
name: `${getMessage('modal.panel.batch.statistic.power.generation.amount')}(kW)`,
|
||||
prop: 'powerGeneration',
|
||||
},
|
||||
],
|
||||
|
||||
@ -49,9 +49,8 @@ export default function DimensionLineSetting(props) {
|
||||
resultText = calculateLength(basicLength, slopeInput1.angleValue).toFixed(0)
|
||||
|
||||
if (slopeInput2) {
|
||||
const angle = slopeInput1 + slopeInput2
|
||||
const length = calculateLength(basicLength, angle)
|
||||
resultText = length.toFixed(2)
|
||||
const length = calculateLength(basicLength, slopeInput1.angleValue, slopeInput2.angleValue)
|
||||
resultText = length.toFixed(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -71,11 +70,18 @@ export default function DimensionLineSetting(props) {
|
||||
}
|
||||
|
||||
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))
|
||||
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 (
|
||||
<WithDraggable isShow={true} pos={pos}>
|
||||
<div className={`modal-pop-wrap xm mount`}>
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import WithDraggable from '@/components/common/draggable/withDraggable'
|
||||
import WithDraggable from '@/components/common/draggable/WithDraggable'
|
||||
import { useEffect, useState } from 'react'
|
||||
import QSelectBox from '@/components/common/select/QSelectBox'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
@ -60,7 +60,7 @@ export default function FlowDirectionSetting(props) {
|
||||
}, [compasDeg])
|
||||
return (
|
||||
<WithDraggable isShow={true} pos={pos}>
|
||||
<div className={`modal-pop-wrap lx mount`}>
|
||||
<div className={`modal-pop-wrap ml mount`}>
|
||||
<div className="modal-head">
|
||||
<h1 className="title">{getMessage('modal.shape.flow.direction.setting')} </h1>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
<div className="compas-box">
|
||||
<div className="compas-box-inner">
|
||||
{Array.from({ length: 180 / 15 + 1 }).map((dot, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={`circle ${compasDeg === 15 * (12 + index) ? 'act' : ''}`}
|
||||
onClick={() => setCompasDeg(15 * (12 + index))}
|
||||
>
|
||||
<i>{13 - index}</i>
|
||||
<div className="draw-flow-wrap">
|
||||
<div className="compas-box">
|
||||
<div className="compas-box-inner">
|
||||
{Array.from({ length: 180 / 15 + 1 }).map((dot, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={`circle ${compasDeg === 15 * (12 + index) ? 'act' : ''}`}
|
||||
onClick={() => setCompasDeg(15 * (12 + index))}
|
||||
>
|
||||
<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>
|
||||
))}
|
||||
{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>
|
||||
|
||||
@ -5,9 +5,7 @@ import { useMessage } from '@/hooks/useMessage'
|
||||
import { canvasState, dotLineGridSettingState, dotLineIntervalSelector } from '@/store/canvasAtom'
|
||||
import { useRecoilState, useRecoilValue, useResetRecoilState, useSetRecoilState } from 'recoil'
|
||||
import { onlyNumberInputChange } from '@/util/input-utils'
|
||||
import { fabric } from 'fabric'
|
||||
import { gridColorState } from '@/store/gridAtom'
|
||||
import { gridDisplaySelector, settingModalGridOptionsState } from '@/store/settingAtom'
|
||||
import { settingModalGridOptionsState } from '@/store/settingAtom'
|
||||
import { useAxios } from '@/hooks/useAxios'
|
||||
import { useSwal } from '@/hooks/useSwal'
|
||||
import { usePopup } from '@/hooks/usePopup'
|
||||
@ -33,7 +31,7 @@ export default function DotLineGrid(props) {
|
||||
// const [modalOption, setModalOption] = useRecoilState(modalState); //modal 열림닫힘 state
|
||||
const [objectNo, setObjectNo] = useState('test123240912001') // 이후 삭제 필요
|
||||
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 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) => {
|
||||
swalFire({ text: getMessage(res.returnMessage) })
|
||||
setDotLineGridSettingState({ ...currentSetting })
|
||||
closePopup(id)
|
||||
closePopup(id, isConfig)
|
||||
})
|
||||
} catch (error) {
|
||||
swalFire({ text: getMessage(res.returnMessage), icon: 'error' })
|
||||
@ -213,7 +211,7 @@ export default function DotLineGrid(props) {
|
||||
className="modal-close"
|
||||
onClick={() => {
|
||||
setIsShow(false)
|
||||
closePopup(id)
|
||||
closePopup(id, isConfig)
|
||||
}}
|
||||
>
|
||||
닫기
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import WithDraggable from '@/components/common/draggable/withDraggable'
|
||||
import WithDraggable from '@/components/common/draggable/WithDraggable'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { contextPopupPositionState } from '@/store/popupAtom'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import WithDraggable from '@/components/common/draggable/withDraggable'
|
||||
import WithDraggable from '@/components/common/draggable/WithDraggable'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { contextPopupPositionState } from '@/store/popupAtom'
|
||||
import { usePopup } from '@/hooks/usePopup'
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import WithDraggable from '@/components/common/draggable/withDraggable'
|
||||
import WithDraggable from '@/components/common/draggable/WithDraggable'
|
||||
import Image from 'next/image'
|
||||
import { useState } from 'react'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import WithDraggable from '@/components/common/draggable/withDraggable'
|
||||
import WithDraggable from '@/components/common/draggable/WithDraggable'
|
||||
import Image from 'next/image'
|
||||
import { useState } from 'react'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
|
||||
@ -1,22 +1,49 @@
|
||||
import { useState, useEffect, useRef } from 'react'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import WithDraggable from '@/components/common/draggable/WithDraggable'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { contextPopupPositionState } from '@/store/popupAtom'
|
||||
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) {
|
||||
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
||||
const { id, pos = contextPopupPosition } = props
|
||||
const { id, pos = contextPopupPosition, title } = props
|
||||
const { getMessage } = useMessage()
|
||||
const { closePopup } = usePopup()
|
||||
const [arrow1, setArrow1] = 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 (
|
||||
<WithDraggable isShow={true} pos={pos}>
|
||||
<div className={`modal-pop-wrap xm mount`}>
|
||||
<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>
|
||||
@ -29,44 +56,40 @@ export default function DormerOffset(props) {
|
||||
<p className="mb5">{getMessage('length')}</p>
|
||||
<div className="input-move-wrap mb5">
|
||||
<div className="input-move">
|
||||
<input type="text" className="input-origin" defaultValue={0} />
|
||||
<input type="text" className="input-origin" ref={arrow1LengthRef} placeholder="0" />
|
||||
</div>
|
||||
<span>mm</span>
|
||||
<div className="direction-move-wrap">
|
||||
<button
|
||||
className={`direction up ${arrow1 === '↑' ? 'act' : ''}`}
|
||||
className={`direction up ${arrow1 === 'up' ? 'act' : ''}`}
|
||||
onClick={() => {
|
||||
setArrow1('↑')
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' }))
|
||||
setArrow1('up')
|
||||
}}
|
||||
></button>
|
||||
<button
|
||||
className={`direction down ${arrow1 === '↓' ? 'act' : ''}`}
|
||||
className={`direction down ${arrow1 === 'down' ? 'act' : ''}`}
|
||||
onClick={() => {
|
||||
setArrow1('↓')
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' }))
|
||||
setArrow1('down')
|
||||
}}
|
||||
></button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="input-move-wrap">
|
||||
<div className="input-move">
|
||||
<input type="text" className="input-origin" defaultValue={0} />
|
||||
<input type="text" className="input-origin" ref={arrow2LengthRef} placeholder="0" />
|
||||
</div>
|
||||
<span>mm</span>
|
||||
<div className="direction-move-wrap">
|
||||
<button
|
||||
className={`direction left ${arrow2 === '←' ? 'act' : ''}`}
|
||||
className={`direction left ${arrow2 === 'left' ? 'act' : ''}`}
|
||||
onClick={() => {
|
||||
setArrow2('←')
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowLeft' }))
|
||||
setArrow2('left')
|
||||
}}
|
||||
></button>
|
||||
<button
|
||||
className={`direction right ${arrow2 === '→' ? 'act' : ''}`}
|
||||
className={`direction right ${arrow2 === 'right' ? 'act' : ''}`}
|
||||
onClick={() => {
|
||||
setArrow2('→')
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowRight' }))
|
||||
setArrow2('right')
|
||||
}}
|
||||
></button>
|
||||
</div>
|
||||
@ -75,7 +98,9 @@ export default function DormerOffset(props) {
|
||||
</div>
|
||||
</div>
|
||||
<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>
|
||||
|
||||
@ -5,7 +5,11 @@ import { useMessage } from '@/hooks/useMessage'
|
||||
import WithDraggable from '@/components/common/draggable/WithDraggable'
|
||||
import { usePopup } from '@/hooks/usePopup'
|
||||
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) {
|
||||
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
||||
@ -13,6 +17,31 @@ export default function SizeSetting(props) {
|
||||
const { id, pos = contextPopupPosition, target } = props
|
||||
const { getMessage } = useMessage()
|
||||
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 (
|
||||
<WithDraggable isShow={true} pos={pos}>
|
||||
@ -28,11 +57,11 @@ export default function SizeSetting(props) {
|
||||
<div className="size-option-top">
|
||||
<div className="size-option-wrap">
|
||||
<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>
|
||||
</div>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
@ -41,11 +70,11 @@ export default function SizeSetting(props) {
|
||||
<div className="size-option-side">
|
||||
<div className="size-option-wrap">
|
||||
<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>
|
||||
</div>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
@ -60,7 +89,9 @@ export default function SizeSetting(props) {
|
||||
</div>
|
||||
</div>
|
||||
<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>
|
||||
|
||||
@ -38,7 +38,7 @@ const PentagonDormer = forwardRef((props, refs) => {
|
||||
<div className="eaves-keraba-td">
|
||||
<div className="outline-form">
|
||||
<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>
|
||||
<span className="thin">mm</span>
|
||||
</div>
|
||||
@ -60,7 +60,7 @@ const PentagonDormer = forwardRef((props, refs) => {
|
||||
<div className="eaves-keraba-td">
|
||||
<div className="outline-form">
|
||||
<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>
|
||||
<span className="thin">mm</span>
|
||||
</div>
|
||||
|
||||
@ -38,7 +38,7 @@ const TriangleDormer = forwardRef((props, refs) => {
|
||||
<div className="eaves-keraba-td">
|
||||
<div className="outline-form">
|
||||
<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>
|
||||
<span className="thin">mm</span>
|
||||
</div>
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
import { useState } from 'react'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import WithDraggable from '@/components/common/draggable/withDraggable'
|
||||
import WithDraggable from '@/components/common/draggable/WithDraggable'
|
||||
|
||||
export default function PanelBatchStatistics() {
|
||||
const { getMessage } = useMessage()
|
||||
|
||||
@ -180,8 +180,10 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, set
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
{getMessage('modal.placement.initial.setting.size')}
|
||||
<button className="tooltip" onClick={() => setShowSizeGuidModal(true)}></button>
|
||||
<div className="tip-wrap">
|
||||
{getMessage('modal.placement.initial.setting.size')}
|
||||
<button className="tooltip" onClick={() => setShowSizeGuidModal(true)}></button>
|
||||
</div>
|
||||
</th>
|
||||
<td>
|
||||
<div className="pop-form-radio">
|
||||
@ -252,8 +254,10 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, set
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
{getMessage('modal.placement.initial.setting.roof.material')}
|
||||
<button className="tooltip" onClick={() => setShowMaterialGuidModal(true)}></button>
|
||||
<div className="tip-wrap">
|
||||
{getMessage('modal.placement.initial.setting.roof.material')}
|
||||
<button className="tooltip" onClick={() => setShowMaterialGuidModal(true)}></button>
|
||||
</div>
|
||||
</th>
|
||||
<td>
|
||||
<div className="placement-option">
|
||||
|
||||
@ -240,7 +240,7 @@ export default function PlacementSurfaceSetting({ id, pos = { x: 50, y: 230 } })
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<WithDraggable isShow={true} pos={{ x: 50, y: 230 }}>
|
||||
<WithDraggable isShow={true} pos={pos}>
|
||||
<div className={`modal-pop-wrap lr-2`}>
|
||||
<div className="modal-head">
|
||||
<h1 className="title">{getMessage('plan.menu.placement.surface.arrangement')} </h1>
|
||||
|
||||
@ -78,14 +78,14 @@ export default function GridOption() {
|
||||
// 점 선 그리드 설정 모달
|
||||
setShowDotLineGridModal(selectedOption.selected)
|
||||
|
||||
addPopup(dotLineId, 2, <DotLineGrid {...dotLineGridProps} />)
|
||||
addPopup(dotLineId, 2, <DotLineGrid {...dotLineGridProps} />, true)
|
||||
} else if (selectedOption.id === 3) {
|
||||
// 흡착점 모드
|
||||
setAdsorptionPointAddMode(selectedOption.selected)
|
||||
} else if (selectedOption.id === 4) {
|
||||
// 그리드 색 설정 모달
|
||||
setShowColorPickerModal(selectedOption.selected)
|
||||
addPopup(colorId, 2, <ColorPickerModal {...colorPickerProps} />)
|
||||
addPopup(colorId, 2, <ColorPickerModal {...colorPickerProps} />, true)
|
||||
}
|
||||
|
||||
setGridOptions(newGridOptions)
|
||||
@ -94,6 +94,7 @@ export default function GridOption() {
|
||||
const dotLineGridProps = {
|
||||
id: dotLineId,
|
||||
setIsShow: setShowDotLineGridModal,
|
||||
isConfig: true,
|
||||
pos: {
|
||||
x: 845,
|
||||
y: 180,
|
||||
@ -106,6 +107,7 @@ export default function GridOption() {
|
||||
setColor: setGridColor,
|
||||
isShow: showColorPickerModal,
|
||||
setIsShow: setShowColorPickerModal,
|
||||
isConfig: true,
|
||||
pos: {
|
||||
x: 785,
|
||||
y: 180,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import DimensionLineSetting from '@/components/floor-plan/modal/setting01/dimensionLine/DimensionLineSetting'
|
||||
@ -67,6 +67,7 @@ export default function SecondOption() {
|
||||
id: fontId,
|
||||
pos: { x: 745, y: 180 },
|
||||
setIsShow: setShowFontSettingModal,
|
||||
isConfig: true,
|
||||
}
|
||||
const planSizeProps = {
|
||||
id: planSizeId,
|
||||
@ -86,36 +87,41 @@ export default function SecondOption() {
|
||||
case 'font1': {
|
||||
//문자 글꼴변경
|
||||
setShowFontSettingModal(true)
|
||||
setShowDimensionLineSettingModal(false)
|
||||
fontProps.type = 'commonText'
|
||||
fontProps.id = fontId + 1
|
||||
addPopup(fontId + 1, 2, <FontSetting {...fontProps} />)
|
||||
addPopup(fontId + 1, 2, <FontSetting {...fontProps} />, true)
|
||||
break
|
||||
}
|
||||
|
||||
case 'font2': {
|
||||
//흐름 방향 글꼴 변경
|
||||
setShowFontSettingModal(true)
|
||||
setShowDimensionLineSettingModal(false)
|
||||
fontProps.type = 'flowText'
|
||||
fontProps.id = fontId + 2
|
||||
addPopup(fontId + 2, 2, <FontSetting {...fontProps} />)
|
||||
addPopup(fontId + 2, 2, <FontSetting {...fontProps} />, true)
|
||||
break
|
||||
}
|
||||
|
||||
case 'font3': {
|
||||
//치수 글꼴변경
|
||||
setShowFontSettingModal(true)
|
||||
|
||||
setShowDimensionLineSettingModal(false)
|
||||
fontProps.type = 'lengthText'
|
||||
fontProps.id = fontId + 3
|
||||
addPopup(fontId + 3, 2, <FontSetting {...fontProps} />)
|
||||
addPopup(fontId + 3, 2, <FontSetting {...fontProps} />, true)
|
||||
break
|
||||
}
|
||||
|
||||
case 'font4': {
|
||||
//회로번호 글꼴변경
|
||||
setShowFontSettingModal(true)
|
||||
setShowDimensionLineSettingModal(false)
|
||||
fontProps.type = 'circuitNumberText'
|
||||
fontProps.id = fontId
|
||||
addPopup(fontId, 2, <FontSetting {...fontProps} />)
|
||||
addPopup(fontId, 2, <FontSetting {...fontProps} />, true)
|
||||
break
|
||||
}
|
||||
|
||||
@ -123,7 +129,7 @@ export default function SecondOption() {
|
||||
//치수선 설정
|
||||
if (!showDimensionLineSettingModal) {
|
||||
setShowDimensionLineSettingModal(true)
|
||||
addPopup(dimensionId, 2, <DimensionLineSetting {...dimensionProps} />)
|
||||
addPopup(dimensionId, 2, <DimensionLineSetting {...dimensionProps} />, true)
|
||||
} else {
|
||||
setShowDimensionLineSettingModal(false)
|
||||
closePopup(dimensionId)
|
||||
@ -134,7 +140,8 @@ export default function SecondOption() {
|
||||
case 'planSize': {
|
||||
//도면크기 설정
|
||||
setShowPlanSizeSettingModal(true)
|
||||
addPopup(planSizeId, 2, <PlanSizeSetting {...planSizeProps} />)
|
||||
setShowDimensionLineSettingModal(false)
|
||||
addPopup(planSizeId, 2, <PlanSizeSetting {...planSizeProps} />, true)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,12 +11,12 @@ import { useRecoilValue } from 'recoil'
|
||||
import { usePopup } from '@/hooks/usePopup'
|
||||
|
||||
export default function SettingModal01(props) {
|
||||
const { setShowDotLineGridModal, setShowFontSettingModal, id } = props
|
||||
console.log(props)
|
||||
const { id } = props
|
||||
const [buttonAct, setButtonAct] = useState(1)
|
||||
const { getMessage } = useMessage()
|
||||
const canGridOptionSeletorValue = useRecoilValue(canGridOptionSeletor)
|
||||
const { addPopup, closePopup } = usePopup()
|
||||
const { closePopup } = usePopup()
|
||||
|
||||
const handleBtnClick = (num) => {
|
||||
setButtonAct(num)
|
||||
}
|
||||
@ -27,7 +27,7 @@ export default function SettingModal01(props) {
|
||||
<div className={`modal-pop-wrap sm mount`}>
|
||||
<div className="modal-head">
|
||||
<h1 className="title">{getMessage('modal.canvas.setting')}</h1>
|
||||
<button className="modal-close" onClick={() => closePopup(id)}>
|
||||
<button className="modal-close" onClick={() => closePopup(id, true)}>
|
||||
닫기
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@ -87,6 +87,7 @@ export default function DimensionLineSetting(props) {
|
||||
setFontColor: setOriginFontColor,
|
||||
fontSize: originFontSize,
|
||||
setFontSize: setOriginFontSize,
|
||||
isConfig: true,
|
||||
id: fontModalId,
|
||||
pos: {
|
||||
x: 455,
|
||||
@ -97,17 +98,17 @@ export default function DimensionLineSetting(props) {
|
||||
const popupHandle = (type) => {
|
||||
switch (type) {
|
||||
case 'color':
|
||||
addPopup(colorModalId, 3, <ColorPickerModal {...colorPickerProps} />)
|
||||
addPopup(colorModalId, 3, <ColorPickerModal {...colorPickerProps} />, true)
|
||||
break
|
||||
case 'font':
|
||||
addPopup(fontModalId, 3, <FontSetting {...fontProps} />)
|
||||
addPopup(fontModalId, 3, <FontSetting {...fontProps} />, true)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<WithDraggable isShow={true} pos={pos}>
|
||||
<div className={`modal-pop-wrap xxxm`}>
|
||||
<div className={`modal-pop-wrap xxxm mount`}>
|
||||
<div className="modal-head">
|
||||
<h1 className="title">{getMessage('modal.canvas.setting.font.plan.absorption.dimension.line')} </h1>
|
||||
<button
|
||||
|
||||
@ -15,14 +15,14 @@ export default function PlanSizeSetting(props) {
|
||||
|
||||
return (
|
||||
<WithDraggable isShow={true} pos={pos}>
|
||||
<div className={`modal-pop-wrap xsm`}>
|
||||
<div className={`modal-pop-wrap xsm mount`}>
|
||||
<div className="modal-head">
|
||||
<h1 className="title">{getMessage('modal.canvas.setting.font.plan.absorption.plan.size.setting')}</h1>
|
||||
<button
|
||||
className="modal-close"
|
||||
onClick={() => {
|
||||
setIsShow(false)
|
||||
closePopup(id)
|
||||
closePopup(id, true)
|
||||
}}
|
||||
>
|
||||
닫기
|
||||
|
||||
@ -5,7 +5,7 @@ import { useRouter, usePathname } from 'next/navigation'
|
||||
import { useAxios } from '@/hooks/useAxios'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import StuffQGrid from './StuffQGrid'
|
||||
import { useRecoilValue, useRecoilState, useSetRecoilState } from 'recoil'
|
||||
import { useRecoilValue, useRecoilState, useSetRecoilState, useResetRecoilState } from 'recoil'
|
||||
import { stuffSearchState } from '@/store/stuffAtom'
|
||||
import { queryStringFormatter, isEmptyArray } from '@/util/common-utils'
|
||||
import dayjs from 'dayjs'
|
||||
@ -19,7 +19,7 @@ import { sessionStore } from '@/store/commonAtom'
|
||||
import { SessionContext } from '@/app/SessionProvider'
|
||||
|
||||
export default function Stuff() {
|
||||
const sessionState = useRecoilValue(sessionStore)
|
||||
const resetStuffRecoil = useResetRecoilState(stuffSearchState)
|
||||
const { session } = useContext(SessionContext)
|
||||
const setAppMessageState = useSetRecoilState(appMessageStore)
|
||||
const stuffSearchParams = useRecoilValue(stuffSearchState)
|
||||
@ -34,9 +34,6 @@ export default function Stuff() {
|
||||
const { get } = useAxios(globalLocaleState)
|
||||
const gridRef = useRef()
|
||||
|
||||
// const [selectedRowData, setSelectedRowData] = useState([])
|
||||
// const [selectedRowDataCount, setSelectedRowDataCount] = useState(0)
|
||||
|
||||
const router = useRouter()
|
||||
const pathname = usePathname()
|
||||
|
||||
@ -67,10 +64,6 @@ export default function Stuff() {
|
||||
field: 'lastEditDatetime',
|
||||
minWidth: 200,
|
||||
headerName: getMessage('stuff.gridHeader.lastEditDatetime'),
|
||||
// headerCheckboxSelection: true,
|
||||
// headerCheckboxSelectionCurrentPageOnly: true, //페이징시 현재 페이지만 체크되도록
|
||||
// checkboxSelection: true,
|
||||
// showDisabledCheckboxes: true,
|
||||
cellStyle: { justifyContent: 'center' },
|
||||
valueFormatter: function (params) {
|
||||
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(() => {
|
||||
// if (isObjectNotEmpty(sessionState)) {
|
||||
if (isObjectNotEmpty(session)) {
|
||||
//물건 메뉴 눌러서 최초 진입
|
||||
if (stuffSearchParams?.code === 'S') {
|
||||
// const params = {
|
||||
// saleStoreId: sessionState?.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,
|
||||
// }
|
||||
|
||||
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()
|
||||
if (stuffSearchParams?.code === 'S') {
|
||||
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,
|
||||
}
|
||||
}
|
||||
// }, [pageNo, sessionState, stuffSearchParams])
|
||||
}, [pageNo, stuffSearchParams])
|
||||
|
||||
useEffect(() => {
|
||||
if (stuffSearchParams?.code === 'E') {
|
||||
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: 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.endRow = 1 * pageSize
|
||||
stuffSearchParams.schSortType = defaultSortType
|
||||
|
||||
setPageNo(1)
|
||||
setStuffSearch({
|
||||
...stuffSearch,
|
||||
code: 'FINISH',
|
||||
})
|
||||
|
||||
//조회를 눌렀을때
|
||||
async function fetchData() {
|
||||
// const apiUrl = `/api/object/list?saleStoreId=${sessionState?.storeId}&${queryStringFormatter(stuffSearchParams)}`
|
||||
const apiUrl = `/api/object/list?saleStoreId=${session?.storeId}&${queryStringFormatter(stuffSearchParams)}`
|
||||
await get({ url: apiUrl }).then((res) => {
|
||||
if (!isEmptyArray(res)) {
|
||||
@ -360,7 +235,10 @@ export default function Stuff() {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fetchData()
|
||||
} else if (stuffSearchParams?.code === 'C') {
|
||||
resetStuffRecoil()
|
||||
}
|
||||
}, [stuffSearchParams])
|
||||
|
||||
@ -375,13 +253,11 @@ export default function Stuff() {
|
||||
setPageSize(e.target.value)
|
||||
setStuffSearch({
|
||||
...stuffSearchParams,
|
||||
// code: 'P',
|
||||
startRow: startRow,
|
||||
endRow: 1 * e.target.value,
|
||||
})
|
||||
|
||||
setPageNo(1)
|
||||
// const apiUrl = `/api/object/list?saleStoreId=${sessionState?.storeId}&${queryStringFormatter(stuffSearchParams)}`
|
||||
const apiUrl = `/api/object/list?saleStoreId=${session?.storeId}&${queryStringFormatter(stuffSearchParams)}`
|
||||
get({ url: apiUrl }).then((res) => {
|
||||
if (!isEmptyArray(res)) {
|
||||
@ -414,7 +290,6 @@ export default function Stuff() {
|
||||
})
|
||||
|
||||
setPageNo(1)
|
||||
// const apiUrl = `/api/object/list?saleStoreId=${sessionState?.storeId}&${queryStringFormatter(stuffSearchParams)}`
|
||||
const apiUrl = `/api/object/list?saleStoreId=${session?.storeId}&${queryStringFormatter(stuffSearchParams)}`
|
||||
get({ url: apiUrl }).then((res) => {
|
||||
if (!isEmptyArray(res)) {
|
||||
|
||||
@ -11,7 +11,6 @@ import { isEmptyArray, isNotEmptyArray, isObjectNotEmpty } from '@/util/common-u
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { useForm } from 'react-hook-form'
|
||||
import { useRecoilValue, useSetRecoilState } from 'recoil'
|
||||
import { sessionStore } from '@/store/commonAtom'
|
||||
import { SessionContext } from '@/app/SessionProvider'
|
||||
import FindAddressPop from './popup/FindAddressPop'
|
||||
import PlanRequestPop from './popup/PlanRequestPop'
|
||||
@ -28,7 +27,6 @@ export default function StuffDetail() {
|
||||
const [selOptions, setSelOptions] = useState('') //선택한 1차점
|
||||
const [otherSelOptions, setOtherSelOptions] = useState('') //선택한 1차점외
|
||||
|
||||
const sessionState = useRecoilValue(sessionStore)
|
||||
const { session } = useContext(SessionContext)
|
||||
|
||||
const router = useRouter()
|
||||
@ -320,12 +318,11 @@ export default function StuffDetail() {
|
||||
let firstList
|
||||
let otherList
|
||||
let favList
|
||||
// if (sessionState?.storeId === 'T01') {
|
||||
if (session?.storeId === 'T01') {
|
||||
url = `/api/object/saleStore/${session?.storeId}/firstList?userId=${session?.userId}`
|
||||
} else {
|
||||
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 {
|
||||
url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=1&userId=${session?.userId}`
|
||||
}
|
||||
@ -400,7 +397,6 @@ export default function StuffDetail() {
|
||||
}
|
||||
})
|
||||
}
|
||||
// }, [objectNo, sessionState])
|
||||
}, [objectNo, session])
|
||||
|
||||
useEffect(() => {
|
||||
@ -420,7 +416,6 @@ export default function StuffDetail() {
|
||||
|
||||
useEffect(() => {
|
||||
if (isObjectNotEmpty(detailData)) {
|
||||
// console.log('상세데이타세팅:::::', detailData)
|
||||
// 도도부현API
|
||||
get({ url: '/api/object/prefecture/list' }).then((res) => {
|
||||
if (!isEmptyArray(res)) {
|
||||
@ -434,23 +429,18 @@ export default function StuffDetail() {
|
||||
let firstList
|
||||
let otherList
|
||||
let favList
|
||||
// if (sessionState?.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}`
|
||||
} else {
|
||||
// if (sessionState.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}`
|
||||
} 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}`
|
||||
}
|
||||
}
|
||||
get({ url: url }).then((res) => {
|
||||
if (!isEmptyArray(res)) {
|
||||
// if (sessionState?.storeId === 'T01') {
|
||||
if (session?.storeId === 'T01') {
|
||||
firstList = res.filter((row) => row.saleStoreLevel === '1')
|
||||
firstList.sort((a, b) => (a.saleStoreId !== 'T01') - (b.saleStoreId !== 'T01') || a.saleStoreId - b.saleStoreId)
|
||||
@ -459,14 +449,19 @@ export default function StuffDetail() {
|
||||
setFavoriteStoreList(favList)
|
||||
setShowSaleStoreList(favList)
|
||||
|
||||
form.setValue('saleStoreId', firstList[0].saleStoreId)
|
||||
form.setValue('saleStoreName', firstList[0].saleStoreName)
|
||||
form.setValue('saleStoreLevel', firstList[0].saleStoreLevel)
|
||||
setSelOptions(firstList[0].saleStoreId)
|
||||
if (detailData.firstAgentId != null) {
|
||||
form.setValue('saleStoreId', detailData.firstAgentId)
|
||||
setSelOptions(detailData.firstAgentId)
|
||||
} else {
|
||||
form.setValue('saleStoreId', detailData.saleStoreId)
|
||||
setSelOptions(detailData.saleStoreId)
|
||||
}
|
||||
|
||||
//상세데이터의 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) => {
|
||||
if (!isEmptyArray(res)) {
|
||||
@ -482,7 +477,6 @@ export default function StuffDetail() {
|
||||
})
|
||||
} else {
|
||||
//1차점 셀렉트박스
|
||||
// if (sessionState?.storeLvl === '1') {
|
||||
if (session?.storeLvl === '1') {
|
||||
firstList = res
|
||||
favList = res.filter((row) => row.priority !== 'B')
|
||||
@ -572,7 +566,6 @@ export default function StuffDetail() {
|
||||
form.setValue('remarks', detailData.remarks)
|
||||
})
|
||||
}
|
||||
// }, [detailData, sessionState])
|
||||
}, [detailData, session])
|
||||
|
||||
//경칭선택 변경 이벤트
|
||||
@ -1282,9 +1275,7 @@ export default function StuffDetail() {
|
||||
|
||||
//1차점 or 2차점 안고르고 임시저장하면
|
||||
if (params.saleStoreId == '') {
|
||||
// params.saleStoreId = sessionState.storeId
|
||||
params.saleStoreId = session.storeId
|
||||
// params.saleStoreLevel = sessionState.storeLvl
|
||||
params.saleStoreLevel = session.storeLvl
|
||||
}
|
||||
|
||||
@ -1448,7 +1439,6 @@ export default function StuffDetail() {
|
||||
</th>
|
||||
<td>
|
||||
<div className="flx-box">
|
||||
{/* {sessionState?.storeId === 'T01' && ( */}
|
||||
{session?.storeId === 'T01' && (
|
||||
<>
|
||||
<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' && (
|
||||
<>
|
||||
<div className="select-wrap mr5" style={{ width: '567px' }}>
|
||||
@ -1514,7 +1503,6 @@ export default function StuffDetail() {
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{/* {sessionState?.storeId !== 'T01' && sessionState?.storeLvl !== '1' && ( */}
|
||||
{session?.storeId !== 'T01' && session?.storeLvl !== '1' && (
|
||||
<>
|
||||
<div className="select-wrap mr5" style={{ width: '567px' }}>
|
||||
@ -1931,7 +1919,6 @@ export default function StuffDetail() {
|
||||
</th>
|
||||
<td>
|
||||
<div className="flx-box">
|
||||
{/* {sessionState?.storeId === 'T01' && ( */}
|
||||
{session?.storeId === 'T01' && (
|
||||
<>
|
||||
<div className="select-wrap mr5" style={{ width: '567px' }}>
|
||||
@ -1947,10 +1934,8 @@ export default function StuffDetail() {
|
||||
onChange={onSelectionChange}
|
||||
getOptionLabel={(x) => x.saleStoreName}
|
||||
getOptionValue={(x) => x.saleStoreId}
|
||||
// isClearable={sessionState?.storeLvl === '1' ? true : false}
|
||||
isClearable={session?.storeLvl === '1' ? true : false}
|
||||
// isDisabled={sessionState?.storeLvl !== '1' ? true : false}
|
||||
isDisabled={session?.storeLvl !== '1' ? true : false}
|
||||
isClearable={detailData.tempFlg === '0' ? false : session?.storeLvl === '1' ? true : false}
|
||||
isDisabled={detailData.tempFlg === '0' ? true : session?.storeLvl !== '1' ? true : false}
|
||||
value={saleStoreList.filter(function (option) {
|
||||
return option.saleStoreId === selOptions
|
||||
})}
|
||||
@ -1967,7 +1952,6 @@ export default function StuffDetail() {
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{/* {sessionState?.storeId !== 'T01' && sessionState?.storeLvl === '1' && ( */}
|
||||
{session?.storeId !== 'T01' && session?.storeLvl === '1' && (
|
||||
<>
|
||||
<div className="select-wrap mr5" style={{ width: '567px' }}>
|
||||
@ -1983,8 +1967,9 @@ export default function StuffDetail() {
|
||||
getOptionLabel={(x) => x.saleStoreName}
|
||||
getOptionValue={(x) => x.saleStoreId}
|
||||
isClearable={false}
|
||||
// isDisabled={sessionState?.storeLvl !== '1' ? true : sessionState?.storeId !== 'T01' ? true : false}
|
||||
isDisabled={session?.storeLvl !== '1' ? true : session?.storeId !== 'T01' ? true : false}
|
||||
isDisabled={
|
||||
detailData.tempFlg === '0' ? true : session?.storeLvl !== '1' ? true : session?.storeId !== 'T01' ? true : false
|
||||
}
|
||||
value={showSaleStoreList.filter(function (option) {
|
||||
return option.saleStoreId === selOptions
|
||||
})}
|
||||
@ -2001,7 +1986,6 @@ export default function StuffDetail() {
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{/* {sessionState?.storeId !== 'T01' && sessionState?.storeLvl !== '1' && ( */}
|
||||
{session?.storeId !== 'T01' && session?.storeLvl !== '1' && (
|
||||
<>
|
||||
<div className="select-wrap mr5" style={{ width: '567px' }}>
|
||||
@ -2062,10 +2046,10 @@ export default function StuffDetail() {
|
||||
onChange={onSelectionChange2}
|
||||
getOptionLabel={(x) => x.saleStoreName}
|
||||
getOptionValue={(x) => x.saleStoreId}
|
||||
// isDisabled={sessionState?.storeLvl === '1' && form.watch('saleStoreId') != '' ? false : true}
|
||||
isDisabled={session?.storeLvl === '1' && form.watch('saleStoreId') != '' ? false : true}
|
||||
// isClearable={sessionState?.storeLvl === '1' ? true : false}
|
||||
isClearable={session?.storeLvl === '1' ? true : false}
|
||||
isDisabled={
|
||||
detailData.tempFlg === '0' ? true : session?.storeLvl === '1' && form.watch('saleStoreId') != '' ? false : true
|
||||
}
|
||||
isClearable={detailData.tempFlg === '0' ? false : session?.storeLvl === '1' ? true : false}
|
||||
value={otherSaleStoreList.filter(function (option) {
|
||||
return option.saleStoreId === otherSelOptions
|
||||
})}
|
||||
|
||||
@ -77,14 +77,14 @@ export default function StuffSearchCondition() {
|
||||
|
||||
if (stuffSearch.code === 'S') {
|
||||
setStuffSearch({
|
||||
schObjectNo: objectNo ? objectNo : stuffSearch?.schObjectNo,
|
||||
schSaleStoreName: stuffSearch?.schSaleStoreName ? stuffSearch?.schSaleStoreName : saleStoreName,
|
||||
schAddress: address ? address : stuffSearch?.schAddress,
|
||||
schObjectName: objectName ? objectName : stuffSearch?.schObjectName,
|
||||
schDispCompanyName: dispCompanyName ? dispCompanyName : stuffSearch?.schDispCompanyName,
|
||||
schObjectNo: objectNo ? objectNo : stuffSearch.schObjectNo,
|
||||
schSaleStoreName: saleStoreName ? saleStoreName : '',
|
||||
schAddress: address ? address : '',
|
||||
schObjectName: objectName ? objectName : '',
|
||||
schDispCompanyName: dispCompanyName ? dispCompanyName : '',
|
||||
schSelSaleStoreId: stuffSearch?.schOtherSelSaleStoreId ? stuffSearch.schOtherSelSaleStoreId : stuffSearch.schSelSaleStoreId,
|
||||
schReceiveUser: receiveUser ? receiveUser : stuffSearch?.schReceiveUser,
|
||||
schDateType: stuffSearch?.schDateType ? stuffSearch.schDateType : dateType,
|
||||
schReceiveUser: receiveUser ? receiveUser : '',
|
||||
schDateType: dateType,
|
||||
schFromDt: dayjs(startDate).format('YYYY-MM-DD'),
|
||||
schToDt: dayjs(endDate).format('YYYY-MM-DD'),
|
||||
code: 'E',
|
||||
@ -94,13 +94,13 @@ export default function StuffSearchCondition() {
|
||||
})
|
||||
} else {
|
||||
setStuffSearch({
|
||||
schObjectNo: objectNo ? objectNo : '',
|
||||
schSaleStoreName: saleStoreName ? saleStoreName : '',
|
||||
schAddress: address ? address : '',
|
||||
schObjectName: objectName ? objectName : '',
|
||||
schDispCompanyName: dispCompanyName ? dispCompanyName : '',
|
||||
schObjectNo: objectNo,
|
||||
schSaleStoreName: saleStoreName,
|
||||
schAddress: address,
|
||||
schObjectName: objectName,
|
||||
schDispCompanyName: dispCompanyName,
|
||||
schSelSaleStoreId: stuffSearch?.schOtherSelSaleStoreId ? stuffSearch.schOtherSelSaleStoreId : stuffSearch.schSelSaleStoreId,
|
||||
schReceiveUser: receiveUser ? receiveUser : '',
|
||||
schReceiveUser: receiveUser,
|
||||
schDateType: dateType,
|
||||
schFromDt: dayjs(startDate).format('YYYY-MM-DD'),
|
||||
schToDt: dayjs(endDate).format('YYYY-MM-DD'),
|
||||
@ -131,14 +131,12 @@ export default function StuffSearchCondition() {
|
||||
setDateType('U')
|
||||
setStartDate(dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD'))
|
||||
setEndDate(dayjs(new Date()).format('YYYY-MM-DD'))
|
||||
// if (sessionState?.storeId === 'T01') {
|
||||
if (session?.storeId === 'T01') {
|
||||
setSchSelSaleStoreId('')
|
||||
handleClear1() //판매대리점선택 자동완성 클리어
|
||||
resetStuffRecoil()
|
||||
setStuffSearch({
|
||||
...stuffSearch,
|
||||
code: 'C',
|
||||
schSelSaleStoreId: '',
|
||||
schOtherSelSaleStoreId: '',
|
||||
})
|
||||
@ -156,23 +154,17 @@ export default function StuffSearchCondition() {
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
// if (isObjectNotEmpty(sessionState)) {
|
||||
if (isObjectNotEmpty(session)) {
|
||||
// storeId가 T01 이거나 storeLvl이 1차점일때만 판매대리점 선택 활성화
|
||||
let url
|
||||
// if (sessionState?.storeId === 'T01') {
|
||||
if (session?.storeId === 'T01') {
|
||||
//T01일떄
|
||||
// url = `/api/object/saleStore/${sessionState?.storeId}/firstList?userId=${sessionState?.userId}`
|
||||
url = `/api/object/saleStore/${session?.storeId}/firstList?userId=${session?.userId}`
|
||||
} else {
|
||||
// if (sessionState.storeLvl === '1') {
|
||||
if (session.storeLvl === '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}`
|
||||
} 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}`
|
||||
}
|
||||
}
|
||||
@ -187,7 +179,6 @@ export default function StuffSearchCondition() {
|
||||
let allList
|
||||
let favList
|
||||
let otherList
|
||||
// if (sessionState?.storeId === 'T01') {
|
||||
if (session?.storeId === 'T01') {
|
||||
allList = res
|
||||
allList.sort((a, b) => (a.saleStoreId !== 'T01') - (b.saleStoreId !== 'T01') || a.saleStoreId - b.saleStoreId)
|
||||
@ -195,17 +186,14 @@ export default function StuffSearchCondition() {
|
||||
setSchSelSaleStoreList(allList)
|
||||
setFavoriteStoreList(favList)
|
||||
setShowSaleStoreList(favList)
|
||||
// setSchSelSaleStoreId(sessionState?.storeId)
|
||||
setSchSelSaleStoreId(session?.storeId)
|
||||
setStuffSearch({
|
||||
...stuffSearch,
|
||||
code: 'S',
|
||||
// schSelSaleStoreId: sessionState?.storeId,
|
||||
schSelSaleStoreId: session?.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}`
|
||||
|
||||
get({ url: url }).then((res) => {
|
||||
@ -222,7 +210,6 @@ export default function StuffSearchCondition() {
|
||||
}
|
||||
})
|
||||
} else {
|
||||
// if (sessionState?.storeLvl === '1') {
|
||||
if (session?.storeLvl === '1') {
|
||||
allList = res
|
||||
favList = res.filter((row) => row.priority !== 'B')
|
||||
@ -250,7 +237,6 @@ export default function StuffSearchCondition() {
|
||||
setOtherSaleStoreList(otherList)
|
||||
|
||||
//선택한 2차점 세션으로 자동셋팅
|
||||
// setOtherSaleStoreId(sessionState?.storeId)
|
||||
setOtherSaleStoreId(session?.storeId)
|
||||
setStuffSearch({
|
||||
...stuffSearch,
|
||||
@ -262,7 +248,6 @@ export default function StuffSearchCondition() {
|
||||
}
|
||||
})
|
||||
}
|
||||
// }, [sessionState])
|
||||
}, [session])
|
||||
|
||||
//초기화 눌렀을 때 1차판매점 자동완성도..
|
||||
@ -296,7 +281,6 @@ export default function StuffSearchCondition() {
|
||||
stuffSearch.schSelSaleStoreId = key.saleStoreId
|
||||
//T01아닌 1차점은 본인으로 디폴트셋팅이고 수정할수없어서 여기안옴
|
||||
//고른 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 otherList
|
||||
get({ url: url }).then((res) => {
|
||||
@ -352,6 +336,7 @@ export default function StuffSearchCondition() {
|
||||
// 엔터 이벤트
|
||||
const handleByOnKeyUp = (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
stuffSearch.code = 'E'
|
||||
onSubmit()
|
||||
}
|
||||
}
|
||||
@ -399,7 +384,7 @@ export default function StuffSearchCondition() {
|
||||
type="text"
|
||||
ref={objectNoRef}
|
||||
className="input-light"
|
||||
defaultValue={stuffSearch.code === 'E' || stuffSearch.code === 'M' ? stuffSearch?.schObjectNo : objectNo}
|
||||
defaultValue={stuffSearch?.schObjectNo ? stuffSearch.schObjectNo : objectNo}
|
||||
onChange={(e) => {
|
||||
setObjectNo(objectNoRef.current.value)
|
||||
}}
|
||||
@ -414,7 +399,7 @@ export default function StuffSearchCondition() {
|
||||
type="text"
|
||||
ref={saleStoreNameRef}
|
||||
className="input-light"
|
||||
defaultValue={stuffSearch.code === 'E' ? stuffSearch?.schSaleStoreName : saleStoreName}
|
||||
defaultValue={stuffSearch?.schSaleStoreName ? stuffSearch.schSaleStoreName : saleStoreName}
|
||||
onChange={(e) => {
|
||||
setSaleStoreName(saleStoreNameRef.current.value)
|
||||
}}
|
||||
@ -429,7 +414,7 @@ export default function StuffSearchCondition() {
|
||||
type="text"
|
||||
ref={addressRef}
|
||||
className="input-light"
|
||||
defaultValue={stuffSearch.code === 'E' ? stuffSearch?.schAddress : address}
|
||||
defaultValue={stuffSearch?.schAddress ? stuffSearch.schAddress : address}
|
||||
onChange={(e) => {
|
||||
setAddress(addressRef.current.value)
|
||||
}}
|
||||
@ -444,7 +429,7 @@ export default function StuffSearchCondition() {
|
||||
type="text"
|
||||
ref={dispCompanyNameRef}
|
||||
className="input-light"
|
||||
defaultValue={stuffSearch.code === 'E' ? stuffSearch?.schDispCompanyName : dispCompanyName}
|
||||
defaultValue={stuffSearch?.schDispCompanyName ? stuffSearch.schDispCompanyName : dispCompanyName}
|
||||
onChange={(e) => {
|
||||
setDispCompanyName(dispCompanyNameRef.current.value)
|
||||
}}
|
||||
@ -461,7 +446,7 @@ export default function StuffSearchCondition() {
|
||||
type="text"
|
||||
ref={objectNameRef}
|
||||
className="input-light"
|
||||
defaultValue={stuffSearch.code === 'E' ? stuffSearch?.schObjectName : objectName}
|
||||
defaultValue={stuffSearch?.schObjectName ? stuffSearch.schObjectName : objectName}
|
||||
onChange={(e) => {
|
||||
setobjectName(objectNameRef.current.value)
|
||||
}}
|
||||
@ -476,7 +461,7 @@ export default function StuffSearchCondition() {
|
||||
type="text"
|
||||
className="input-light"
|
||||
ref={receiveUserRef}
|
||||
defaultValue={stuffSearch.code === 'E' ? stuffSearch?.schReceiveUser : receiveUser}
|
||||
defaultValue={stuffSearch?.schReceiveUser ? stuffSearch.schReceiveUser : receiveUser}
|
||||
onChange={(e) => {
|
||||
setReceiveUser(receiveUserRef.current.value)
|
||||
}}
|
||||
@ -488,7 +473,6 @@ export default function StuffSearchCondition() {
|
||||
<td colSpan={3}>
|
||||
<div className="form-flex-wrap">
|
||||
<div className="select-wrap mr5" style={{ flex: 1 }}>
|
||||
{/* {sessionState?.storeId === 'T01' && ( */}
|
||||
{session?.storeId === 'T01' && (
|
||||
<Select
|
||||
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}
|
||||
isClearable={true}
|
||||
/>
|
||||
)}
|
||||
{/* {sessionState?.storeId !== 'T01' && sessionState?.storeLvl === '1' && ( */}
|
||||
{session?.storeId !== 'T01' && session?.storeLvl === '1' && (
|
||||
<Select
|
||||
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}
|
||||
isClearable={false}
|
||||
/>
|
||||
)}
|
||||
{/* {sessionState?.storeId !== 'T01' && sessionState?.storeLvl !== '1' && ( */}
|
||||
{session?.storeId !== 'T01' && session?.storeLvl !== '1' && (
|
||||
<Select
|
||||
id="long-value-select1"
|
||||
@ -620,11 +600,10 @@ export default function StuffSearchCondition() {
|
||||
type="radio"
|
||||
name="radio_ptype"
|
||||
id="radio_u"
|
||||
checked={stuffSearch?.schDateType === 'U' ? true : false}
|
||||
checked={dateType === 'U' ? true : false}
|
||||
value={'U'}
|
||||
onChange={(e) => {
|
||||
setDateType(e.target.value)
|
||||
setStuffSearch({ ...stuffSearch, code: 'S', schDateType: e.target.value })
|
||||
}}
|
||||
/>
|
||||
<label htmlFor="radio_u">{getMessage('stuff.search.schDateTypeU')}</label>
|
||||
@ -634,11 +613,10 @@ export default function StuffSearchCondition() {
|
||||
type="radio"
|
||||
name="radio_ptype"
|
||||
id="radio_r"
|
||||
checked={stuffSearch?.schDateType === 'R' ? true : false}
|
||||
checked={dateType === 'R' ? true : false}
|
||||
value={'R'}
|
||||
onChange={(e) => {
|
||||
setDateType(e.target.value)
|
||||
setStuffSearch({ ...stuffSearch, code: 'S', schDateType: e.target.value })
|
||||
}}
|
||||
/>
|
||||
<label htmlFor="radio_r">{getMessage('stuff.search.schDateTypeR')}</label>
|
||||
|
||||
@ -7,7 +7,7 @@ import { useMessage } from '@/hooks/useMessage'
|
||||
import { useRouter, useSearchParams } from 'next/navigation'
|
||||
import { floorPlanObjectState } from '@/store/floorPlanObjectAtom'
|
||||
import { useSetRecoilState } from 'recoil'
|
||||
|
||||
import { queryStringFormatter } from '@/util/common-utils'
|
||||
export default function StuffSubHeader({ type }) {
|
||||
const { getMessage } = useMessage()
|
||||
const router = useRouter()
|
||||
@ -25,8 +25,13 @@ export default function StuffSubHeader({ type }) {
|
||||
const moveFloorPlan = () => {
|
||||
setFloorPlanObjectNo({ floorPlanObjectNo: objectNo })
|
||||
|
||||
//상단 헤더에서 이동할땐 무조건 첫번째 플랜으로
|
||||
router.push('/floor-plan/estimate/5/1')
|
||||
const param = {
|
||||
pid: '1',
|
||||
}
|
||||
//확인필요
|
||||
const url = `/floor-plan?${queryStringFormatter(param)}`
|
||||
console.log(url)
|
||||
router.push(url)
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@ -59,7 +59,8 @@ export function useCanvasConfigInitialize() {
|
||||
|
||||
const canvasLoadInit = () => {
|
||||
roofInit() //화면표시 초기화
|
||||
groupInit()
|
||||
groupDimensionInit()
|
||||
reGroupInit() //그룹 객체 재그룹
|
||||
}
|
||||
|
||||
const gridInit = () => {
|
||||
@ -95,8 +96,8 @@ export function useCanvasConfigInitialize() {
|
||||
})
|
||||
}
|
||||
|
||||
const groupInit = () => {
|
||||
const groups = canvas.getObjects().filter((obj) => obj.groupYn)
|
||||
const groupDimensionInit = () => {
|
||||
const groups = canvas.getObjects().filter((obj) => obj.groupYn && obj.name === 'dimensionGroup')
|
||||
const groupIds = []
|
||||
|
||||
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 }
|
||||
}
|
||||
|
||||
@ -20,7 +20,6 @@ export function useCommonUtils() {
|
||||
const dimensionLineTextFont = useRecoilValue(fontSelector('dimensionLineText'))
|
||||
const commonTextFont = useRecoilValue(fontSelector('commonText'))
|
||||
const [commonUtils, setCommonUtilsState] = useRecoilState(commonUtilsState)
|
||||
|
||||
const { addPopup } = usePopup()
|
||||
|
||||
useEffect(() => {
|
||||
@ -278,7 +277,7 @@ export function useCommonUtils() {
|
||||
groupObjects.push(distanceText)
|
||||
|
||||
const group = new fabric.Group(groupObjects, {
|
||||
name: 'dimensionLine',
|
||||
name: 'dimensionGroup',
|
||||
selectable: true,
|
||||
originX: 'center',
|
||||
originY: 'center',
|
||||
@ -522,10 +521,10 @@ export function useCommonUtils() {
|
||||
if (object) {
|
||||
canvas?.remove(object)
|
||||
|
||||
if (object.id) {
|
||||
const group = canvas.getObjects().filter((obj) => obj.id === object.id)
|
||||
group.forEach((obj) => canvas?.remove(obj))
|
||||
}
|
||||
// if (object.id) {
|
||||
// const group = canvas.getObjects().filter((obj) => obj.id === object.id)
|
||||
// group.forEach((obj) => canvas?.remove(obj))
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@ -547,7 +546,7 @@ export function useCommonUtils() {
|
||||
initEvent()
|
||||
obj.setCoords()
|
||||
updateGroupObjectCoords(obj, originLeft, originTop)
|
||||
// canvas?.renderAll()
|
||||
canvas?.renderAll()
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -585,6 +584,10 @@ export function useCommonUtils() {
|
||||
editable: false,
|
||||
id: uuidv4(), //복사된 객체라 새로 따준다
|
||||
})
|
||||
|
||||
//객체가 그룹일 경우에는 그룹 아이디를 따로 넣어준다
|
||||
if (clonedObj.type === 'group') clonedObj.set({ groupId: uuidv4() })
|
||||
|
||||
initEvent()
|
||||
})
|
||||
}
|
||||
@ -720,41 +723,74 @@ export function useCommonUtils() {
|
||||
}
|
||||
|
||||
// 그룹 이동 시 라인 및 각 객체의 좌표를 절대 좌표로 업데이트하는 함수
|
||||
function updateGroupObjectCoords(group, originLeft, originTop) {
|
||||
const diffrenceLeft = group.left - originLeft
|
||||
const diffrenceTop = group.top - originTop
|
||||
function updateGroupObjectCoords(targetObj, originLeft, originTop) {
|
||||
const diffrenceLeft = targetObj.left - originLeft
|
||||
const diffrenceTop = targetObj.top - originTop
|
||||
|
||||
group.getObjects().forEach((obj) => {
|
||||
// 그룹 내 객체의 절대 좌표를 계산
|
||||
if (targetObj.type === 'group') {
|
||||
targetObj.getObjects().forEach((obj) => {
|
||||
// 그룹 내 객체의 절대 좌표를 계산
|
||||
|
||||
const originObjLeft = obj.left
|
||||
const originObjTop = obj.top
|
||||
const originObjLeft = obj.left
|
||||
const originObjTop = obj.top
|
||||
|
||||
if (obj.type === 'line') {
|
||||
// Line 객체의 경우, x1, y1, x2, y2 절대 좌표 계산
|
||||
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({
|
||||
x1: obj.x1 + diffrenceLeft,
|
||||
y1: obj.y1 + diffrenceTop,
|
||||
x2: obj.x2 + diffrenceLeft,
|
||||
y2: obj.y2 + diffrenceTop,
|
||||
})
|
||||
|
||||
obj.set({
|
||||
left: originObjLeft,
|
||||
top: originObjTop,
|
||||
})
|
||||
} else {
|
||||
// 다른 객체의 경우 left, top 절대 좌표 설정
|
||||
obj.set({
|
||||
...obj,
|
||||
left: obj.left,
|
||||
top: obj.top,
|
||||
})
|
||||
obj.set({
|
||||
left: originObjLeft,
|
||||
top: originObjTop,
|
||||
})
|
||||
obj.fire('modified')
|
||||
} else {
|
||||
// 다른 객체의 경우 left, top 절대 좌표 설정
|
||||
obj.set({
|
||||
left: obj.left,
|
||||
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()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const deleteOuterLineObject = () => {
|
||||
|
||||
176
src/hooks/floorPlan/estimate/useEstimateController.js
Normal file
176
src/hooks/floorPlan/estimate/useEstimateController.js
Normal 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,
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,5 @@
|
||||
'use client'
|
||||
import { useEffect } from 'react'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { canvasState } from '@/store/canvasAtom'
|
||||
@ -9,13 +10,63 @@ import { useSwal } from '@/hooks/useSwal'
|
||||
import * as turf from '@turf/turf'
|
||||
import { usePolygon } from '@/hooks/usePolygon'
|
||||
import { QPolygon } from '@/components/fabric/QPolygon'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { fontSelector } from '@/store/fontAtom'
|
||||
|
||||
export function useObjectBatch({ isHidden, setIsHidden }) {
|
||||
const { getMessage } = useMessage()
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
const { addCanvasMouseEventListener, initEvent } = useEvent()
|
||||
const { addCanvasMouseEventListener, initEvent, addDocumentEventListener } = useEvent()
|
||||
const { swalFire } = useSwal()
|
||||
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 selectedType = objectPlacement.typeRef.current.find((radio) => radio.checked).value
|
||||
@ -26,7 +77,7 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
|
||||
let rect, isDown, origX, origY
|
||||
let selectedSurface
|
||||
//프리입력
|
||||
console.log('useObjectBatch', isHidden)
|
||||
|
||||
if (selectedType === INPUT_TYPE.FREE) {
|
||||
addCanvasMouseEventListener('mouse:down', (e) => {
|
||||
isDown = true
|
||||
@ -115,7 +166,7 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
|
||||
}
|
||||
|
||||
isDown = false
|
||||
rect.set({ name: objName })
|
||||
rect.set({ name: objName, parentId: selectedSurface.id })
|
||||
rect.setCoords()
|
||||
initEvent()
|
||||
|
||||
@ -159,6 +210,7 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
|
||||
lockRotation: true,
|
||||
lockScalingX: true,
|
||||
lockScalingY: true,
|
||||
parentId: selectedSurface.id,
|
||||
})
|
||||
|
||||
//개구냐 그림자냐에 따라 변경
|
||||
@ -196,7 +248,7 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
|
||||
}
|
||||
|
||||
isDown = false
|
||||
rect.set({ name: objName })
|
||||
rect.set({ name: objName, parentId: selectedSurface.id })
|
||||
rect.setCoords()
|
||||
initEvent()
|
||||
if (setIsHidden) setIsHidden(false)
|
||||
@ -232,6 +284,7 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
|
||||
: 0
|
||||
const directionRef = dormerPlacement.directionRef.current
|
||||
let dormer, dormerOffset, isDown, selectedSurface, pentagonPoints, pentagonOffsetPoints
|
||||
const id = uuidv4()
|
||||
|
||||
if (height === '' || pitch === '' || height <= 0 || pitch <= 0) {
|
||||
swalFire({ text: getMessage('common.canvas.validate.size'), icon: 'error' })
|
||||
@ -310,6 +363,7 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
|
||||
originX: 'center',
|
||||
originY: 'top',
|
||||
angle: angle,
|
||||
objectId: id,
|
||||
})
|
||||
canvas?.add(dormerOffset)
|
||||
}
|
||||
@ -322,7 +376,7 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
|
||||
|
||||
//지붕 밖으로 그렸을때
|
||||
if (!turf.booleanWithin(trianglePolygon, selectedSurfacePolygon)) {
|
||||
swalFire({ text: '개구를 배치할 수 없습니다.', icon: 'error' })
|
||||
swalFire({ text: '도머를 배치할 수 없습니다.', icon: 'error' })
|
||||
//일단 지워
|
||||
deleteTempObjects()
|
||||
return
|
||||
@ -358,7 +412,7 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
|
||||
}) //오프셋이 있을땐 같이 도머로 만든다
|
||||
|
||||
const leftTriangle = new QPolygon(splitedTriangle[0], {
|
||||
fill: 'transparent',
|
||||
fill: 'white',
|
||||
stroke: 'black',
|
||||
strokeWidth: 1,
|
||||
selectable: true,
|
||||
@ -366,16 +420,18 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
|
||||
lockMovementY: true, // Y 축 이동 잠금
|
||||
lockRotation: true, // 회전 잠금
|
||||
viewLengthText: true,
|
||||
fontSize: 14,
|
||||
direction: direction,
|
||||
originX: 'center',
|
||||
originY: 'center',
|
||||
name: dormerName,
|
||||
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], {
|
||||
fill: 'transparent',
|
||||
fill: 'white',
|
||||
stroke: 'black',
|
||||
strokeWidth: 1,
|
||||
selectable: true,
|
||||
@ -383,26 +439,66 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
|
||||
lockMovementY: true, // Y 축 이동 잠금
|
||||
lockRotation: true, // 회전 잠금
|
||||
viewLengthText: true,
|
||||
fontSize: 14,
|
||||
direction: direction,
|
||||
originX: 'center',
|
||||
originY: 'center',
|
||||
name: dormerName,
|
||||
pitch: pitch,
|
||||
fontSize: lengthTextFont.fontSize.value,
|
||||
fontStyle: lengthTextFont.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal',
|
||||
fontWeight: lengthTextFont.fontWeight.value,
|
||||
})
|
||||
|
||||
canvas?.add(leftTriangle)
|
||||
canvas?.add(rightTriangle)
|
||||
// canvas?.add(leftTriangle)
|
||||
// canvas?.add(rightTriangle)
|
||||
|
||||
//패턴
|
||||
setSurfaceShapePattern(leftTriangle)
|
||||
setSurfaceShapePattern(rightTriangle)
|
||||
//방향
|
||||
|
||||
drawDirectionArrow(leftTriangle)
|
||||
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
|
||||
initEvent()
|
||||
dbClickEvent()
|
||||
if (setIsHidden) setIsHidden(false)
|
||||
}
|
||||
})
|
||||
@ -498,8 +594,7 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
|
||||
|
||||
//지붕 밖으로 그렸을때
|
||||
if (!turf.booleanWithin(pentagonPolygon, selectedSurfacePolygon)) {
|
||||
swalFire({ text: '개구를 배치할 수 없습니다.', icon: 'error' })
|
||||
|
||||
swalFire({ text: '도머를 배치할 수 없습니다.', icon: 'error' })
|
||||
//일단 지워
|
||||
deleteTempObjects()
|
||||
return
|
||||
@ -568,8 +663,8 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
|
||||
pitch: pitch,
|
||||
})
|
||||
|
||||
canvas?.add(leftPentagon)
|
||||
canvas?.add(rightPentagon)
|
||||
// canvas?.add(leftPentagon)
|
||||
// canvas?.add(rightPentagon)
|
||||
|
||||
//패턴
|
||||
setSurfaceShapePattern(leftPentagon)
|
||||
@ -578,8 +673,47 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
|
||||
drawDirectionArrow(leftPentagon)
|
||||
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
|
||||
initEvent()
|
||||
|
||||
dbClickEvent()
|
||||
if (setIsHidden) setIsHidden(false)
|
||||
}
|
||||
})
|
||||
@ -656,7 +790,6 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
|
||||
{ x: triangle.left + triangle.height, y: triangle.top - triangle.height },
|
||||
]
|
||||
}
|
||||
|
||||
return [leftPoints, rightPoints]
|
||||
}
|
||||
|
||||
@ -739,10 +872,157 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
|
||||
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 {
|
||||
applyOpeningAndShadow,
|
||||
applyDormers,
|
||||
splitDormerTriangle,
|
||||
splitDormerPentagon,
|
||||
resizeObjectBatch,
|
||||
moveObjectBatch,
|
||||
dormerOffsetKeyEvent,
|
||||
dormerOffset,
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
|
||||
import { canvasState } from '@/store/canvasAtom'
|
||||
import { adsorptionPointModeState, adsorptionRangeState, canvasState } from '@/store/canvasAtom'
|
||||
import { globalLocaleStore } from '@/store/localeAtom'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { useAxios } from '@/hooks/useAxios'
|
||||
@ -9,8 +9,6 @@ import { settingModalFirstOptionsState, settingModalSecondOptionsState } from '@
|
||||
import { setSurfaceShapePattern } from '@/util/canvas-util'
|
||||
import { POLYGON_TYPE } from '@/common/common'
|
||||
|
||||
import { adsorptionPointModeState, adsorptionRangeState } from '@/store/canvasAtom'
|
||||
|
||||
export function useCanvasSetting() {
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
|
||||
@ -225,9 +223,9 @@ export function useCanvasSetting() {
|
||||
const option1 = settingModalFirstOptions.option1
|
||||
|
||||
// 'allocDisplay' 할당 표시
|
||||
// 'outlineDisplay' 외벽선 표시 'outerLine', 'wallLine'
|
||||
// 'outlineDisplay' 외벽선 표시 'outerLine', POLYGON_TYPE.WALL
|
||||
// 'gridDisplay' 그리드 표시 'lindGrid', 'dotGrid'
|
||||
// 'lineDisplay' 지붕선 표시 'roof', 'roofBase'
|
||||
// 'lineDisplay' 지붕선 표시 'roof', POLYGON_TYPE.ROOF
|
||||
// 'wordDisplay' 문자 표시
|
||||
// 'circuitNumDisplay' 회로번호 표시
|
||||
// 'flowDisplay' 흐름방향 표시 'arrow'
|
||||
@ -244,13 +242,13 @@ export function useCanvasSetting() {
|
||||
optionName = ['1']
|
||||
break
|
||||
case 'outlineDisplay': //외벽선 표시
|
||||
optionName = ['outerLine', 'wallLine']
|
||||
optionName = ['outerLine', POLYGON_TYPE.WALL]
|
||||
break
|
||||
case 'gridDisplay': //그리드 표시
|
||||
optionName = ['lindGrid', 'dotGrid']
|
||||
break
|
||||
case 'lineDisplay': //지붕선 표시
|
||||
optionName = ['roof', 'roofBase']
|
||||
optionName = ['roof', POLYGON_TYPE.ROOF]
|
||||
break
|
||||
case 'wordDisplay': //문자 표시
|
||||
optionName = ['6']
|
||||
|
||||
@ -2,6 +2,7 @@ import { useRecoilState, useRecoilValue } from 'recoil'
|
||||
import { canvasState } from '@/store/canvasAtom'
|
||||
import { useEffect } from 'react'
|
||||
import { settingModalFirstOptionsState } from '@/store/settingAtom'
|
||||
import { POLYGON_TYPE } from '@/common/common'
|
||||
|
||||
export function useFirstOption() {
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
@ -12,9 +13,9 @@ export function useFirstOption() {
|
||||
const option1 = settingModalFirstOptions.option1
|
||||
|
||||
// 'allocDisplay' 할당 표시
|
||||
// 'outlineDisplay' 외벽선 표시 'outerLine', 'wallLine'
|
||||
// 'outlineDisplay' 외벽선 표시 'outerLine', POLYGON_TYPE.WALL
|
||||
// 'gridDisplay' 그리드 표시 'lindGrid', 'dotGrid'
|
||||
// 'lineDisplay' 지붕선 표시 'roof', 'roofBase'
|
||||
// 'lineDisplay' 지붕선 표시 'roof', POLYGON_TYPE.ROOF
|
||||
// 'wordDisplay' 문자 표시
|
||||
// 'circuitNumDisplay' 회로번호 표시
|
||||
// 'flowDisplay' 흐름방향 표시 'arrow'
|
||||
@ -30,13 +31,13 @@ export function useFirstOption() {
|
||||
optionName = ['1']
|
||||
break
|
||||
case 'outlineDisplay': //외벽선 표시
|
||||
optionName = ['outerLine', 'wallLine']
|
||||
optionName = ['outerLine', POLYGON_TYPE.WALL]
|
||||
break
|
||||
case 'gridDisplay': //그리드 표시
|
||||
optionName = ['lineGrid', 'dotGrid', 'adsorptionPoint', 'tempGrid']
|
||||
break
|
||||
case 'lineDisplay': //지붕선 표시
|
||||
optionName = ['roof', 'roofBase']
|
||||
optionName = ['roof', POLYGON_TYPE.ROOF]
|
||||
break
|
||||
case 'wordDisplay': //문자 표시
|
||||
optionName = ['6']
|
||||
|
||||
@ -15,14 +15,15 @@ import {
|
||||
outerLineLength2State,
|
||||
outerLineTypeState,
|
||||
} 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 { useAdsorptionPoint } from '@/hooks/useAdsorptionPoint'
|
||||
import { useSwal } from '@/hooks/useSwal'
|
||||
import { booleanPointInPolygon } from '@turf/turf'
|
||||
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 { POLYGON_TYPE } from '@/common/common'
|
||||
|
||||
// 보조선 작성
|
||||
export function useAuxiliaryDrawing(id) {
|
||||
@ -80,7 +81,7 @@ export function useAuxiliaryDrawing(id) {
|
||||
|
||||
useEffect(() => {
|
||||
// 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) {
|
||||
swalFire({ text: '지붕형상이 없습니다.' })
|
||||
closePopup(id)
|
||||
@ -561,7 +562,7 @@ export function useAuxiliaryDrawing(id) {
|
||||
return
|
||||
}
|
||||
|
||||
const roofBases = canvas.getObjects().filter((obj) => obj.name === 'roofBase')
|
||||
const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
|
||||
/*const allLines = [...auxiliaryLines]
|
||||
|
||||
roofBases.forEach((roofBase) => {
|
||||
@ -586,7 +587,7 @@ export function useAuxiliaryDrawing(id) {
|
||||
return
|
||||
}
|
||||
// 기존 점과 겹치는지 확인
|
||||
if (interSectionPointsWithRoofLines.some((point) => point.x === intersectionPoint.x && point.y === intersectionPoint.y)) {
|
||||
if (interSectionPointsWithRoofLines.some((point) => isSamePoint(point, intersectionPoint))) {
|
||||
return
|
||||
}
|
||||
|
||||
@ -611,9 +612,41 @@ export function useAuxiliaryDrawing(id) {
|
||||
},
|
||||
)
|
||||
lineHistory.current.push(newLine)
|
||||
lineHistory.current = lineHistory.current.filter((history) => history !== line1)
|
||||
removeLine(line1)
|
||||
intersectionPoints.current.push(...interSectionPointsWithRoofLines)
|
||||
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 = lineHistory.current.filter((history) => history !== line1)
|
||||
removeLine(line1)
|
||||
})
|
||||
|
||||
@ -742,7 +776,7 @@ export function useAuxiliaryDrawing(id) {
|
||||
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은 제거
|
||||
// 겹치는 선 하나는 canvas에서 제거한다.
|
||||
@ -772,9 +806,13 @@ export function useAuxiliaryDrawing(id) {
|
||||
})
|
||||
const roofInnerLines = innerLines.filter((line) => {
|
||||
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 =
|
||||
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) {
|
||||
line.attributes = { ...line.attributes, roofId: roofBase.id }
|
||||
|
||||
@ -3,7 +3,7 @@ import { useRecoilValue } from 'recoil'
|
||||
import { ANGLE_TYPE, canvasState, currentAngleTypeSelector, pitchTextSelector } from '@/store/canvasAtom'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
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 { useMode } from '@/hooks/useMode'
|
||||
import { outerLineFixState } from '@/store/outerLineAtom'
|
||||
@ -54,7 +54,7 @@ export function useEavesGableEdit(id) {
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
const wallLines = canvas.getObjects().filter((obj) => obj.name === 'wallLine')
|
||||
const wallLines = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.WALL)
|
||||
wallLines.forEach((wallLine) => {
|
||||
convertPolygonToLines(wallLine)
|
||||
})
|
||||
@ -160,7 +160,7 @@ export function useEavesGableEdit(id) {
|
||||
attributes,
|
||||
})
|
||||
|
||||
const roofBases = canvas?.getObjects().filter((obj) => obj.name === 'roofBase')
|
||||
const roofBases = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
|
||||
|
||||
roofBases.forEach((roof) => {
|
||||
roof.innerLines.forEach((line) => {
|
||||
@ -169,7 +169,7 @@ export function useEavesGableEdit(id) {
|
||||
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')
|
||||
removeTargets.forEach((obj) => {
|
||||
canvas.remove(obj)
|
||||
|
||||
@ -1,9 +1,12 @@
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { canvasState } from '@/store/canvasAtom'
|
||||
import { canvasState, currentObjectState } from '@/store/canvasAtom'
|
||||
import { usePopup } from '@/hooks/usePopup'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
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) {
|
||||
@ -15,6 +18,8 @@ export function useMovementSetting(id) {
|
||||
const { initEvent, addCanvasMouseEventListener } = useEvent()
|
||||
const { closePopup } = usePopup()
|
||||
const { getMessage } = useMessage()
|
||||
const currentObject = useRecoilValue(currentObjectState)
|
||||
const selectedObject = useRef(null)
|
||||
const buttonType = [
|
||||
{ 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 },
|
||||
@ -37,42 +42,231 @@ export function useMovementSetting(id) {
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
removeFlowLine()
|
||||
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])
|
||||
|
||||
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) => {
|
||||
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()
|
||||
addCanvasMouseEventListener('mouse:move', mouseMoveEvent)
|
||||
addCanvasMouseEventListener('mouse:down', mouseDownEvent)
|
||||
return () => {
|
||||
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) => {
|
||||
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()
|
||||
}
|
||||
}, [])
|
||||
|
||||
const mouseMoveEvent = (e) => {
|
||||
if (typeRef.current === TYPE.FLOW_LINE) {
|
||||
flowLineEvent(e)
|
||||
} else {
|
||||
updownEvent(e)
|
||||
useEffect(() => {
|
||||
const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') // 기존 outerLine의 selectable true
|
||||
outerLines.forEach((line) => {
|
||||
line.set({ stroke: 'black' })
|
||||
})
|
||||
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()
|
||||
if (!target) {
|
||||
return
|
||||
@ -113,12 +307,24 @@ export function useMovementSetting(id) {
|
||||
|
||||
const handleSave = () => {
|
||||
if (type === TYPE.FLOW_LINE) {
|
||||
// 동선이동
|
||||
if (FLOW_LINE_REF.DOWN_LEFT_RADIO_REF.current.checked) {
|
||||
// 높이 변경: 아래, 왼쪽 체크
|
||||
} else {
|
||||
// 높이 변경: 위, 오른쪽 체크
|
||||
const flowLine = canvas.getObjects().find((obj) => obj.name === 'flowLine')
|
||||
|
||||
const currentLine = flowLine.currentLine
|
||||
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 {
|
||||
// 형 올림내림
|
||||
if (UP_DOWN_REF.UP_RADIO_REF.current.checked) {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
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 { canvasState, currentObjectState } from '@/store/canvasAtom'
|
||||
import { useMode } from '@/hooks/useMode'
|
||||
@ -135,7 +135,7 @@ export function usePropertiesSetting(id) {
|
||||
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]
|
||||
|
||||
|
||||
@ -7,6 +7,7 @@ import { useSwal } from '@/hooks/useSwal'
|
||||
import { usePolygon } from '@/hooks/usePolygon'
|
||||
import { roofDisplaySelector } from '@/store/settingAtom'
|
||||
import { usePopup } from '@/hooks/usePopup'
|
||||
import { POLYGON_TYPE } from '@/common/common'
|
||||
|
||||
// 지붕면 할당
|
||||
export function useRoofAllocationSetting(id) {
|
||||
@ -81,12 +82,12 @@ export function useRoofAllocationSetting(id) {
|
||||
const [selectedRoofMaterial, setSelectedRoofMaterial] = useState(roofMaterials[0])
|
||||
|
||||
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) {
|
||||
swalFire({ text: '할당할 지붕이 없습니다.' })
|
||||
closePopup(id)
|
||||
}
|
||||
// if (type === 'roofBase') {
|
||||
// if (type === POLYGON_TYPE.ROOF) {
|
||||
// // 지붕면 할당
|
||||
//
|
||||
// } else if ('roof') {
|
||||
@ -104,8 +105,8 @@ export function useRoofAllocationSetting(id) {
|
||||
|
||||
// 선택한 지붕재로 할당
|
||||
const handleSave = () => {
|
||||
const roofBases = canvas.getObjects().filter((obj) => obj.name === 'roofBase')
|
||||
const wallLines = canvas.getObjects().filter((obj) => obj.name === 'wallLine')
|
||||
const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
|
||||
const wallLines = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.WALL)
|
||||
roofBases.forEach((roofBase) => {
|
||||
try {
|
||||
splitPolygonWithLines(roofBase)
|
||||
@ -117,7 +118,7 @@ export function useRoofAllocationSetting(id) {
|
||||
canvas.remove(line)
|
||||
})
|
||||
|
||||
canvas.remove(roofBase)
|
||||
// canvas.remove(roofBase)
|
||||
})
|
||||
|
||||
wallLines.forEach((wallLine) => {
|
||||
|
||||
@ -4,7 +4,7 @@ import { useEffect, useRef, useState } from 'react'
|
||||
import { useLine } from '@/hooks/useLine'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
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 { usePolygon } from '@/hooks/usePolygon'
|
||||
import { outerLineFixState } from '@/store/outerLineAtom'
|
||||
@ -60,7 +60,7 @@ export function useRoofShapePassivitySetting(id) {
|
||||
useEffect(() => {
|
||||
if (!isLoading) return
|
||||
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)
|
||||
|
||||
@ -185,7 +185,7 @@ export function useRoofShapePassivitySetting(id) {
|
||||
}
|
||||
|
||||
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 lines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
|
||||
exceptObjs.forEach((obj) => {
|
||||
@ -199,10 +199,10 @@ export function useRoofShapePassivitySetting(id) {
|
||||
let wall
|
||||
|
||||
if (isFix.current) {
|
||||
wall = addPolygonByLines(lines, { name: 'wallLine', fill: 'transparent', stroke: 'black' })
|
||||
wall = addPolygonByLines(lines, { name: POLYGON_TYPE.WALL, fill: 'transparent', stroke: 'black' })
|
||||
} 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) => {
|
||||
line.attributes = initLines.current[idx].attributes
|
||||
})
|
||||
|
||||
@ -2,7 +2,7 @@ import { useEffect, useRef, useState } from 'react'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { useRecoilValue, useSetRecoilState } from 'recoil'
|
||||
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 { useMode } from '@/hooks/useMode'
|
||||
import { useLine } from '@/hooks/useLine'
|
||||
@ -129,7 +129,7 @@ export function useRoofShapeSetting(id) {
|
||||
|
||||
useEffect(() => {
|
||||
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')
|
||||
outerLines.forEach((line) => {
|
||||
showLine(line)
|
||||
@ -376,20 +376,20 @@ export function useRoofShapeSetting(id) {
|
||||
// 기존 wallLine, roofBase 제거
|
||||
canvas
|
||||
.getObjects()
|
||||
.filter((obj) => obj.name === 'wallLine')
|
||||
.filter((obj) => obj.name === POLYGON_TYPE.WALL)
|
||||
.forEach((line) => {
|
||||
canvas.remove(line)
|
||||
})
|
||||
|
||||
canvas
|
||||
.getObjects()
|
||||
.filter((obj) => obj.name === 'roofBase')
|
||||
.filter((obj) => obj.name === POLYGON_TYPE.ROOF)
|
||||
.forEach((obj) => {
|
||||
canvas.remove(...obj.innerLines)
|
||||
canvas.remove(obj)
|
||||
})
|
||||
|
||||
const polygon = addPolygonByLines(outerLines, { name: 'wallLine' })
|
||||
const polygon = addPolygonByLines(outerLines, { name: POLYGON_TYPE.WALL })
|
||||
polygon.lines = [...outerLines]
|
||||
|
||||
addPitchTextsByOuterLines()
|
||||
|
||||
@ -12,10 +12,12 @@ import { useEvent } from '@/hooks/useEvent'
|
||||
import { usePopup } from '@/hooks/usePopup'
|
||||
import { roofDisplaySelector } from '@/store/settingAtom'
|
||||
import { usePolygon } from '@/hooks/usePolygon'
|
||||
import { fontSelector } from '@/store/fontAtom'
|
||||
|
||||
export function useSurfaceShapeBatch() {
|
||||
const { getMessage } = useMessage()
|
||||
const { drawDirectionArrow } = usePolygon()
|
||||
const lengthTextFont = useRecoilValue(fontSelector('lengthText'))
|
||||
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
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 {
|
||||
applySurfaceShape,
|
||||
deleteAllSurfacesAndObjects,
|
||||
moveSurfaceShapeBatch,
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { useState } from 'react'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { canvasSizeState, canvasState, canvasZoomState, currentObjectState, fontFamilyState, fontSizeState } from '@/store/canvasAtom'
|
||||
@ -18,6 +18,10 @@ export function useCanvasEvent() {
|
||||
const lengthTextOption = useRecoilValue(fontSelector('lengthText'))
|
||||
const { modifiedPlanFlag, setModifiedPlanFlag } = usePlan()
|
||||
|
||||
useEffect(() => {
|
||||
canvas?.setZoom(canvasZoom / 100)
|
||||
}, [canvasZoom])
|
||||
|
||||
// 기본적인 이벤트 필요시 추가
|
||||
const attachDefaultEventOnCanvas = () => {
|
||||
removeEventOnCanvas()
|
||||
@ -205,13 +209,52 @@ export function useCanvasEvent() {
|
||||
created: (e) => {
|
||||
const target = e.selected[0]
|
||||
setCurrentObject(target)
|
||||
const { selected } = e
|
||||
|
||||
if (selected.length > 0) {
|
||||
selected.forEach((obj) => {
|
||||
if (obj.type === 'QPolygon') {
|
||||
obj.set({ stroke: 'red' })
|
||||
}
|
||||
})
|
||||
canvas.renderAll()
|
||||
}
|
||||
},
|
||||
cleared: (e) => {
|
||||
setCurrentObject(null)
|
||||
const { deselected } = e
|
||||
|
||||
if (deselected.length > 0) {
|
||||
deselected.forEach((obj) => {
|
||||
if (obj.type === 'QPolygon') {
|
||||
obj.set({ stroke: 'black' })
|
||||
}
|
||||
})
|
||||
}
|
||||
canvas.renderAll()
|
||||
},
|
||||
updated: (e) => {
|
||||
const target = e.selected[0]
|
||||
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 = () => {
|
||||
setCanvasZoom(100)
|
||||
canvas.set({ zoom: 1 })
|
||||
@ -376,5 +427,6 @@ export function useCanvasEvent() {
|
||||
setCanvasForEvent,
|
||||
attachDefaultEventOnCanvas,
|
||||
handleZoomClear,
|
||||
handleZoom,
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 RowInsert from '@/components/floor-plan/modal/module/row/RowInsert'
|
||||
import CircuitNumberEdit from '@/components/floor-plan/modal/module/CircuitNumberEdit'
|
||||
import { useObjectBatch } from '@/hooks/object/useObjectBatch'
|
||||
import { useSurfaceShapeBatch } from '@/hooks/surface/useSurfaceShapeBatch'
|
||||
|
||||
export function useContextMenu() {
|
||||
const currentMenu = useRecoilValue(currentMenuState) // 현재 메뉴
|
||||
@ -48,7 +50,8 @@ export function useContextMenu() {
|
||||
const [cell, setCell] = useState(null)
|
||||
const [column, setColumn] = useState(null)
|
||||
const { handleZoomClear } = useCanvasEvent()
|
||||
|
||||
const { moveObjectBatch } = useObjectBatch({})
|
||||
const { moveSurfaceShapeBatch } = useSurfaceShapeBatch()
|
||||
const currentMenuSetting = () => {
|
||||
switch (currentMenu) {
|
||||
case MENU.PLAN_DRAWING:
|
||||
@ -228,21 +231,21 @@ export function useContextMenu() {
|
||||
y: 180,
|
||||
})
|
||||
setCurrentContextMenu(menu)
|
||||
currentMenuSetting()
|
||||
setQContextMenu({ ...qContextMenu, visible: false })
|
||||
}
|
||||
|
||||
const handleKeyup = (e) => {
|
||||
let menu = null
|
||||
|
||||
for (let i = 0; i < contextMenu.length; i++) {
|
||||
const temp = contextMenu[i].filter((menu) => {
|
||||
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(() => {
|
||||
@ -254,8 +257,8 @@ export function useContextMenu() {
|
||||
}, [currentContextMenu])
|
||||
|
||||
useEffect(() => {
|
||||
console.log('currentObject', currentObject)
|
||||
if (currentObject?.name) {
|
||||
console.log(currentObject?.name)
|
||||
switch (currentObject.name) {
|
||||
case 'triangleDormer':
|
||||
case 'pentagonDormer':
|
||||
@ -270,16 +273,19 @@ export function useContextMenu() {
|
||||
id: 'dormerRemove',
|
||||
shortcut: ['d', 'D'],
|
||||
name: `${getMessage('contextmenu.remove')}(D)`,
|
||||
fn: () => deleteObject(),
|
||||
},
|
||||
{
|
||||
id: 'dormerMove',
|
||||
shortcut: ['m', 'M'],
|
||||
name: `${getMessage('contextmenu.move')}(M)`,
|
||||
fn: () => moveObjectBatch(),
|
||||
},
|
||||
{
|
||||
id: 'dormerCopy',
|
||||
shortcut: ['c', 'C'],
|
||||
name: `${getMessage('contextmenu.copy')}(C)`,
|
||||
fn: () => copyObject(),
|
||||
},
|
||||
{
|
||||
id: 'roofMaterialEdit',
|
||||
@ -289,7 +295,7 @@ export function useContextMenu() {
|
||||
{
|
||||
id: 'dormerOffset',
|
||||
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',
|
||||
name: '사이즈 변경',
|
||||
component: <SizeSetting id={popupId} />,
|
||||
component: <SizeSetting id={popupId} target={currentObject} />,
|
||||
},
|
||||
{
|
||||
id: 'roofMaterialRemove',
|
||||
shortcut: ['d', 'D'],
|
||||
name: `${getMessage('contextmenu.remove')}(D)`,
|
||||
fn: () => deleteObject(),
|
||||
},
|
||||
{
|
||||
id: 'roofMaterialMove',
|
||||
shortcut: ['m', 'M'],
|
||||
name: `${getMessage('contextmenu.move')}(M)`,
|
||||
fn: () => moveSurfaceShapeBatch(),
|
||||
},
|
||||
{
|
||||
id: 'roofMaterialCopy',
|
||||
shortcut: ['c', 'C'],
|
||||
name: `${getMessage('contextmenu.copy')}(C)`,
|
||||
fn: () => copyObject(),
|
||||
},
|
||||
],
|
||||
[
|
||||
@ -343,25 +352,30 @@ export function useContextMenu() {
|
||||
{
|
||||
id: 'sizeEdit',
|
||||
name: getMessage('contextmenu.size.edit'),
|
||||
component: <SizeSetting id={popupId} target={currentObject} />,
|
||||
},
|
||||
{
|
||||
id: 'openingRemove',
|
||||
shortcut: ['d', 'D'],
|
||||
name: `${getMessage('contextmenu.remove')}(D)`,
|
||||
fn: () => deleteObject(),
|
||||
},
|
||||
{
|
||||
id: 'openingMove',
|
||||
shortcut: ['m', 'M'],
|
||||
name: `${getMessage('contextmenu.move')}(M)`,
|
||||
fn: () => moveObject(),
|
||||
},
|
||||
{
|
||||
id: 'openingCopy',
|
||||
shortcut: ['c', 'C'],
|
||||
name: `${getMessage('contextmenu.copy')}(C)`,
|
||||
fn: () => copyObject(),
|
||||
},
|
||||
{
|
||||
id: 'openingOffset',
|
||||
name: getMessage('contextmenu.opening.offset'),
|
||||
component: <DormerOffset id={popupId} title={getMessage('contextmenu.opening.offset')} />,
|
||||
},
|
||||
],
|
||||
])
|
||||
@ -445,7 +459,7 @@ export function useContextMenu() {
|
||||
],
|
||||
])
|
||||
break
|
||||
case 'dimensionLine':
|
||||
case 'dimensionGroup':
|
||||
setContextMenu([
|
||||
[
|
||||
{
|
||||
@ -466,7 +480,7 @@ export function useContextMenu() {
|
||||
{
|
||||
id: 'dimensionLineDisplayEdit',
|
||||
name: getMessage('contextmenu.display.edit'),
|
||||
component: <DimensionLineSetting id={popupId} />,
|
||||
component: <DimensionLineSetting id={popupId} isConfig={false} />,
|
||||
},
|
||||
],
|
||||
])
|
||||
@ -477,22 +491,25 @@ export function useContextMenu() {
|
||||
{
|
||||
id: 'sizeEdit',
|
||||
name: getMessage('contextmenu.size.edit'),
|
||||
component: <SizeSetting id={popupId} />,
|
||||
component: <SizeSetting id={popupId} target={currentObject} />,
|
||||
},
|
||||
{
|
||||
id: 'remove',
|
||||
shortcut: ['d', 'D'],
|
||||
name: `${getMessage('contextmenu.remove')}(D)`,
|
||||
fn: () => deleteObject(),
|
||||
},
|
||||
{
|
||||
id: 'move',
|
||||
shortcut: ['m', 'M'],
|
||||
name: `${getMessage('contextmenu.move')}(M)`,
|
||||
fn: () => moveObject(),
|
||||
},
|
||||
{
|
||||
id: 'copy',
|
||||
shortcut: ['c', 'C'],
|
||||
name: `${getMessage('contextmenu.copy')}(C)`,
|
||||
fn: () => copyObject(),
|
||||
},
|
||||
],
|
||||
])
|
||||
|
||||
@ -1,12 +1,11 @@
|
||||
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 { 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 { useDotLineGrid } from '@/hooks/useDotLineGrid'
|
||||
import { useTempGrid } from '@/hooks/useTempGrid'
|
||||
import { gridDisplaySelector } from '@/store/settingAtom'
|
||||
|
||||
export function useEvent() {
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
@ -100,7 +99,13 @@ export function useEvent() {
|
||||
const distance = calculateDistance(pointer, closestLine)
|
||||
|
||||
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,
|
||||
removeAllMouseEventListeners,
|
||||
removeAllDocumentEventListeners,
|
||||
removeDocumentEvent,
|
||||
removeMouseEvent,
|
||||
removeMouseLine,
|
||||
initEvent,
|
||||
|
||||
@ -36,10 +36,7 @@ import { QPolygon } from '@/components/fabric/QPolygon'
|
||||
import offsetPolygon from '@/util/qpolygon-utils'
|
||||
import { isObjectNotEmpty } from '@/util/common-utils'
|
||||
import * as turf from '@turf/turf'
|
||||
import { INPUT_TYPE, Mode } from '@/common/common'
|
||||
import { m } from 'framer-motion'
|
||||
import { set } from 'react-hook-form'
|
||||
import { FaWineGlassEmpty } from 'react-icons/fa6'
|
||||
import { INPUT_TYPE, LINE_TYPE, Mode, POLYGON_TYPE } from '@/common/common'
|
||||
|
||||
export function useMode() {
|
||||
const [mode, setMode] = useRecoilState(modeState)
|
||||
@ -1509,6 +1506,59 @@ export function useMode() {
|
||||
*벽 지붕 외곽선 생성 polygon을 입력받아 만들기
|
||||
*/
|
||||
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) //지붕 그리기
|
||||
roof.drawHelpLine()
|
||||
// roof.divideLine()
|
||||
@ -1677,21 +1727,6 @@ export function useMode() {
|
||||
}
|
||||
|
||||
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 originPolygon = new QPolygon(wall.points, { fontSize: 0 })
|
||||
originPolygon.setViewLengthText(false)
|
||||
@ -1711,13 +1746,33 @@ export function useMode() {
|
||||
return { x1: point.x, y1: point.y }
|
||||
}),
|
||||
)
|
||||
roof.name = 'roofBase'
|
||||
roof.name = POLYGON_TYPE.ROOF
|
||||
roof.setWall(wall)
|
||||
|
||||
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)
|
||||
setWall(wall)
|
||||
|
||||
|
||||
@ -55,31 +55,38 @@ export function usePlan() {
|
||||
/**
|
||||
* 현재 캔버스에 그려진 데이터를 추출
|
||||
*/
|
||||
const currentCanvasData = () => {
|
||||
const currentCanvasData = (mode = '') => {
|
||||
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) {
|
||||
groups.forEach((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()
|
||||
if (groups.length > 0) {
|
||||
groups.forEach((group) => {
|
||||
canvas?.remove(group)
|
||||
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()
|
||||
@ -163,7 +170,7 @@ export function usePlan() {
|
||||
* 페이지 내 캔버스를 저장
|
||||
*/
|
||||
const saveCanvas = async (userId) => {
|
||||
const canvasStatus = currentCanvasData()
|
||||
const canvasStatus = currentCanvasData('save')
|
||||
initCanvasPlans.some((plan) => plan.id === currentCanvasPlan.id)
|
||||
? await putCanvasStatus(canvasStatus)
|
||||
: await postCanvasStatus(userId, canvasStatus)
|
||||
|
||||
@ -113,6 +113,12 @@ export const usePolygon = () => {
|
||||
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
|
||||
.getObjects()
|
||||
.filter((obj) => obj.name === 'flowText' && obj.parent === polygon.arrow)
|
||||
|
||||
@ -4,47 +4,90 @@ import { contextPopupState, popupState } from '@/store/popupAtom'
|
||||
export function usePopup() {
|
||||
const [popup, setPopup] = useRecoilState(popupState)
|
||||
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)
|
||||
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) => {
|
||||
setPopup({ children: [...filterDepth(depth)] })
|
||||
setPopup({
|
||||
config: [...filterDepth(depth)],
|
||||
other: [],
|
||||
})
|
||||
}
|
||||
|
||||
const filterChildrenPopup = (id) => {
|
||||
const target = popup.children.filter((child) => child.id === id)
|
||||
if (target.length !== 0) {
|
||||
return popup.children.filter((child) => child.depth <= target[0].depth)
|
||||
const filterChildrenPopup = (id, isConfig) => {
|
||||
let target = []
|
||||
if (isConfig) {
|
||||
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) => {
|
||||
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 = () => {
|
||||
setPopup({ children: [] })
|
||||
setPopup({
|
||||
other: [],
|
||||
config: [],
|
||||
})
|
||||
}
|
||||
|
||||
const closePrevPopup = () => {
|
||||
setPopup({ children: [...popup.children.slice(popup.children.length - 1)] })
|
||||
setPopup({
|
||||
config: [...popup?.slice(popup?.length - 1)],
|
||||
other: [],
|
||||
})
|
||||
}
|
||||
|
||||
const filterDepth = (depth) => {
|
||||
return [...popup.children.filter((child) => child.depth !== depth)]
|
||||
const filterDepth = (depth, isConfig) => {
|
||||
if (isConfig) {
|
||||
return [...popup?.config.filter((child) => child.depth < depth)]
|
||||
} else {
|
||||
return [...popup?.other.filter((child) => child.depth < depth)]
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
popup,
|
||||
setPopup,
|
||||
addPopup,
|
||||
closePopup,
|
||||
closePopups,
|
||||
|
||||
@ -123,6 +123,7 @@
|
||||
"modal.module.basic.setting.auto.placement": "設定値に自動配置",
|
||||
"plan.menu.module.circuit.setting.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.name": "名称",
|
||||
"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.overload": "過積最大枚数",
|
||||
"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.auto": "自動回路割り当て",
|
||||
"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.circuit.num.fix": "番号確定",
|
||||
"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.estimate": "見積",
|
||||
"plan.menu.estimate.roof.alloc": "屋根面の割り当て",
|
||||
@ -148,6 +157,7 @@
|
||||
"modal.roof.alloc.select.parallel": "並列式",
|
||||
"modal.roof.alloc.select.stairs": "カスケード",
|
||||
"modal.roof.alloc.apply": "選択した屋根材として割り当て",
|
||||
"plan.menu.estimate.docDown": "文書のダウンロード",
|
||||
"plan.menu.estimate.save": "保存",
|
||||
"plan.menu.estimate.reset": "初期化",
|
||||
"plan.menu.estimate.copy": "コピー",
|
||||
@ -473,6 +483,19 @@
|
||||
"commons.east": "ドン",
|
||||
"commons.south": "南",
|
||||
"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.sub_name": "太陽光発電システム図面管理サイト",
|
||||
"board.notice.title": "お知らせ",
|
||||
@ -783,18 +806,18 @@
|
||||
"surface.shape.validate.size.4to5": "④길이는 ⑤보다 큰 값을 넣어주세요.",
|
||||
"estimate.detail.header.title": "基本情報",
|
||||
"estimate.detail.objectNo": "品番",
|
||||
"estimate.detail.estimateNo": "見積書番号",
|
||||
"estimate.detail.createDatetime": "登録日",
|
||||
"estimate.detail.docNo": "見積書番号",
|
||||
"estimate.detail.drawingEstimateCreateDate": "登録日",
|
||||
"estimate.detail.lastEditDatetime": "変更日時",
|
||||
"estimate.detail.saleStoreId": "一次販売店名",
|
||||
"estimate.detail.estimateDate": "見積日",
|
||||
"estimate.detail.otherSaleStoreId": "二次販売店名",
|
||||
"estimate.detail.receiveUser": "担当者 ",
|
||||
"estimate.detail.title": "案件名",
|
||||
"estimate.detail.remarks": "メモ",
|
||||
"estimate.detail.orderType": "注文分類",
|
||||
"estimate.detail.objectName": "案件名",
|
||||
"estimate.detail.objectRemarks": "メモ",
|
||||
"estimate.detail.estimateType": "注文分類",
|
||||
"estimate.detail.roofCns": "屋根材・仕様施工",
|
||||
"estimate.detail.note": "備考",
|
||||
"estimate.detail.remarks": "備考",
|
||||
"estimate.detail.nextSubmit": "後日資料提出",
|
||||
"estimate.detail.header.fileList1": "ファイル添付",
|
||||
"estimate.detail.fileList.btn": "ファイル選択",
|
||||
@ -813,7 +836,10 @@
|
||||
"estimate.detail.sepcialEstimateProductInfo.calcFormula2": "PKG単価 (W)×PKG容量(W)",
|
||||
"estimate.detail.header.showPrice": "価格表示",
|
||||
"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.btn3": "製品削除"
|
||||
}
|
||||
|
||||
@ -127,6 +127,7 @@
|
||||
"modal.module.basic.setting.auto.placement": "설정값으로 자동 배치",
|
||||
"plan.menu.module.circuit.setting.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.name": "명칭",
|
||||
"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.overload": "과적최대매수",
|
||||
"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.auto": "자동 회로 할당",
|
||||
"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.circuit.num.fix": "번호 확정",
|
||||
"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.estimate": "견적서",
|
||||
"plan.menu.estimate.roof.alloc": "지붕면 할당",
|
||||
@ -152,6 +161,7 @@
|
||||
"modal.roof.alloc.select.parallel": "병렬식",
|
||||
"modal.roof.alloc.select.stairs": "계단식",
|
||||
"modal.roof.alloc.apply": "선택한 지붕재로 할당",
|
||||
"plan.menu.estimate.docDown": "문서 다운로드",
|
||||
"plan.menu.estimate.save": "저장",
|
||||
"plan.menu.estimate.reset": "초기화",
|
||||
"plan.menu.estimate.copy": "복사",
|
||||
@ -479,6 +489,19 @@
|
||||
"commons.east": "동",
|
||||
"commons.south": "남",
|
||||
"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.sub_name": "태양광 발전 시스템 도면관리 사이트",
|
||||
"board.notice.title": "공지사항",
|
||||
@ -789,18 +812,18 @@
|
||||
"surface.shape.validate.size.4to5": "④길이는 ⑤보다 큰 값을 넣어주세요.",
|
||||
"estimate.detail.header.title": "기본정보",
|
||||
"estimate.detail.objectNo": "물건번호",
|
||||
"estimate.detail.estimateNo": "견적서 번호",
|
||||
"estimate.detail.createDatetime": "등록일",
|
||||
"estimate.detail.docNo": "견적서 번호",
|
||||
"estimate.detail.drawingEstimateCreateDate": "등록일",
|
||||
"estimate.detail.lastEditDatetime": "변경일시",
|
||||
"estimate.detail.saleStoreId": "1차 판매점명",
|
||||
"estimate.detail.estimateDate": "견적일",
|
||||
"estimate.detail.otherSaleStoreId": "2차 판매점명",
|
||||
"estimate.detail.receiveUser": "담당자",
|
||||
"estimate.detail.title": "안건명",
|
||||
"estimate.detail.remarks": "메모",
|
||||
"estimate.detail.orderType": "주문분류",
|
||||
"estimate.detail.objectName": "안건명",
|
||||
"estimate.detail.objectRemarks": "메모",
|
||||
"estimate.detail.estimateType": "주문분류",
|
||||
"estimate.detail.roofCns": "지붕재・사양시공",
|
||||
"estimate.detail.note": "비고",
|
||||
"estimate.detail.remarks": "비고",
|
||||
"estimate.detail.nextSubmit": "후일자료제출",
|
||||
"estimate.detail.header.fileList1": "파일첨부",
|
||||
"estimate.detail.fileList.btn": "파일선택",
|
||||
@ -819,7 +842,10 @@
|
||||
"estimate.detail.sepcialEstimateProductInfo.calcFormula2": "PKG단가(W) * PKG용량(W)",
|
||||
"estimate.detail.header.showPrice": "가격표시",
|
||||
"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.btn3": "제품삭제"
|
||||
}
|
||||
|
||||
@ -7,3 +7,9 @@ export const floorPlanObjectState = atom({
|
||||
},
|
||||
dangerouslyAllowMutability: true,
|
||||
})
|
||||
|
||||
export const estimateState = atom({
|
||||
key: `estimateState`,
|
||||
default: {},
|
||||
dangerouslyAllowMutability: true,
|
||||
})
|
||||
|
||||
@ -7,7 +7,8 @@ import { atom } from 'recoil'
|
||||
export const popupState = atom({
|
||||
key: 'popupState',
|
||||
default: {
|
||||
children: [],
|
||||
config: [],
|
||||
other: [],
|
||||
},
|
||||
dangerouslyAllowMutability: true,
|
||||
})
|
||||
|
||||
@ -5,7 +5,7 @@ export const settingModalFirstOptionsState = atom({
|
||||
default: {
|
||||
option1: [
|
||||
{ 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: 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 },
|
||||
|
||||
@ -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{
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
border: 1px solid #ECF0F4;
|
||||
border-radius: 3px;
|
||||
margin-bottom: 30px;
|
||||
.special-note-check-item{
|
||||
padding: 14px 10px;
|
||||
border-right: 1px solid #ECF0F4;
|
||||
border-top: 1px solid #ECF0F4;
|
||||
&:nth-child(5n){
|
||||
border-right: none;
|
||||
}
|
||||
&:nth-child(-n+5){
|
||||
border-top: none;
|
||||
}
|
||||
border: 1px solid #ECF0F4;
|
||||
margin-top: -1px;
|
||||
margin-right: -1px;
|
||||
&.act{
|
||||
background-color: #F7F9FA;
|
||||
}
|
||||
@ -1312,6 +1339,7 @@
|
||||
font-weight: 400;
|
||||
color: #999999;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
flex: 1 ;
|
||||
background-color: inherit;
|
||||
|
||||
@ -239,6 +239,7 @@ footer{
|
||||
nav{
|
||||
.nav-list{
|
||||
.nav-item{
|
||||
a,
|
||||
button{
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
@ -37,9 +37,10 @@
|
||||
top: 50%;
|
||||
left: 0;
|
||||
transform: translateY(-50%);
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
background: url(../../public/static/images/main/id_icon.svg)no-repeat center;
|
||||
background-size: cover;
|
||||
}
|
||||
}
|
||||
.store-arr{
|
||||
|
||||
@ -391,19 +391,22 @@ $alert-color: #101010;
|
||||
table-layout: fixed;
|
||||
tr{
|
||||
th{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: $pop-normal-size;
|
||||
color: $pop-color;
|
||||
font-weight: $pop-bold-weight;
|
||||
padding: 18px 0;
|
||||
border-bottom: 1px solid #424242;
|
||||
vertical-align: middle;
|
||||
.tip-wrap{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
td{
|
||||
font-size: $pop-normal-size;
|
||||
color: $pop-color;
|
||||
border-bottom: 1px solid #424242;
|
||||
padding-left: 20px;
|
||||
padding: 18px 0 18px 20px;
|
||||
vertical-align: middle;
|
||||
.flex-box{
|
||||
display: flex;
|
||||
@ -479,6 +482,7 @@ $alert-color: #101010;
|
||||
color: $pop-color;
|
||||
font-weight: $pop-normal-weight;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.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{
|
||||
padding: 24px 0;
|
||||
@ -1136,6 +1157,10 @@ $alert-color: #101010;
|
||||
.object-direction-wrap{
|
||||
flex: 1;
|
||||
}
|
||||
&:first-child{
|
||||
flex: none;
|
||||
width: 195px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1208,6 +1233,7 @@ $alert-color: #101010;
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
background-color: #fff;
|
||||
border-radius: 50%;
|
||||
}
|
||||
i{
|
||||
color: #fff;
|
||||
@ -1232,6 +1258,9 @@ $alert-color: #101010;
|
||||
}
|
||||
}
|
||||
|
||||
.draw-flow-wrap{
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
// 지붕모듈선택
|
||||
.roof-module-tab{
|
||||
|
||||
@ -263,12 +263,12 @@ export const getDegreeByChon = (chon) => {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 각도를 입력받아 촌을 반환
|
||||
* @param degree
|
||||
* @returns {number}
|
||||
*/
|
||||
export const getChonByDegree = (degree) => {
|
||||
// tan(theta) = height / base
|
||||
const radians = (degree * Math.PI) / 180
|
||||
return Number(Number(Math.tan(radians) * 10).toFixed(2))
|
||||
return Number((Math.tan((degree * Math.PI) / 180) * 10).toFixed(2))
|
||||
}
|
||||
|
||||
/**
|
||||
@ -521,9 +521,11 @@ export function isPointOnLine(line, point) {
|
||||
const a = line.y2 - line.y1
|
||||
const b = line.x1 - line.x2
|
||||
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 찾기
|
||||
* @param point
|
||||
@ -766,9 +768,9 @@ export const triangleToPolygon = (triangle) => {
|
||||
const halfWidth = triangle.width / 2
|
||||
const height = triangle.height
|
||||
|
||||
points.push({ x: triangle.left + halfWidth, y: triangle.top })
|
||||
points.push({ x: triangle.left, y: triangle.top + height })
|
||||
points.push({ x: triangle.left + triangle.width, y: triangle.top + height })
|
||||
points.push({ x: triangle.left, y: triangle.top })
|
||||
points.push({ x: triangle.left - halfWidth, y: triangle.top + height })
|
||||
points.push({ x: triangle.left + halfWidth, y: triangle.top + height })
|
||||
|
||||
return points
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user