This commit is contained in:
minsik 2024-11-06 15:52:36 +09:00
commit 9045015a86
15 changed files with 810 additions and 10871 deletions

10656
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@ -10,7 +10,7 @@ import SingleDatePicker from '../common/datepicker/SingleDatePicker'
import EstimateFileUploader from './EstimateFileUploader' import EstimateFileUploader from './EstimateFileUploader'
import { useAxios } from '@/hooks/useAxios' import { useAxios } from '@/hooks/useAxios'
import { globalLocaleStore } from '@/store/localeAtom' import { globalLocaleStore } from '@/store/localeAtom'
import { isObjectNotEmpty } from '@/util/common-utils' import { isNotEmptyArray, isObjectNotEmpty } from '@/util/common-utils'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { useCommonCode } from '@/hooks/common/useCommonCode' import { useCommonCode } from '@/hooks/common/useCommonCode'
import Select from 'react-select' import Select from 'react-select'
@ -21,6 +21,12 @@ export default function Estimate({ params }) {
const [planNo, setPlanNo] = useState('') // const [planNo, setPlanNo] = useState('') //
const [files, setFiles] = useState([]) // const [files, setFiles] = useState([]) //
//
const [checkItems, setCheckItems] = useState(new Set())
const [checkedList, setCheckedList] = useState([])
const [showContentCode, setShowContentCode] = useState('ATTR001')
// //
const [hidden, setHidden] = useState(false) const [hidden, setHidden] = useState(false)
@ -40,6 +46,11 @@ export default function Estimate({ params }) {
// //
const { state, setState } = useEstimateController(params.pid) const { state, setState } = useEstimateController(params.pid)
// LIST
// List
const [specialNoteList, setSpecialNoteList] = useState([])
const globalLocaleState = useRecoilValue(globalLocaleStore) const globalLocaleState = useRecoilValue(globalLocaleStore)
const { get, post } = useAxios(globalLocaleState) const { get, post } = useAxios(globalLocaleState)
@ -65,17 +76,57 @@ export default function Estimate({ params }) {
if (code1 != null) { if (code1 != null) {
setHonorificCodeList(code1) setHonorificCodeList(code1)
} }
// API
//http://localhost:8080/api/estimate/special-note-list
}, []) }, [])
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 // set
useEffect(() => { useEffect(() => {
let estimateDatej = dayjs(startDate).format('YYYY-MM-DD') let estimateDate = dayjs(startDate).format('YYYY-MM-DD')
setState({ estimateDate: estimateDatej }) setState({ estimateDate: estimateDate })
}, [startDate]) }, [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()
}
return ( return (
<div className="sub-content estimate"> <div className="sub-content estimate">
<div className="sub-content-inner"> <div className="sub-content-inner">
@ -359,17 +410,47 @@ export default function Estimate({ params }) {
{/* 견적 특이사항 코드영역시작 */} {/* 견적 특이사항 코드영역시작 */}
<div className={`estimate-check-wrap ${hidden ? 'hide' : ''}`}> <div className={`estimate-check-wrap ${hidden ? 'hide' : ''}`}>
<div className="estimate-check-inner"> <div className="estimate-check-inner">
<div className="special-note-check-wrap"></div> <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"> <div className="calculation-estimate">
<dl> {specialNoteList.map((row) => {
<dt>제목11??</dt> if (row.code === showContentCode) {
<dd>제목1 비고</dd> return (
</dl> <dl>
<dl> <dt>{row.codeNm}</dt>
<dt>제목22??</dt> <dd>{row.remarks}</dd>
<dd>제목2 비고</dd> </dl>
</dl> )
}
})}
</div> </div>
{/* 견적특이사항 선택한 내용?영역끝 */} {/* 견적특이사항 선택한 내용?영역끝 */}
</div> </div>

View File

@ -137,6 +137,22 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
}) })
}) })
this.on('polygonMoved', () => {
//폴리곤일때만 사용
let matrix = this.calcTransformMatrix()
let transformedPoints = this.get('points')
.map((p) => {
return new fabric.Point(p.x - this.pathOffset.x, p.y - this.pathOffset.y)
})
.map((p) => {
return fabric.util.transformPoint(p, matrix)
})
this.set('points', transformedPoints)
this.set('pathOffset', { x: this.left, y: this.top })
this.setCoords()
})
// polygon.fillCell({ width: 50, height: 30, padding: 10 }) // polygon.fillCell({ width: 50, height: 30, padding: 10 })
}, },
@ -211,6 +227,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
let points = this.getCurrentPoints() let points = this.getCurrentPoints()
this.texts = []
points.forEach((start, i) => { points.forEach((start, i) => {
const end = points[(i + 1) % points.length] const end = points[(i + 1) % points.length]
const dx = end.x - start.x const dx = end.x - start.x

View File

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

View File

@ -8,6 +8,8 @@ import { contextPopupPositionState } from '@/store/popupAtom'
import { useRef, useState, useEffect } from 'react' import { useRef, useState, useEffect } from 'react'
import { useObjectBatch } from '@/hooks/object/useObjectBatch' import { useObjectBatch } from '@/hooks/object/useObjectBatch'
import { useEvent } from '@/hooks/useEvent' import { useEvent } from '@/hooks/useEvent'
import { BATCH_TYPE, POLYGON_TYPE } from '@/common/common'
import { useSurfaceShapeBatch } from '@/hooks/surface/useSurfaceShapeBatch'
export default function SizeSetting(props) { export default function SizeSetting(props) {
const contextPopupPosition = useRecoilValue(contextPopupPositionState) const contextPopupPosition = useRecoilValue(contextPopupPositionState)
@ -15,8 +17,8 @@ export default function SizeSetting(props) {
const { id, pos = contextPopupPosition, target } = props const { id, pos = contextPopupPosition, target } = props
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { closePopup } = usePopup() const { closePopup } = usePopup()
const { reSizeObjectBatch } = useObjectBatch({}) const { resizeObjectBatch } = useObjectBatch({})
const { reSizePolygon } = useSurfaceShapeBatch()
const widthRef = useRef(null) const widthRef = useRef(null)
const heightRef = useRef(null) const heightRef = useRef(null)
@ -30,7 +32,15 @@ export default function SizeSetting(props) {
const width = widthRef.current.value const width = widthRef.current.value
const height = heightRef.current.value const height = heightRef.current.value
reSizeObjectBatch(settingTarget, target, width, height) if (
target.name === BATCH_TYPE.OPENING ||
target.name === BATCH_TYPE.SHADOW ||
target.name === BATCH_TYPE.TRIANGLE_DORMER ||
target.name === BATCH_TYPE.PENTAGON_DORMER ||
target.name === POLYGON_TYPE.ROOF
) {
resizeObjectBatch(settingTarget, target, width, height)
}
} }
return ( return (

View File

@ -167,68 +167,13 @@ export default function Stuff() {
} }
} }
//
// const getSelectedRowdata = (data) => {
// setSelectedRowData(data)
// setSelectedRowDataCount(data.length)
// }
//
// const fnDeleteRowData = (data) => {
// if (data.length === 0) {
// return alert(' ')
// }
// let errCount = 0
// data.forEach((cell) => {
// if (!cell.objectNo) {
// if (errCount === 0) {
// alert(' ')
// }
// errCount++
// }
// })
// }
//
// let newCount = 0
// const addRowItems = () => {
// // console.log('girdRef::::::', gridRef.current.api)
// const newItems = [
// {
// mission: newCount + 1,
// successful: true,
// },
// ]
// gridRef.current.api.applyTransaction({
// add: newItems,
// addIndex: newCount,
// })
// newCount++
// }
//
// const removeRowItems = () => {
// // console.log('selectedRowData::', selectedRowData)
// let errCount = 0
// selectedRowData.forEach((cell) => {
// if (!cell.company) {
// let newSelectedRowData = selectedRowData.filter((item) => item.company == null)
// gridRef.current.api.applyTransaction({ remove: newSelectedRowData })
// } else {
// if (errCount === 0) {
// alert(' .')
// }
// errCount++
// }
// })
// }
// //
useEffect(() => { useEffect(() => {
if (isObjectNotEmpty(session)) { if (isObjectNotEmpty(session)) {
if (stuffSearchParams?.code === 'S') { if (stuffSearchParams?.code === 'S') {
const params = { const params = {
saleStoreId: stuffSearchParams.schSelSaleStoreId, // saleStoreId: stuffSearchParams.schSelSaleStoreId,
saleStoreId: session.storeId,
schObjectNo: stuffSearchParams?.schObjectNo, schObjectNo: stuffSearchParams?.schObjectNo,
schAddress: stuffSearchParams?.schAddress, schAddress: stuffSearchParams?.schAddress,
schObjectName: stuffSearchParams?.schObjectName, schObjectName: stuffSearchParams?.schObjectName,
@ -245,7 +190,6 @@ export default function Stuff() {
: stuffSearchParams.schSelSaleStoreId, : stuffSearchParams.schSelSaleStoreId,
schSortType: stuffSearchParams.schSortType, schSortType: stuffSearchParams.schSortType,
} }
async function fetchData() { async function fetchData() {
const apiUrl = `/api/object/list?${queryStringFormatter(params)}` const apiUrl = `/api/object/list?${queryStringFormatter(params)}`
await get({ await get({
@ -297,10 +241,7 @@ export default function Stuff() {
// //
async function fetchData() { async function fetchData() {
let saleStoreId const apiUrl = `/api/object/list?saleStoreId=${session?.storeId}&${queryStringFormatter(stuffSearchParams)}`
saleStoreId = stuffSearchParams?.schSelSaleStoreId ? stuffSearchParams.schSelSaleStoreId : session?.storeId
// const apiUrl = `/api/object/list?saleStoreId=${session?.storeId}&${queryStringFormatter(stuffSearchParams)}`
const apiUrl = `/api/object/list?saleStoreId=${saleStoreId}&${queryStringFormatter(stuffSearchParams)}`
await get({ url: apiUrl }).then((res) => { await get({ url: apiUrl }).then((res) => {
if (!isEmptyArray(res)) { if (!isEmptyArray(res)) {
setGridProps({ ...gridProps, gridData: res, count: res[0].totCnt }) setGridProps({ ...gridProps, gridData: res, count: res[0].totCnt })
@ -331,10 +272,7 @@ export default function Stuff() {
}) })
setPageNo(1) setPageNo(1)
let saleStoreId const apiUrl = `/api/object/list?saleStoreId=${session?.storeId}&${queryStringFormatter(stuffSearchParams)}`
saleStoreId = stuffSearchParams?.schSelSaleStoreId ? stuffSearchParams.schSelSaleStoreId : session?.storeId
// const apiUrl = `/api/object/list?saleStoreId=${session?.storeId}&${queryStringFormatter(stuffSearchParams)}`
const apiUrl = `/api/object/list?saleStoreId=${saleStoreId}&${queryStringFormatter(stuffSearchParams)}`
get({ url: apiUrl }).then((res) => { get({ url: apiUrl }).then((res) => {
if (!isEmptyArray(res)) { if (!isEmptyArray(res)) {
setGridProps({ ...gridProps, gridData: res, count: res[0].totCnt }) setGridProps({ ...gridProps, gridData: res, count: res[0].totCnt })
@ -366,10 +304,7 @@ export default function Stuff() {
}) })
setPageNo(1) setPageNo(1)
let saleStoreId const apiUrl = `/api/object/list?saleStoreId=${session?.storeId}&${queryStringFormatter(stuffSearchParams)}`
saleStoreId = stuffSearchParams?.schSelSaleStoreId ? stuffSearchParams.schSelSaleStoreId : session?.storeId
// const apiUrl = `/api/object/list?saleStoreId=${session?.storeId}&${queryStringFormatter(stuffSearchParams)}`
const apiUrl = `/api/object/list?saleStoreId=${saleStoreId}&${queryStringFormatter(stuffSearchParams)}`
get({ url: apiUrl }).then((res) => { get({ url: apiUrl }).then((res) => {
if (!isEmptyArray(res)) { if (!isEmptyArray(res)) {
setGridProps({ ...gridProps, gridData: res, count: res[0].totCnt }) setGridProps({ ...gridProps, gridData: res, count: res[0].totCnt })

View File

@ -420,7 +420,6 @@ export default function StuffDetail() {
useEffect(() => { useEffect(() => {
if (isObjectNotEmpty(detailData)) { if (isObjectNotEmpty(detailData)) {
// console.log(':::::', detailData)
// API // API
get({ url: '/api/object/prefecture/list' }).then((res) => { get({ url: '/api/object/prefecture/list' }).then((res) => {
if (!isEmptyArray(res)) { if (!isEmptyArray(res)) {
@ -434,23 +433,18 @@ export default function StuffDetail() {
let firstList let firstList
let otherList let otherList
let favList let favList
// if (sessionState?.storeId === 'T01') {
if (session?.storeId === 'T01') { if (session?.storeId === 'T01') {
// url = `/api/object/saleStore/${sessionState?.storeId}/firstList?userId=${sessionState?.userId}`
url = `/api/object/saleStore/${session?.storeId}/firstList?userId=${session?.userId}` url = `/api/object/saleStore/${session?.storeId}/firstList?userId=${session?.userId}`
} else { } else {
// if (sessionState.storeLvl === '1') {
if (session.storeLvl === '1') { if (session.storeLvl === '1') {
// url = `/api/object/saleStore/${sessionState?.storeId}/list?firstFlg=1&userId=${sessionState?.userId}`
url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=1&userId=${session?.userId}` url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=1&userId=${session?.userId}`
} else { } else {
// url = `/api/object/saleStore/${sessionState?.storeId}/list?firstFlg=1&userId=${sessionState?.userId}`
url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=1&userId=${session?.userId}` url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=1&userId=${session?.userId}`
} }
} }
get({ url: url }).then((res) => { get({ url: url }).then((res) => {
if (!isEmptyArray(res)) { if (!isEmptyArray(res)) {
// if (sessionState?.storeId === 'T01') {
if (session?.storeId === 'T01') { if (session?.storeId === 'T01') {
firstList = res.filter((row) => row.saleStoreLevel === '1') firstList = res.filter((row) => row.saleStoreLevel === '1')
firstList.sort((a, b) => (a.saleStoreId !== 'T01') - (b.saleStoreId !== 'T01') || a.saleStoreId - b.saleStoreId) firstList.sort((a, b) => (a.saleStoreId !== 'T01') - (b.saleStoreId !== 'T01') || a.saleStoreId - b.saleStoreId)
@ -459,14 +453,19 @@ export default function StuffDetail() {
setFavoriteStoreList(favList) setFavoriteStoreList(favList)
setShowSaleStoreList(favList) setShowSaleStoreList(favList)
form.setValue('saleStoreId', firstList[0].saleStoreId) if (detailData.firstAgentId != null) {
form.setValue('saleStoreName', firstList[0].saleStoreName) form.setValue('saleStoreId', detailData.firstAgentId)
form.setValue('saleStoreLevel', firstList[0].saleStoreLevel) setSelOptions(detailData.firstAgentId)
setSelOptions(firstList[0].saleStoreId) } else {
form.setValue('saleStoreId', detailData.saleStoreId)
setSelOptions(detailData.saleStoreId)
}
// 1 2 // 1 2
// url = `/api/object/saleStore/${detailData?.saleStoreId}/list?firstFlg=0&userId=${sessionState?.userId}`
url = `/api/object/saleStore/${detailData?.saleStoreId}/list?firstFlg=0&userId=${session?.userId}` let data = detailData?.firstAgentId ? detailData.firstAgentId : detailData.saleStoreId
// url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=0&userId=${session?.userId}`
url = `/api/object/saleStore/${data}/list?firstFlg=0&userId=${session?.userId}`
get({ url: url }).then((res) => { get({ url: url }).then((res) => {
if (!isEmptyArray(res)) { if (!isEmptyArray(res)) {
@ -482,7 +481,6 @@ export default function StuffDetail() {
}) })
} else { } else {
//1 //1
// if (sessionState?.storeLvl === '1') {
if (session?.storeLvl === '1') { if (session?.storeLvl === '1') {
firstList = res firstList = res
favList = res.filter((row) => row.priority !== 'B') favList = res.filter((row) => row.priority !== 'B')
@ -572,7 +570,6 @@ export default function StuffDetail() {
form.setValue('remarks', detailData.remarks) form.setValue('remarks', detailData.remarks)
}) })
} }
// }, [detailData, sessionState])
}, [detailData, session]) }, [detailData, session])
// //
@ -1931,7 +1928,6 @@ export default function StuffDetail() {
</th> </th>
<td> <td>
<div className="flx-box"> <div className="flx-box">
{/* {sessionState?.storeId === 'T01' && ( */}
{session?.storeId === 'T01' && ( {session?.storeId === 'T01' && (
<> <>
<div className="select-wrap mr5" style={{ width: '567px' }}> <div className="select-wrap mr5" style={{ width: '567px' }}>
@ -1947,10 +1943,8 @@ export default function StuffDetail() {
onChange={onSelectionChange} onChange={onSelectionChange}
getOptionLabel={(x) => x.saleStoreName} getOptionLabel={(x) => x.saleStoreName}
getOptionValue={(x) => x.saleStoreId} getOptionValue={(x) => x.saleStoreId}
// isClearable={sessionState?.storeLvl === '1' ? true : false} isClearable={detailData.tempFlg === '0' ? false : session?.storeLvl === '1' ? true : false}
isClearable={session?.storeLvl === '1' ? true : false} isDisabled={detailData.tempFlg === '0' ? true : session?.storeLvl !== '1' ? true : false}
// isDisabled={sessionState?.storeLvl !== '1' ? true : false}
isDisabled={session?.storeLvl !== '1' ? true : false}
value={saleStoreList.filter(function (option) { value={saleStoreList.filter(function (option) {
return option.saleStoreId === selOptions return option.saleStoreId === selOptions
})} })}
@ -1967,7 +1961,6 @@ export default function StuffDetail() {
</div> </div>
</> </>
)} )}
{/* {sessionState?.storeId !== 'T01' && sessionState?.storeLvl === '1' && ( */}
{session?.storeId !== 'T01' && session?.storeLvl === '1' && ( {session?.storeId !== 'T01' && session?.storeLvl === '1' && (
<> <>
<div className="select-wrap mr5" style={{ width: '567px' }}> <div className="select-wrap mr5" style={{ width: '567px' }}>
@ -1983,8 +1976,9 @@ export default function StuffDetail() {
getOptionLabel={(x) => x.saleStoreName} getOptionLabel={(x) => x.saleStoreName}
getOptionValue={(x) => x.saleStoreId} getOptionValue={(x) => x.saleStoreId}
isClearable={false} isClearable={false}
// isDisabled={sessionState?.storeLvl !== '1' ? true : sessionState?.storeId !== 'T01' ? true : false} isDisabled={
isDisabled={session?.storeLvl !== '1' ? true : session?.storeId !== 'T01' ? true : false} detailData.tempFlg === '0' ? true : session?.storeLvl !== '1' ? true : session?.storeId !== 'T01' ? true : false
}
value={showSaleStoreList.filter(function (option) { value={showSaleStoreList.filter(function (option) {
return option.saleStoreId === selOptions return option.saleStoreId === selOptions
})} })}
@ -2001,7 +1995,6 @@ export default function StuffDetail() {
</div> </div>
</> </>
)} )}
{/* {sessionState?.storeId !== 'T01' && sessionState?.storeLvl !== '1' && ( */}
{session?.storeId !== 'T01' && session?.storeLvl !== '1' && ( {session?.storeId !== 'T01' && session?.storeLvl !== '1' && (
<> <>
<div className="select-wrap mr5" style={{ width: '567px' }}> <div className="select-wrap mr5" style={{ width: '567px' }}>
@ -2062,10 +2055,10 @@ export default function StuffDetail() {
onChange={onSelectionChange2} onChange={onSelectionChange2}
getOptionLabel={(x) => x.saleStoreName} getOptionLabel={(x) => x.saleStoreName}
getOptionValue={(x) => x.saleStoreId} getOptionValue={(x) => x.saleStoreId}
// isDisabled={sessionState?.storeLvl === '1' && form.watch('saleStoreId') != '' ? false : true} isDisabled={
isDisabled={session?.storeLvl === '1' && form.watch('saleStoreId') != '' ? false : true} detailData.tempFlg === '0' ? true : session?.storeLvl === '1' && form.watch('saleStoreId') != '' ? false : true
// isClearable={sessionState?.storeLvl === '1' ? true : false} }
isClearable={session?.storeLvl === '1' ? true : false} isClearable={detailData.tempFlg === '0' ? false : session?.storeLvl === '1' ? true : false}
value={otherSaleStoreList.filter(function (option) { value={otherSaleStoreList.filter(function (option) {
return option.saleStoreId === otherSelOptions return option.saleStoreId === otherSelOptions
})} })}

View File

@ -100,8 +100,6 @@ export function useCanvasConfigInitialize() {
const groups = canvas.getObjects().filter((obj) => obj.groupYn && obj.name === 'dimensionGroup') const groups = canvas.getObjects().filter((obj) => obj.groupYn && obj.name === 'dimensionGroup')
const groupIds = [] const groupIds = []
console.log('groupDimensionInit', groups)
groups.forEach((group) => { groups.forEach((group) => {
if (!groupIds.includes(group.id)) { if (!groupIds.includes(group.id)) {
groupIds.push(group.id) groupIds.push(group.id)
@ -157,6 +155,7 @@ export function useCanvasConfigInitialize() {
//그룹아이디로 캔버스의 객체를 조회함 //그룹아이디로 캔버스의 객체를 조회함
const groupObjects = canvas.getObjects().filter((obj) => obj.groupId === id || obj.id === 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 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 = [] let objectArray = []
@ -181,8 +180,14 @@ export function useCanvasConfigInitialize() {
lockMovementY: true, lockMovementY: true,
originX: 'center', originX: 'center',
originY: 'center', originY: 'center',
parentId: objectsParentId,
}) })
canvas.add(group) canvas.add(group)
//그룹 객체 재그룹 완료
group.getObjects().forEach((obj) => {
obj.fire('modified')
})
}) })
} }

View File

@ -16,7 +16,7 @@ import { fontSelector } from '@/store/fontAtom'
export function useObjectBatch({ isHidden, setIsHidden }) { export function useObjectBatch({ isHidden, setIsHidden }) {
const { getMessage } = useMessage() const { getMessage } = useMessage()
const canvas = useRecoilValue(canvasState) const canvas = useRecoilValue(canvasState)
const { addCanvasMouseEventListener, initEvent } = useEvent() const { addCanvasMouseEventListener, initEvent, addDocumentEventListener } = useEvent()
const { swalFire } = useSwal() const { swalFire } = useSwal()
const { drawDirectionArrow } = usePolygon() const { drawDirectionArrow } = usePolygon()
const lengthTextFont = useRecoilValue(fontSelector('lengthText')) const lengthTextFont = useRecoilValue(fontSelector('lengthText'))
@ -33,10 +33,16 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
}, []) }, [])
const dbClickEvent = () => { const dbClickEvent = () => {
console.log('dbClickEvent 실행')
const dormerObject = canvas.getObjects().filter((obj) => obj.name === BATCH_TYPE.TRIANGLE_DORMER || obj.name === BATCH_TYPE.PENTAGON_DORMER) const dormerObject = canvas.getObjects().filter((obj) => obj.name === BATCH_TYPE.TRIANGLE_DORMER || obj.name === BATCH_TYPE.PENTAGON_DORMER)
console.log('dormerObject', dormerObject)
if (dormerObject) { if (dormerObject) {
canvas.off('mouse:dblclick')
canvas.on('mouse:dblclick', (e) => { canvas.on('mouse:dblclick', (e) => {
console.log('event', e)
if (e.target && e.target instanceof fabric.Group) { if (e.target && e.target instanceof fabric.Group) {
const pointer = canvas.getPointer(e.e) const pointer = canvas.getPointer(e.e)
const objects = e.target._objects const objects = e.target._objects
@ -71,7 +77,7 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
let rect, isDown, origX, origY let rect, isDown, origX, origY
let selectedSurface let selectedSurface
//프리입력 //프리입력
console.log('useObjectBatch', isHidden)
if (selectedType === INPUT_TYPE.FREE) { if (selectedType === INPUT_TYPE.FREE) {
addCanvasMouseEventListener('mouse:down', (e) => { addCanvasMouseEventListener('mouse:down', (e) => {
isDown = true isDown = true
@ -160,7 +166,7 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
} }
isDown = false isDown = false
rect.set({ name: objName }) rect.set({ name: objName, parentId: selectedSurface.id })
rect.setCoords() rect.setCoords()
initEvent() initEvent()
@ -204,6 +210,7 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
lockRotation: true, lockRotation: true,
lockScalingX: true, lockScalingX: true,
lockScalingY: true, lockScalingY: true,
parentId: selectedSurface.id,
}) })
//개구냐 그림자냐에 따라 변경 //개구냐 그림자냐에 따라 변경
@ -241,7 +248,7 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
} }
isDown = false isDown = false
rect.set({ name: objName }) rect.set({ name: objName, parentId: selectedSurface.id })
rect.setCoords() rect.setCoords()
initEvent() initEvent()
if (setIsHidden) setIsHidden(false) if (setIsHidden) setIsHidden(false)
@ -483,6 +490,9 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
subTargetCheck: true, subTargetCheck: true,
name: dormerName, name: dormerName,
id: id, id: id,
parentId: selectedSurface.id,
originX: 'center',
originY: 'center',
}) })
canvas?.add(objectGroup) canvas?.add(objectGroup)
@ -693,6 +703,10 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
subTargetCheck: true, subTargetCheck: true,
name: dormerName, name: dormerName,
id: id, id: id,
parentId: selectedSurface.id,
groupYn: true,
originX: 'center',
originY: 'center',
}) })
canvas?.add(objectGroup) canvas?.add(objectGroup)
@ -858,9 +872,7 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
return [leftPoints, rightPoints] return [leftPoints, rightPoints]
} }
const reSizeObjectBatch = (side, target, width, height) => { const resizeObjectBatch = (side, target, width, height) => {
const targetObj = canvas.getActiveObject()
const objectWidth = target.width const objectWidth = target.width
const objectHeight = target.height const objectHeight = target.height
const changeWidth = (width / 10 / objectWidth).toFixed(2) const changeWidth = (width / 10 / objectWidth).toFixed(2)
@ -884,7 +896,6 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
const newCoords = target.getPointByOrigin(sideX, sideY) const newCoords = target.getPointByOrigin(sideX, sideY)
target.set({ target.set({
...target,
originX: sideX, originX: sideX,
originY: sideY, originY: sideY,
left: newCoords.x, left: newCoords.x,
@ -894,8 +905,8 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
target.setCoords() target.setCoords()
canvas?.renderAll() //변경 좌표를 한번 적용 canvas?.renderAll() //변경 좌표를 한번 적용
target.scaleX = changeWidth === 0 ? 1 : changeWidth target.scaleX = changeWidth || 1
target.scaleY = changeHeight === 0 ? 1 : changeHeight target.scaleY = changeHeight || 1
//크기 변경후 좌표를 재 적용 //크기 변경후 좌표를 재 적용
const changedCoords = target.getPointByOrigin('center', 'center') const changedCoords = target.getPointByOrigin('center', 'center')
@ -910,9 +921,12 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
if (target.name === 'roof') { if (target.name === 'roof') {
//얘는 일단 도머에 적용함 //얘는 일단 도머에 적용함
target._objects.forEach((obj) => { if (target.type === 'group') {
setSurfaceShapePattern(obj) target._objects.forEach((obj) => setSurfaceShapePattern(obj))
}) } else {
setSurfaceShapePattern(target)
target.fire('modified')
}
} }
// target.setCoords() // target.setCoords()
canvas.renderAll() canvas.renderAll()
@ -923,30 +937,34 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
const reGroupObject = (groupObj) => { const reGroupObject = (groupObj) => {
groupObj._restoreObjectsState() //이건 실행만 되도 그룹이 변경됨 groupObj._restoreObjectsState() //이건 실행만 되도 그룹이 변경됨
const reGroupObjects = [] const reGroupObjects = []
groupObj._objects.forEach((obj) => { groupObj._objects.forEach((obj) => {
const newObj = new QPolygon(obj.getCurrentPoints(), { const newObj = new QPolygon(obj.getCurrentPoints(), {
...obj, ...obj,
points: obj.getCurrentPoints(), points: obj.getCurrentPoints(),
scaleX: 1, scaleX: 1,
scaleY: 1, scaleY: 1,
texts: [],
}) })
reGroupObjects.push(newObj) reGroupObjects.push(newObj)
canvas.remove(obj) canvas.remove(obj)
if (obj.direction) { if (obj.direction) {
drawDirectionArrow(obj) drawDirectionArrow(obj)
} }
newObj.fire('modified')
}) })
const reGroup = new fabric.Group(reGroupObjects, { const reGroup = new fabric.Group(reGroupObjects, {
subTargetCheck: true, subTargetCheck: true,
name: groupObj.name, name: groupObj.name,
id: groupObj.id, id: groupObj.id,
groupYn: true, groupYn: true,
parentId: groupObj.parentId,
originX: 'center',
originY: 'center',
}) })
canvas?.add(reGroup) canvas?.add(reGroup)
canvas?.remove(groupObj) canvas?.remove(groupObj)
return reGroup
} }
const moveObjectBatch = () => { const moveObjectBatch = () => {
@ -965,17 +983,46 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
}) })
initEvent() initEvent()
obj.setCoords() obj.setCoords()
reGroupObject(obj) if (obj.type === 'group') reGroupObject(obj)
}) })
} }
} }
const dormerOffsetKeyEvent = (setArrow1, setArrow2) => {
addDocumentEventListener('keydown', document, (e) => {
if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
const keyEvent = e.key === 'ArrowDown' ? 'down' : 'up'
setArrow1(keyEvent)
} else if (e.key === 'ArrowLeft' || e.key === 'ArrowRight') {
const keyEvent = e.key === 'ArrowLeft' ? 'left' : 'right'
setArrow2(keyEvent)
}
})
}
const dormerOffset = (arrow1, arrow2, length1, length2) => {
length1 = parseInt(length1) / 10
length2 = parseInt(length2) / 10
const dormer = canvas.getActiveObject()
if (length1) dormer.top = arrow1 === 'down' ? dormer.top + length1 : dormer.top - length1
if (length2) dormer.left = arrow2 === 'left' ? dormer.left - length2 : dormer.left + length2
if (dormer.type === 'group') {
const newDormer = reGroupObject(dormer)
canvas?.setActiveObject(newDormer)
}
canvas.renderAll()
}
return { return {
applyOpeningAndShadow, applyOpeningAndShadow,
applyDormers, applyDormers,
splitDormerTriangle, splitDormerTriangle,
splitDormerPentagon, splitDormerPentagon,
reSizeObjectBatch, resizeObjectBatch,
moveObjectBatch, moveObjectBatch,
dormerOffsetKeyEvent,
dormerOffset,
} }
} }

View File

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

View File

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

View File

@ -33,6 +33,7 @@ import RowRemove from '@/components/floor-plan/modal/module/row/RowRemove'
import RowInsert from '@/components/floor-plan/modal/module/row/RowInsert' import RowInsert from '@/components/floor-plan/modal/module/row/RowInsert'
import CircuitNumberEdit from '@/components/floor-plan/modal/module/CircuitNumberEdit' import CircuitNumberEdit from '@/components/floor-plan/modal/module/CircuitNumberEdit'
import { useObjectBatch } from '@/hooks/object/useObjectBatch' import { useObjectBatch } from '@/hooks/object/useObjectBatch'
import { useSurfaceShapeBatch } from '@/hooks/surface/useSurfaceShapeBatch'
export function useContextMenu() { export function useContextMenu() {
const currentMenu = useRecoilValue(currentMenuState) // 현재 메뉴 const currentMenu = useRecoilValue(currentMenuState) // 현재 메뉴
@ -50,7 +51,7 @@ export function useContextMenu() {
const [column, setColumn] = useState(null) const [column, setColumn] = useState(null)
const { handleZoomClear } = useCanvasEvent() const { handleZoomClear } = useCanvasEvent()
const { moveObjectBatch } = useObjectBatch({}) const { moveObjectBatch } = useObjectBatch({})
const { moveSurfaceShapeBatch } = useSurfaceShapeBatch()
const currentMenuSetting = () => { const currentMenuSetting = () => {
switch (currentMenu) { switch (currentMenu) {
case MENU.PLAN_DRAWING: case MENU.PLAN_DRAWING:
@ -256,6 +257,7 @@ export function useContextMenu() {
}, [currentContextMenu]) }, [currentContextMenu])
useEffect(() => { useEffect(() => {
console.log('currentObject', currentObject)
if (currentObject?.name) { if (currentObject?.name) {
switch (currentObject.name) { switch (currentObject.name) {
case 'triangleDormer': case 'triangleDormer':
@ -293,7 +295,7 @@ export function useContextMenu() {
{ {
id: 'dormerOffset', id: 'dormerOffset',
name: getMessage('contextmenu.dormer.offset'), name: getMessage('contextmenu.dormer.offset'),
component: <DormerOffset id={popupId} />, component: <DormerOffset id={popupId} title={getMessage('contextmenu.dormer.offset')} />,
}, },
], ],
]) ])
@ -304,22 +306,25 @@ export function useContextMenu() {
{ {
id: 'sizeEdit', id: 'sizeEdit',
name: '사이즈 변경', name: '사이즈 변경',
component: <SizeSetting id={popupId} />, component: <SizeSetting id={popupId} target={currentObject} />,
}, },
{ {
id: 'roofMaterialRemove', id: 'roofMaterialRemove',
shortcut: ['d', 'D'], shortcut: ['d', 'D'],
name: `${getMessage('contextmenu.remove')}(D)`, name: `${getMessage('contextmenu.remove')}(D)`,
fn: () => deleteObject(),
}, },
{ {
id: 'roofMaterialMove', id: 'roofMaterialMove',
shortcut: ['m', 'M'], shortcut: ['m', 'M'],
name: `${getMessage('contextmenu.move')}(M)`, name: `${getMessage('contextmenu.move')}(M)`,
fn: () => moveSurfaceShapeBatch(),
}, },
{ {
id: 'roofMaterialCopy', id: 'roofMaterialCopy',
shortcut: ['c', 'C'], shortcut: ['c', 'C'],
name: `${getMessage('contextmenu.copy')}(C)`, name: `${getMessage('contextmenu.copy')}(C)`,
fn: () => copyObject(),
}, },
], ],
[ [
@ -370,6 +375,7 @@ export function useContextMenu() {
{ {
id: 'openingOffset', id: 'openingOffset',
name: getMessage('contextmenu.opening.offset'), name: getMessage('contextmenu.opening.offset'),
component: <DormerOffset id={popupId} title={getMessage('contextmenu.opening.offset')} />,
}, },
], ],
]) ])

View File

@ -77,11 +77,9 @@ export function usePlan() {
}) })
//디렉션이 있는 경우에만 //디렉션이 있는 경우에만
if (group.lineDirection) { if (group.lineDirection) obj.set({ lineDirection: group.lineDirection })
obj.set({ //부모객체가 있으면 (면형상 위에 도머등..)
lineDirection: group.lineDirection, if (group.parentId) obj.set({ parentId: group.parentId })
})
}
canvas?.add(obj) canvas?.add(obj)
obj.setCoords() obj.setCoords()