Merge branch 'dev' into feature/test-jy

This commit is contained in:
Jaeyoung Lee 2024-11-26 13:16:46 +09:00
commit d5d6cda1f0
22 changed files with 1417 additions and 436 deletions

View File

@ -1,10 +1,11 @@
import WithDraggable from '@/components/common/draggable/WithDraggable' import WithDraggable from '@/components/common/draggable/WithDraggable'
import QSelectBox from '@/components/common/select/QSelectBox' import QSelectBox from '@/components/common/select/QSelectBox'
import { usePopup } from '@/hooks/usePopup' import { usePopup } from '@/hooks/usePopup'
import { useState } from 'react' import { useEffect, useState } from 'react'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { useRecoilValue } from 'recoil' import { useRecoilValue } from 'recoil'
import { contextPopupPositionState } from '@/store/popupAtom' import { contextPopupPositionState } from '@/store/popupAtom'
import { useCanvasSetting } from '@/hooks/option/useCanvasSetting'
const fonts = [ const fonts = [
{ id: 1, name: 'MS PGothic', value: 'MS PGothic' }, { id: 1, name: 'MS PGothic', value: 'MS PGothic' },
@ -12,7 +13,6 @@ const fonts = [
{ id: 3, name: 'Yu Gothic', value: 'Yu Gothic' }, { id: 3, name: 'Yu Gothic', value: 'Yu Gothic' },
{ id: 4, name: '@Yu Gothic UI', value: '@Yu Gothic UI' }, { id: 4, name: '@Yu Gothic UI', value: '@Yu Gothic UI' },
{ id: 5, name: 'Yu Gothic UI', value: 'Yu Gothic UI' }, { id: 5, name: 'Yu Gothic UI', value: 'Yu Gothic UI' },
3,
] ]
const fontSizes = [ const fontSizes = [
@ -36,14 +36,11 @@ export default function FontSetting(props) {
const [selectedFontWeight, setSelectedFontWeight] = useState(font.fontWeight) const [selectedFontWeight, setSelectedFontWeight] = useState(font.fontWeight)
const [selectedFontSize, setSelectedFontSize] = useState(font.fontSize) const [selectedFontSize, setSelectedFontSize] = useState(font.fontSize)
const [selectedFontColor, setSelectedFontColor] = useState(font.fontColor) const [selectedFontColor, setSelectedFontColor] = useState(font.fontColor)
const fontOptions = [ const fontOptions = [
{ id: 'normal', name: getMessage('font.style.normal'), value: 'normal' }, { id: 'normal', name: getMessage('font.style.normal'), value: 'normal' },
{ id: 'italic', name: getMessage('font.style.italic'), value: 'italic' }, { id: 'italic', name: getMessage('font.style.italic'), value: 'italic' },
{ { id: 'bold', name: getMessage('font.style.bold'), value: 'bold' },
id: 'bold',
name: getMessage('font.style.bold'),
value: 'bold',
},
{ id: 'boldAndItalic', name: getMessage('font.style.bold.italic'), value: 'boldAndItalic' }, { id: 'boldAndItalic', name: getMessage('font.style.bold.italic'), value: 'boldAndItalic' },
] ]
const fontColors = [ const fontColors = [
@ -57,12 +54,30 @@ export default function FontSetting(props) {
{ id: 'gold', name: getMessage('color.gold'), value: 'gold' }, { id: 'gold', name: getMessage('color.gold'), value: 'gold' },
{ id: 'darkblue', name: getMessage('color.darkblue'), value: 'darkblue' }, { id: 'darkblue', name: getMessage('color.darkblue'), value: 'darkblue' },
] ]
useEffect(() => {
if (font.fontFamily) {
setSelectedFont(fonts.filter((data) => data.value === font.fontFamily)[0])
}
if (font.fontWeight) {
setSelectedFontWeight(fontOptions.filter((data) => data.value === font.fontWeight)[0])
}
if (font.fontSize) {
setSelectedFontSize(fontSizes.filter((data) => data.value === font.fontSize)[0])
}
if (font.fontColor) {
setSelectedFontColor(fontColors.filter((data) => data.value === font.fontColor)[0])
}
}, [])
const handleSaveBtn = () => { const handleSaveBtn = () => {
onSave({ onSave({
fontFamily: selectedFont, fontFamily: selectedFont,
fontWeight: selectedFontWeight,
fontSize: selectedFontSize, fontSize: selectedFontSize,
fontColor: selectedFontColor, fontColor: selectedFontColor,
fontWeight: selectedFontWeight,
}) })
if (setIsShow) setIsShow(false) if (setIsShow) setIsShow(false)
closePopup(id, isConfig) closePopup(id, isConfig)
@ -117,9 +132,9 @@ export default function FontSetting(props) {
<span <span
style={{ style={{
fontFamily: selectedFont?.value ?? '', fontFamily: selectedFont?.value ?? '',
fontWeight: selectedFontWeight?.value?.toLowerCase().includes('bold') ? 'bold' : 'normal',
fontStyle: selectedFontWeight?.value?.toLowerCase().includes('italic') ? 'italic' : 'normal',
fontSize: selectedFontSize?.value ?? '12px', fontSize: selectedFontSize?.value ?? '12px',
fontStyle: selectedFontWeight?.value.toLowerCase().includes('italic') ? 'italic' : 'normal',
fontWeight: selectedFontWeight?.value.toLowerCase().includes('bold') ? 'bold' : 'normal',
color: selectedFontColor?.value ?? 'black', color: selectedFontColor?.value ?? 'black',
}} }}
> >

View File

@ -15,11 +15,12 @@ import { useCommonCode } from '@/hooks/common/useCommonCode'
import { useEstimateController } from '@/hooks/floorPlan/estimate/useEstimateController' import { useEstimateController } from '@/hooks/floorPlan/estimate/useEstimateController'
import { SessionContext } from '@/app/SessionProvider' import { SessionContext } from '@/app/SessionProvider'
import Select, { components } from 'react-select' import Select, { components } from 'react-select'
import { convertNumberToPriceDecimal } from '@/util/common-utils' import { convertNumberToPriceDecimal, convertNumberToPriceDecimalToFixed } from '@/util/common-utils'
import ProductFeaturesPop from './popup/ProductFeaturesPop' import ProductFeaturesPop from './popup/ProductFeaturesPop'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
export default function Estimate({ params }) { export default function Estimate({ params }) {
const [specialNoteFirstFlg, setSpecialNoteFirstFlg] = useState(false)
const fixedKey = 'itemKey' const fixedKey = 'itemKey'
const [itemChangeYn, setItemChangeYn] = useState(false) const [itemChangeYn, setItemChangeYn] = useState(false)
const { session } = useContext(SessionContext) const { session } = useContext(SessionContext)
@ -102,44 +103,97 @@ export default function Estimate({ params }) {
useEffect(() => { useEffect(() => {
// API // API
// () // ()
let url = `/api/estimate/special-note-list` if (!specialNoteFirstFlg) {
get({ url: url }).then((res) => { let url = `/api/estimate/special-note-list`
if (isNotEmptyArray(res)) { get({ url: url }).then((res) => {
if (estimateContextState?.estimateOption) { if (isNotEmptyArray(res)) {
res.map((row) => { if (estimateContextState?.estimateOption) {
let estimateOption = estimateContextState?.estimateOption?.split('、') res.map((row) => {
row.check = false let estimateOption = estimateContextState?.estimateOption?.split('、')
estimateOption.map((row2) => { row.check = false
if (row2 === row.code) { estimateOption.map((row2) => {
if (row2 === row.code) {
row.check = true
}
})
//detail
//ATTR003,ATTR007
if (row.code === 'ATTR003') {
row.check = true
}
if (row.code === 'ATTR007') {
row.check = true row.check = true
} }
}) })
//detail
//ATTR003,ATTR007 setSpecialNoteList(res)
if (row.code === 'ATTR003') {
row.check = true setSpecialNoteFirstFlg(true)
} }
if (row.code === 'ATTR007') {
row.check = true
}
})
setSpecialNoteList(res)
} }
} })
}) }
}, [estimateContextState?.estimateOption]) }, [estimateContextState?.estimateOption])
//
// useEffect(() => {
// // API
// // ()
// let url = `/api/estimate/special-note-title-list`
// // let url = `/api/estimate/special-note-list`
// get({ url: url }).then((res) => {
// if (isNotEmptyArray(res)) {
// if (estimateContextState?.estimateOption) {
// res.map((row) => {
// // console.log('API:::', row)
// //ATTR001ATTR002ATTR009ATTR010
// let estimateOption = estimateContextState?.estimateOption?.split('')
// row.check = false
// estimateOption.map((row2) => {
// if (row.pkgYn === '0') {
// if (row2 === row.code) {
// row.check = true
// }
// } else {
// if (row.code.includes(row2)) {
// row.check = true
// return
// }
// }
// })
// //detail
// //ATTR003,ATTR007
// if (row.code === 'ATTR003') {
// row.check = true
// }
// if (row.code === 'ATTR007') {
// row.check = true
// }
// })
// setSpecialNoteList(res)
// }
// }
// })
// }, [estimateContextState?.estimateOption])
//API
let begin = 1
useEffect(() => {
if (begin === 1) {
setStartDate(estimateContextState?.estimateDate)
begin++
}
}, [estimateContextState?.estimateDate])
// set // set
useEffect(() => { useEffect(() => {
let estimateDate = dayjs(startDate).format('YYYY-MM-DD') let estimateDate = dayjs(startDate).format('YYYY-MM-DD')
setEstimateContextState({ estimateDate: estimateDate }) if (begin === 1) {
setEstimateContextState({ estimateDate: estimateDate })
}
}, [startDate]) }, [startDate])
//API
useEffect(() => {
setStartDate(estimateContextState?.estimateDate)
}, [estimateContextState?.estimateDate])
useEffect(() => { useEffect(() => {
// setEstimateContextState // setEstimateContextState
if (isNotEmptyArray(specialNoteList)) { if (isNotEmptyArray(specialNoteList)) {
@ -231,6 +285,15 @@ export default function Estimate({ params }) {
} }
}) })
} }
//
setEstimateContextState({
pkgAsp: '0',
pkgTotPrice: '0',
})
//YJOD UNIT_PRICE
handlePricing('UNIT_PRICE')
} }
setItemChangeYn(true) setItemChangeYn(true)
@ -296,9 +359,8 @@ export default function Estimate({ params }) {
//itemList itemId unitPrice 0 //itemList itemId unitPrice 0
//itemId salePrice //itemId salePrice
if (data.result.code === 200) { if (data.result.code === 200) {
console.log('data::확인해서 넣기:::::::::', data.data.pkgUnitPrice)
setEstimateContextState({ setEstimateContextState({
pkgAsp: data.data.pkgUnitPrice, pkgAsp: data?.data?.pkgUnitPrice,
}) })
//PKG //PKG
onChangePkgAsp(data.data.pkgUnitPrice) onChangePkgAsp(data.data.pkgUnitPrice)
@ -309,7 +371,11 @@ export default function Estimate({ params }) {
data.data2.map((item2) => { data.data2.map((item2) => {
if (item2) { if (item2) {
if (item2.itemId === item.itemId) { if (item2.itemId === item.itemId) {
updateList.push({ ...item, salePrice: item2.unitPrice, saleTotPrice: (item.amount * item2.unitPrice).toString() }) updateList.push({
...item,
salePrice: item2.unitPrice === null ? '0' : item2.unitPrice,
saleTotPrice: (item.amount * item2.unitPrice).toString(),
})
checkYn = true checkYn = true
} }
} }
@ -319,7 +385,6 @@ export default function Estimate({ params }) {
updateList.push({ ...item, salePrice: '0', saleTotPrice: '0' }) updateList.push({ ...item, salePrice: '0', saleTotPrice: '0' })
} }
}) })
setEstimateContextState({ setEstimateContextState({
priceCd: showPriceCd, priceCd: showPriceCd,
itemList: updateList, itemList: updateList,
@ -333,6 +398,24 @@ export default function Estimate({ params }) {
}) })
} }
const getAbledItems = (items) => {
return items.filter((items) => items.paDispOrder === null)
}
const onChangeSelectAll = (e) => {
if (e.target.checked) {
const allCheckedSelection = new Set(getAbledItems(estimateContextState.itemList).map((item) => item.dispOrder))
setSelection(allCheckedSelection)
} else {
setSelection(new Set())
}
}
const isSelectedAll = () => {
return selection.size === getAbledItems(estimateContextState.itemList).length
}
//row //row
const onChangeSelect = (dispOrder) => { const onChangeSelect = (dispOrder) => {
const newSelection = new Set(selection) const newSelection = new Set(selection)
@ -358,13 +441,13 @@ export default function Estimate({ params }) {
let totVolKw = estimateContextState.totVolKw * 1000 let totVolKw = estimateContextState.totVolKw * 1000
let pkgTotPrice = pkgAsp.replaceAll(',', '') * totVolKw let pkgTotPrice = pkgAsp.replaceAll(',', '') * totVolKw
setEstimateContextState({ setEstimateContextState({
pkgAsp: pkgAsp, pkgAsp: pkgAsp,
pkgTotPrice: pkgTotPrice.toFixed(3), pkgTotPrice: pkgTotPrice.toFixed(3),
}) })
// //
// setItemChangeYn(true) setItemChangeYn(true)
} else {
} }
} }
@ -392,7 +475,6 @@ export default function Estimate({ params }) {
return item return item
} }
}) })
setEstimateContextState({ setEstimateContextState({
itemList: updateList, itemList: updateList,
}) })
@ -429,7 +511,7 @@ export default function Estimate({ params }) {
setItemChangeYn(true) setItemChangeYn(true)
} }
// // /
const onChangeDisplayItem = (itemId, dispOrder, index) => { const onChangeDisplayItem = (itemId, dispOrder, index) => {
const param = { const param = {
itemId: itemId, itemId: itemId,
@ -438,7 +520,7 @@ export default function Estimate({ params }) {
let updateList = [] let updateList = []
let updates = {} let updates = {}
get({ url: apiUrl }).then((res) => { get({ url: apiUrl }).then((res) => {
// console.log(':::::::', res) console.log('아이템디테일::::::::', res)
updates.objectNo = objectNo updates.objectNo = objectNo
updates.planNo = planNo updates.planNo = planNo
updates.itemId = res.itemId updates.itemId = res.itemId
@ -460,6 +542,12 @@ export default function Estimate({ params }) {
updates.delFlg = '0' // 0 updates.delFlg = '0' // 0
updates.saleTotPrice = (res.salePrice * estimateContextState.itemList[index].amount).toString() updates.saleTotPrice = (res.salePrice * estimateContextState.itemList[index].amount).toString()
if (estimateContextState.estimateType === 'YJSS') {
if (res.pkgMaterialFlg === '0') {
//updates.showSalePrice = '0'
//updates.showSaleTotPrice = '0'
}
}
//104671 //104671
let bomList = res.itemBomList let bomList = res.itemBomList
@ -467,7 +555,8 @@ export default function Estimate({ params }) {
if (item.dispOrder === dispOrder) { if (item.dispOrder === dispOrder) {
return { ...item, ...updates } return { ...item, ...updates }
} else if (item.paDispOrder === dispOrder) { } else if (item.paDispOrder === dispOrder) {
return { ...item, delFlg: '1' } return { ...item, delFlg: '0' }
// return { ...item, delFlg: '1' }
} else { } else {
return item return item
} }
@ -475,8 +564,21 @@ export default function Estimate({ params }) {
//paDispOrder //paDispOrder
if (bomList) { if (bomList) {
bomList.map((bomItem, index) => { bomList.map((bomItem, index) => {
let newItemDispOrder = Math.max(...estimateContextState.itemList.map((item) => item.dispOrder)) let maxItemDispOrder = Math.max(...estimateContextState.itemList.map((item) => item.dispOrder))
bomItem.dispOrder = index + 1 + newItemDispOrder if (maxItemDispOrder == dispOrder) {
bomItem.dispOrder = (index + 1 + maxItemDispOrder).toString()
bomItem.paDispOrder = dispOrder
bomItem.salePrice = '0'
//unitPrice??
bomItem.saleTotPrice = '0'
} else {
bomItem.dispOrder = (index + 1 + Number(dispOrder)).toString()
bomItem.paDispOrder = dispOrder
bomItem.salePrice = '0'
//unitPrice??
bomItem.saleTotPrice = '0'
}
bomItem.delFlg = '0' bomItem.delFlg = '0'
bomItem.objectNo = objectNo bomItem.objectNo = objectNo
bomItem.planNo = planNo bomItem.planNo = planNo
@ -539,21 +641,31 @@ export default function Estimate({ params }) {
useEffect(() => { useEffect(() => {
if (itemChangeYn) { if (itemChangeYn) {
console.log(' 토탈만들어주기::::::::::', estimateContextState.itemList)
let totAmount = 0 let totAmount = 0
let totVolKw = 0 let totVolKw = 0
let supplyPrice = 0 let supplyPrice = 0
let vatPrice = 0 let vatPrice = 0
let totPrice = 0 let totPrice = 0
let addPkgPrice = 0 let addSupplyPrice = 0
if (estimateContextState.estimateType === 'YJOD') { if (estimateContextState.estimateType === 'YJOD') {
console.log('YJOD 토탈만들어주기::::::::::', estimateContextState.itemList)
estimateContextState.itemList.sort((a, b) => {
return a.dispOrder - b.dispOrder
})
estimateContextState.itemList.map((item) => { estimateContextState.itemList.map((item) => {
//delete item.showSalePrice
//delete item.showSaleTotPrice
if (item.delFlg === '0') { if (item.delFlg === '0') {
const amount = Number(item.amount.replace(/[^0-9]/g, '').replaceAll(',', '')) let amount = Number(item?.amount?.replace(/[^0-9]/g, '').replaceAll(',', ''))
if (isNaN(amount)) {
const price = Number(item.saleTotPrice.replaceAll(',', '')) amount = 0
}
let price = Number(item?.saleTotPrice?.replaceAll(',', ''))
if (isNaN(price)) {
price = 0
}
if (item.moduleFlg === '1') { if (item.moduleFlg === '1') {
//(Kw) 1 //(Kw) 1
const volKw = (item.pnowW * amount) / 1000 const volKw = (item.pnowW * amount) / 1000
@ -577,32 +689,55 @@ export default function Estimate({ params }) {
}) })
} else { } else {
//YJSS //YJSS
console.log('YJSS 토탈만들어주기::::::::::', estimateContextState.itemList)
estimateContextState.itemList.sort((a, b) => {
return a.dispOrder - b.dispOrder
})
estimateContextState.itemList.map((item) => { estimateContextState.itemList.map((item) => {
if (item.delFlg === '0') { if (item.delFlg === '0') {
const amount = Number(item.amount.replace(/[^0-9]/g, '').replaceAll(',', '')) let amount = Number(item.amount?.replace(/[^0-9]/g, '').replaceAll(',', ''))
const price = Number(item.saleTotPrice.replaceAll(',', '')) let price = Number(item.saleTotPrice?.replaceAll(',', ''))
const salePrice = Number(item.salePrice.replaceAll(',', '')) let salePrice = Number(item.salePrice?.replaceAll(',', ''))
if (isNaN(amount)) {
amount = 0
}
if (isNaN(price)) {
price = 0
}
if (isNaN(salePrice)) {
salePrice = 0
}
if (item.moduleFlg === '1') { if (item.moduleFlg === '1') {
//(Kw) 1 //(Kw) 1
const volKw = (item.pnowW * amount) / 1000 const volKw = (item.pnowW * amount) / 1000
totVolKw += volKw totVolKw += volKw
} }
if (item.pkgMaterialFlg === '1') {
const pkgPrice = amount * salePrice
//YJSS PKG *
addPkgPrice += pkgPrice
}
// const price // const price
totAmount += amount
supplyPrice += price supplyPrice += price
totAmount += amount
if (item.pkgMaterialFlg === '1') {
const pkgPrice = amount * salePrice
//
//YJSS PKG(1) * (supplyPrice)
addSupplyPrice += pkgPrice
}
if (!item.paDispOrder) {
//paDispOrder
if (item.pkgMaterialFlg === '0') {
//item.showSalePrice = '0'
//item.showSaleTotPrice = '0'
}
}
} }
}) })
supplyPrice += addSupplyPrice
vatPrice = supplyPrice * 0.1 vatPrice = supplyPrice * 0.1
totPrice = supplyPrice + vatPrice totPrice = supplyPrice + vatPrice
setEstimateContextState({ setEstimateContextState({
totAmount: totAmount, totAmount: totAmount,
totVolKw: totVolKw.toFixed(3), totVolKw: totVolKw.toFixed(3),
@ -845,6 +980,23 @@ export default function Estimate({ params }) {
setEstimateContextState({ setEstimateContextState({
fileFlg: e.target.checked ? '1' : '0', fileFlg: e.target.checked ? '1' : '0',
}) })
if (e.target.checked) {
if (specialNoteList.length > 0) {
specialNoteList.map((item) => {
if (item.code === 'ATTR019') {
item.check = true
}
})
}
} else {
if (specialNoteList.length > 0) {
specialNoteList.map((item) => {
if (item.code === 'ATTR019') {
item.check = false
}
})
}
}
}} }}
/> />
<label htmlFor="next" style={{ color: '#101010' }}> <label htmlFor="next" style={{ color: '#101010' }}>
@ -927,7 +1079,6 @@ export default function Estimate({ params }) {
{/* SpecialNoteList반복문 */} {/* SpecialNoteList반복문 */}
{specialNoteList.length > 0 && {specialNoteList.length > 0 &&
specialNoteList.map((row) => { specialNoteList.map((row) => {
// console.log('rowL::::', row)
return ( return (
<div <div
key={uuidv4()} key={uuidv4()}
@ -941,7 +1092,7 @@ export default function Estimate({ params }) {
type="checkbox" type="checkbox"
id={row.code} id={row.code}
checked={!!row.check} checked={!!row.check}
disabled={row.code === 'ATTR001' || row.code === 'ATTR002' ? true : false} disabled={row.code === 'ATTR001' ? true : false}
onChange={(event) => { onChange={(event) => {
setSpecialNoteList((specialNote) => setSpecialNoteList((specialNote) =>
specialNote.map((temp) => (temp.code === row.code ? { ...temp, check: !temp.check } : temp)), specialNote.map((temp) => (temp.code === row.code ? { ...temp, check: !temp.check } : temp)),
@ -990,19 +1141,19 @@ export default function Estimate({ params }) {
</div> </div>
<div className="estimate-box"> <div className="estimate-box">
<div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.totVolKw')}</div> <div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.totVolKw')}</div>
<div className="estimate-name blue">{convertNumberToPriceDecimal(estimateContextState?.totVolKw)}</div> <div className="estimate-name blue">{convertNumberToPriceDecimalToFixed(estimateContextState?.totVolKw, 2)}</div>
</div> </div>
<div className="estimate-box"> <div className="estimate-box">
<div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.supplyPrice')}</div> <div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.supplyPrice')}</div>
<div className="estimate-name blue">{convertNumberToPriceDecimal(estimateContextState?.supplyPrice)}</div> <div className="estimate-name blue">{convertNumberToPriceDecimalToFixed(estimateContextState?.supplyPrice, 2)}</div>
</div> </div>
<div className="estimate-box"> <div className="estimate-box">
<div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.vatPrice')}</div> <div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.vatPrice')}</div>
<div className="estimate-name blue">{convertNumberToPriceDecimal(estimateContextState?.vatPrice)}</div> <div className="estimate-name blue">{convertNumberToPriceDecimalToFixed(estimateContextState?.vatPrice, 2)}</div>
</div> </div>
<div className="estimate-box"> <div className="estimate-box">
<div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.totPrice')}</div> <div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.totPrice')}</div>
<div className="estimate-name red">{convertNumberToPriceDecimal(estimateContextState?.totPrice)}</div> <div className="estimate-name red">{convertNumberToPriceDecimalToFixed(estimateContextState?.totPrice, 2)}</div>
</div> </div>
</div> </div>
</div> </div>
@ -1028,7 +1179,7 @@ export default function Estimate({ params }) {
<input <input
type="text" type="text"
className="input-light" className="input-light"
value={estimateContextState?.pkgAsp} value={estimateContextState?.estimateType === 'YJSS' ? estimateContextState?.pkgAsp : '0'}
onChange={(e) => { onChange={(e) => {
onChangePkgAsp(e.target.value) onChangePkgAsp(e.target.value)
}} }}
@ -1036,9 +1187,9 @@ export default function Estimate({ params }) {
</div> </div>
</td> </td>
<th>{getMessage('estimate.detail.sepcialEstimateProductInfo.pkgWeight')}</th> <th>{getMessage('estimate.detail.sepcialEstimateProductInfo.pkgWeight')}</th>
<td>{convertNumberToPriceDecimal(estimateContextState?.totVolKw)}</td> <td>{convertNumberToPriceDecimalToFixed(estimateContextState?.totVolKw, 2)}</td>
<th>{getMessage('estimate.detail.sepcialEstimateProductInfo.pkgPrice')}</th> <th>{getMessage('estimate.detail.sepcialEstimateProductInfo.pkgPrice')}</th>
<td>{convertNumberToPriceDecimal(estimateContextState?.pkgTotPrice)}</td> <td>{convertNumberToPriceDecimalToFixed(estimateContextState?.pkgTotPrice, 2)}</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
@ -1096,13 +1247,11 @@ export default function Estimate({ params }) {
</li> </li>
</ul> </ul>
<div className="product-edit-btn"> <div className="product-edit-btn">
{/* <button className="btn-origin navy mr5" type="button" onClick={addItem}> */}
<button <button
className="btn-origin navy mr5" className="btn-origin navy mr5"
type="button" type="button"
onClick={() => { onClick={() => {
addItem() addItem()
// setItemChangeYn(true)
}} }}
> >
<span className="plus"></span> <span className="plus"></span>
@ -1121,19 +1270,20 @@ export default function Estimate({ params }) {
<table> <table>
<colgroup> <colgroup>
<col width={50} /> <col width={50} />
<col width={100} /> <col width={50} />
<col /> <col />
<col width={250} /> <col width={250} />
<col width={100} /> <col width={90} />
<col width={100} /> <col width={80} />
<col width={200} /> <col width={140} />
<col width={240} /> <col width={190} />
</colgroup> </colgroup>
<thead> <thead>
<tr> <tr>
<th> <th>
<div className="d-check-box pop no-text" style={{ display: 'none' }}> {/* <div className="d-check-box pop no-text" style={{ display: 'none' }}> */}
<input type="checkbox" id="ch97" /> <div className="d-check-box pop no-text">
<input type="checkbox" id="ch97" checked={isSelectedAll()} onChange={onChangeSelectAll} />
<label htmlFor="ch97"></label> <label htmlFor="ch97"></label>
</div> </div>
</th> </th>
@ -1219,12 +1369,12 @@ export default function Estimate({ params }) {
<input <input
type="text" type="text"
className="input-light al-r" className="input-light al-r"
// value={item?.amount} value={convertNumberToPriceDecimal(item?.amount?.replaceAll(',', ''))}
value={convertNumberToPriceDecimal(item?.amount.replaceAll(',', ''))}
disabled={item.itemId === '' || !!item?.paDispOrder} disabled={item.itemId === '' || !!item?.paDispOrder}
onChange={(e) => { onChange={(e) => {
onChangeAmount(e.target.value, item.dispOrder, index) onChangeAmount(e.target.value, item.dispOrder, index)
}} }}
maxLength={6}
/> />
</div> </div>
</td> </td>
@ -1235,7 +1385,8 @@ export default function Estimate({ params }) {
<input <input
type="text" type="text"
className="input-light al-r" className="input-light al-r"
value={convertNumberToPriceDecimal(item?.salePrice.replaceAll(',', ''))} // value={convertNumberToPriceDecimal(item?.showSalePrice === '0' ? null : item?.salePrice?.replaceAll(',', ''))}
value={convertNumberToPriceDecimal(item?.salePrice?.replaceAll(',', ''))}
disabled={ disabled={
estimateContextState?.estimateType === 'YJSS' estimateContextState?.estimateType === 'YJSS'
? item?.paDispOrder ? item?.paDispOrder
@ -1246,6 +1397,7 @@ export default function Estimate({ params }) {
onChange={(e) => { onChange={(e) => {
onChangeSalePrice(e.target.value, item.dispOrder, index) onChangeSalePrice(e.target.value, item.dispOrder, index)
}} }}
maxLength={12}
/> />
</div> </div>
{/* <div className="btn-area"> {/* <div className="btn-area">
@ -1253,7 +1405,12 @@ export default function Estimate({ params }) {
</div> */} </div> */}
</div> </div>
</td> </td>
<td className="al-r">{convertNumberToPriceDecimal(item?.saleTotPrice.replaceAll(',', ''))}</td> <td className="al-r">{convertNumberToPriceDecimalToFixed(item?.saleTotPrice?.replaceAll(',', ''), 2)}</td>
{/* {item?.showSaleTotPrice === '0' ? (
<td className="al-r"></td>
) : (
<td className="al-r">{convertNumberToPriceDecimalToFixed(item?.saleTotPrice?.replaceAll(',', ''), 2)}</td>
)} */}
</tr> </tr>
) )
} else { } else {

View File

@ -1,22 +1,20 @@
'use client' 'use client'
import { useEffect, useState } from 'react' import { useContext, useEffect } from 'react'
import { useRecoilState, useRecoilValue } from 'recoil' //import { useRecoilState } from 'recoil'
import { settingModalFirstOptionsState, settingModalSecondOptionsState } from '@/store/settingAtom'
import CanvasMenu from '@/components/floor-plan/CanvasMenu' import CanvasMenu from '@/components/floor-plan/CanvasMenu'
import { useCanvasMenu } from '@/hooks/common/useCanvasMenu' import { useCanvasMenu } from '@/hooks/common/useCanvasMenu'
import { useCanvasSetting } from '@/hooks/option/useCanvasSetting' import { useCanvasSetting } from '@/hooks/option/useCanvasSetting'
import { usePopup } from '@/hooks/usePopup' import { usePopup } from '@/hooks/usePopup'
//import { FloorPlanContext } from '@/app/floor-plan/FloorPlanProvider'
//import { correntObjectNoState } from '@/store/settingAtom'
import '@/styles/contents.scss' import '@/styles/contents.scss'
export default function FloorPlan({ children }) { export default function FloorPlan({ children }) {
const [settingModalFirstOptions, setSettingModalFirstOptions] = useRecoilState(settingModalFirstOptionsState) //const { floorPlanState, setFloorPlanState } = useContext(FloorPlanContext)
const [settingModalSecondOptions, setSettingModalSecondOptions] = useRecoilState(settingModalSecondOptionsState) //const [correntObjectNo, setCorrentObjectNo] = useRecoilState(correntObjectNoState)
const [objectNo, setObjectNo] = useState('test123240912001') //
const { closeAll } = usePopup() const { closeAll } = usePopup()
const { menuNumber, setMenuNumber } = useCanvasMenu() const { menuNumber, setMenuNumber } = useCanvasMenu()
const { fetchSettings } = useCanvasSetting() const { fetchSettings } = useCanvasSetting()
const modalProps = { const modalProps = {
@ -25,11 +23,13 @@ export default function FloorPlan({ children }) {
} }
useEffect(() => { useEffect(() => {
///setCorrentObjectNo(floorPlanState.objectNo)
//console.log('FloorPlan objectNo ', floorPlanState.objectNo, correntObjectNo)
fetchSettings() fetchSettings()
return () => { return () => {
closeAll() closeAll()
} }
}, [objectNo]) }, [])
return ( return (
<> <>

View File

@ -1,3 +1,5 @@
'use client'
import { useContext, useEffect } from 'react' import { useContext, useEffect } from 'react'
import { FloorPlanContext } from '@/app/floor-plan/FloorPlanProvider' import { FloorPlanContext } from '@/app/floor-plan/FloorPlanProvider'

View File

@ -49,6 +49,7 @@ export default function AuxiliaryDrawing({ id, pos = { x: 50, y: 230 } }) {
handleFix, handleFix,
buttonAct, buttonAct,
setButtonAct, setButtonAct,
cutAuxiliary,
} = useAuxiliaryDrawing(id) } = useAuxiliaryDrawing(id)
const outerLineProps = { const outerLineProps = {
@ -151,6 +152,9 @@ export default function AuxiliaryDrawing({ id, pos = { x: 50, y: 230 } }) {
<button className="btn-frame modal mr5" onClick={handleRollback}> <button className="btn-frame modal mr5" onClick={handleRollback}>
{getMessage('modal.cover.outline.rollback')} {getMessage('modal.cover.outline.rollback')}
</button> </button>
<button className="btn-frame modal mr5" onClick={cutAuxiliary}>
{getMessage('contextmenu.auxiliary.cut')}
</button>
<button className="btn-frame modal act" onClick={() => handleFix(id)}> <button className="btn-frame modal act" onClick={() => handleFix(id)}>
{getMessage('apply')} {getMessage('apply')}
</button> </button>

View File

@ -5,8 +5,8 @@ import { useRecoilValue } from 'recoil'
import { contextPopupPositionState } from '@/store/popupAtom' import { contextPopupPositionState } from '@/store/popupAtom'
import { useState } from 'react' import { useState } from 'react'
import { currentObjectState } from '@/store/canvasAtom' import { currentObjectState } from '@/store/canvasAtom'
import { useLine } from '@/hooks/useLine'
import { useAuxiliaryDrawing } from '@/hooks/roofcover/useAuxiliaryDrawing' import { useAuxiliaryDrawing } from '@/hooks/roofcover/useAuxiliaryDrawing'
import { useSwal } from '@/hooks/useSwal'
export default function AuxiliaryEdit(props) { export default function AuxiliaryEdit(props) {
const contextPopupPosition = useRecoilValue(contextPopupPositionState) const contextPopupPosition = useRecoilValue(contextPopupPositionState)
@ -18,9 +18,13 @@ export default function AuxiliaryEdit(props) {
const [horizonSize, setHorizonSize] = useState('0') const [horizonSize, setHorizonSize] = useState('0')
const [arrow1, setArrow1] = useState(null) const [arrow1, setArrow1] = useState(null)
const [arrow2, setArrow2] = useState(null) const [arrow2, setArrow2] = useState(null)
const { addLine, removeLine } = useLine()
const currentObject = useRecoilValue(currentObjectState) const currentObject = useRecoilValue(currentObjectState)
const { swalFire } = useSwal()
const handleSave = () => { const handleSave = () => {
if (!horizonSize || !verticalSize || !arrow1 || !arrow2) {
swalFire({ title: '길이와 방향을 입력하세요.', type: 'alert' })
return
}
if (type === 'copy') { if (type === 'copy') {
if (currentObject) { if (currentObject) {
copy( copy(

View File

@ -3,12 +3,14 @@ import WithDraggable from '@/components/common/draggable/WithDraggable'
import { usePopup } from '@/hooks/usePopup' import { usePopup } from '@/hooks/usePopup'
import { useRecoilValue } from 'recoil' import { useRecoilValue } from 'recoil'
import { contextPopupPositionState } from '@/store/popupAtom' import { contextPopupPositionState } from '@/store/popupAtom'
import { useCanvas } from '@/hooks/useCanvas'
export default function AuxiliarySize(props) { export default function AuxiliarySize(props) {
const contextPopupPosition = useRecoilValue(contextPopupPositionState) const contextPopupPosition = useRecoilValue(contextPopupPositionState)
const { id, pos = contextPopupPosition } = props const { id, pos = contextPopupPosition } = props
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { closePopup } = usePopup() const { closePopup } = usePopup()
const { currentObject } = useCanvas()
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`}>
@ -26,7 +28,7 @@ export default function AuxiliarySize(props) {
</div> </div>
<div className="outline-form mb15"> <div className="outline-form mb15">
<div className="input-grid mr5" style={{ flex: '1 1 auto' }}> <div className="input-grid mr5" style={{ flex: '1 1 auto' }}>
<input type="text" className="input-origin block" defaultValue={100} /> <input type="text" className="input-origin block" defaultValue={currentObject?.length} readOnly={true} />
</div> </div>
<span className="thin">mm</span> <span className="thin">mm</span>
</div> </div>
@ -45,14 +47,14 @@ export default function AuxiliarySize(props) {
</div> </div>
<div className="outline-form mb15"> <div className="outline-form mb15">
<div className="input-grid mr5" style={{ flex: '1 1 auto' }}> <div className="input-grid mr5" style={{ flex: '1 1 auto' }}>
<input type="text" className="input-origin block" defaultValue={100} /> <input type="text" className="input-origin block" defaultValue={100} readOnly={true} />
</div> </div>
<span className="thin">mm</span> <span className="thin">mm</span>
</div> </div>
<div className="outline-form"> <div className="outline-form">
<span style={{ width: 'auto' }}>{getMessage('length')}</span> <span style={{ width: 'auto' }}>{getMessage('length')}</span>
<div className="input-grid mr5"> <div className="input-grid mr5">
<input type="text" className="input-origin block" defaultValue={100} /> <input type="text" className="input-origin block" defaultValue={currentObject?.length} readOnly={true} />
</div> </div>
<span className="thin">mm</span> <span className="thin">mm</span>
</div> </div>

View File

@ -82,7 +82,7 @@ const PentagonDormer = forwardRef((props, refs) => {
</div> </div>
</div> </div>
<div className="discrimination-box"> <div className="discrimination-box">
<div className="discrimination-tit">方向の選択</div> <div className="discrimination-tit">{getMessage('modal.object.setting.direction.select')}</div>
<div className="object-direction-wrap"> <div className="object-direction-wrap">
<div className="plane-direction"> <div className="plane-direction">
<span className="top">{getMessage('commons.north')}</span> <span className="top">{getMessage('commons.north')}</span>

View File

@ -60,7 +60,7 @@ const TriangleDormer = forwardRef((props, refs) => {
</div> </div>
</div> </div>
<div className="discrimination-box"> <div className="discrimination-box">
<div className="discrimination-tit">方向の選択</div> <div className="discrimination-tit">{getMessage('modal.object.setting.direction.select')}</div>
<div className="object-direction-wrap"> <div className="object-direction-wrap">
<div className="plane-direction"> <div className="plane-direction">
<span className="top">{getMessage('commons.north')}</span> <span className="top">{getMessage('commons.north')}</span>

View File

@ -1,19 +1,46 @@
import React, { useEffect, useState } from 'react' import React, { useEffect, useState } from 'react'
import { useCanvasSetting } from '@/hooks/option/useCanvasSetting' import { useCanvasSetting } from '@/hooks/option/useCanvasSetting'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { POLYGON_TYPE } from '@/common/common'
import { setSurfaceShapePattern } from '@/util/canvas-util'
export default function FirstOption() { export default function FirstOption() {
const [objectNo, setObjectNo] = useState('test123240912001') //
const { settingModalFirstOptions, setSettingModalFirstOptions } = useCanvasSetting()
const { settingModalSecondOptions, setSettingModalSecondOptions } = useCanvasSetting()
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { canvas, settingModalFirstOptions, setSettingModalFirstOptions } = useCanvasSetting()
const { fetchSettings, frontSettings, onClickOption } = useCanvasSetting() const { option1, option2, dimensionDisplay } = settingModalFirstOptions
// //
useEffect(() => { useEffect(() => {
console.log('FirstOption useEffect 실행') console.log('FirstOption useEffect 실행')
}, [objectNo]) }, [])
const onClickOption = async (item) => {
// ( )
if (item.column === 'corridorDimension' || item.column === 'realDimension' || item.column === 'noneDimension') {
const options = settingModalFirstOptions?.dimensionDisplay.map((option) => {
option.selected = option.id === item.id
return option
})
// ( )
} else if (item.column === 'onlyBorder' || item.column === 'lineHatch' || item.column === 'allPainted') {
const options2 = settingModalFirstOptions?.option2.map((option2) => {
option2.selected = option2.id === item.id
return option2
})
const polygons = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
polygons.forEach((polygon) => {
setSurfaceShapePattern(polygon, item.column)
})
// ( )
} else {
item.selected = !item.selected
}
setSettingModalFirstOptions({ ...settingModalFirstOptions, option1, option2, dimensionDisplay, fontFlag: true })
}
return ( return (
<> <>

View File

@ -16,32 +16,40 @@ export default function SecondOption() {
const [showDimensionLineSettingModal, setShowDimensionLineSettingModal] = useState(false) const [showDimensionLineSettingModal, setShowDimensionLineSettingModal] = useState(false)
const [showPlanSizeSettingModal, setShowPlanSizeSettingModal] = useState(false) const [showPlanSizeSettingModal, setShowPlanSizeSettingModal] = useState(false)
const [globalFont, setGlobalFont] = useRecoilState(globalFontAtom) const [globalFont, setGlobalFont] = useRecoilState(globalFontAtom)
const [objectNo, setObjectNo] = useState('test123240912001') //
const { settingModalSecondOptions, setSettingModalSecondOptions } = useCanvasSetting()
const { adsorptionPointMode, setAdsorptionPointMode } = useCanvasSetting()
const { fetchSettings, frontSettings, onClickOption } = useCanvasSetting()
const commonFont = useRecoilValue(fontSelector('commonText')) const commonFont = useRecoilValue(fontSelector('commonText'))
const flowFont = useRecoilValue(fontSelector('flowText')) const flowFont = useRecoilValue(fontSelector('flowText'))
const lengthFont = useRecoilValue(fontSelector('lengthText')) const lengthFont = useRecoilValue(fontSelector('lengthText'))
const circuitNumberTextFont = useRecoilValue(fontSelector('circuitNumberText')) const circuitNumberTextFont = useRecoilValue(fontSelector('circuitNumberText'))
const [dimensionId, setDimensionId] = useState(uuidv4()) const [dimensionId, setDimensionId] = useState(uuidv4())
const [fontId, setFontId] = useState(uuidv4()) const [fontId, setFontId] = useState(uuidv4())
const [planSizeId, setPlanSizeId] = useState(uuidv4()) const [planSizeId, setPlanSizeId] = useState(uuidv4())
const {
//horizon, setHorizon, vertical, setVertical,
// originHorizon,
// setOriginHorizon,
// originVertical,
// setOriginVertical,
planSizeSettingMode,
setPlanSizeSettingMode,
settingModalSecondOptions,
setSettingModalSecondOptions,
adsorptionPointMode,
setAdsorptionPointMode,
setAdsorptionRange,
} = useCanvasSetting()
const { option3, option4 } = settingModalSecondOptions
// const [horizon, setHorizon] = useState(originHorizon)
// const [vertical, setVertical] = useState(originVertical)
// //
useEffect(() => { useEffect(() => {
//fetchSettings() console.log('SecondOption useEffect 실행', planSizeSettingMode)
}, [objectNo]) }, [])
const dimensionProps = {
id: dimensionId,
isShow: showDimensionLineSettingModal,
setIsShow: setShowDimensionLineSettingModal,
}
const [horizon, setHorizon] = useState(1600)
const [vertical, setVertical] = useState(1600)
const handlePopup = (type) => { const handlePopup = (type) => {
setShowDimensionLineSettingModal(false)
setShowPlanSizeSettingModal(false) setShowPlanSizeSettingModal(false)
setShowFontSettingModal(false) setShowFontSettingModal(false)
@ -51,8 +59,8 @@ export default function SecondOption() {
setShowFontSettingModal(true) setShowFontSettingModal(true)
setShowDimensionLineSettingModal(false) setShowDimensionLineSettingModal(false)
fontProps.type = 'commonText' fontProps.type = 'commonText'
fontProps.id = fontId + 1
fontProps.font = commonFont fontProps.font = commonFont
fontProps.id = fontId + 1
addPopup(fontId + 1, 2, <FontSetting {...fontProps} />, true) addPopup(fontId + 1, 2, <FontSetting {...fontProps} />, true)
break break
} }
@ -96,9 +104,9 @@ export default function SecondOption() {
setShowDimensionLineSettingModal(true) setShowDimensionLineSettingModal(true)
fontProps.font = { fontProps.font = {
fontFamily: '', fontFamily: '',
fontColor: '',
fontSize: '',
fontWeight: '', fontWeight: '',
fontSize: '',
fontColor: '',
} }
addPopup(dimensionId, 2, <DimensionLineSetting {...dimensionProps} />, true) addPopup(dimensionId, 2, <DimensionLineSetting {...dimensionProps} />, true)
} else { } else {
@ -110,9 +118,13 @@ export default function SecondOption() {
case 'planSize': { case 'planSize': {
// //
setShowPlanSizeSettingModal(true) if (!showPlanSizeSettingModal) {
setShowDimensionLineSettingModal(false) setShowPlanSizeSettingModal(true)
addPopup(planSizeId, 2, <PlanSizeSetting {...planSizeProps} />, true) addPopup(planSizeId, 2, <PlanSizeSetting {...planSizeProps} />, true)
} else {
setShowPlanSizeSettingModal(false)
closePopup(planSizeId, true)
}
break break
} }
} }
@ -123,11 +135,12 @@ export default function SecondOption() {
return { return {
...prev, ...prev,
[fontProps.type]: { [fontProps.type]: {
fontFamily: font.fontFamily, fontFamily: font.fontFamily.value,
fontWeight: font.fontWeight, fontWeight: font.fontWeight.value,
fontSize: font.fontSize, fontSize: font.fontSize.value,
fontColor: font.fontColor, fontColor: font.fontColor.value,
}, },
fontFlag: true,
} }
}) })
} }
@ -140,16 +153,48 @@ export default function SecondOption() {
isConfig: true, isConfig: true,
} }
const dimensionProps = {
id: dimensionId,
isShow: showDimensionLineSettingModal,
setIsShow: setShowDimensionLineSettingModal,
}
const planSizeProps = { const planSizeProps = {
id: planSizeId, id: planSizeId,
horizon, // horizon,
setHorizon, // setHorizon,
vertical, // vertical,
setVertical, // setVertical,
horizon: planSizeSettingMode.originHorizon,
vertical: planSizeSettingMode.originVertical,
isShow: showPlanSizeSettingModal,
setIsShow: setShowPlanSizeSettingModal, setIsShow: setShowPlanSizeSettingModal,
pos: { x: 1025, y: 180 }, pos: { x: 1025, y: 180 },
} }
const onClickOption = async (item) => {
// ( )
if (
item.column === 'adsorpRangeSmall' ||
item.column === 'adsorpRangeSmallSemi' ||
item.column === 'adsorpRangeMedium' ||
item.column === 'adsorpRangeLarge'
) {
// option4
//const updatedOption4 = option4.map((option) => (option.id === item.id ? { ...option, selected: true } : { ...option, selected: false }))
const options = settingModalSecondOptions?.option4.map((option4) => {
option4.selected = option4.id === item.id
return option4
})
setSettingModalSecondOptions({ ...settingModalSecondOptions, option3, option4, fontFlag: true })
} else if (item === 'adsorpPoint') {
setAdsorptionPointMode({ ...adsorptionPointMode, adsorptionPoint: !adsorptionPointMode.adsorptionPoint, fontFlag: true })
}
//setAdsorptionRange(item.range) //
setAdsorptionRange(50)
}
return ( return (
<> <>
<div className="modal-check-btn-wrap"> <div className="modal-check-btn-wrap">
@ -184,12 +229,12 @@ export default function SecondOption() {
</button> </button>
<button <button
className="adsorption-point act" className="adsorption-point act"
onClick={(e) => { onClick={() => {
setAdsorptionPointMode(!adsorptionPointMode) onClickOption('adsorpPoint')
}} }}
> >
<span>{getMessage('modal.canvas.setting.font.plan.absorption.point')}</span> <span>{getMessage('modal.canvas.setting.font.plan.absorption.point')}</span>
<i>{adsorptionPointMode ? 'ON' : 'OFF'}</i> <i>{adsorptionPointMode.adsorptionPoint ? 'ON' : 'OFF'}</i>
</button> </button>
</div> </div>
</div> </div>

View File

@ -9,6 +9,28 @@ import { useMessage } from '@/hooks/useMessage'
import { dimensionLineSettingsState } from '@/store/commonUtilsAtom' import { dimensionLineSettingsState } from '@/store/commonUtilsAtom'
import { useRecoilState } from 'recoil' import { useRecoilState } from 'recoil'
import { globalFontAtom } from '@/store/fontAtom' import { globalFontAtom } from '@/store/fontAtom'
import { useCanvasSetting } from '@/hooks/option/useCanvasSetting'
const fonts = [
{ id: 1, name: 'MS PGothic', value: 'MS PGothic' },
{ id: 2, name: '@Yu Gothic', value: '@Yu Gothic' },
{ id: 3, name: 'Yu Gothic', value: 'Yu Gothic' },
{ id: 4, name: '@Yu Gothic UI', value: '@Yu Gothic UI' },
{ id: 5, name: 'Yu Gothic UI', value: 'Yu Gothic UI' },
//3,
]
const fontSizes = [
...Array.from({ length: 4 }).map((_, index) => {
return { id: index + 8, name: index + 8, value: index + 8 }
}),
...Array.from({ length: 9 }).map((_, index) => {
return { id: (index + 6) * 2, name: (index + 6) * 2, value: (index + 6) * 2 }
}),
{ id: 36, name: 36, value: 36 },
{ id: 48, name: 48, value: 48 },
{ id: 72, name: 72, value: 72 },
]
export default function DimensionLineSetting(props) { export default function DimensionLineSetting(props) {
const { isShow, setIsShow, id, pos = { x: 985, y: 180 } } = props const { isShow, setIsShow, id, pos = { x: 985, y: 180 } } = props
@ -16,28 +38,79 @@ export default function DimensionLineSetting(props) {
const pixels = Array.from({ length: 5 }).map((_, index) => { const pixels = Array.from({ length: 5 }).map((_, index) => {
return { id: index, name: index + 1, value: index + 1 } return { id: index, name: index + 1, value: index + 1 }
}) })
const [dimensionLineSettings, setDimensionLineSettings] = useRecoilState(dimensionLineSettingsState) //const [dimensionLineSettings, setDimensionLineSettings] = useRecoilState(dimensionLineSettingsState)
const [globalFont, setGlobalFont] = useRecoilState(globalFontAtom) //const [originPixel, setOriginPixel] = useState(dimensionLineSettings.pixel)
const [originPixel, setOriginPixel] = useState(dimensionLineSettings.pixel) //const [originColor, setOriginColor] = useState(dimensionLineSettings.color)
const [originColor, setOriginColor] = useState(dimensionLineSettings.color) //const [globalFont, setGlobalFont] = useRecoilState(globalFontAtom)
const [originFont, setOriginFont] = useState(globalFont.dimensionLineText.fontFamily)
const [originFontColor, setOriginFontColor] = useState(globalFont.dimensionLineText.fontColor)
const [originFontSize, setOriginFontSize] = useState(globalFont.dimensionLineText.fontSize)
const [originFontWeight, setOriginFontWeight] = useState(globalFont.dimensionLineText.fontWeight)
const [fontModalId, setFontModalId] = useState(uuidv4())
const [fontModalId, setFontModalId] = useState(uuidv4())
const [colorModalId, setColorModalId] = useState(uuidv4()) const [colorModalId, setColorModalId] = useState(uuidv4())
const [showColorPickerModal, setShowColorPickerModal] = useState(false) const [showColorPickerModal, setShowColorPickerModal] = useState(false)
const [showFontModal, setShowFontModal] = useState(false) const [showFontModal, setShowFontModal] = useState(false)
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { globalFont, setGlobalFont, dimensionLineSettings, setDimensionLineSettings } = useCanvasSetting()
const [originFont, setOriginFont] = useState(globalFont.dimensionLineText.fontFamily)
const [originFontWeight, setOriginFontWeight] = useState(globalFont.dimensionLineText.fontWeight)
const [originFontSize, setOriginFontSize] = useState(globalFont.dimensionLineText.fontSize)
const [originFontColor, setOriginFontColor] = useState(globalFont.dimensionLineText.fontColor)
const [originPixel, setOriginPixel] = useState(dimensionLineSettings.pixel)
const [originColor, setOriginColor] = useState(dimensionLineSettings.color)
const fontOptions = [
{ id: 'normal', name: getMessage('font.style.normal'), value: 'normal' },
{ id: 'italic', name: getMessage('font.style.italic'), value: 'italic' },
{ id: 'bold', name: getMessage('font.style.bold'), value: 'bold' },
{ id: 'boldAndItalic', name: getMessage('font.style.bold.italic'), value: 'boldAndItalic' },
]
const fontColors = [
{ id: 'black', name: getMessage('color.black'), value: 'black' },
{ id: 'red', name: getMessage('color.red'), value: 'red' },
{ id: 'blue', name: getMessage('color.blue'), value: 'blue' },
{ id: 'gray', name: getMessage('color.gray'), value: 'gray' },
{ id: 'yellow', name: getMessage('color.yellow'), value: 'yellow' },
{ id: 'green', name: getMessage('color.green'), value: 'green' },
{ id: 'pink', name: getMessage('color.pink'), value: 'pink' },
{ id: 'gold', name: getMessage('color.gold'), value: 'gold' },
{ id: 'darkblue', name: getMessage('color.darkblue'), value: 'darkblue' },
]
let originPixelView
useEffect(() => { useEffect(() => {
if (originPixel) { if (originPixel) {
setOriginPixel(pixels?.filter((data) => data.value === originPixel)[0]) setOriginPixel(pixels?.filter((data) => data.value === originPixel)[0])
originPixelView = originPixel
} }
if (globalFont.dimensionLineText.fontFamily) {
setOriginFont(fonts.filter((data) => data.value === globalFont.dimensionLineText.fontFamily)[0])
}
if (globalFont.dimensionLineText.fontWeight) {
setOriginFontWeight(fontOptions.filter((data) => data.value === globalFont.dimensionLineText.fontWeight)[0])
}
if (globalFont.dimensionLineText.fontSize) {
setOriginFontSize(fontSizes.filter((data) => data.value === globalFont.dimensionLineText.fontSize)[0])
}
if (globalFont.dimensionLineText.fontColor) {
setOriginFontColor(fontColors.filter((data) => data.value === globalFont.dimensionLineText.fontColor)[0])
}
setIsShow(true) setIsShow(true)
}, []) }, [])
useEffect(() => {
if (originPixel.name) {
originPixelView = originPixel.name
setOriginPixel(originPixel)
}
}, [originPixel])
useEffect(() => { useEffect(() => {
if (!isShow) { if (!isShow) {
closePopups([fontModalId, colorModalId]) closePopups([fontModalId, colorModalId])
@ -46,9 +119,9 @@ export default function DimensionLineSetting(props) {
const handleFontSave = (font) => { const handleFontSave = (font) => {
setOriginFont(font.fontFamily) setOriginFont(font.fontFamily)
setOriginFontWeight(font.fontWeight)
setOriginFontSize(font.fontSize) setOriginFontSize(font.fontSize)
setOriginFontColor(font.fontColor) setOriginFontColor(font.fontColor)
setOriginFontWeight(font.fontWeight)
} }
const handleColorSave = () => {} const handleColorSave = () => {}
@ -69,10 +142,10 @@ export default function DimensionLineSetting(props) {
isShow: showFontModal, isShow: showFontModal,
setIsShow: setShowFontModal, setIsShow: setShowFontModal,
font: { font: {
fontFamily: originFont, fontFamily: originFont?.value,
fontSize: originFontSize, fontWeight: originFontWeight?.value,
fontColor: originFontColor, fontSize: originFontSize?.value,
fontWeight: originFontWeight, fontColor: originFontColor?.value,
}, },
onSave: handleFontSave, onSave: handleFontSave,
isConfig: true, isConfig: true,
@ -99,17 +172,18 @@ export default function DimensionLineSetting(props) {
return { return {
...prev, ...prev,
dimensionLineText: { dimensionLineText: {
fontFamily: originFont, fontFamily: originFont.value,
fontWeight: originFontWeight, fontWeight: originFontWeight.value,
fontSize: originFontSize, fontSize: originFontSize.value,
fontColor: originFontColor, fontColor: originFontColor.value,
}, },
fontFlag: true,
} }
}) })
setDimensionLineSettings((prev) => { setDimensionLineSettings((prev) => {
return { return {
...prev, ...prev,
pixel: originPixel?.value, pixel: originPixel.name,
color: originColor, color: originColor,
} }
}) })
@ -125,8 +199,8 @@ export default function DimensionLineSetting(props) {
<button <button
className="modal-close" className="modal-close"
onClick={() => { onClick={() => {
closePopups([fontModalId, colorModalId, id])
setIsShow(false) setIsShow(false)
closePopups([fontModalId, colorModalId, id])
}} }}
> >
닫기 닫기
@ -160,10 +234,10 @@ export default function DimensionLineSetting(props) {
className="font" className="font"
style={{ style={{
fontFamily: originFont?.value ?? '', fontFamily: originFont?.value ?? '',
color: originFontColor.value ?? 'black', fontWeight: originFontWeight?.value?.toLowerCase().includes('bold') ? 'bold' : 'normal',
fontStyle: originFontWeight?.value?.toLowerCase().includes('italic') ? 'italic' : 'normal',
fontSize: originFontSize?.value ?? '12px', fontSize: originFontSize?.value ?? '12px',
fontStyle: originFontWeight?.value.toLowerCase().includes('italic') ? 'italic' : 'normal', color: originFontColor?.value ?? 'black',
fontWeight: originFontWeight?.value.toLowerCase().includes('bold') ? 'bold' : 'normal',
}} }}
> >
9,999 9,999
@ -173,7 +247,7 @@ export default function DimensionLineSetting(props) {
style={{ style={{
backgroundColor: originColor, backgroundColor: originColor,
borderColor: originColor, borderColor: originColor,
height: originPixel?.value, height: originPixel.name,
}} }}
></span> ></span>
</div> </div>

View File

@ -1,18 +1,64 @@
import { useState } from 'react' import { useEffect, useState } from 'react'
import { useRecoilValue } from 'recoil' import { useRecoilValue } from 'recoil'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { usePopup } from '@/hooks/usePopup' import { usePopup } from '@/hooks/usePopup'
import WithDraggable from '@/components/common/draggable/WithDraggable' import WithDraggable from '@/components/common/draggable/WithDraggable'
import { canvasState } from '@/store/canvasAtom' import { canvasState } from '@/store/canvasAtom'
import { useCanvasSetting } from '@/hooks/option/useCanvasSetting'
import { onlyNumberInputChange } from '@/util/input-utils'
export default function PlanSizeSetting(props) { export default function PlanSizeSetting(props) {
const { horizon, setHorizon, vertical, setVertical, id, pos = { x: 985, y: 180 }, setIsShow } = props const { setIsShow, horizon, vertical, id, pos = { x: 985, y: 180 } } = props
const { closePopup } = usePopup() const { closePopup } = usePopup()
const { getMessage } = useMessage() const { getMessage } = useMessage()
const [originHorizon, setOriginHorizon] = useState(horizon)
const [originVertical, setOriginVertical] = useState(vertical)
const canvas = useRecoilValue(canvasState) const canvas = useRecoilValue(canvasState)
// const [originHorizon, setOriginHorizon] = useCanvasSetting(horizon)
// const [originVertical, setOriginVertical] = useCanvasSetting(vertical)
// const { originHorizon, setOriginHorizon, originVertical, setOriginVertical } = useCanvasSetting()
const { planSizeSettingMode, setPlanSizeSettingMode } = useCanvasSetting()
//
useEffect(() => {
console.log('PlanSizeSetting useEffect 실행')
console.log('11111', planSizeSettingMode)
//setOriginHorizon({ originHorizon: horizon, flag: false })
//setOriginVertical({ originVertical: vertical, flag: false })
}, [])
const onSave = () => {
console.log('22222', planSizeSettingMode)
setPlanSizeSettingMode((prev) => {
console.log('4', prev)
return {
...prev,
originHorizon: planSizeSettingMode.originHorizon,
originVertical: planSizeSettingMode.originVertical,
flag: true,
}
})
canvas.setWidth(planSizeSettingMode.originHorizon)
canvas.setHeight(planSizeSettingMode.originVertical)
canvas.renderAll()
setIsShow(false)
closePopup(id, true)
}
const changeInput = (value, e) => {
const { name } = e.target
console.log('name', name, value)
setPlanSizeSettingMode((prev) => {
return {
...prev,
[name]: value,
flag: false,
}
})
}
return ( return (
<WithDraggable isShow={true} pos={pos}> <WithDraggable isShow={true} pos={pos}>
<div className={`modal-pop-wrap xsm mount`}> <div className={`modal-pop-wrap xsm mount`}>
@ -33,7 +79,15 @@ export default function PlanSizeSetting(props) {
<div className="outline-form mb10"> <div className="outline-form mb10">
<span style={{ width: 'auto' }}>{getMessage('common.horizon')}</span> <span style={{ width: 'auto' }}>{getMessage('common.horizon')}</span>
<div className="input-grid mr5" style={{ width: '90px' }}> <div className="input-grid mr5" style={{ width: '90px' }}>
<input type="text" className="input-origin block" value={originHorizon} onChange={(e) => setOriginHorizon(Number(e.target.value))} /> <input
type="text"
className="input-origin block"
name={`originHorizon`}
value={planSizeSettingMode.originHorizon}
//onChange={(e) => setPlanSizeSettingMode({ ...planSizeSettingMode, originHorizon: Number(e.target.value), flag: false })}
//onFocus={(e) => (originHorizon.current.value = '')}
onChange={(e) => onlyNumberInputChange(e, changeInput)}
/>
</div> </div>
<span className="thin">mm</span> <span className="thin">mm</span>
</div> </div>
@ -43,26 +97,18 @@ export default function PlanSizeSetting(props) {
<input <input
type="text" type="text"
className="input-origin block" className="input-origin block"
value={originVertical} name={`originVertical`}
onChange={(e) => setOriginVertical(Number(e.target.value))} value={planSizeSettingMode.originVertical}
//onChange={(e) => setPlanSizeSettingMode({ ...planSizeSettingMode, originVertical: Number(e.target.value), flag: false })}
//onFocus={(e) => (originVertical.current.value = '')}
onChange={(e) => onlyNumberInputChange(e, changeInput)}
/> />
</div> </div>
<span className="thin">mm</span> <span className="thin">mm</span>
</div> </div>
</div> </div>
<div className="grid-btn-wrap"> <div className="grid-btn-wrap">
<button <button className="btn-frame modal act" onClick={onSave}>
className="btn-frame modal act"
onClick={() => {
setHorizon(originHorizon)
setVertical(originVertical)
setIsShow(false)
closePopup(id)
canvas.setWidth(originHorizon)
canvas.setHeight(originVertical)
canvas.renderAll()
}}
>
{getMessage('modal.common.save')} {getMessage('modal.common.save')}
</button> </button>
</div> </div>

View File

@ -12,7 +12,7 @@ export function useFont() {
const circuitNumberText = useRecoilValue(fontSelector('circuitNumberText')) const circuitNumberText = useRecoilValue(fontSelector('circuitNumberText'))
useEffect(() => { useEffect(() => {
if (canvas) { if (canvas && commonText.fontWeight?.value) {
const textObjs = canvas?.getObjects().filter((obj) => obj.name === 'commonText') const textObjs = canvas?.getObjects().filter((obj) => obj.name === 'commonText')
textObjs.forEach((obj) => { textObjs.forEach((obj) => {
obj.set({ obj.set({
@ -28,7 +28,7 @@ export function useFont() {
}, [commonText]) }, [commonText])
useEffect(() => { useEffect(() => {
if (canvas) { if (canvas && dimensionLineText.fontWeight?.value) {
const textObjs = canvas?.getObjects().filter((obj) => obj.name === 'dimensionLineText') const textObjs = canvas?.getObjects().filter((obj) => obj.name === 'dimensionLineText')
textObjs.forEach((obj) => { textObjs.forEach((obj) => {
obj.set({ obj.set({
@ -44,8 +44,8 @@ export function useFont() {
}, [dimensionLineText]) }, [dimensionLineText])
useEffect(() => { useEffect(() => {
if (canvas) { if (canvas && flowText.fontWeight?.value) {
const textObjs = canvas.getObjects().filter((obj) => obj.name === 'flowText') const textObjs = canvas?.getObjects().filter((obj) => obj.name === 'flowText')
textObjs.forEach((obj) => { textObjs.forEach((obj) => {
obj.set({ obj.set({
fontFamily: flowText.fontFamily.value, fontFamily: flowText.fontFamily.value,
@ -60,8 +60,8 @@ export function useFont() {
}, [flowText]) }, [flowText])
useEffect(() => { useEffect(() => {
if (canvas) { if (canvas && lengthText.fontWeight?.value) {
const textObjs = canvas.getObjects().filter((obj) => obj.name === 'lengthText') const textObjs = canvas?.getObjects().filter((obj) => obj.name === 'lengthText')
textObjs.forEach((obj) => { textObjs.forEach((obj) => {
obj.set({ obj.set({
fontFamily: lengthText.fontFamily.value, fontFamily: lengthText.fontFamily.value,

View File

@ -80,7 +80,7 @@ export const useEstimateController = (planNo) => {
itemId: '', //제품번호 itemId: '', //제품번호
itemNo: '', itemNo: '',
itemName: '', //형명 itemName: '', //형명
amount: '', //수량 amount: '0', //수량
unitPrice: '0', unitPrice: '0',
unit: '', //단위 unit: '', //단위
salePrice: '', //단가 salePrice: '', //단가
@ -185,13 +185,14 @@ export const useEstimateController = (planNo) => {
return alert(getMessage('estimate.detail.save.requiredItem')) return alert(getMessage('estimate.detail.save.requiredItem'))
} }
console.log('최종 아이템 정보::;', estimateData.itemList)
estimateData.itemList.map((item) => { estimateData.itemList.map((item) => {
item.amount = item.amount.replaceAll(',', '') item.amount = item.amount?.replaceAll(',', '')
item.salePrice = parseFloat(item.salePrice.replaceAll(',', '')).toFixed(2) item.salePrice = parseFloat(item.salePrice?.replaceAll(',', '')).toFixed(2)
item.saleTotPrice = parseFloat(item.saleTotPrice.replaceAll(',', '')).toFixed(2) item.saleTotPrice = parseFloat(item.saleTotPrice?.replaceAll(',', '')).toFixed(2)
}) })
console.log('최종 정보::;', estimateData)
console.log('최종 정보::;', estimateData)
//2. 상세데이터 저장 //2. 상세데이터 저장
// return // return
try { try {

View File

@ -5,6 +5,7 @@ import { rectToPolygon, setSurfaceShapePattern } from '@/util/canvas-util'
import { roofDisplaySelector } from '@/store/settingAtom' import { roofDisplaySelector } from '@/store/settingAtom'
import offsetPolygon from '@/util/qpolygon-utils' import offsetPolygon from '@/util/qpolygon-utils'
import { QPolygon } from '@/components/fabric/QPolygon' import { QPolygon } from '@/components/fabric/QPolygon'
import { QLine } from '@/components/fabric/QLine'
import { moduleSetupSurfaceState, moduleIsSetupState } from '@/store/canvasAtom' import { moduleSetupSurfaceState, moduleIsSetupState } from '@/store/canvasAtom'
import { useEvent } from '@/hooks/useEvent' import { useEvent } from '@/hooks/useEvent'
import { POLYGON_TYPE, BATCH_TYPE } from '@/common/common' import { POLYGON_TYPE, BATCH_TYPE } from '@/common/common'
@ -53,6 +54,11 @@ export function useModuleBasicSetting() {
setupSurface.setViewLengthText(false) setupSurface.setViewLengthText(false)
canvas.add(setupSurface) canvas.add(setupSurface)
bottomModuleLine(setupSurface)
topModuleLine(setupSurface)
leftModuleLine(setupSurface)
// rightModuleLine(setupSurface)
//지붕면 선택 금지 //지붕면 선택 금지
roof.set({ roof.set({
selectable: false, selectable: false,
@ -597,7 +603,7 @@ export function useModuleBasicSetting() {
name: 'module', name: 'module',
}) })
tempModule.setViewLengthText(false) tempModule.setViewLengthText(false)
canvas?.add(tempModule) // canvas?.add(tempModule)
moduleSetupArray.push(tempModule) moduleSetupArray.push(tempModule)
} }
} else { } else {
@ -617,7 +623,7 @@ export function useModuleBasicSetting() {
lineRow: row, lineRow: row,
name: 'module', name: 'module',
}) })
canvas?.add(tempModule) // canvas?.add(tempModule)
moduleSetupArray.push(tempModule) moduleSetupArray.push(tempModule)
} }
} }
@ -765,41 +771,417 @@ export function useModuleBasicSetting() {
return hull return hull
} }
const calcMinXByHeightDistance = (nowSurface, index, reverse) => { const bottomModuleLine = (nowSurface) => {
function calculateSlopeIntercept(x1, y1, x2, y2) { let selectedLine = null
console.log('Intercept', x1, y1, x2, y2)
const slope = (y2 - y1) / (x2 - x1) const sortedLines = sortLinesByTopLeft(nowSurface.lines)
const intercept = y1 - slope * x1
return { slope, intercept } const moduleWidthLength = 173.3 + 5 //임시 약간 여유를 줌
// if (nowSurface.flowDirection === 'east') {
// const leftFlow = nowSurface.lines.reduce(
// (acc, line, index) => {
// if (line.x1 < acc.x1 || (line.x1 === acc.x1 && line.y1 < acc.y1)) {
// return { x1: line.x1, y1: line.y1, index: index }
// }
// return acc
// },
// { x1: Infinity, y1: Infinity, index: -1 }, // 초기값: 무한대와 유효하지 않은 인덱스
// )
// selectedLine = leftFlow
// } else if (nowSurface.flowDirection === 'west') {
// const rightFlow = nowSurface.lines.reduce(
// (acc, line, index) => {
// if (line.x1 > acc.x1 || (line.x1 === acc.x1 && line.y1 > acc.y1)) {
// return { x1: line.x1, y1: line.y1, index: index }
// }
// return acc
// },
// { x1: 0, y1: 0, index: -1 }, // 초기값: 무한대와 유효하지 않은 인덱스
// )
// selectedLine = rightFlow
// } else if (nowSurface.flowDirection === 'north') {
// const topFlow = nowSurface.lines.reduce(
// (acc, line, index) => {
// if (line.y1 < acc.y1 || (line.y1 === acc.y1 && line.x1 < acc.x1)) {
// return { x1: line.x1, y1: line.y1, index: index }
// }
// return acc
// },
// { x1: Infinity, y1: Infinity, index: -1 }, // 초기값: 무한대와 유효하지 않은 인덱스
// )
// selectedLine = topFlow
// } else {
const bottomFlow = nowSurface.lines.reduce(
(acc, line, index) => {
if (line.y1 > acc.y1 || (line.y1 === acc.y1 && line.x1 > acc.x1)) {
return { x1: line.x1, y1: line.y1, index: index }
}
return acc
},
{ x1: 0, y1: 0, index: -1 }, // 초기값: 무한대와 유효하지 않은 인덱스
)
selectedLine = bottomFlow
// }
let prevLines = nowSurface.lines[(selectedLine.index - 1 + nowSurface.lines.length) % nowSurface.lines.length]
let nextLines = nowSurface.lines[selectedLine.index]
const overlapCoords = { x: nextLines.x1, y: nextLines.y1 } //겹치는 꼭지점
const m1 = (prevLines.y2 - prevLines.y1) / (prevLines.x2 - prevLines.x1)
const m2 = (nextLines.y2 - nextLines.y1) / (nextLines.x2 - nextLines.x1)
const c1 = prevLines.y1 - m1 * prevLines.x1
const c2 = nextLines.y1 - m2 * nextLines.x1
// Step 2: Calculate intersection point
let xIntersectPrev = 0
let yIntersectPrev = 0
let xIntersectNext = 0
let yIntersectNext = 0
let endPoint = prevLines.y1 > nextLines.y2 ? prevLines.y1 : nextLines.y2
let biggerEndPoint = prevLines.y1 > nextLines.y2 ? 'left' : 'right'
//bottom일 경우
xIntersectPrev = (endPoint - c1) / m1
yIntersectPrev = m1 * xIntersectPrev + c1
xIntersectNext = (endPoint - c2) / m2
yIntersectNext = m2 * xIntersectNext + c2
let lineCoords
let polygonCoords
let ratio = 1
if (biggerEndPoint === 'left') {
//왼쪽이 더 밑이면 우측 라인에 절편으로 계산
lineCoords = [prevLines.x1, yIntersectNext, xIntersectNext, yIntersectNext]
polygonCoords = [
{ x: prevLines.x1, y: yIntersectNext },
{ x: xIntersectNext, y: yIntersectNext },
{ x: overlapCoords.x, y: overlapCoords.y },
]
ratio = moduleWidthLength / Math.abs(prevLines.x1 - xIntersectNext)
} else {
lineCoords = [xIntersectPrev, yIntersectPrev, nextLines.x2, yIntersectPrev]
polygonCoords = [
{ x: xIntersectPrev, y: yIntersectPrev },
{ x: nextLines.x2, y: yIntersectPrev },
{ x: overlapCoords.x, y: overlapCoords.y },
]
ratio = moduleWidthLength / Math.abs(nextLines.x2 - xIntersectPrev)
} }
let prevLines = nowSurface.lines[(index - 1 + nowSurface.lines.length) % nowSurface.lines.length] const tempTriangle = new QPolygon(polygonCoords, {
let nextLines = nowSurface.lines[index] fill: 'transparent',
stroke: 'green',
strokeWidth: 2,
originY: 'bottom',
strokeDashArray: [5, 5],
// fontSize: 15,
})
// 선분 정보 // canvas.add(tempTriangle)
const l1 = prevLines
const l2 = nextLines
const lineLength = 172.2
// l1과 l2의 기울기 및 절편 let cloneCoords = []
let { slope: m1, intercept: b1 } = calculateSlopeIntercept(l1.x1, l1.y1, l1.x2, l1.y2) tempTriangle.clone((clone) => {
let { slope: m2, intercept: b2 } = calculateSlopeIntercept(l2.x1, l2.y1, l2.x2, l2.y2) clone.scale(ratio)
cloneCoords = clone.getCurrentPoints()
})
console.log(m1, b1, m2, b2) //아래쪽에선 잴 작은
const vertexPoints = cloneCoords.reduce((acc, point, index) => (acc['y'] > point['y'] ? acc : point))
// 가로선 x1 계산 const differenceDistance = overlapCoords.x - vertexPoints.x
const x1 = (m2 * lineLength + b2 - b1) / (m1 - m2)
const x2 = x1 + lineLength // 끝점 x2
// 가로선 y값 계산 const newTriangleCoords = cloneCoords.map((point) => {
const y0 = m1 * x1 + b1 return { x: point.x + differenceDistance, y: point.y }
})
// 결과 출력 const deleteBottomPoint = newTriangleCoords.reduce((acc, point) => (acc['y'] > point['y'] ? acc : point))
const deleteIndex = newTriangleCoords.indexOf(deleteBottomPoint)
if (deleteIndex !== -1) newTriangleCoords.splice(deleteIndex, 1)
console.log({ x1: x1, y1: y0, x2: x2, y2: y0 }) const newLine = new QLine([newTriangleCoords[0].x, newTriangleCoords[0].y, newTriangleCoords[1].x, newTriangleCoords[1].y], {
return { x1: x1, y1: y0, x2: x2, y2: y0 } fill: 'transparent',
stroke: 'red',
strokeWidth: 2,
selectable: true,
fontSize: 14,
})
canvas.add(newLine)
return newLine
}
const topModuleLine = (nowSurface) => {
let selectedLine = null
const moduleWidthLength = 173.3 + 5 //임시 약간 여유를 줌
const topFlow = nowSurface.lines.reduce(
(acc, line, index) => {
if (line.y1 < acc.y1 || (line.y1 === acc.y1 && line.x1 < acc.x1)) {
return { x1: line.x1, y1: line.y1, index: index }
}
return acc
},
{ x1: Infinity, y1: Infinity, index: -1 }, // 초기값: 무한대와 유효하지 않은 인덱스
)
selectedLine = topFlow
let prevLines = nowSurface.lines[(selectedLine.index - 1 + nowSurface.lines.length) % nowSurface.lines.length]
let nextLines = nowSurface.lines[selectedLine.index]
const overlapCoords = { x: nextLines.x1, y: nextLines.y1 } //겹치는 꼭지점
const m1 = (prevLines.y2 - prevLines.y1) / (prevLines.x2 - prevLines.x1)
const m2 = (nextLines.y2 - nextLines.y1) / (nextLines.x2 - nextLines.x1)
const c1 = prevLines.y1 - m1 * prevLines.x1
const c2 = nextLines.y1 - m2 * nextLines.x1
// Step 2: Calculate intersection point
let xIntersectPrev = 0
let yIntersectPrev = 0
let xIntersectNext = 0
let yIntersectNext = 0
let endPoint = prevLines.y1 > nextLines.y2 ? nextLines.y2 : prevLines.y1
let biggerEndPoint = prevLines.y1 < nextLines.y2 ? 'left' : 'right'
//bottom일 경우
xIntersectPrev = (endPoint - c1) / m1
yIntersectPrev = m1 * xIntersectPrev + c1
xIntersectNext = (endPoint - c2) / m2
yIntersectNext = m2 * xIntersectNext + c2
let lineCoords
let polygonCoords
let ratio = 1
if (biggerEndPoint === 'left') {
//왼쪽이 더 밑이면 우측 라인에 절편으로 계산
lineCoords = [prevLines.x1, yIntersectNext, xIntersectNext, yIntersectNext]
polygonCoords = [
{ x: prevLines.x1, y: yIntersectNext },
{ x: xIntersectNext, y: yIntersectNext },
{ x: overlapCoords.x, y: overlapCoords.y },
]
ratio = moduleWidthLength / Math.abs(prevLines.x1 - xIntersectNext)
} else {
lineCoords = [xIntersectPrev, yIntersectPrev, nextLines.x2, yIntersectPrev]
polygonCoords = [
{ x: xIntersectPrev, y: yIntersectPrev },
{ x: nextLines.x2, y: yIntersectPrev },
{ x: overlapCoords.x, y: overlapCoords.y },
]
ratio = moduleWidthLength / Math.abs(nextLines.x2 - xIntersectPrev)
}
const tempTriangle = new QPolygon(polygonCoords, {
fill: 'transparent',
stroke: 'green',
strokeWidth: 2,
originY: 'top',
strokeDashArray: [5, 5],
// fontSize: 15,
})
// canvas.add(tempTriangle)
let cloneCoords = []
tempTriangle.clone((clone) => {
clone.scale(ratio)
cloneCoords = clone.getCurrentPoints()
})
//아래쪽에선 잴 작은
const vertexPoints = cloneCoords.reduce((acc, point, index) => (acc['y'] < point['y'] ? acc : point))
const differenceDistance = overlapCoords.x - vertexPoints.x
const newTriangleCoords = cloneCoords.map((point) => {
return { x: point.x + differenceDistance, y: point.y }
})
// const newTriangle1 = new QPolygon(newTriangleCoords, {
// fill: 'transparent',
// stroke: 'red',
// strokeWidth: 1,
// selectable: true,
// fontSize: 14,
// })
// canvas.add(newTriangle1)
const deleteBottomPoint = newTriangleCoords.reduce((acc, point) => (acc['y'] < point['y'] ? acc : point))
const deleteIndex = newTriangleCoords.indexOf(deleteBottomPoint)
if (deleteIndex !== -1) newTriangleCoords.splice(deleteIndex, 1)
const newLine = new QLine([newTriangleCoords[0].x, newTriangleCoords[0].y, newTriangleCoords[1].x, newTriangleCoords[1].y], {
fill: 'transparent',
stroke: 'red',
strokeWidth: 2,
selectable: true,
fontSize: 14,
})
canvas.add(newLine)
return newLine
}
const leftModuleLine = (nowSurface) => {
let selectedLine = null
sortLinesByTopLeft(nowSurface)
console.log('nowSurface', nowSurface)
const moduleWidthLength = 173.3 + 5 //임시 약간 여유를 줌
const leftFlow = nowSurface.lines.reduce(
(acc, line, index) => {
if (line.x1 < acc.x1 || (line.x1 === acc.x1 && line.y1 < acc.y1)) {
return { x1: line.x1, y1: line.y1, index: index }
}
return acc
},
{ x1: Infinity, y1: Infinity, index: -1 }, // 초기값: 무한대와 유효하지 않은 인덱스
)
selectedLine = leftFlow
let prevLines = nowSurface.lines[(selectedLine.index - 1 + nowSurface.lines.length) % nowSurface.lines.length]
let nextLines = nowSurface.lines[selectedLine.index]
const overlapCoords = { x: nextLines.x1, y: nextLines.y1 } //겹치는 꼭지점
const m1 = (prevLines.y2 - prevLines.y1) / (prevLines.x2 - prevLines.x1)
const m2 = (nextLines.y2 - nextLines.y1) / (nextLines.x2 - nextLines.x1)
const c1 = prevLines.y1 - m1 * prevLines.x1
const c2 = nextLines.y1 - m2 * nextLines.x1
// Step 2: Calculate intersection point
let xIntersectPrev = 0
let yIntersectPrev = 0
let xIntersectNext = 0
let yIntersectNext = 0
let biggerEndPoint = prevLines.x1 > nextLines.x2 ? 'top' : 'bottom'
console.log('prevLines.x1', prevLines.x1)
console.log('nextLines.x2', nextLines.x2)
console.log('biggerEndPoint', biggerEndPoint)
//bottom일 경우
xIntersectPrev = prevLines.x1
yIntersectPrev = m1 * xIntersectPrev + c1
xIntersectNext = prevLines.x1
yIntersectNext = m2 * xIntersectNext + c2
let lineCoords
let polygonCoords
let ratio = 1
if (biggerEndPoint === 'top') {
//윗쪽이이 더 밑이면 아래 라인에 절편으로 계산
lineCoords = [prevLines.x1, yIntersectNext, xIntersectNext, yIntersectNext]
polygonCoords = [
{ x: prevLines.x1, y: yIntersectNext },
{ x: xIntersectNext, y: yIntersectNext },
{ x: overlapCoords.x, y: overlapCoords.y },
]
ratio = moduleWidthLength / Math.abs(prevLines.x1 - xIntersectNext)
} else {
lineCoords = [xIntersectPrev, prevLines.y1, xIntersectPrev, yIntersectPrev]
polygonCoords = [
{ x: xIntersectNext, y: prevLines.y1 },
{ x: xIntersectNext, y: yIntersectNext },
{ x: overlapCoords.x, y: overlapCoords.y },
]
ratio = moduleWidthLength / Math.abs(prevLines.y1 - yIntersectNext)
}
const tempTriangle = new QPolygon(polygonCoords, {
fill: 'transparent',
stroke: 'green',
strokeWidth: 2,
originX: 'left',
strokeDashArray: [5, 5],
// fontSize: 15,
selectable: true,
})
// canvas.add(tempTriangle)
let cloneCoords = []
tempTriangle.clone((clone) => {
clone.scale(ratio)
cloneCoords = clone.getCurrentPoints()
// canvas.add(clone)
})
canvas.remove(tempTriangle)
//left에선 가장 왼쪽
const vertexPoints = cloneCoords.reduce((acc, point, index) => (acc['x'] < point['x'] ? acc : point))
const differenceDistance = overlapCoords.y - vertexPoints.y
const newTriangleCoords = cloneCoords.map((point) => {
return { x: point.x, y: point.y + differenceDistance }
})
// const newTriangle1 = new QPolygon(newTriangleCoords, {
// fill: 'transparent',
// stroke: 'red',
// strokeWidth: 1,
// selectable: true,
// fontSize: 14,
// })
// canvas.add(newTriangle1)
const deleteLeftPoint = newTriangleCoords.reduce((acc, point) => (acc['x'] < point['x'] ? acc : point))
const deleteIndex = newTriangleCoords.indexOf(deleteLeftPoint)
if (deleteIndex !== -1) newTriangleCoords.splice(deleteIndex, 1)
const newLine = new QLine([newTriangleCoords[0].x, newTriangleCoords[0].y, newTriangleCoords[1].x, newTriangleCoords[1].y], {
fill: 'transparent',
stroke: 'red',
strokeWidth: 2,
viewLengthText: false,
// selectable: true,
fontSize: 14,
})
canvas.add(newLine)
return newLine
}
function sortLinesByTopLeft(surface) {
// 좌측 상단 기준으로 정렬
const sortedLines = surface.lines.sort((a, b) => {
// x1, y1 값을 기준으로 정렬
if (a.x1 !== b.x1) {
return a.x1 - b.x1 // x1 기준 정렬
} else {
return a.y1 - b.y1 // x1이 같으면 y1 기준 정렬
}
})
// 정렬된 결과를 기반으로 좌표 재정렬
sortedLines.forEach((line) => {
// 좌측 상단이 (0,0) 기준이 되도록 좌표 이동
const minX = Math.min(line.x1, line.x2)
const minY = Math.min(line.y1, line.y2)
line.set({
x1: line.x1 - minX,
y1: line.y1 - minY,
x2: line.x2 - minX,
y2: line.y2 - minY,
})
})
surface.set({
sortedLines: sortedLines,
})
return sortedLines
} }
return { return {

View File

@ -1,16 +1,22 @@
import { useEffect, useState } from 'react' import { useCallback, useEffect, useState } from 'react'
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil' import { useRecoilState, useRecoilValue } from 'recoil'
import { adsorptionPointModeState, adsorptionRangeState, canvasState } from '@/store/canvasAtom' import { adsorptionPointModeState, adsorptionRangeState, canvasState, planSizeSettingState } from '@/store/canvasAtom'
import { globalLocaleStore } from '@/store/localeAtom' import { globalLocaleStore } from '@/store/localeAtom'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { useAxios } from '@/hooks/useAxios' import { useAxios } from '@/hooks/useAxios'
import { useSwal } from '@/hooks/useSwal' import { useSwal } from '@/hooks/useSwal'
import { corridorDimensionSelector, settingModalFirstOptionsState, settingModalSecondOptionsState } from '@/store/settingAtom' import { correntObjectNoState, corridorDimensionSelector, settingModalFirstOptionsState, settingModalSecondOptionsState } from '@/store/settingAtom'
import { setSurfaceShapePattern } from '@/util/canvas-util'
import { POLYGON_TYPE } from '@/common/common' import { POLYGON_TYPE } from '@/common/common'
import { fontSelector, globalFontAtom } from '@/store/fontAtom'
import { dimensionLineSettingsState } from '@/store/commonUtilsAtom'
let objectNo
export function useCanvasSetting() { export function useCanvasSetting() {
const canvas = useRecoilValue(canvasState) const canvas = useRecoilValue(canvasState)
// canvas가 null이 아닐 때에만 getObjects 호출
const canvasObjects = canvas ? canvas.getObjects() : []
const [correntObjectNo, setCorrentObjectNo] = useRecoilState(correntObjectNoState)
const [settingModalFirstOptions, setSettingModalFirstOptions] = useRecoilState(settingModalFirstOptionsState) const [settingModalFirstOptions, setSettingModalFirstOptions] = useRecoilState(settingModalFirstOptionsState)
const [settingModalSecondOptions, setSettingModalSecondOptions] = useRecoilState(settingModalSecondOptionsState) const [settingModalSecondOptions, setSettingModalSecondOptions] = useRecoilState(settingModalSecondOptionsState)
@ -25,9 +31,21 @@ export function useCanvasSetting() {
const { swalFire } = useSwal() const { swalFire } = useSwal()
const [adsorptionPointMode, setAdsorptionPointMode] = useRecoilState(adsorptionPointModeState) const [adsorptionPointMode, setAdsorptionPointMode] = useRecoilState(adsorptionPointModeState)
const setAdsorptionRange = useSetRecoilState(adsorptionRangeState) const [adsorptionRange, setAdsorptionRange] = useRecoilState(adsorptionRangeState)
const [planSizeSettingMode, setPlanSizeSettingMode] = useRecoilState(adsorptionRangeState)
//const setAdsorptionRange = useSetRecoilState(adsorptionRangeState)
const [objectNo, setObjectNo] = useState('test123240912001') // 이후 삭제 필요 const [selectedFont, setSelectedFont] = useState()
const [selectedFontWeight, setSelectedFontWeight] = useState()
const [selectedFontSize, setSelectedFontSize] = useState()
const [selectedFontColor, setSelectedFontColor] = useState()
const [globalFont, setGlobalFont] = useRecoilState(globalFontAtom)
//const [objectNo, setObjectNo] = useState('test123240912001') // 이후 삭제 필요
const [dimensionLineSettings, setDimensionLineSettings] = useRecoilState(dimensionLineSettingsState)
//const [originHorizon, setOriginHorizon] = useState({ originHorizon: 1600, flag: false })
//const [originVertical, setOriginVertical] = useState({ originVertical: 1600, flag: false })
useEffect(() => { useEffect(() => {
if (!canvas) { if (!canvas) {
@ -56,199 +74,354 @@ export function useCanvasSetting() {
}) })
break break
} }
canvas.renderAll() canvas?.renderAll()
}, [corridorDimension]) }, [corridorDimension])
useEffect(() => { useEffect(() => {
console.log('useCanvasSetting useEffect 실행1') console.log('useCanvasSetting useEffect 실행1', correntObjectNo)
fetchSettings() }, [])
}, [objectNo])
//흡착점 ON/OFF 변경 시
useEffect(() => { useEffect(() => {
console.log('useCanvasSetting useEffect 실행2') console.log('useCanvasSetting useEffect 실행2', adsorptionPointMode.fontFlag, correntObjectNo)
//fetchSettings()
//onClickOption() if (adsorptionPointMode.fontFlag) {
//fetchSettings() onClickOption2()
frontSettings()
fetchSettings()
}
}, [adsorptionPointMode]) }, [adsorptionPointMode])
// 1 과 2 변경 시
useEffect(() => { useEffect(() => {
console.log('useCanvasSetting useEffect 실행3') console.log('useCanvasSetting useEffect 실행3', settingModalFirstOptions.fontFlag, settingModalSecondOptions.fontFlag, correntObjectNo)
//fetchSettings() if (settingModalFirstOptions.fontFlag || settingModalSecondOptions.fontFlag) {
//onClickOption() onClickOption2()
//fetchSettings() fetchSettings()
}
frontSettings()
}, [settingModalFirstOptions, settingModalSecondOptions]) }, [settingModalFirstOptions, settingModalSecondOptions])
// 글꼴 변경 시
useEffect(() => {
console.log('useCanvasSetting useEffect 실행4', globalFont.fontFlag, correntObjectNo)
if (globalFont.fontFlag) {
onClickOption2()
frontSettings()
fetchSettings()
}
}, [globalFont])
// 도명크기 변경 시
useEffect(() => {
console.log(
'useCanvasSetting useEffect 실행5',
correntObjectNo,
planSizeSettingMode.flag,
planSizeSettingMode.originHorizon,
planSizeSettingMode.originVertical,
)
if (planSizeSettingMode.flag) {
onClickOption2()
frontSettings()
fetchSettings()
}
}, [planSizeSettingMode.flag])
const fetchSettings = async () => { const fetchSettings = async () => {
try { try {
const res = await get({ url: `/api/canvas-management/canvas-settings/by-object/${objectNo}` }) const res = await get({ url: `/api/canvas-management/canvas-settings/by-object/${correntObjectNo}` })
console.log('res', res) console.log('res', res)
const optionData1 = settingModalFirstOptions.option1.map((item) => ({ ...item, selected: res[item.column] }))
const optionData2 = settingModalFirstOptions.option2.map((item) => ({ ...item, selected: res[item.column] }))
const optionData3 = settingModalSecondOptions.option3.map((item) => ({ ...item }))
const optionData4 = settingModalSecondOptions.option4.map((item) => ({ ...item, selected: res[item.column] }))
const optionData5 = settingModalFirstOptions.dimensionDisplay.map((item) => ({ ...item }))
const patternData = { if (res) {
adsorpPoint: res.adsorpPoint, const optionData1 = settingModalFirstOptions.option1.map((item) => ({ ...item, selected: res[item.column] }))
const optionData2 = settingModalFirstOptions.option2.map((item) => ({ ...item, selected: res[item.column] }))
const optionData3 = settingModalSecondOptions.option3.map((item) => ({ ...item }))
const optionData4 = settingModalSecondOptions.option4.map((item) => ({ ...item, selected: res[item.column] }))
const optionData5 = settingModalFirstOptions.dimensionDisplay.map((item) => ({ ...item }))
//setObjectNo(floorPlanState.objectNo)
//흡착점 ON/OFF
setAdsorptionPointMode({ ...adsorptionPointMode, adsorptionPoint: res.adsorpPoint, fontFlag: false })
//치수선 설정
setDimensionLineSettings({ ...dimensionLineSettings, pixel: res.originPixel, color: res.originColor })
//도면크기 설정
setPlanSizeSettingMode({
...planSizeSettingMode,
originHorizon: res.originHorizon,
originVertical: res.originVertical,
flag: false,
})
// setOriginHorizon({
// ...originHorizon,
// originHorizon: res.originHorizon,
// flag: false,
// })
// setOriginVertical({
// ...originVertical,
// originVertical: res.originVertical,
// flag: false,
// })
// 데이터 설정
setSettingModalFirstOptions({
...settingModalFirstOptions,
option1: optionData1,
option2: optionData2,
dimensionDisplay: optionData5,
fontFlag: false,
})
setSettingModalSecondOptions({
...settingModalSecondOptions,
option3: optionData3,
option4: optionData4,
fontFlag: false,
})
const fontPatternData = {
commonText: {
//문자 글꼴 조회 데이터
fontFamily: res.wordFont,
fontWeight: res.wordFontStyle,
fontSize: res.wordFontSize,
fontColor: res.wordFontColor,
},
flowText: {
//흐름방향 글꼴 조회 데이터
fontFamily: res.flowFont,
fontWeight: res.flowFontStyle,
fontSize: res.flowFontSize,
fontColor: res.flowFontColor,
},
dimensionLineText: {
//치수 글꼴 조회 데이터
fontFamily: res.dimensioFont,
fontWeight: res.dimensioFontStyle,
fontSize: res.dimensioFontSize,
fontColor: res.dimensioFontColor,
},
circuitNumberText: {
//회로번호 글꼴 조회 데이터
fontFamily: res.circuitNumFont,
fontWeight: res.circuitNumFontStyle,
fontSize: res.circuitNumFontSize,
fontColor: res.circuitNumFontColor,
},
lengthText: {
//치수선 글꼴 조회 데이터
fontFamily: res.lengthFont,
fontWeight: res.lengthFontStyle,
fontSize: res.lengthFontSize,
fontColor: res.lengthFontColor,
},
//글꼴 설정 Flag
fontFlag: false,
}
//console.log('fontPatternData', fontPatternData)
//조회된 글꼴 데이터 set
setGlobalFont(fontPatternData)
} else {
//조회된 글꼴 데이터가 없는 경우
//흡착점 ON/OFF
setAdsorptionPointMode({ ...adsorptionPointMode, adsorptionPoint: false, fontFlag: false })
//치수선 설정
setDimensionLineSettings({ ...dimensionLineSettings })
//도면크기 설정
setPlanSizeSettingMode({
...planSizeSettingMode,
flag: false,
})
// setOriginHorizon({
// ...originHorizon,
// flag: false,
// })
// setOriginVertical({
// ...originVertical,
// flag: false,
// })
// 데이터 설정
setSettingModalFirstOptions({
...settingModalFirstOptions,
fontFlag: false,
})
setSettingModalSecondOptions({
...settingModalSecondOptions,
fontFlag: false,
})
//console.log('globalFont2', globalFont)
const fontPatternData = {
commonText: {
//문자 글꼴
fontFamily: globalFont.commonText.fontFamily.value,
fontWeight: globalFont.commonText.fontWeight.value,
fontSize: globalFont.commonText.fontSize.value,
fontColor: globalFont.commonText.fontColor.value,
},
flowText: {
//흐름방향 글꼴
fontFamily: globalFont.flowText.fontFamily.value,
fontWeight: globalFont.flowText.fontWeight.value,
fontSize: globalFont.flowText.fontSize.value,
fontColor: globalFont.flowText.fontColor.value,
},
dimensionLineText: {
//치수 글꼴
fontFamily: globalFont.dimensionLineText.fontFamily.value,
fontWeight: globalFont.dimensionLineText.fontWeight.value,
fontSize: globalFont.dimensionLineText.fontSize.value,
fontColor: globalFont.dimensionLineText.fontColor.value,
},
circuitNumberText: {
//회로번호 글꼴
fontFamily: globalFont.circuitNumberText.fontFamily.value,
fontWeight: globalFont.circuitNumberText.fontWeight.value,
fontSize: globalFont.circuitNumberText.fontSize.value,
fontColor: globalFont.circuitNumberText.fontColor.value,
},
lengthText: {
//치수선 글꼴
fontFamily: globalFont.lengthText.fontFamily.value,
fontWeight: globalFont.lengthText.fontWeight.value,
fontSize: globalFont.lengthText.fontSize.value,
fontColor: globalFont.lengthText.fontColor.value,
},
//글꼴 설정 Flag
fontFlag: false,
}
//console.log('fontPatternData', fontPatternData)
setGlobalFont(fontPatternData)
//setGlobalFont({ ...globalFont, fontFlag: false })
} }
frontSettings()
// 데이터 설정
setSettingModalFirstOptions({
option1: optionData1,
option2: optionData2,
dimensionDisplay: optionData5,
})
setSettingModalSecondOptions({
option3: optionData3,
option4: optionData4,
})
setAdsorptionPointMode(patternData.adsorpPoint)
console.log('adsorptionPointMode', adsorptionPointMode)
} catch (error) { } catch (error) {
console.error('Data fetching error:', error) console.error('Data fetching error:', error)
} }
} }
// 옵션 클릭 후 저장 // 옵션 클릭 후 저장
const onClickOption = async (item) => { const onClickOption2 = useCallback(async () => {
//치수 표시(단 건 선택) // 서버에 전송할 데이터
if (item.column === 'corridorDimension' || item.column === 'realDimension' || item.column === 'noneDimension') { const dataToSend = {
console.log('치수 표시 ', item) firstOption1: option1.map((item) => ({
const options = settingModalFirstOptions?.dimensionDisplay.map((option) => { column: item.column,
option.selected = option.id === item.id selected: item.selected,
return option })),
}) firstOption2: option2.map((item) => ({
column: item.column,
//화면 표시(단 건 선택) selected: item.selected,
} else if (item.column === 'onlyBorder' || item.column === 'lineHatch' || item.column === 'allPainted') { })),
console.log('화면 표시 ', item) firstOption3: dimensionDisplay.map((item) => ({
const options2 = settingModalFirstOptions?.option2.map((option2) => { column: item.column,
option2.selected = option2.id === item.id selected: item.selected,
return option2 })),
}) secondOption2: option4.map((item) => ({
column: item.column,
const polygons = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF) selected: item.selected,
})),
polygons.forEach((polygon) => {
setSurfaceShapePattern(polygon, item.column)
})
//흡착범위 설정(단 건 선택)
} else if (
item.column === 'adsorpRangeSmall' ||
item.column === 'adsorpRangeSmallSemi' ||
item.column === 'adsorpRangeMedium' ||
item.column === 'adsorpRangeLarge'
) {
console.log('화면 표시2 ', item, option4)
// option4에서 한 개만 선택 가능하도록 처리
const updatedOption4 = option4.map((option) =>
option.id === item.id
? { ...option, selected: true }
: {
...option,
selected: false,
},
)
setSettingModalSecondOptions({ option3, option4: updatedOption4 })
//흡착점 ON / OFF
} else if (item === 'adsorpPoint') {
console.log('흡착점 ON / OFF ', item)
const options2 = settingModalFirstOptions?.option2.map((option2) => {
option2.selected = option2.id === item.id
return option2
})
const polygons = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
polygons.forEach((polygon) => {
setSurfaceShapePattern(polygon, item.column)
})
//디스플레이 설정(다 건 선택)
} else {
//console.log('디스플레이 설정1 ', item.column)
console.log('디스플레이 설정 ', item)
item.selected = !item.selected
} }
setSettingModalFirstOptions({ option1, option2, dimensionDisplay }) const patternData = {
//견적서 번호
objectNo: correntObjectNo,
//디스플레이 설정(다중)
allocDisplay: dataToSend.firstOption1[0].selected,
outlineDisplay: dataToSend.firstOption1[1].selected,
gridDisplay: dataToSend.firstOption1[2].selected,
lineDisplay: dataToSend.firstOption1[3].selected,
wordDisplay: dataToSend.firstOption1[4].selected,
circuitNumDisplay: dataToSend.firstOption1[5].selected,
flowDisplay: dataToSend.firstOption1[6].selected,
trestleDisplay: dataToSend.firstOption1[7].selected,
imageDisplay: dataToSend.firstOption1[8].selected,
totalDisplay: dataToSend.firstOption1[9].selected,
//차수 표시(단 건)
corridorDimension: dataToSend.firstOption3[0].selected,
realDimension: dataToSend.firstOption3[1].selected,
noneDimension: dataToSend.firstOption3[2].selected,
//화면 표시(단 건)
onlyBorder: dataToSend.firstOption2[0].selected,
lineHatch: dataToSend.firstOption2[1].selected,
allPainted: dataToSend.firstOption2[2].selected,
//흡착범위 설정(단 건)
adsorpRangeSmall: dataToSend.secondOption2[0].selected,
adsorpRangeSmallSemi: dataToSend.secondOption2[1].selected,
adsorpRangeMedium: dataToSend.secondOption2[2].selected,
adsorpRangeLarge: dataToSend.secondOption2[3].selected,
//흡착점 ON/OFF
adsorpPoint: adsorptionPointMode.adsorptionPoint,
//??: adsorptionRange, 사용여부 확인 필요
try { //글꼴 설정
// 서버에 전송할 데이터 //문자 글꼴
const dataToSend = { wordFont: globalFont.commonText.fontFamily,
firstOption1: option1.map((item) => ({ wordFontStyle: globalFont.commonText.fontWeight,
column: item.column, wordFontSize: globalFont.commonText.fontSize,
selected: item.selected, wordFontColor: globalFont.commonText.fontColor,
})),
firstOption2: option2.map((item) => ({
column: item.column,
selected: item.selected,
})),
firstOption3: dimensionDisplay.map((item) => ({
column: item.column,
selected: item.selected,
})),
// secondOption1: secondOptions[0].option1.map((item) => ({
// name: item.id,
// name: item.name,
// // 필요한 경우 데이터 항목 추가
// })),
secondOption2: option4.map((item) => ({
column: item.column,
selected: item.selected,
})),
}
const patternData = { //흐름방향 글꼴
objectNo, flowFont: globalFont.flowText.fontFamily,
//디스플레이 설정(다중) flowFontStyle: globalFont.flowText.fontWeight,
allocDisplay: dataToSend.firstOption1[0].selected, flowFontSize: globalFont.flowText.fontSize,
outlineDisplay: dataToSend.firstOption1[1].selected, flowFontColor: globalFont.flowText.fontColor,
gridDisplay: dataToSend.firstOption1[2].selected,
lineDisplay: dataToSend.firstOption1[3].selected,
wordDisplay: dataToSend.firstOption1[4].selected,
circuitNumDisplay: dataToSend.firstOption1[5].selected,
flowDisplay: dataToSend.firstOption1[6].selected,
trestleDisplay: dataToSend.firstOption1[7].selected,
imageDisplay: dataToSend.firstOption1[8].selected,
totalDisplay: dataToSend.firstOption1[9].selected,
//차수 표시(단 건)
corridorDimension: dataToSend.firstOption3[0].selected,
realDimension: dataToSend.firstOption3[1].selected,
noneDimension: dataToSend.firstOption3[2].selected,
//화면 표시(단 건)
onlyBorder: dataToSend.firstOption2[0].selected,
lineHatch: dataToSend.firstOption2[1].selected,
allPainted: dataToSend.firstOption2[2].selected,
//흡착범위 설정(단 건)
adsorpRangeSmall: dataToSend.secondOption2[0].selected,
adsorpRangeSmallSemi: dataToSend.secondOption2[1].selected,
adsorpRangeMedium: dataToSend.secondOption2[2].selected,
adsorpRangeLarge: dataToSend.secondOption2[3].selected,
//흡착점 ON/OFF
adsorpPoint: adsorptionPointMode,
}
console.log('patternData ', patternData) //치수 글꼴
dimensioFont: globalFont.dimensionLineText.fontFamily,
dimensioFontStyle: globalFont.dimensionLineText.fontWeight,
dimensioFontSize: globalFont.dimensionLineText.fontSize,
dimensioFontColor: globalFont.dimensionLineText.fontColor,
// HTTP POST 요청 보내기 //회로번호 글꼴
await post({ url: `/api/canvas-management/canvas-settings`, data: patternData }).then((res) => { circuitNumFont: globalFont.circuitNumberText.fontFamily,
circuitNumFontStyle: globalFont.circuitNumberText.fontWeight,
circuitNumFontSize: globalFont.circuitNumberText.fontSize,
circuitNumFontColor: globalFont.circuitNumberText.fontColor,
//치수선 글꼴
lengthFont: globalFont.lengthText.fontFamily,
lengthFontStyle: globalFont.lengthText.fontWeight,
lengthFontSize: globalFont.lengthText.fontSize,
lengthFontColor: globalFont.lengthText.fontColor,
//치수선 설정
originPixel: dimensionLineSettings.pixel,
originColor: dimensionLineSettings.color,
//치수선 설정
originHorizon: planSizeSettingMode.originHorizon,
originVertical: planSizeSettingMode.originVertical,
// originHorizon: originHorizon.originHorizon,
// originVertical: originVertical.originVertical,
}
console.log('patternData ', patternData)
// HTTP POST 요청 보내기
await post({ url: `/api/canvas-management/canvas-settings`, data: patternData })
.then((res) => {
swalFire({ text: getMessage(res.returnMessage) }) swalFire({ text: getMessage(res.returnMessage) })
// Canvas 디스플레이 설정 시 해당 옵션 적용 // Canvas 디스플레이 설정 시 해당 옵션 적용
frontSettings() frontSettings()
}) })
} catch (error) { .catch((error) => {
swalFire({ text: getMessage(res.returnMessage), icon: 'error' }) swalFire({ text: getMessage(res.returnMessage), icon: 'error' })
} })
setAdsorptionRange(item.range) //setAdsorptionRange(item.range)
} }, [settingModalFirstOptions, settingModalSecondOptions, adsorptionPointMode, globalFont, planSizeSettingMode])
// Canvas 디스플레이 설정 시 해당 옵션 적용 // Canvas 디스플레이 설정 시 해당 옵션 적용
const frontSettings = async () => { const frontSettings = async () => {
@ -260,7 +433,7 @@ export function useCanvasSetting() {
// 'lineDisplay' 지붕선 표시 'roof', POLYGON_TYPE.ROOF // 'lineDisplay' 지붕선 표시 'roof', POLYGON_TYPE.ROOF
// 'wordDisplay' 문자 표시 // 'wordDisplay' 문자 표시
// 'circuitNumDisplay' 회로번호 표시 // 'circuitNumDisplay' 회로번호 표시
// 'flowDisplay' 흐름방향 표시 'arrow' // 'flowDisplay' 흐름방향 표시 'arrow', 'flowText'
// 'trestleDisplay' 가대 표시 // 'trestleDisplay' 가대 표시
// 'imageDisplay' 이미지 표시 // 'imageDisplay' 이미지 표시
// 'totalDisplay' 집계표 표시 // 'totalDisplay' 집계표 표시
@ -304,8 +477,8 @@ export function useCanvasSetting() {
// 표시 선택 상태(true/false) // 표시 선택 상태(true/false)
optionSelected = option1[i].selected optionSelected = option1[i].selected
canvas //canvas.getObjects() >> canvasObjects
.getObjects() canvasObjects
.filter((obj) => optionName.includes(obj.name)) .filter((obj) => optionName.includes(obj.name))
//.filter((obj) => obj.name === optionName) //.filter((obj) => obj.name === optionName)
.forEach((obj) => { .forEach((obj) => {
@ -313,7 +486,7 @@ export function useCanvasSetting() {
//obj.set({ visible: !obj.visible }) //obj.set({ visible: !obj.visible })
}) })
canvas.renderAll() canvas?.renderAll()
// console.log( // console.log(
// 'optionName', // 'optionName',
@ -324,14 +497,31 @@ export function useCanvasSetting() {
} }
return { return {
canvas,
settingModalFirstOptions, settingModalFirstOptions,
setSettingModalFirstOptions, setSettingModalFirstOptions,
settingModalSecondOptions, settingModalSecondOptions,
setSettingModalSecondOptions, setSettingModalSecondOptions,
adsorptionPointMode, adsorptionPointMode,
setAdsorptionPointMode, setAdsorptionPointMode,
adsorptionRange,
setAdsorptionRange,
fetchSettings, fetchSettings,
onClickOption, //onClickOption,
frontSettings, frontSettings,
globalFont,
setGlobalFont,
selectedFont,
setSelectedFont,
selectedFontWeight,
setSelectedFontWeight,
selectedFontSize,
setSelectedFontSize,
selectedFontColor,
setSelectedFontColor,
dimensionLineSettings,
setDimensionLineSettings,
planSizeSettingMode,
setPlanSizeSettingMode,
} }
} }

View File

@ -92,7 +92,7 @@ export function useAuxiliaryDrawing(id) {
addCanvasMouseEventListener('mouse:move', mouseMove) addCanvasMouseEventListener('mouse:move', mouseMove)
addCanvasMouseEventListener('mouse:down', mouseDown) addCanvasMouseEventListener('mouse:down', mouseDown)
addDocumentEventListener('contextmenu', document, cutAuxiliary) // addDocumentEventListener('contextmenu', document, cutAuxiliary)
addDocumentEventListener('keydown', document, keydown[type]) addDocumentEventListener('keydown', document, keydown[type])
return () => { return () => {
@ -135,23 +135,6 @@ export function useAuxiliaryDrawing(id) {
}) })
} }
const addBisectorLine = (target) => {
const slope = (target.y2 - target.y1) / (target.x2 - target.x1)
const bisectorSlope = -1 / slope
const length = target.length
const dx = length / Math.sqrt(1 + bisectorSlope * bisectorSlope)
const dy = bisectorSlope * dx
const endX = (target.x1 + target.x2) / 2
const endY = (target.y1 + target.y2) / 2
addLine([dx, dy, endX, endY], {
stroke: 'red',
strokeWidth: 1,
selectable: true,
name: 'auxiliaryLine',
})
}
const keydown = { const keydown = {
outerLine: (e) => { outerLine: (e) => {
if (mousePointerArr.current.length === 0) { if (mousePointerArr.current.length === 0) {
@ -565,8 +548,24 @@ export function useAuxiliaryDrawing(id) {
otherAdsorptionPoints.push(intersectionPoint) otherAdsorptionPoints.push(intersectionPoint)
}) })
}) })
let innerLinePoints = []
canvas
.getObjects()
.filter((obj) => obj.innerLines)
.forEach((polygon) => {
polygon.innerLines.forEach((line) => {
innerLinePoints.push({ x: line.x1, y: line.y1 })
innerLinePoints.push({ x: line.x2, y: line.y2 })
})
})
const adsorptionPoints = [...getAdsorptionPoints(), ...roofAdsorptionPoints.current, ...otherAdsorptionPoints, ...intersectionPoints.current] const adsorptionPoints = [
...getAdsorptionPoints(),
...roofAdsorptionPoints.current,
...otherAdsorptionPoints,
...intersectionPoints.current,
...innerLinePoints,
]
let arrivalPoint = { x: pointer.x, y: pointer.y } let arrivalPoint = { x: pointer.x, y: pointer.y }
@ -825,7 +824,6 @@ export function useAuxiliaryDrawing(id) {
//lineHistory.current에 있는 선들 중 startPoint와 endPoint가 겹치는 line은 제거 //lineHistory.current에 있는 선들 중 startPoint와 endPoint가 겹치는 line은 제거
// 겹치는 선 하나는 canvas에서 제거한다. // 겹치는 선 하나는 canvas에서 제거한다.
const tempLines = [...lineHistory.current] const tempLines = [...lineHistory.current]
lineHistory.current = [] lineHistory.current = []
tempLines.forEach((line) => { tempLines.forEach((line) => {
@ -849,27 +847,30 @@ export function useAuxiliaryDrawing(id) {
const tempPolygonPoints = [...roofBase.points].map((obj) => { const tempPolygonPoints = [...roofBase.points].map((obj) => {
return { x: Math.round(obj.x), y: Math.round(obj.y) } return { x: Math.round(obj.x), y: Math.round(obj.y) }
}) })
const roofInnerLines = innerLines.filter((line) => { const roofInnerLines = [...roofBase.innerLines, ...innerLines].filter((line) => {
const inPolygon1 = const inPolygon1 =
tempPolygonPoints.some((point) => point.x === line.x1 && point.y === line.y1) || tempPolygonPoints.some((point) => Math.round(point.x) === Math.round(line.x1) && Math.round(point.y) === Math.round(line.y1)) ||
roofBase.inPolygon({ x: line.x1, y: line.y1 }) || roofBase.inPolygon({ x: Math.round(line.x1), y: Math.round(line.y1) }) ||
roofBase.lines.some((line) => isPointOnLine(line, { x: line.x1, y: line.y1 })) roofBase.lines.some((line) => isPointOnLine(line, { x: Math.round(line.x1), y: Math.round(line.y1) }))
const inPolygon2 = const inPolygon2 =
tempPolygonPoints.some((point) => point.x === line.x2 && point.y === line.y2) || tempPolygonPoints.some((point) => Math.round(point.x) === Math.round(line.x2) && Math.round(point.y) === Math.round(line.y2)) ||
roofBase.inPolygon({ x: line.x2, y: line.y2 }) || roofBase.inPolygon({ x: Math.round(line.x2), y: Math.round(line.y2) }) ||
roofBase.lines.some((line) => isPointOnLine(line, { x: line.x2, y: line.y2 })) roofBase.lines.some((line) => isPointOnLine(line, { x: Math.round(line.x2), y: Math.round(line.y2) }))
if (inPolygon1 && inPolygon2) { if (inPolygon1 && inPolygon2) {
line.attributes = { ...line.attributes, roofId: roofBase.id, actualSize: 0, planeSize: line.getLength() } line.attributes = {
...line.attributes,
roofId: roofBase.id,
actualSize: line.attributes?.actualSize ?? 0,
planeSize: line.getLength(),
}
return true return true
} }
}) })
roofBase.innerLines = [...roofInnerLines] roofBase.innerLines = lineHistory.current.length !== 0 ? [...roofInnerLines] : roofBase.innerLines
canvas.renderAll() canvas.renderAll()
}) })
closePopup(id) closePopup(id)
} }
@ -903,6 +904,6 @@ export function useAuxiliaryDrawing(id) {
setButtonAct, setButtonAct,
move, move,
copy, copy,
addBisectorLine, cutAuxiliary,
} }
} }

View File

@ -34,6 +34,7 @@ import { useObjectBatch } from '@/hooks/object/useObjectBatch'
import { useSurfaceShapeBatch } from '@/hooks/surface/useSurfaceShapeBatch' import { useSurfaceShapeBatch } from '@/hooks/surface/useSurfaceShapeBatch'
import { fontSelector, globalFontAtom } from '@/store/fontAtom' import { fontSelector, globalFontAtom } from '@/store/fontAtom'
import { useLine } from '@/hooks/useLine' import { useLine } from '@/hooks/useLine'
import { useSwal } from '@/hooks/useSwal'
export function useContextMenu() { export function useContextMenu() {
const canvas = useRecoilValue(canvasState) const canvas = useRecoilValue(canvasState)
@ -56,6 +57,7 @@ export function useContextMenu() {
const [globalFont, setGlobalFont] = useRecoilState(globalFontAtom) const [globalFont, setGlobalFont] = useRecoilState(globalFontAtom)
const { addLine, removeLine } = useLine() const { addLine, removeLine } = useLine()
const commonTextFont = useRecoilValue(fontSelector('commonText')) const commonTextFont = useRecoilValue(fontSelector('commonText'))
const { swalFire } = useSwal()
const currentMenuSetting = () => { const currentMenuSetting = () => {
switch (currentMenu) { switch (currentMenu) {
@ -145,6 +147,9 @@ export function useContextMenu() {
shortcut: ['d', 'D'], shortcut: ['d', 'D'],
name: `${getMessage('contextmenu.auxiliary.remove')}(D)`, name: `${getMessage('contextmenu.auxiliary.remove')}(D)`,
fn: () => { fn: () => {
const roof = canvas.getObjects().filter((obj) => obj.id === currentObject.attributes.roofId)[0]
const innerLines = roof.innerLines?.filter((line) => currentObject.id !== line.id)
roof.innerLines = [...innerLines]
canvas.remove(currentObject) canvas.remove(currentObject)
canvas.discardActiveObject() canvas.discardActiveObject()
}, },
@ -176,21 +181,34 @@ export function useContextMenu() {
endY = (currentObject.y1 + currentObject.y2) / 2 - dy endY = (currentObject.y1 + currentObject.y2) / 2 - dy
} }
addLine([startX, startY, endX, endY], { const line = addLine([startX, startY, endX, endY], {
stroke: 'red', stroke: 'red',
strokeWidth: 1, strokeWidth: 1,
selectable: true, selectable: true,
name: 'auxiliaryLine', name: 'auxiliaryLine',
attributes: { ...currentObject.attributes },
}) })
canvas
.getObjects()
.filter((obj) => obj.id === currentObject.attributes.roofId)[0]
.innerLines.push(line)
}, },
}, },
{
id: 'auxiliaryCut',
name: getMessage('contextmenu.auxiliary.cut'),
},
{ {
id: 'auxiliaryRemoveAll', id: 'auxiliaryRemoveAll',
name: getMessage('contextmenu.auxiliary.remove.all'), name: getMessage('contextmenu.auxiliary.remove.all'),
fn: () => {
if (!currentObject) {
swalFire({ text: '지붕을 선택해주세요.' })
return
}
const innerLines = canvas.getObjects().filter((obj) => obj.id === currentObject.attributes.roofId)[0].innerLines
innerLines.forEach((line) => {
canvas.remove(line)
})
innerLines.length = 0
canvas.renderAll()
},
}, },
], ],
]) ])

View File

@ -222,6 +222,12 @@ export const adsorptionRangeState = atom({
default: 50, default: 50,
}) })
// 도면크기 설정
export const planSizeSettingState = atom({
key: 'planSizeSettingMode',
default: { originHorizon: 1600, originVertical: 1600 },
})
// 점,선 그리드 설정 // 점,선 그리드 설정
export const dotLineGridSettingState = atom({ export const dotLineGridSettingState = atom({
key: 'gridSettingState', key: 'gridSettingState',

View File

@ -1,10 +1,10 @@
import { atom, selectorFamily } from 'recoil' import { atom, selectorFamily } from 'recoil'
const defaultFont = { const defaultFont = {
fontFamily: { name: 'MS PGothic', value: 'MS PGothic' }, fontFamily: { id: 1, name: 'MS PGothic', value: 'MS PGothic' },
fontWeight: { name: '보통', value: 'normal' }, fontWeight: { id: 'normal', name: '보통', value: 'normal' },
fontSize: { name: '16', value: '16' }, fontSize: { id: 16, name: 16, value: 16 },
fontColor: { name: '검정색', value: 'black' }, fontColor: { id: 'black', name: '검정색', value: 'black' },
} }
export const globalFontAtom = atom({ export const globalFontAtom = atom({

View File

@ -67,6 +67,13 @@ export const convertNumberToPriceDecimal = (value) => {
else return '' else return ''
} }
// 43000.458 --> 43,000.46
export const convertNumberToPriceDecimalToFixed = (value, fixed) => {
if (value) return Number(value).toLocaleString(undefined, { minimumFractionDigits: fixed, maximumFractionDigits: fixed })
else if (value === 0) return 0
else return ''
}
// 전화번호, FAX 번호 숫자 or '-'만 입력 체크 // 전화번호, FAX 번호 숫자 or '-'만 입력 체크
export const inputTelNumberCheck = (e) => { export const inputTelNumberCheck = (e) => {
const input = e.target const input = e.target