Merge pull request 'dev' (#764) from dev into prd-deploy

Reviewed-on: #764
This commit is contained in:
ysCha 2026-04-08 16:24:27 +09:00
commit 1935baf471
3 changed files with 436 additions and 700 deletions

View File

@ -130,6 +130,7 @@ export default function StepUp(props) {
if (selectedSerQty) { if (selectedSerQty) {
selectedSerQty.roofSurfaceList.forEach((roofSurface) => { selectedSerQty.roofSurfaceList.forEach((roofSurface) => {
const targetSurface = canvas.getObjects().filter((obj) => obj.id === roofSurface.roofSurfaceId)[0] const targetSurface = canvas.getObjects().filter((obj) => obj.id === roofSurface.roofSurfaceId)[0]
if (!targetSurface) return
const moduleIds = targetSurface.modules.map((module) => module.id) const moduleIds = targetSurface.modules.map((module) => module.id)
/** 기존 모듈 텍스트 삭제 */ /** 기존 모듈 텍스트 삭제 */
@ -406,37 +407,94 @@ export default function StepUp(props) {
/** /**
* 선택 핸들러 함수 추가 * 선택 핸들러 함수 추가
*/ */
/**
* 주어진 stepUpListData 기준으로 캔버스의 모든 module circuit 텍스트를 다시 그린다.
* 모든 pcsItem selected serQty 순회해 적용한다.
*/
const applyCircuitsToCanvas = (stepUpListSrc) => {
if (!stepUpListSrc?.[0]?.pcsItemList) return
/** 모든 모듈 circuit 데이터 초기화 */
canvas
.getObjects()
.filter((obj) => obj.name === POLYGON_TYPE.MODULE)
.forEach((module) => {
module.circuit = null
module.circuitNumber = null
module.pcsItemId = null
})
/** 기존 circuit 텍스트 객체 모두 제거 */
canvas
.getObjects()
.filter((obj) => obj.name === 'circuitNumber')
.forEach((text) => canvas.remove(text))
stepUpListSrc[0].pcsItemList.forEach((pcsItem) => {
const sel = pcsItem.serQtyList?.find((sq) => sq.selected)
if (!sel) return
sel.roofSurfaceList.forEach((roofSurface) => {
const targetSurface = canvas.getObjects().filter((obj) => obj.id === roofSurface.roofSurfaceId)[0]
if (!targetSurface) return
roofSurface.moduleList.forEach((module) => {
const targetModule = canvas.getObjects().find((obj) => obj.id === module.uniqueId)
if (targetModule && module.circuit !== '' && module.circuit !== null) {
const moduleCircuitText = new fabric.Text(module.circuit, {
left: targetModule.left + targetModule.width / 2,
top: targetModule.top + targetModule.height / 2,
fontFamily: circuitNumberText.fontFamily.value,
fontWeight: circuitNumberText.fontWeight.value.toLowerCase().includes('bold') ? 'bold' : 'normal',
fontStyle: circuitNumberText.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal',
fontSize: circuitNumberText.fontSize.value,
fill: circuitNumberText.fontColor.value,
width: targetModule.width,
height: targetModule.height,
textAlign: 'center',
originX: 'center',
originY: 'center',
name: 'circuitNumber',
parentId: targetModule.id,
circuitInfo: module.pcsItemId,
visible: isDisplayCircuitNumber,
})
targetModule.circuit = moduleCircuitText
targetModule.pcsItemId = module.pcsItemId
targetModule.circuitNumber = module.circuit
canvas.add(moduleCircuitText)
}
})
})
})
canvas.renderAll()
setModuleStatisticsData()
}
const handleRowClick = (mainIdx, subIdx, applyParalQty = null) => { const handleRowClick = (mainIdx, subIdx, applyParalQty = null) => {
/** 자동 승압 설정인 경우만 실행 */ /** 자동 승압 설정인 경우만 실행 */
if (allocationType !== 'auto') return if (allocationType !== 'auto') return
let tempStepUpListData = [...stepUpListData] let tempStepUpListData = [...stepUpListData]
let selectedData = {}
tempStepUpListData[0].pcsItemList[mainIdx].serQtyList.forEach((item, index) => { tempStepUpListData[0].pcsItemList[mainIdx].serQtyList.forEach((item, index) => {
if (index === subIdx) {
selectedData = item
}
item.selected = index === subIdx item.selected = index === subIdx
}) })
/** 선택된 행 정보 저장 */ /** 선택된 행 정보 저장 */
setStepUpListData(tempStepUpListData) setStepUpListData(tempStepUpListData)
/** PCS 2개 이상 또는 첫번째 PCS 선택 시에만 실행 */ /** PCS 2개 이상 또는 첫번째 PCS 선택 시에만 실행 */
if (tempStepUpListData[0].pcsItemList.length > 1 && mainIdx === 0) { const needsRefetch =
tempStepUpListData[0].pcsItemList.length > 1 && mainIdx === 0
if (needsRefetch) {
/** 파워컨디셔너 옵션 조회 요청 파라미터 */ /** 파워컨디셔너 옵션 조회 요청 파라미터 */
const params = { const params = {
...props.getOptYn(), // Y/N ...props.getOptYn(), // Y/N
useModuleItemList: props.getUseModuleItemList(), // List useModuleItemList: props.getUseModuleItemList(), // List
roofSurfaceList: props.getRoofSurfaceList(), // roofSurfaceList: props.getRoofSurfaceList(), //
pcsItemList: props.getSelectedPcsItemList().map((pcsItem, index) => { pcsItemList: props.getSelectedPcsItemList().map((pcsItem, index) => {
/** PCS
* tempStepUpListData에서 해당 PCS 아이템 찾기
* uniqueIndex를 사용하여 매칭
*/
const matchingPcsItem = tempStepUpListData[0].pcsItemList.find((item) => item.uniqueIndex === `${pcsItem.itemId}_${index}`) const matchingPcsItem = tempStepUpListData[0].pcsItemList.find((item) => item.uniqueIndex === `${pcsItem.itemId}_${index}`)
/** 선택된 serQty 찾기 */
const selectedSerQty = matchingPcsItem?.serQtyList.find((serQty) => serQty.selected)?.serQty || 0 const selectedSerQty = matchingPcsItem?.serQtyList.find((serQty) => serQty.selected)?.serQty || 0
if (index === 0) { if (index === 0) {
return { return {
@ -453,86 +511,30 @@ export default function StepUp(props) {
} }
/** PCS가 1개 이고 2번째 또는 3번째 PCS serQty가 0인 경우는 추천 API 실행하지 않음 */ /** PCS가 1개 이고 2번째 또는 3번째 PCS serQty가 0인 경우는 추천 API 실행하지 않음 */
if (params.pcsItemList.length !== 1 && (params.pcsItemList[1]?.applySerQty !== 0 || params.pcsItemList[2]?.applySerQty) !== 0) { const skipApi = !(params.pcsItemList.length !== 1 && (params.pcsItemList[1]?.applySerQty !== 0 || params.pcsItemList[2]?.applySerQty) !== 0)
/** PCS 승압설정 정보 조회 */
if (!skipApi) {
/**
* PCS 승압설정 정보 조회. 캔버스 갱신은 응답을 받은 뒤에 수행해야
* "한박자 늦게" 반영되는 stale state 문제가 발생하지 않는다.
*/
getPcsVoltageStepUpList(params).then((res) => { getPcsVoltageStepUpList(params).then((res) => {
if (res?.result.resultCode === 'S' && res?.data) { if (res?.result.resultCode === 'S' && res?.data) {
const dataArray = Array.isArray(res.data) ? res.data : [res.data] const dataArray = Array.isArray(res.data) ? res.data : [res.data]
const stepUpListData = formatStepUpListData(dataArray) const newStepUpListData = formatStepUpListData(dataArray)
setStepUpListData(newStepUpListData)
/** PCS 승압설정 정보 SET */ applyCircuitsToCanvas(newStepUpListData)
setStepUpListData(stepUpListData)
/** PCS 옵션 조회 */
// const formattedOptCodes = formatOptionCodes(res.data.optionList)
// setOptCodes(formattedOptCodes)
// setSeletedOption(formattedOptCodes[0])
} else { } else {
swalFire({ text: getMessage('common.message.send.error') }) swalFire({ text: getMessage('common.message.send.error') })
applyCircuitsToCanvas(tempStepUpListData)
} }
}) })
return
} }
} }
/** 모듈 목록 삭제 */ /** API 재조회가 없는 경로: 현재 tempStepUpListData 로 즉시 캔버스 갱신 */
canvas applyCircuitsToCanvas(tempStepUpListData)
.getObjects()
.filter((obj) => obj.name === POLYGON_TYPE.MODULE)
.forEach((module) => {
module.circuit = null
module.circuitNumber = null
module.pcsItemId = null
})
/** 선택된 모듈 목록 추가 */
selectedData.roofSurfaceList.forEach((roofSurface) => {
const targetSurface = canvas.getObjects().filter((obj) => obj.id === roofSurface.roofSurfaceId)[0]
const moduleIds = targetSurface.modules.map((module) => {
return module.id
})
/** 모듈 목록 삭제 */
canvas
.getObjects()
.filter((obj) => moduleIds.includes(obj.parentId))
.map((text) => {
canvas.remove(text)
})
/** 모듈 목록 추가 */
canvas.renderAll()
roofSurface.moduleList.forEach((module) => {
const targetModule = canvas.getObjects().find((obj) => obj.id === module.uniqueId)
if (targetModule && module.circuit !== '' && module.circuit !== null) {
const moduleCircuitText = new fabric.Text(module.circuit, {
left: targetModule.left + targetModule.width / 2,
top: targetModule.top + targetModule.height / 2,
fontFamily: circuitNumberText.fontFamily.value,
fontWeight: circuitNumberText.fontWeight.value.toLowerCase().includes('bold') ? 'bold' : 'normal',
fontStyle: circuitNumberText.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal',
fontSize: circuitNumberText.fontSize.value,
fill: circuitNumberText.fontColor.value,
width: targetModule.width,
height: targetModule.height,
textAlign: 'center',
originX: 'center',
originY: 'center',
name: 'circuitNumber',
parentId: targetModule.id,
circuitInfo: module.pcsItemId,
visible: isDisplayCircuitNumber,
})
targetModule.circuit = moduleCircuitText
targetModule.pcsItemId = module.pcsItemId
targetModule.circuitNumber = module.circuit
canvas.add(moduleCircuitText)
}
})
})
canvas.renderAll()
setModuleStatisticsData()
} }
/** /**

View File

@ -1765,10 +1765,10 @@ export function useMode() {
const reversedVertices = [...polygon.vertices].reverse() const reversedVertices = [...polygon.vertices].reverse()
const reversedLines = [] const reversedLines = []
const n = lines.length const n = lines.length
// lines[i] 는 edge i (vertex[i] → vertex[i+1]) 에 해당. reverse 후의 edge j 는 // reverse 후 edge j 는 원본 edge (n-2-j) 의 역방향에 해당
// 원본 edge (n - 1 - j) 와 같은 선분이므로 lines 배열도 동일 매핑으로 재정렬. // (reverse edge 0 = v[n-1]→v[n-2] = 원본 edge n-2 역방향, ..., 마지막 edge = 원본 edge n-1 역방향)
for (let j = 0; j < n; j++) { for (let j = 0; j < n; j++) {
reversedLines.push(lines[(n - 1 - j) % n]) reversedLines.push(lines[((n - 2 - j) % n + n) % n])
} }
polygon = createRoofPolygon(reversedVertices) polygon = createRoofPolygon(reversedVertices)
lines = reversedLines lines = reversedLines
@ -1777,7 +1777,7 @@ export function useMode() {
const offsetEdges = [] const offsetEdges = []
polygon.edges.forEach((edge, i) => { polygon.edges.forEach((edge, i) => {
const offset = lines[i % lines.length].attributes.offset const offset = lines[i % lines.length].attributes.offset === 0 ? 1 : lines[i % lines.length].attributes.offset
const dx = edge.outwardNormal.x * offset const dx = edge.outwardNormal.x * offset
const dy = edge.outwardNormal.y * offset const dy = edge.outwardNormal.y * offset
offsetEdges.push(createOffsetEdge(edge, dx, dy)) offsetEdges.push(createOffsetEdge(edge, dx, dy))
@ -1819,8 +1819,9 @@ export function useMode() {
const reversedVertices = [...polygon.vertices].reverse() const reversedVertices = [...polygon.vertices].reverse()
const reversedLines = [] const reversedLines = []
const n = lines.length const n = lines.length
// reverse 후 edge j 는 원본 edge (n-2-j) 의 역방향에 해당
for (let j = 0; j < n; j++) { for (let j = 0; j < n; j++) {
reversedLines.push(lines[(n - 1 - j) % n]) reversedLines.push(lines[((n - 2 - j) % n + n) % n])
} }
polygon = createRoofPolygon(reversedVertices) polygon = createRoofPolygon(reversedVertices)
lines = reversedLines lines = reversedLines
@ -1829,8 +1830,7 @@ export function useMode() {
const offsetEdges = [] const offsetEdges = []
polygon.edges.forEach((edge, i) => { polygon.edges.forEach((edge, i) => {
const offset = lines[i % lines.length].attributes.offset const offset = lines[i % lines.length].attributes.offset === 0 ? 1 : lines[i % lines.length].attributes.offset
const dx = edge.inwardNormal.x * offset const dx = edge.inwardNormal.x * offset
const dy = edge.inwardNormal.y * offset const dy = edge.inwardNormal.y * offset
offsetEdges.push(createOffsetEdge(edge, dx, dy)) offsetEdges.push(createOffsetEdge(edge, dx, dy))

File diff suppressed because it is too large Load Diff