Merge branch 'dev' into dev-yj

# Conflicts:
#	src/components/floor-plan/CanvasFrame.jsx
This commit is contained in:
yjnoh 2024-10-28 16:24:05 +09:00
commit 08b9ed5e0d
23 changed files with 503 additions and 45 deletions

View File

@ -0,0 +1,9 @@
import Estimate from '@/components/estimate/Estimate'
export default function EstimatePage() {
return (
<>
<Estimate />
</>
)
}

View File

@ -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>
</>
)
}

View File

@ -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 />
</>
)
}

View 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>
)
}

View File

@ -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) {

View File

@ -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

View File

@ -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">

View File

@ -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>
)
}

View File

@ -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>
</>
)

View File

@ -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')
})
}

View File

@ -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>
)

View File

@ -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')
}

View File

@ -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}` })
})
}

View 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,
}
}

View File

@ -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)
}

View File

@ -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) => {

View File

@ -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

View File

@ -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(/&ang;/g, '∠')
return cs.replace(/##/g, '"').replace(/&ang;/g, '∠').replace(/&deg;/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,

View File

@ -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": "製品削除"
}

View File

@ -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": "제품삭제"
}

View File

@ -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' ? '寸' : '°'
},
})

View 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
View File

@ -0,0 +1,6 @@
import { atom } from 'recoil'
export const menuNumberState = atom({
key: 'menuNumberState',
default: null,
})