Compare commits

...

6 Commits

9 changed files with 243 additions and 101 deletions

View File

@ -504,6 +504,7 @@ export default function Estimate({}) {
saleStoreId: estimateContextState.sapSaleStoreId,
sapSalesStoreCd: estimateContextState.sapSalesStoreCd,
docTpCd: estimateContextState.estimateType,
secSapSalesStoreCd:(estimateContextState.secSapSalesStoreCd?.length > 0 && showPriceCd === 'QSP_PRICE')? estimateContextState.secSapSalesStoreCd :'',
priceCd: showPriceCd,
itemIdList: estimateContextState.itemList.filter((item) => item.delFlg === '0' && item.paDispOrder === null),
}
@ -1216,6 +1217,23 @@ export default function Estimate({}) {
}
}, [estimateContextState?.itemList, cableItemList])
const [agencyCustList, setAgencyCustList] = useState([])
useEffect(() => {
// 952 - 2 sapSalesStoreCd
if(estimateContextState?.sapSalesStoreCd && session?.storeLvl === '1') {
const param = {
sapSalesStoreCd: estimateContextState.sapSalesStoreCd,
}
const apiUrl = `api/estimate/agency-cust-list?${queryStringFormatter(param)}`
get({ url: apiUrl }).then((res) => {
if (isNotEmptyArray(res?.data)) {
setAgencyCustList(res?.data);
}
})
}
}, [estimateContextState?.sapSalesStoreCd])
return (
<div className="sub-content estimate">
<div className="sub-content-inner">
@ -1360,41 +1378,72 @@ export default function Estimate({}) {
{getMessage('estimate.detail.estimateType')} <span className="important">*</span>
</th>
<td colSpan={3}>
<div className="radio-wrap">
{/*pkgRank is null, empty 인 경우 : 사용불가, 이전에 등록된 경우 사용가능, style로 제어*/}
<div className="d-check-radio light mr10" style={{display:
(isNotEmptyArray(storePriceList) > 0
&& storePriceList[0].pkgRank !== null
&& storePriceList[0].pkgRank !== ''
|| estimateContextState?.estimateType === 'YJSS') ? "" : "none"}}>
<input
type="radio"
name="estimateType"
id="YJSS"
value={'YJSS'}
checked={estimateContextState?.estimateType === 'YJSS' ? true : false}
onChange={(e) => {
//
setHandlePricingFlag(true)
setEstimateContextState({ estimateType: e.target.value })
}}
/>
<label htmlFor="YJSS">{getMessage('estimate.detail.estimateType.yjss')}</label>
<div className="form-flex-wrap">
<div className="radio-wrap">
{/*pkgRank is null, empty 인 경우 : 사용불가, 이전에 등록된 경우 사용가능, style로 제어*/}
<div className="d-check-radio light mr10" style={{display:
(isNotEmptyArray(storePriceList) > 0
&& storePriceList[0].pkgRank !== null
&& storePriceList[0].pkgRank !== ''
|| estimateContextState?.estimateType === 'YJSS') ? "" : "none"}}>
<input
type="radio"
name="estimateType"
id="YJSS"
value={'YJSS'}
checked={estimateContextState?.estimateType === 'YJSS' ? true : false}
onChange={(e) => {
//
setHandlePricingFlag(true)
setEstimateContextState({ estimateType: e.target.value })
}}
/>
<label htmlFor="YJSS">{getMessage('estimate.detail.estimateType.yjss')}</label>
</div>
<div className="d-check-radio light">
<input
type="radio"
name="estimateType"
id="YJOD"
value={'YJOD'}
checked={estimateContextState?.estimateType === 'YJOD' ? true : false}
onChange={(e) => {
setHandlePricingFlag(true)
setEstimateContextState({ estimateType: e.target.value })
}}
/>
<label htmlFor="YJOD">{getMessage('estimate.detail.estimateType.yjod')}</label>
</div>
</div>
<div className="d-check-radio light">
<input
type="radio"
name="estimateType"
id="YJOD"
value={'YJOD'}
checked={estimateContextState?.estimateType === 'YJOD' ? true : false}
{ (session?.storeLvl === '1' && agencyCustList.length > 0 )? (
<div className="form-flex-select ml10">
<label htmlFor="">{getMessage('estimate.detail.agency')}</label>
<div className="select-wrap" style={{ width: '400px' }}>
<Select
id="agencyName"
instanceId="agencyName"
className="react-select-custom"
classNamePrefix="custom"
placeholder="Select"
options={agencyCustList}
onChange={(e) => {
setHandlePricingFlag(true)
setEstimateContextState({ estimateType: e.target.value })
if (isObjectNotEmpty(e)) {
setEstimateContextState({ secSapSalesStoreCd: e.sapSalesStoreCd })
} else {
setEstimateContextState({ secSapSalesStoreCd: '' })
}
}}
getOptionLabel={(x) => x.sapSalesStoreNm}
getOptionValue={(x) => x.sapSalesStoreCd}
isClearable={true}
isSearchable={true}
value={agencyCustList.filter(function (option) {
return option.sapSalesStoreCd === estimateContextState.secSapSalesStoreCd
})}
/>
<label htmlFor="YJOD">{getMessage('estimate.detail.estimateType.yjod')}</label>
</div>
</div>
) : ('')}
</div>
</td>
</tr>

View File

@ -77,7 +77,7 @@ export default function BasicSetting({ id, pos = { x: 50, y: 230 } }) {
const { trigger: orientationTrigger } = useCanvasPopupStatusController(1)
const { trigger: trestleTrigger } = useCanvasPopupStatusController(2)
const { trigger: placementTrigger } = useCanvasPopupStatusController(3)
const roofsStore = useRecoilValue(roofsState)
const [roofsStore, setRoofsStore] = useRecoilState(roofsState)
// const { initEvent } = useContext(EventContext)
const { manualModuleSetup, autoModuleSetup, manualFlatroofModuleSetup, autoFlatroofModuleSetup, manualModuleLayoutSetup } =
@ -95,6 +95,7 @@ export default function BasicSetting({ id, pos = { x: 50, y: 230 } }) {
}),
)
console.log(roofsStore)
setModuleSelectionData({
...moduleSelectionData,
roofConstructions: roofsStore.map((roof) => {
@ -255,6 +256,7 @@ export default function BasicSetting({ id, pos = { x: 50, y: 230 } }) {
const trestleProps = {
roofs,
setRoofs,
setRoofsStore,
tabNum,
setTabNum,
moduleSelectionData,

View File

@ -352,8 +352,8 @@ const Placement = forwardRef((props, refs) => {
<tr>
<td>
<div className="color-wrap">
<span className="color-box" style={{ backgroundColor: roofOutlineColor(item.addRoof.index) }}></span>
<span className="name">{item.addRoof.roofMatlNmJp}</span>
<span className="color-box" style={{ backgroundColor: roofOutlineColor(item.addRoof?.index) }}></span>
<span className="name">{item.addRoof?.roofMatlNmJp}</span>
</div>
</td>
{moduleRowColArray[index]?.map((item) => (

View File

@ -6,12 +6,11 @@ import { useMessage } from '@/hooks/useMessage'
import { currentAngleTypeSelector, pitchTextSelector } from '@/store/canvasAtom'
import { roofsState } from '@/store/roofAtom'
import { moduleSelectionDataState, selectedModuleState } from '@/store/selectedModuleOptions'
import { isObjectNotEmpty } from '@/util/common-utils'
import { forwardRef, useContext, useEffect, useImperativeHandle, useRef, useState } from 'react'
import { useRecoilState, useRecoilValue } from 'recoil'
const Trestle = forwardRef((props, ref) => {
const { tabNum, setTabNum, trestleTrigger, roofs, setRoofs, moduleSelectionData, setModuleSelectionData } = props
const { tabNum, setTabNum, trestleTrigger, roofs, setRoofs, moduleSelectionData, setModuleSelectionData, setRoofsStore } = props
const { getMessage } = useMessage()
// const [selectedTrestle, setSelectedTrestle] = useState()
const currentAngleType = useRecoilValue(currentAngleTypeSelector)
@ -66,8 +65,6 @@ const Trestle = forwardRef((props, ref) => {
restoreModuleInstArea()
}, [roofs])
useEffect(() => {}, [moduleSelectionData])
useEffect(() => {
if (flag && moduleSelectionData) {
if (JSON.stringify(tempModuleSelectionData.current) === JSON.stringify(moduleSelectionData)) {
@ -82,7 +79,16 @@ const Trestle = forwardRef((props, ref) => {
setEavesMargin(selectedRoof?.eavesMargin ?? 0)
setRidgeMargin(selectedRoof?.ridgeMargin ?? 0)
setKerabaMargin(selectedRoof?.kerabaMargin ?? 0)
dispatch({ type: 'SET_INITIALIZE', roof: { ...selectedRoof, ...moduleSelectionData } })
setLengthBase(Math.round(selectedRoof?.lenBase ?? 0))
if (moduleSelectionData?.roofConstructions?.length >= selectedRoof.index + 1) {
const { construction, trestle, trestleDetail } = moduleSelectionData?.roofConstructions[selectedRoof.index]
dispatch({
type: 'SET_INITIALIZE',
roof: { common: moduleSelectionData.common, module: moduleSelectionData.module, construction, trestle, trestleDetail, ...selectedRoof },
})
} else {
dispatch({ type: 'SET_INITIALIZE', roof: { ...selectedRoof, ...moduleSelectionData } })
}
}
}, [selectedRoof])
@ -180,33 +186,6 @@ const Trestle = forwardRef((props, ref) => {
},
})
}
const handleChangeRoofMaterial = (index) => {
const newAddedRoofs = roofs.map((roof, i) => {
if (i === selectedRoof.index) {
return {
...selectedRoof,
eavesMargin,
ridgeMargin,
kerabaMargin,
roofIndex: roof.index,
trestle: {
lengthBase: lengthBase,
...selectedRaftBase,
...selectedTrestle,
...selectedConstMthd,
...selectedRoofBase,
},
construction: {
...constructionList.find((data) => data.constTp === trestleState.constTp),
},
trestleDetail: trestleDetail,
}
}
return roof
})
setRoofs(newAddedRoofs)
setSelectedRoof(newAddedRoofs[index])
}
const handleConstruction = (index) => {
if (constructionList[index]?.constPossYn === 'Y') {
@ -227,7 +206,7 @@ const Trestle = forwardRef((props, ref) => {
roofPitch: Math.round(selectedRoof?.roofPchBase ?? 0),
constTp: constructionList[index].constTp,
mixMatlNo: selectedModules.mixMatlNo,
workingWidth: selectedRoof?.length.toString() ?? '',
workingWidth: selectedRoof?.length?.toString() ?? '',
// snowGdPossYn: constructionList[index].snowGdPossYn,
// cvrYn: constructionList[index].cvrYn,
},
@ -240,9 +219,38 @@ const Trestle = forwardRef((props, ref) => {
}
}
const handleChangeRoofMaterial = (index) => {
const newAddedRoofs = roofs.map((roof, i) => {
if (i === selectedRoof.index) {
return {
...selectedRoof,
eavesMargin,
ridgeMargin,
kerabaMargin,
roofIndex: selectedRoof.index,
trestle: {
lengthBase: lengthBase,
...selectedRaftBase,
...selectedTrestle,
...selectedConstMthd,
...selectedRoofBase,
},
construction: {
...constructionList.find((data) => data.constTp === trestleState.constTp),
},
trestleDetail: trestleDetail,
}
}
return roof
})
setRoofs(newAddedRoofs)
setSelectedRoof(newAddedRoofs[index])
console.log(newAddedRoofs)
}
const isComplete = async () => {
const newAddedRoofs = roofs.map((roof, i) => {
if (i === selectedRoof?.roofIndex) {
if (i === selectedRoof?.index) {
return {
...selectedRoof,
eavesMargin,
@ -286,7 +294,7 @@ const Trestle = forwardRef((props, ref) => {
})
const newRoofs = newAddedRoofs.map((roof) => {
const { addRoof, trestle, construction, ...rest } = roof
const { addRoof, construction, trestle, trestleDetail, roofConstructions, ...rest } = roof
return rest
})
if (result) {
@ -321,23 +329,25 @@ const Trestle = forwardRef((props, ref) => {
}),
new Promise((resolve) => {
trestleTrigger({
roofConstruction: newAddedRoofs.map((roof, index) => ({
const roofConstruction = newAddedRoofs.map((roof, index) => ({
roofIndex: roof.index,
addRoof: newRoofs[index],
trestle: {
...roof.trestle,
raftBaseCd: roof.raftBaseCd,
},
construction: {
...constructionList.find((construction) => newAddedRoofs[index].construction.constTp === construction.constTp),
roofIndex: roof.index,
addRoof: newRoofs[index],
trestle: {
...selectedTrestle,
raftBaseCd: roof.raftBaseCd,
},
construction: {
...constructionList.find((construction) => trestleState.constTp === construction.constTp),
roofIndex: roof.index,
setupCover: roof.cvrYn === 'Y',
setupSnowCover: roof.snowGdYn === 'Y',
selectedIndex: roof.index,
},
})),
setupCover: roof.cvrYn === 'Y',
setupSnowCover: roof.snowGdYn === 'Y',
selectedIndex: roof.index,
},
}))
trestleTrigger({
roofConstruction,
})
setRoofsStore(roofConstruction)
resolve()
}),
]
@ -383,7 +393,7 @@ const Trestle = forwardRef((props, ref) => {
<input
type="text"
className="input-origin block"
value={trestleState?.lengthBase}
value={lengthBase}
onChange={(e) => setLengthBase(e.target.value)}
disabled={selectedRoof.lenAuth === 'R'}
/>

View File

@ -23,21 +23,21 @@ const trestleReducer = (state, action) => {
moduleTpCd: action.roof.module?.itemTp ?? '',
roofMatlCd: action.roof?.roofMatlCd ?? '',
raftBaseCd: action.roof?.raftBaseCd ?? null,
trestleMkrCd: action.roof.roofConstructions[action.roof.index].trestle?.trestleMkrCd ?? null,
constMthdCd: action.roof.roofConstructions[action.roof.index].trestle?.constMthdCd ?? null,
constTp: action.roof.roofConstructions[action.roof.index].construction?.constTp ?? null,
roofBaseCd: action.roof.roofConstructions[action.roof.index].trestle?.roofBaseCd ?? null,
trestleMkrCd: action.roof.trestle?.trestleMkrCd ?? null,
constMthdCd: action.roof.trestle?.constMthdCd ?? null,
constTp: action.roof.construction?.constTp ?? null,
roofBaseCd: action.roof.trestle?.roofBaseCd ?? null,
workingWidth: action.roof.workingWidth ?? 0,
lengthBase: action.roof.roofConstructions[action.roof.index].addRoof?.length ?? 0,
lengthBase: action.roof?.length ?? 0,
illuminationTp: action.roof.common.illuminationTp ?? null,
instHt: action.roof.common.instHt ?? null,
stdWindSpeed: action.roof.common.stdWindSpeed ?? null,
stdSnowLd: action.roof.common.stdSnowLd ?? null,
inclCd: action.roof.roofConstructions[action.roof.index].addRoof?.pitch ?? null,
roofPitch: action.roof.roofConstructions[action.roof.index].addRoof?.roofPchBase ?? 0,
eavesMargin: action.roof.roofConstructions[action.roof.index].addRoof?.eavesMargin ?? null,
ridgeMargin: action.roof.roofConstructions[action.roof.index].addRoof?.ridgeMargin ?? null,
kerabaMargin: action.roof.roofConstructions[action.roof.index].addRoof?.kerabaMargin ?? null,
inclCd: action.roof?.pitch ?? null,
roofPitch: action.roof?.roofPchBase ?? 0,
eavesMargin: action.roof?.eavesMargin ?? null,
ridgeMargin: action.roof?.ridgeMargin ?? null,
kerabaMargin: action.roof?.kerabaMargin ?? null,
}
default:
return state

View File

@ -708,7 +708,9 @@ export const useTrestle = () => {
})
// trestles 배열에서 null인 경우 제거
const params = { trestles, pcses, modules }
const dblCblTotCnt = getTotalConnectCableCnt()
const params = { trestles, pcses, modules, dblCblTotCnt }
//견적서 itemList 조회
const { data, data2, result } = await getQuotationItem(params)
@ -2270,9 +2272,9 @@ export const useTrestle = () => {
}
return (
(Math.abs(m1X - m2X) <= maxX && Math.abs(m1Y - m2Y) <= maxY) ||
(Math.abs(Math.abs(m1X - m2X) - maxX / 2) <= halfMaxX && Math.abs(Math.abs(m1Y - m2Y) - maxY) <= halfMaxY) ||
(Math.abs(Math.abs(m1X - m2X) - maxX) <= halfMaxX && Math.abs(Math.abs(m1Y - m2Y) - maxY / 2) <= halfMaxY)
(Math.abs(m1X - m2X) < maxX - moduleIntvlHor / 10 && Math.abs(m1Y - m2Y) < maxY - moduleIntvlVer / 10) ||
(Math.abs(Math.abs(m1X - m2X) - maxX / 2) < halfMaxX + 1 && Math.abs(Math.abs(m1Y - m2Y) - maxY) < halfMaxY + 1) ||
(Math.abs(Math.abs(m1X - m2X) - maxX) < halfMaxX + 1 && Math.abs(Math.abs(m1Y - m2Y) - maxY / 2) < halfMaxY + 1)
)
} else if (direction === 'east' || direction === 'west') {
maxX = height + moduleIntvlHor / 10
@ -2292,9 +2294,9 @@ export const useTrestle = () => {
}
return (
(Math.abs(m1X - m2X) <= maxY && Math.abs(m1Y - m2Y) <= maxX) ||
(Math.abs(Math.abs(m1X - m2X) - maxY / 2) <= halfMaxY && Math.abs(Math.abs(m1Y - m2Y) - maxX) <= halfMaxX) ||
(Math.abs(Math.abs(m1X - m2X) - maxY) <= halfMaxY && Math.abs(Math.abs(m1Y - m2Y) - maxX / 2) <= halfMaxX)
(Math.abs(m1X - m2X) <= maxY - moduleIntvlVer / 10 && Math.abs(m1Y - m2Y) <= maxX - moduleIntvlHor / 10) ||
(Math.abs(Math.abs(m1X - m2X) - maxY / 2) < halfMaxY + 1 && Math.abs(Math.abs(m1Y - m2Y) - maxX) < halfMaxX + 1) ||
(Math.abs(Math.abs(m1X - m2X) - maxY) < halfMaxY + 1 && Math.abs(Math.abs(m1Y - m2Y) - maxX / 2) < halfMaxX + 1)
)
}
})
@ -3035,6 +3037,73 @@ export const useTrestle = () => {
return surfaces.every((surface) => surface.isComplete)
}
const groupByType = (originArr = []) => {
const grouped = {}
const newArr = [...originArr]
// 타입별로 객체들을 분류
for (const item of newArr) {
if (!grouped[item.circuitNumber]) {
grouped[item.circuitNumber] = []
}
grouped[item.circuitNumber].push(item)
}
// 객체를 배열로 변환
return Object.values(grouped)
}
function groupByCircuitAndSurface(arr) {
const circuitGroups = {}
for (const item of arr) {
const { circuitNumber, surfaceId } = item
if (!circuitGroups[circuitNumber]) {
circuitGroups[circuitNumber] = new Map()
}
const surfaceMap = circuitGroups[circuitNumber]
const key = surfaceId
if (!surfaceMap.has(key)) {
surfaceMap.set(key, [])
}
surfaceMap.get(key).push(item)
}
// 결과: circuitNumber별 surface 그룹 수
const result = {}
for (const [circuit, surfaceMap] of Object.entries(circuitGroups)) {
result[circuit] = surfaceMap.size
}
return result
}
// 양단 케이블 구하는 공식
const getTotalConnectCableCnt = () => {
let cnt = 0
const surfaces = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE)
const modules = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE)
surfaces.forEach((surface) => {
const modules = surface.modules
const groups = groupByType(modules)
groups.forEach((group) => {
const result = groupPoints(group, surface)
cnt += result.length - 1
})
})
const groupByCircuitAndSurfaceCnt = groupByCircuitAndSurface(modules)
Object.keys(groupByCircuitAndSurfaceCnt).forEach((key) => {
cnt += groupByCircuitAndSurfaceCnt[key] - 1
})
return cnt
}
return {
apply,
getTrestleParams,

View File

@ -905,6 +905,7 @@
"estimate.detail.estimateType": "注文分類",
"estimate.detail.estimateType.yjss": "住宅PKG",
"estimate.detail.estimateType.yjod": "積上げYJOD",
"estimate.detail.agency": "2次店名",
"estimate.detail.roofCns": "屋根材・施工区分",
"estimate.detail.remarks": "備考",
"estimate.detail.fileFlg": "後日資料提出",

View File

@ -906,6 +906,7 @@
"estimate.detail.estimateType": "주문분류",
"estimate.detail.estimateType.yjss": "住宅PKG",
"estimate.detail.estimateType.yjod": "積上げ( YJOD )",
"estimate.detail.agency": "2차점명",
"estimate.detail.roofCns": "지붕재・사양시공",
"estimate.detail.remarks": "비고",
"estimate.detail.fileFlg": "후일자료제출",

View File

@ -47,6 +47,16 @@ table{
}
.form-flex-wrap{
@include flexbox;
.form-flex-select{
@include flexbox;
label{
flex: none;
margin-right: 5px;
}
.form-select{
min-width: 300px;
}
}
}
.date-picker-wrap{
width: 100%;