Merge branch 'dev' into dev-yj
# Conflicts: # src/components/floor-plan/CanvasFrame.jsx
This commit is contained in:
commit
08b9ed5e0d
9
src/app/floor-plan/[mid]/[pid]/page.jsx
Normal file
9
src/app/floor-plan/[mid]/[pid]/page.jsx
Normal file
@ -0,0 +1,9 @@
|
||||
import Estimate from '@/components/estimate/Estimate'
|
||||
|
||||
export default function EstimatePage() {
|
||||
return (
|
||||
<>
|
||||
<Estimate />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -1,11 +1,17 @@
|
||||
'use client'
|
||||
import FloorPlan from '@/components/floor-plan/FloorPlan'
|
||||
import { FloorPlanProvider } from './FloorPlanProvider'
|
||||
import CanvasLayout from '@/components/floor-plan/CanvasLayout'
|
||||
|
||||
export default function FloorPlanLayout({ children }) {
|
||||
console.log('FloorPlanLayout')
|
||||
return (
|
||||
<>
|
||||
<FloorPlanProvider>{children}</FloorPlanProvider>
|
||||
<FloorPlanProvider>
|
||||
<FloorPlan>
|
||||
<CanvasLayout>{children}</CanvasLayout>
|
||||
</FloorPlan>
|
||||
</FloorPlanProvider>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
import CanvasFrame from '@/components/floor-plan/CanvasFrame'
|
||||
import FloorPlan from '@/components/floor-plan/FloorPlan'
|
||||
|
||||
export default function FloorPlanPage() {
|
||||
return (
|
||||
<>
|
||||
<FloorPlan />
|
||||
<CanvasFrame />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
307
src/components/estimate/Estimate.jsx
Normal file
307
src/components/estimate/Estimate.jsx
Normal file
@ -0,0 +1,307 @@
|
||||
'use client'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import SingleDatePicker from '../common/datepicker/SingleDatePicker'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { floorPlanObjectState } from '@/store/floorPlanObjectAtom'
|
||||
|
||||
export default function Estimate() {
|
||||
const { getMessage } = useMessage()
|
||||
const objectRecoil = useRecoilValue(floorPlanObjectState)
|
||||
console.log('견적서화면이군요', objectRecoil.floorPlanObjectNo)
|
||||
return (
|
||||
<div className="sub-content estimate">
|
||||
<div className="sub-content-inner">
|
||||
{/* 물건번호, 견적서번호, 등록일, 변경일시 시작 */}
|
||||
<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">RX524231020006 (Plan No: 1)</div>
|
||||
</div>
|
||||
<div className="estimate-box">
|
||||
<div className="estimate-tit">{getMessage('estimate.detail.estimateNo')}</div>
|
||||
<div className="estimate-name">5242310200065242</div>
|
||||
</div>
|
||||
<div className="estimate-box">
|
||||
<div className="estimate-tit">{getMessage('estimate.detail.createDatetime')}</div>
|
||||
<div className="estimate-name">9999.09.28</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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* 물건번호, 견적서번호, 등록일, 변경일시 끝 */}
|
||||
{/* 기본정보 시작 */}
|
||||
<div className="sub-content-box">
|
||||
<div className="sub-table-box">
|
||||
<div className="table-box-title-wrap">
|
||||
<div className="title-wrap">
|
||||
<h3>{getMessage('estimate.detail.header.title')}</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div className="common-table bt-able">
|
||||
<table>
|
||||
<colgroup>
|
||||
<col style={{ width: '160px' }} />
|
||||
<col />
|
||||
<col style={{ width: '160px' }} />
|
||||
<col />
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
{/* 1차 판매점명 */}
|
||||
<th>{getMessage('estimate.detail.saleStoreId')}</th>
|
||||
<td></td>
|
||||
{/* 견적일 */}
|
||||
<th>
|
||||
{getMessage('estimate.detail.estimateDate')} <span className="important">*</span>
|
||||
</th>
|
||||
<td>
|
||||
<div className="date-picker" style={{ width: '350px' }}>
|
||||
<SingleDatePicker />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
{/* 2차 판매점명 */}
|
||||
<th>{getMessage('estimate.detail.otherSaleStoreId')}</th>
|
||||
<td></td>
|
||||
{/* 담당자 */}
|
||||
<th>
|
||||
{getMessage('estimate.detail.receiveUser')} <span className="important">*</span>
|
||||
</th>
|
||||
<td>
|
||||
<div className="input-wrap" style={{ width: '350px' }}>
|
||||
<input type="text" className="input-light" defaultValue={'물건정보에서 입력한 담당자명 표시'} />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
{/* 안건명 */}
|
||||
<th>
|
||||
{getMessage('estimate.detail.title')} <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={'안건명:::'} />
|
||||
</div>
|
||||
<div className="input-wrap" style={{ width: '200px' }}>
|
||||
<input type="text" className="input-light" defaultValue={'경칭?'} />
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
{/* 메모 */}
|
||||
<th>{getMessage('estimate.detail.remarks')}</th>
|
||||
<td colSpan={3}>물건정보에서 입력한 메모 표시</td>
|
||||
</tr>
|
||||
<tr>
|
||||
{/* 주문분류 */}
|
||||
<th>
|
||||
注文分類/주문분류 <span className="important">*</span>
|
||||
</th>
|
||||
<td colSpan={3}>
|
||||
<div className="radio-wrap"></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
{/* 지붕재・사양시공 최대4개*/}
|
||||
<th>屋根材・仕様施工 / 지붕재・사양시공</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>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
{/* 비고 */}
|
||||
<th>備考 /비고</th>
|
||||
<td colSpan={3}>
|
||||
<div className="input-wrap">
|
||||
<input type="text" className="input-light" />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{/* 파일첨부 시작 */}
|
||||
<div className="table-box-title-wrap">
|
||||
<div className="title-wrap">
|
||||
<h3>{getMessage('estimate.detail.header.fileList1')}</h3>
|
||||
<div className="d-check-box light mr5">
|
||||
<input type="checkbox" id="" />
|
||||
<label htmlFor="" style={{ color: '#101010' }}>
|
||||
後日資料提出 / 후일자료제출
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="common-table mb10">
|
||||
<table>
|
||||
<colgroup>
|
||||
<col style={{ width: '160px' }} />
|
||||
<col />
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>{getMessage('estimate.detail.header.fileList1')}</th>
|
||||
<td>
|
||||
<div className="drag-file-box">
|
||||
<div className="btn-area">
|
||||
<button className="btn-origin grey">{getMessage('estimate.detail.fileList.btn')}</button>
|
||||
</div>
|
||||
<div className="drag-file-area">
|
||||
<p>Drag file here</p>
|
||||
<ul className="file-list"></ul>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{/* 첨부파일 목록 시작 */}
|
||||
<div className="common-table bt-able">
|
||||
<table>
|
||||
<colgroup>
|
||||
<col style={{ width: '160px' }} />
|
||||
<col />
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>{getMessage('estimate.detail.header.fileList2')}</th>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{/* 첨부파일 목록 끝 */}
|
||||
{/* 파일첨부 끝 */}
|
||||
{/* 견적특이사항 시작 */}
|
||||
<div className="table-box-title-wrap">
|
||||
<div className="title-wrap">
|
||||
<h3 className="product">{getMessage('estimate.detail.header.specialEstimate')}</h3>
|
||||
<div className="product_tit">{getMessage('estimate.detail.header.specialEstimateProductInfo')}</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* 공통코드영역시작 */}
|
||||
<div className="special-note-check-wrap"></div>
|
||||
{/* 공통코드영역끝 */}
|
||||
{/* 견적특이사항 내용영역시작 */}
|
||||
<div className="calculation-estimate"></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="estimate-list-wrap one">
|
||||
<div className="estimate-box">
|
||||
<div className="estimate-tit">수량 (PCS)</div>
|
||||
<div className="estimate-name blue">74</div>
|
||||
</div>
|
||||
<div className="estimate-box">
|
||||
<div className="estimate-tit">용량 (Kw)</div>
|
||||
<div className="estimate-name blue">8300</div>
|
||||
</div>
|
||||
<div className="estimate-box">
|
||||
<div className="estimate-tit">공급가액</div>
|
||||
<div className="estimate-name blue">6,798,900</div>
|
||||
</div>
|
||||
<div className="estimate-box">
|
||||
<div className="estimate-tit">부가세 (10%)</div>
|
||||
<div className="estimate-name blue">679,890</div>
|
||||
</div>
|
||||
<div className="estimate-box">
|
||||
<div className="estimate-tit">총액</div>
|
||||
<div className="estimate-name red">7,478,790</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* YJOD면 아래영역 숨김 */}
|
||||
<div className="common-table bt-able">
|
||||
<table>
|
||||
<colgroup>
|
||||
<col style={{ width: '160px' }} />
|
||||
<col />
|
||||
<col style={{ width: '160px' }} />
|
||||
<col />
|
||||
<col style={{ width: '160px' }} />
|
||||
<col />
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>
|
||||
{getMessage('estimate.detail.sepcialEstimateProductInfo.pkgUnitPrice')}
|
||||
<span className="important">*</span>
|
||||
</th>
|
||||
<td>
|
||||
<div className="input-wrap">
|
||||
<input type="text" className="input-light" />
|
||||
</div>
|
||||
</td>
|
||||
<th>{getMessage('estimate.detail.sepcialEstimateProductInfo.pkgWeight')}</th>
|
||||
<td>(모듈수량 * 수량) / 100)</td>
|
||||
<th>{getMessage('estimate.detail.sepcialEstimateProductInfo.pkgPrice')}</th>
|
||||
<td>PKG단가(W) * PKG용량(W)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{/* 제품정보 끝 */}
|
||||
{/* 가격표시영역시작 */}
|
||||
<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>
|
||||
<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">
|
||||
<span className="ico"></span>
|
||||
{getMessage('estimate.detail.showPrice.description')}
|
||||
</div>
|
||||
</div>
|
||||
<div className="product-edit-btn">
|
||||
<button className="btn-origin navy mr5">
|
||||
<span className="plus"></span>
|
||||
{getMessage('estimate.detail.showPrice.btn2')}
|
||||
</button>
|
||||
<button className="btn-origin grey">
|
||||
<span className="minus"></span>
|
||||
{getMessage('estimate.detail.showPrice.btn3')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* 가격표시영역끝 */}
|
||||
{/* html테이블시작 */}
|
||||
<div className="q-grid no-cols"></div>
|
||||
{/* html테이블끝 */}
|
||||
</div>
|
||||
</div>
|
||||
{/* 기본정보끝 */}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -152,7 +152,7 @@ export const QLine = fabric.util.createClass(fabric.Line, {
|
||||
|
||||
getLength() {
|
||||
//10배 곱해진 값 return
|
||||
return Number(this.length.toFixed(2) * 10)
|
||||
return Number(this.length.toFixed(1)) * 10
|
||||
},
|
||||
|
||||
setViewLengthText(bool) {
|
||||
|
||||
@ -183,7 +183,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
||||
const end = points[(i + 1) % points.length]
|
||||
const dx = end.x - start.x
|
||||
const dy = end.y - start.y
|
||||
const length = Number(Math.sqrt(dx * dx + dy * dy).toFixed(2)) * 10
|
||||
const length = Number(Math.sqrt(dx * dx + dy * dy).toFixed(1)) * 10
|
||||
|
||||
let midPoint
|
||||
|
||||
|
||||
@ -17,21 +17,23 @@ import { useCommonUtils } from '@/hooks/common/useCommonUtils'
|
||||
import { MENU } from '@/common/common'
|
||||
import PanelBatchStatistics from '@/components/floor-plan/modal/panelBatch/PanelBatchStatistics'
|
||||
|
||||
export default function CanvasFrame({ plan }) {
|
||||
export default function CanvasFrame() {
|
||||
const canvasRef = useRef(null)
|
||||
const [modifiedPlanFlag, setModifiedPlanFlag] = useRecoilState(modifiedPlanFlagState)
|
||||
const { canvas } = useCanvas('canvas')
|
||||
const { canvasLoadInit, gridInit } = useCanvasConfigInitialize()
|
||||
const currentMenu = useRecoilValue(currentMenuState)
|
||||
const { contextMenu, handleClick, handleKeyup } = useContextMenu()
|
||||
const { checkCanvasObjectEvent, resetModifiedPlans } = usePlan()
|
||||
|
||||
const { selectedPlan, checkCanvasObjectEvent, checkUnsavedCanvasPlan, resetModifiedPlans } = usePlan()
|
||||
|
||||
useEvent()
|
||||
|
||||
const loadCanvas = () => {
|
||||
if (canvas) {
|
||||
canvas?.clear() // 캔버스를 초기화합니다.
|
||||
if (plan?.canvasStatus) {
|
||||
canvas?.loadFromJSON(JSON.parse(plan.canvasStatus), function () {
|
||||
if (selectedPlan?.canvasStatus) {
|
||||
canvas?.loadFromJSON(JSON.parse(selectedPlan.canvasStatus), function () {
|
||||
canvasLoadInit() //config된 상태로 캔버스 객체를 그린다
|
||||
canvas?.renderAll() // 캔버스를 다시 그립니다.
|
||||
})
|
||||
@ -41,15 +43,15 @@ export default function CanvasFrame({ plan }) {
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (modifiedPlanFlag && plan?.id) {
|
||||
checkCanvasObjectEvent(plan.id)
|
||||
if (modifiedPlanFlag && selectedPlan?.id) {
|
||||
checkCanvasObjectEvent(selectedPlan.id)
|
||||
}
|
||||
}, [modifiedPlanFlag])
|
||||
|
||||
useEffect(() => {
|
||||
loadCanvas()
|
||||
resetModifiedPlans()
|
||||
}, [plan, canvas])
|
||||
}, [selectedPlan, canvas])
|
||||
|
||||
return (
|
||||
<div className="canvas-frame">
|
||||
|
||||
@ -9,9 +9,11 @@ import { usePlan } from '@/hooks/usePlan'
|
||||
import { modifiedPlansState } from '@/store/canvasAtom'
|
||||
import { globalLocaleStore } from '@/store/localeAtom'
|
||||
import { SessionContext } from '@/app/SessionProvider'
|
||||
import { useCanvasMenu } from '@/hooks/common/useCanvasMenu'
|
||||
|
||||
export default function CanvasLayout(props) {
|
||||
const { menuNumber } = props
|
||||
export default function CanvasLayout({ children }) {
|
||||
// const { menuNumber } = props
|
||||
const { menuNumber } = useCanvasMenu()
|
||||
const { session } = useContext(SessionContext)
|
||||
const [objectNo, setObjectNo] = useState('test123240822001') // 이후 삭제 필요
|
||||
const [modifiedPlans, setModifiedPlans] = useRecoilState(modifiedPlansState) // 변경된 canvas plan
|
||||
@ -60,7 +62,7 @@ export default function CanvasLayout(props) {
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<CanvasFrame plan={plans.find((plan) => plan.isCurrent === true)} />
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -7,26 +7,31 @@ import { globalLocaleStore } from '@/store/localeAtom'
|
||||
import { settingModalFirstOptionsState, settingModalSecondOptionsState } from '@/store/settingAtom'
|
||||
import CanvasMenu from '@/components/floor-plan/CanvasMenu'
|
||||
import CanvasLayout from '@/components/floor-plan/CanvasLayout'
|
||||
import { useCanvasMenu } from '@/hooks/common/useCanvasMenu'
|
||||
import '@/styles/contents.scss'
|
||||
|
||||
export default function FloorPlan() {
|
||||
export default function FloorPlan({ children }) {
|
||||
const globalLocaleState = useRecoilValue(globalLocaleStore)
|
||||
const { get } = useAxios(globalLocaleState)
|
||||
const [settingModalFirstOptions, setSettingModalFirstOptions] = useRecoilState(settingModalFirstOptionsState)
|
||||
const [settingModalSecondOptions, setSettingModalSecondOptions] = useRecoilState(settingModalSecondOptionsState)
|
||||
const [objectNo, setObjectNo] = useState('test123240912001') // 이후 삭제 필요
|
||||
|
||||
const [menuNumber, setMenuNumber] = useState(null)
|
||||
// const [menuNumber, setMenuNumber] = useState(null)
|
||||
const { menuNumber, setMenuNumber } = useCanvasMenu()
|
||||
|
||||
const modalProps = {
|
||||
menuNumber,
|
||||
setMenuNumber,
|
||||
}
|
||||
useEffect(() => {
|
||||
console.log('FloorPlan useEffect 실행')
|
||||
fetchSettings()
|
||||
}, [objectNo])
|
||||
|
||||
useEffect(() => {
|
||||
setMenuNumber(1)
|
||||
}, [])
|
||||
|
||||
// Canvas Setting 조회
|
||||
const fetchSettings = async () => {
|
||||
try {
|
||||
@ -57,9 +62,7 @@ export default function FloorPlan() {
|
||||
<>
|
||||
<div className="canvas-wrap">
|
||||
<CanvasMenu {...modalProps} />
|
||||
<div className={`canvas-content ${menuNumber === 2 || menuNumber === 3 || menuNumber === 4 ? 'active' : ''}`}>
|
||||
<CanvasLayout menuNumber={menuNumber} />
|
||||
</div>
|
||||
<div className={`canvas-content ${menuNumber === 2 || menuNumber === 3 || menuNumber === 4 ? 'active' : ''}`}>{children}</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
|
||||
@ -10,15 +10,18 @@ import { globalLocaleStore } from '@/store/localeAtom'
|
||||
import { isEmptyArray, isNotEmptyArray, isObjectNotEmpty } from '@/util/common-utils'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { useForm } from 'react-hook-form'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { useRecoilValue, useSetRecoilState } from 'recoil'
|
||||
import { sessionStore } from '@/store/commonAtom'
|
||||
import FindAddressPop from './popup/FindAddressPop'
|
||||
import PlanRequestPop from './popup/PlanRequestPop'
|
||||
import WindSelectPop from './popup/WindSelectPop'
|
||||
import { useCommonCode } from '@/hooks/common/useCommonCode'
|
||||
import StuffPlanQGrid from './StuffPlanQGrid'
|
||||
import { floorPlanObjectState } from '@/store/floorPlanObjectAtom'
|
||||
|
||||
export default function StuffDetail() {
|
||||
const setFloorPlanObjectNo = useSetRecoilState(floorPlanObjectState) //견적서 화면용 물건번호리코일
|
||||
|
||||
const inputReceiveUserEl = useRef(null) //담당자ref
|
||||
const inputObjectNameEl = useRef(null) //물건명ref
|
||||
const inputZipNoEl = useRef(null) //우편번호ref
|
||||
@ -254,7 +257,9 @@ export default function StuffDetail() {
|
||||
type="button"
|
||||
className="grid-btn"
|
||||
onClick={() => {
|
||||
console.log('견적서조회버튼클릭')
|
||||
//mid:5(견적서), /pid:플랜번호
|
||||
setFloorPlanObjectNo({ floorPlanObjectNo: params.data.objectNo })
|
||||
router.push(`/floor-plan/5/${params.data.planNo}`)
|
||||
}}
|
||||
>
|
||||
<span className="file"></span>
|
||||
@ -267,7 +272,8 @@ export default function StuffDetail() {
|
||||
console.log('엑셀버튼클릭')
|
||||
}}
|
||||
>
|
||||
<span className="excel"></span>Excel
|
||||
<span className="excel"></span>
|
||||
{getMessage('stuff.detail.planGrid.btn2')}
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
@ -1077,6 +1083,7 @@ export default function StuffDetail() {
|
||||
//상세화면으로 전환
|
||||
if (res.status === 201) {
|
||||
alert(getMessage('stuff.detail.save'))
|
||||
setFloorPlanObjectNo({ floorPlanObjectNo: objectNo })
|
||||
router.push(`/management/stuff/detail?objectNo=${res.data.objectNo.toString()}`)
|
||||
}
|
||||
})
|
||||
@ -1085,6 +1092,7 @@ export default function StuffDetail() {
|
||||
await promisePut({ url: apiUrl, data: params }).then((res) => {
|
||||
if (res.status === 201) {
|
||||
alert(getMessage('stuff.detail.save'))
|
||||
setFloorPlanObjectNo({ floorPlanObjectNo: objectNo })
|
||||
router.refresh()
|
||||
}
|
||||
})
|
||||
@ -1153,7 +1161,8 @@ export default function StuffDetail() {
|
||||
alert(getMessage('stuff.detail.delete.message1'))
|
||||
} else {
|
||||
if (confirm(getMessage('common.message.data.delete'))) {
|
||||
del({ url: `/api/object/${objectNo}` }).then((res) => {
|
||||
del({ url: `/api/object/${objectNo}` }).then(() => {
|
||||
setFloorPlanObjectNo({ floorPlanObjectNo: '' })
|
||||
router.push('/management/stuff')
|
||||
})
|
||||
}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { useState, useMemo, useCallback, useEffect } from 'react'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { AgGridReact } from 'ag-grid-react'
|
||||
import 'ag-grid-community/styles/ag-grid.css'
|
||||
import 'ag-grid-community/styles/ag-theme-quartz.css'
|
||||
@ -8,7 +9,7 @@ export default function StuffPlanQGrid(props) {
|
||||
const [rowData, setRowData] = useState(null)
|
||||
// const [gridApi, setGridApi] = useState(null)
|
||||
const [colDefs, setColDefs] = useState(planGridColumns)
|
||||
|
||||
const { getMessage } = useMessage()
|
||||
const defaultColDef = useMemo(() => {
|
||||
return {
|
||||
flex: 1,
|
||||
@ -46,6 +47,7 @@ export default function StuffPlanQGrid(props) {
|
||||
pagination={isPageable}
|
||||
domLayout="autoHeight"
|
||||
suppressCellFocus={true}
|
||||
overlayNoRowsTemplate={`<span className="ag-overlay-loading-center">${getMessage('stuff.grid.noData')}</span>`}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -5,14 +5,15 @@ import Link from 'next/link'
|
||||
import Image from 'next/image'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { useRouter, useSearchParams } from 'next/navigation'
|
||||
import { stuffSearchState } from '@/store/stuffAtom'
|
||||
import { floorPlanObjectState } from '@/store/floorPlanObjectAtom'
|
||||
import { useSetRecoilState } from 'recoil'
|
||||
|
||||
export default function StuffSubHeader({ type }) {
|
||||
const { getMessage } = useMessage()
|
||||
const router = useRouter()
|
||||
|
||||
const setSchObjectNo = useSetRecoilState(stuffSearchState)
|
||||
const setFloorPlanObjectNo = useSetRecoilState(floorPlanObjectState)
|
||||
|
||||
useEffect(() => {
|
||||
window.scrollTo(0, 0)
|
||||
}, [])
|
||||
@ -22,7 +23,7 @@ export default function StuffSubHeader({ type }) {
|
||||
|
||||
// url에 물건번호로 도면작성화면으로 이동
|
||||
const moveFloorPlan = () => {
|
||||
setSchObjectNo(objectNo)
|
||||
setFloorPlanObjectNo({ floorPlanObjectNo: objectNo })
|
||||
|
||||
router.push('/floor-plan')
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { useEffect } from 'react'
|
||||
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
|
||||
import { basicSettingState, roofDisplaySelector, settingModalFirstOptionsState } from '@/store/settingAtom'
|
||||
import { canvasState, dotLineGridSettingState, pitchText, pitchTextSelector } from '@/store/canvasAtom'
|
||||
import { canvasState, dotLineGridSettingState, pitchText, pitchTextSelector, showAngleUnitSelector } from '@/store/canvasAtom'
|
||||
import { getChonByDegree, getDegreeByChon, setSurfaceShapePattern } from '@/util/canvas-util'
|
||||
import { useFont } from '@/hooks/common/useFont'
|
||||
import { useGrid } from '@/hooks/common/useGrid'
|
||||
@ -16,6 +16,7 @@ export function useCanvasConfigInitialize() {
|
||||
const setGlobalFonts = useSetRecoilState(globalFontAtom)
|
||||
const setDotLineGridSetting = useSetRecoilState(dotLineGridSettingState)
|
||||
const pitchText = useRecoilValue(pitchTextSelector)
|
||||
const angleUnit = useRecoilValue(showAngleUnitSelector)
|
||||
const {} = useFont()
|
||||
const {} = useGrid()
|
||||
const {} = useRoof()
|
||||
@ -33,15 +34,22 @@ export function useCanvasConfigInitialize() {
|
||||
|
||||
useEffect(() => {
|
||||
if (!canvas) return
|
||||
const texts = canvas.getObjects().filter((obj) => obj.name === 'pitchText' || obj.name === 'flowText')
|
||||
const offsetTexts = canvas.getObjects().filter((obj) => obj.name === 'pitchText')
|
||||
const flowTexts = canvas.getObjects().filter((obj) => obj.name === 'flowText')
|
||||
if (basicSetting.roofAngleSet === 'slope') {
|
||||
texts.forEach((obj) => {
|
||||
offsetTexts.forEach((obj) => {
|
||||
obj.set({ text: `${obj.originText}-∠${obj.pitch}${angleUnit}` })
|
||||
})
|
||||
flowTexts.forEach((obj) => {
|
||||
obj.set({ text: `${obj.originText}-∠${obj.pitch}${pitchText}` })
|
||||
})
|
||||
}
|
||||
|
||||
if (basicSetting.roofAngleSet === 'flat') {
|
||||
texts.forEach((obj) => {
|
||||
offsetTexts.forEach((obj) => {
|
||||
obj.set({ text: `${obj.originText}-∠${getDegreeByChon(obj.pitch)}${angleUnit}` })
|
||||
})
|
||||
flowTexts.forEach((obj) => {
|
||||
obj.set({ text: `${obj.originText}-∠${getDegreeByChon(obj.pitch)}${pitchText}` })
|
||||
})
|
||||
}
|
||||
|
||||
11
src/hooks/common/useCanvasMenu.js
Normal file
11
src/hooks/common/useCanvasMenu.js
Normal file
@ -0,0 +1,11 @@
|
||||
import { menuNumberState } from '@/store/menuAtom'
|
||||
import { useRecoilState } from 'recoil'
|
||||
|
||||
export const useCanvasMenu = () => {
|
||||
const [menuNumber, setMenuNumber] = useRecoilState(menuNumberState)
|
||||
|
||||
return {
|
||||
menuNumber,
|
||||
setMenuNumber,
|
||||
}
|
||||
}
|
||||
@ -38,6 +38,8 @@ export function useRoofShapeSetting(id) {
|
||||
const setCurrentMenu = useSetRecoilState(currentMenuState)
|
||||
const outerLineFix = useRecoilValue(outerLineFixState)
|
||||
|
||||
const isFixRef = useRef(false)
|
||||
|
||||
const pitchRef = useRef(null)
|
||||
const jerkinHeadPitchRef = useRef(null)
|
||||
|
||||
@ -60,6 +62,10 @@ export function useRoofShapeSetting(id) {
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (!isFixRef.current) {
|
||||
return
|
||||
}
|
||||
|
||||
const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
|
||||
const pitchTexts = canvas.getObjects().filter((obj) => obj.name === 'pitchText')
|
||||
canvas.remove(...pitchTexts)
|
||||
@ -83,7 +89,6 @@ export function useRoofShapeSetting(id) {
|
||||
})
|
||||
|
||||
addPitchText(line)
|
||||
line.setViewLengthText(false)
|
||||
}
|
||||
})
|
||||
canvas.renderAll()
|
||||
@ -388,6 +393,7 @@ export function useRoofShapeSetting(id) {
|
||||
canvas?.renderAll()
|
||||
roof.drawHelpLine()
|
||||
// setShowRoofShapeSettingModal(false)
|
||||
isFixRef.current = true
|
||||
closePopup(id)
|
||||
}
|
||||
|
||||
|
||||
@ -11,6 +11,7 @@ import {
|
||||
modifiedPlanFlagState,
|
||||
} from '@/store/canvasAtom'
|
||||
import { QPolygon } from '@/components/fabric/QPolygon'
|
||||
import { fontSelector } from '@/store/fontAtom'
|
||||
|
||||
// 캔버스에 필요한 이벤트
|
||||
export function useCanvasEvent() {
|
||||
@ -22,6 +23,7 @@ export function useCanvasEvent() {
|
||||
const fontFamily = useRecoilValue(fontFamilyState)
|
||||
const [canvasZoom, setCanvasZoom] = useRecoilState(canvasZoomState)
|
||||
const [modifiedPlanFlag, setModifiedPlanFlag] = useRecoilState(modifiedPlanFlagState)
|
||||
const lengthTextOption = useRecoilValue(fontSelector('lengthText'))
|
||||
|
||||
// 기본적인 이벤트 필요시 추가
|
||||
const attachDefaultEventOnCanvas = () => {
|
||||
@ -108,16 +110,17 @@ export function useCanvasEvent() {
|
||||
})
|
||||
}
|
||||
|
||||
if (target.type.toLowerCase().includes('text')) {
|
||||
target.set({ fontSize })
|
||||
target.set({ fontFamily })
|
||||
}
|
||||
|
||||
if (target.name === 'lengthText') {
|
||||
if (target.name === 'lengthText' && target.type.toLowerCase().includes('text') > 0) {
|
||||
const x = target.left
|
||||
const y = target.top
|
||||
target.lockMovementX = false
|
||||
target.lockMovementY = false
|
||||
|
||||
target.fill = lengthTextOption.fontColor.value
|
||||
target.fontFamily = lengthTextOption.fontFamily.value
|
||||
target.fontSize = lengthTextOption.fontSize.value
|
||||
target.fontWeight = lengthTextOption.fontWeight.value
|
||||
|
||||
// Add a property to store the previous value
|
||||
const previousValue = target.text
|
||||
target.on('selected', (e) => {
|
||||
|
||||
@ -1,5 +1,13 @@
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { ANGLE_TYPE, canvasState, currentAngleTypeSelector, fontFamilyState, fontSizeState } from '@/store/canvasAtom'
|
||||
import {
|
||||
ANGLE_TYPE,
|
||||
canvasState,
|
||||
currentAngleTypeSelector,
|
||||
fontFamilyState,
|
||||
fontSizeState,
|
||||
pitchTextSelector,
|
||||
showAngleUnitSelector,
|
||||
} from '@/store/canvasAtom'
|
||||
import { QLine } from '@/components/fabric/QLine'
|
||||
import { getChonByDegree, getDegreeByChon } from '@/util/canvas-util'
|
||||
|
||||
@ -8,6 +16,8 @@ export const useLine = () => {
|
||||
const fontSize = useRecoilValue(fontSizeState)
|
||||
const fontFamily = useRecoilValue(fontFamilyState)
|
||||
const currentAngleType = useRecoilValue(currentAngleTypeSelector)
|
||||
const pitchText = useRecoilValue(pitchTextSelector)
|
||||
const angleUnit = useRecoilValue(showAngleUnitSelector)
|
||||
|
||||
const addLine = (points = [], options) => {
|
||||
const line = new QLine(points, {
|
||||
@ -81,8 +91,8 @@ export const useLine = () => {
|
||||
|
||||
const textStr =
|
||||
currentAngleType === ANGLE_TYPE.SLOPE
|
||||
? `${attributes.offset ? attributes.offset * 10 : attributes.width * 10}${attributes.pitch ? '-∠' + attributes.pitch : ''}`
|
||||
: `${attributes.offset ? attributes.offset * 10 : attributes.width * 10}${attributes.pitch ? '-∠' + getDegreeByChon(attributes.pitch) : ''}`
|
||||
? `${attributes.offset ? attributes.offset * 10 : attributes.width * 10}${attributes.pitch ? '-∠' + attributes.pitch + angleUnit : ''}`
|
||||
: `${attributes.offset ? attributes.offset * 10 : attributes.width * 10}${attributes.pitch ? '-∠' + getDegreeByChon(attributes.pitch) + angleUnit : ''}`
|
||||
|
||||
if (direction === 'top') {
|
||||
left = (startPoint.x + endPoint.x) / 2
|
||||
|
||||
@ -8,6 +8,7 @@ import { useSwal } from '@/hooks/useSwal'
|
||||
|
||||
export function usePlan() {
|
||||
const [planNum, setPlanNum] = useState(0)
|
||||
const [selectedPlan, setSelectedPlan] = useState(null)
|
||||
|
||||
const [canvas, setCanvas] = useRecoilState(canvasState)
|
||||
const [currentCanvasPlan, setCurrentCanvasPlan] = useRecoilState(currentCanvasPlanState)
|
||||
@ -65,6 +66,7 @@ export function usePlan() {
|
||||
'text',
|
||||
'pitch',
|
||||
'uuid',
|
||||
'originText',
|
||||
])
|
||||
|
||||
const str = JSON.stringify(objs)
|
||||
@ -144,7 +146,7 @@ export function usePlan() {
|
||||
* DB에 저장된 데이터를 canvas에서 사용할 수 있도록 포맷화
|
||||
*/
|
||||
const dbToCanvasFormat = (cs) => {
|
||||
return cs.replace(/##/g, '"').replace(/∠/g, '∠')
|
||||
return cs.replace(/##/g, '"').replace(/∠/g, '∠').replace(/°/g, '°')
|
||||
}
|
||||
|
||||
/**
|
||||
@ -278,6 +280,7 @@ export function usePlan() {
|
||||
}
|
||||
useEffect(() => {
|
||||
setCurrentCanvasPlan(plans.find((plan) => plan.isCurrent) || null)
|
||||
setSelectedPlan(plans.find((plan) => plan.isCurrent))
|
||||
}, [plans])
|
||||
|
||||
/**
|
||||
@ -365,6 +368,7 @@ export function usePlan() {
|
||||
return {
|
||||
canvas,
|
||||
plans,
|
||||
selectedPlan,
|
||||
modifiedPlans,
|
||||
checkCanvasObjectEvent,
|
||||
resetModifiedPlans,
|
||||
|
||||
@ -707,5 +707,29 @@
|
||||
"surface.shape.validate.size.1to23": "①길이는 ②+③보다 큰 값을 넣어주세요.",
|
||||
"surface.shape.validate.size.2to3": "②길이는 ③보다 큰 값을 넣어주세요.",
|
||||
"surface.shape.validate.size.3to4": "③길이는 ④보다 큰 값을 넣어주세요.",
|
||||
"surface.shape.validate.size.4to5": "④길이는 ⑤보다 큰 값을 넣어주세요."
|
||||
"surface.shape.validate.size.4to5": "④길이는 ⑤보다 큰 값을 넣어주세요.",
|
||||
"estimate.detail.header.title": "基本情報",
|
||||
"estimate.detail.objectNo": "品番",
|
||||
"estimate.detail.estimateNo": "見積書番号",
|
||||
"estimate.detail.createDatetime": "登録日",
|
||||
"estimate.detail.lastEditDatetime": "変更日時",
|
||||
"estimate.detail.saleStoreId": "一次販売店名",
|
||||
"estimate.detail.estimateDate": "見積日",
|
||||
"estimate.detail.otherSaleStoreId": "二次販売店名",
|
||||
"estimate.detail.receiveUser": "担当者 ",
|
||||
"estimate.detail.title": "案件名",
|
||||
"estimate.detail.remarks": "メモ",
|
||||
"estimate.detail.header.fileList1": "ファイル添付",
|
||||
"estimate.detail.fileList.btn": "ファイル選択",
|
||||
"estimate.detail.header.fileList2": "添付ファイル一覧",
|
||||
"estimate.detail.header.specialEstimate": "見積もりの具体的な",
|
||||
"estimate.detail.header.specialEstimateProductInfo": "製品情報",
|
||||
"estimate.detail.sepcialEstimateProductInfo.pkgUnitPrice": "住宅PKG単価 (W)",
|
||||
"estimate.detail.sepcialEstimateProductInfo.pkgWeight": "PKG容量 (Kw)",
|
||||
"estimate.detail.sepcialEstimateProductInfo.pkgPrice": "PKG金額",
|
||||
"estimate.detail.header.showPrice": "価格表示",
|
||||
"estimate.detail.showPrice.btn1": "Pricing",
|
||||
"estimate.detail.showPrice.description": "クリックして製品の特異性を確認する",
|
||||
"estimate.detail.showPrice.btn2": "製品を追加",
|
||||
"estimate.detail.showPrice.btn3": "製品削除"
|
||||
}
|
||||
|
||||
@ -712,5 +712,29 @@
|
||||
"surface.shape.validate.size.1to23": "①길이는 ②+③보다 큰 값을 넣어주세요.",
|
||||
"surface.shape.validate.size.2to3": "②길이는 ③보다 큰 값을 넣어주세요.",
|
||||
"surface.shape.validate.size.3to4": "③길이는 ④보다 큰 값을 넣어주세요.",
|
||||
"surface.shape.validate.size.4to5": "④길이는 ⑤보다 큰 값을 넣어주세요."
|
||||
"surface.shape.validate.size.4to5": "④길이는 ⑤보다 큰 값을 넣어주세요.",
|
||||
"estimate.detail.header.title": "기본정보",
|
||||
"estimate.detail.objectNo": "물건번호",
|
||||
"estimate.detail.estimateNo": "견적서 번호",
|
||||
"estimate.detail.createDatetime": "등록일",
|
||||
"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.header.fileList1": "파일첨부",
|
||||
"estimate.detail.fileList.btn": "파일선택",
|
||||
"estimate.detail.header.fileList2": "첨부파일 목록",
|
||||
"estimate.detail.header.specialEstimate": "견적특이사항",
|
||||
"estimate.detail.header.specialEstimateProductInfo": "제품정보",
|
||||
"estimate.detail.sepcialEstimateProductInfo.pkgUnitPrice": "주택PKG단가 (W)",
|
||||
"estimate.detail.sepcialEstimateProductInfo.pkgWeight": "PKG 용량 (Kw)",
|
||||
"estimate.detail.sepcialEstimateProductInfo.pkgPrice": "PKG 금액",
|
||||
"estimate.detail.header.showPrice": "가격표시",
|
||||
"estimate.detail.showPrice.btn1": "Pricing",
|
||||
"estimate.detail.showPrice.description": "클릭하여 제품 특이사항 확인",
|
||||
"estimate.detail.showPrice.btn2": "제품추가",
|
||||
"estimate.detail.showPrice.btn3": "제품삭제"
|
||||
}
|
||||
|
||||
@ -353,3 +353,14 @@ export const pitchTextSelector = selector({
|
||||
return roofAngleSet === 'slope' ? '寸' : '度'
|
||||
},
|
||||
})
|
||||
|
||||
//각도 표시, offset 길이에서는 각도가 한자가 아닌 도형으로 표시되어야 한다.
|
||||
export const showAngleUnitSelector = selector({
|
||||
key: 'showAngleUnitSelector',
|
||||
get: ({ get }) => {
|
||||
const basicSettingStateValue = get(basicSettingState)
|
||||
const roofAngleSet = basicSettingStateValue.roofAngleSet
|
||||
|
||||
return roofAngleSet === 'slope' ? '寸' : '°'
|
||||
},
|
||||
})
|
||||
|
||||
9
src/store/floorPlanObjectAtom.js
Normal file
9
src/store/floorPlanObjectAtom.js
Normal file
@ -0,0 +1,9 @@
|
||||
import { atom } from 'recoil'
|
||||
import { v1 } from 'uuid'
|
||||
export const floorPlanObjectState = atom({
|
||||
key: `floorPlanObjectState/${v1()}`,
|
||||
default: {
|
||||
objectNo: '', //물건번호
|
||||
},
|
||||
dangerouslyAllowMutability: true,
|
||||
})
|
||||
6
src/store/menuAtom.js
Normal file
6
src/store/menuAtom.js
Normal file
@ -0,0 +1,6 @@
|
||||
import { atom } from 'recoil'
|
||||
|
||||
export const menuNumberState = atom({
|
||||
key: 'menuNumberState',
|
||||
default: null,
|
||||
})
|
||||
Loading…
x
Reference in New Issue
Block a user