dev #128

Merged
ysCha merged 11 commits from dev into dev-deploy 2025-06-19 15:42:20 +09:00
8 changed files with 109 additions and 13 deletions

View File

@ -38,10 +38,14 @@ const cropImage = async (Key, width, height, left, top) => {
} }
const buffer = Buffer.concat(chunks) const buffer = Buffer.concat(chunks)
const image = await Jimp.read(buffer) let image = await Jimp.read(buffer)
image.autocrop({ tolerance: 0.0002, leaveBorder: 10 }) image.autocrop({ tolerance: 0.0002, leaveBorder: 10 })
return await image.getBuffer('image/png')
const resizedImage = await resizeImage(image).then((result) => {
return result
})
return await resizedImage.getBuffer('image/png')
// Convert stream to buffer // Convert stream to buffer
// const chunks = [] // const chunks = []
@ -77,6 +81,60 @@ const cropImage = async (Key, width, height, left, top) => {
} }
} }
//크롭된 이미지를 가지고 높이 기준 -> 너비 기준으로 비율 변경 후 이미지에 추가
const resizeImage = async (image) => {
// //이미지를 cm로 변환
let imageHeightCm = Math.round((image.bitmap.height * 2.54) / 96) //원본 이미지 센치로 변환
let imageWidthCm = Math.round((image.bitmap.width * 2.54) / 96) //원본 이미지 센치로 변환
const calcWidthRatio = 12.89 / imageHeightCm //원본 비율 계산
let convertHeightCm = Math.round(imageHeightCm * calcWidthRatio) //변환된 이미지 CM
let convertWidthCm = Math.round(imageWidthCm * calcWidthRatio) //변환된 이미지 CM
let convertHeightBitmap = Math.round((convertHeightCm * 96) / 2.54) //비트맵 사이즈로 변환
let convertWidthBitmap = Math.round((convertWidthCm * 96) / 2.54) //비트맵 사이즈로 변환
//높이 기준으로 리사이즈를 함
image.resize({ w: convertWidthBitmap, h: convertHeightBitmap, mode: Jimp.RESIZE_BILINEAR })
//높이를 기준으로 변경 후에 가로 폭이 더 넓으면 가로폭 기준으로 다시 사이즈를 조절한다
if (convertWidthCm > 35.4) {
//너비가 더 클때
imageHeightCm = Math.round((image.bitmap.height * 2.54) / 96) //높이 기준으로 리사이즈된 이미지 크기
imageWidthCm = Math.round((image.bitmap.width * 2.54) / 96) //높이 기준으로 리사이즈된 이미지 크기
const calcWidthRatio = 35.4 / imageWidthCm //리사이즈된 이미지 가로 비율 계산
convertHeightCm = Math.round(imageHeightCm * calcWidthRatio) //너비 기준 이미지 비율 계산
convertWidthCm = Math.round(imageWidthCm * calcWidthRatio) //변환된 이미지 CM
convertHeightBitmap = Math.round((convertHeightCm * 96) / 2.54) //비트맵 사이즈로 변환
convertWidthBitmap = Math.round((convertWidthCm * 96) / 2.54) //비트맵 사이즈로 변환
image.resize({ w: convertWidthBitmap, h: convertHeightBitmap, mode: Jimp.RESIZE_BILINEAR }) //이미지 리사이즈
}
//엑셀 템플릿 너비 35.4cm, 높이 12.89cm
const convertStandardWidth = Math.round((35.4 * 96) / 2.54)
const convertStandardHeight = Math.round((12.89 * 96) / 2.54)
//엑셀 템플릿 사이즈로 배경 이미지를 생성
const mixedImage = new Jimp({ width: convertStandardWidth, height: convertStandardHeight, color: 0xffffffff })
//이미지를 중앙에 배치
const x = Math.floor((mixedImage.bitmap.width - image.bitmap.width) / 2)
const y = Math.floor((mixedImage.bitmap.height - image.bitmap.height) / 2)
//이미지를 배경 이미지에 합성
mixedImage.composite(image, x, y, {
mode: Jimp.BLEND_SOURCE_OVER,
opacitySource: 1, // 원본 투명도 유지
opacityDest: 1,
})
return mixedImage
}
export async function POST(req) { export async function POST(req) {
try { try {
const formData = await req.formData() const formData = await req.formData()

View File

@ -212,6 +212,7 @@ export const SAVE_KEY = [
'endPoint', 'endPoint',
'editable', 'editable',
'isSortedPoints', 'isSortedPoints',
'isMultipleOf45',
] ]
export const OBJECT_PROTOTYPE = [fabric.Line.prototype, fabric.Polygon.prototype, fabric.Triangle.prototype, fabric.Group.prototype] export const OBJECT_PROTOTYPE = [fabric.Line.prototype, fabric.Polygon.prototype, fabric.Triangle.prototype, fabric.Group.prototype]

View File

@ -731,7 +731,7 @@ export default function Estimate({}) {
/* 케이블 select 변경시 */ /* 케이블 select 변경시 */
const onChangeDisplayCableItem = (value, itemList) => { const onChangeDisplayCableItem = (value, itemList) => {
itemList.map((item, index) => { itemList.map((item, index) => {
if (item.dispCableFlg === '1' && item.itemTpCd !== 'M12') { if (item.dispCableFlg === '1' && item.itemTpCd !== 'M12' && item.itemTpCd !== 'S13') {
if (value !== '') { if (value !== '') {
onChangeDisplayItem(value, item.dispOrder, index, true) onChangeDisplayItem(value, item.dispOrder, index, true)
} }
@ -743,7 +743,7 @@ export default function Estimate({}) {
/* 케이블 select 변경시 */ /* 케이블 select 변경시 */
const onChangeDisplayDoubleCableItem = (value, itemList) => { const onChangeDisplayDoubleCableItem = (value, itemList) => {
itemList.map((item, index) => { itemList.map((item, index) => {
if (item.dispCableFlg === '1' && item.itemTpCd === 'M12') { if (item.dispCableFlg === '1' && (item.itemTpCd === 'M12' || item.itemTpCd === 'S13')) {
if (value !== '') { if (value !== '') {
onChangeDisplayItem(value, item.dispOrder, index, true) onChangeDisplayItem(value, item.dispOrder, index, true)
} }
@ -1103,7 +1103,7 @@ export default function Estimate({}) {
if (item.dispCableFlg === '1' ) { if (item.dispCableFlg === '1' ) {
dispCableFlgCnt++ dispCableFlgCnt++
if(item.itemTpCd === 'M12') { if(item.itemTpCd === 'M12' || item.itemTpCd === 'S13') {
setCableDbItem(item.itemId) setCableDbItem(item.itemId)
}else{ }else{
setCableItem(item.itemId) setCableItem(item.itemId)
@ -1179,7 +1179,7 @@ export default function Estimate({}) {
if (item.dispCableFlg === '1'){ if (item.dispCableFlg === '1'){
if(item.itemTpCd === 'M12') { if(item.itemTpCd === 'M12' || item.itemTpCd === 'S13') {
setCableDbItem(item.itemId) setCableDbItem(item.itemId)
}else{ }else{
setCableItem(item.itemId) setCableItem(item.itemId)
@ -2031,13 +2031,13 @@ export default function Estimate({}) {
getOptionValue={(x) => x.clRefChr1} getOptionValue={(x) => x.clRefChr1}
components={{ components={{
SingleValue: ({ children, ...props }) => { SingleValue: ({ children, ...props }) => {
return <components.SingleValue {...props}>{(item.itemTpCd === 'M12')? item.itemName : props.data.clRefChr3}</components.SingleValue> return <components.SingleValue {...props}>{(item.itemTpCd === 'M12' || item.itemTpCd === 'S13')? item.itemName : props.data.clRefChr3}</components.SingleValue>
}, },
}} }}
isClearable={false} isClearable={false}
isDisabled={true} isDisabled={true}
value={cableItemList.filter(function (option) { value={cableItemList.filter(function (option) {
return (item.itemTpCd === 'M12')? item.itemId : option.clRefChr1 === item.itemId return (item.itemTpCd === 'M12' || item.itemTpCd === 'S13' )? item.itemId : option.clRefChr1 === item.itemId
})} })}
/> />
)} )}

View File

@ -210,8 +210,26 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
line.startPoint = point line.startPoint = point
line.endPoint = nextPoint line.endPoint = nextPoint
this.lines.push(line) this.lines.push(line)
this.calculateDegree()
}) })
}, },
calculateDegree() {
const degrees = []
// polygon.lines를 순회하며 각도를 구해 출력
this.lines.forEach((line, idx) => {
const dx = line.x2 - line.x1
const dy = line.y2 - line.y1
const rad = Math.atan2(dy, dx)
const degree = (rad * 180) / Math.PI
degrees.push(degree)
})
function isMultipleOf45(degree, epsilon = 1) {
return Math.abs(degree % 45) <= epsilon || Math.abs((degree % 45) - 45) <= epsilon
}
this.isMultipleOf45 = degrees.every((degree) => isMultipleOf45(degree))
},
/** /**
* 보조선 그리기 * 보조선 그리기

View File

@ -174,6 +174,19 @@ const Trestle = forwardRef((props, ref) => {
}) })
} }
const onChangeHajebichi = (e) => {
setHajebichi(e)
dispatch({
type: 'SET_HAJEBICHI',
roof: {
moduleTpCd: selectedModules.itemTp ?? '',
roofMatlCd: selectedRoof?.roofMatlCd ?? '',
raft: selectedRaftBase?.clCode,
hajebichi: e,
},
})
}
const onChangeTrestleMaker = (e) => { const onChangeTrestleMaker = (e) => {
setSelectedTrestle(e) setSelectedTrestle(e)
dispatch({ dispatch({
@ -578,7 +591,7 @@ const Trestle = forwardRef((props, ref) => {
type="text" type="text"
className="input-origin block" className="input-origin block"
disabled={selectedRoof.roofPchAuth === 'R'} disabled={selectedRoof.roofPchAuth === 'R'}
onChange={(e) => setHajebichi(e.target.value)} onChange={(e) => onChangeHajebichi(e.target.value)}
value={hajebichi} value={hajebichi}
/> />
</div> </div>

View File

@ -5,6 +5,7 @@ import { useMasterController } from '@/hooks/common/useMasterController'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { useSwal } from '@/hooks/useSwal' import { useSwal } from '@/hooks/useSwal'
import { pcsCheckState } from '@/store/circuitTrestleAtom' import { pcsCheckState } from '@/store/circuitTrestleAtom'
import { sessionStore } from '@/store/commonAtom'
import { globalLocaleStore } from '@/store/localeAtom' import { globalLocaleStore } from '@/store/localeAtom'
import { moduleSelectionDataState, selectedModuleState } from '@/store/selectedModuleOptions' import { moduleSelectionDataState, selectedModuleState } from '@/store/selectedModuleOptions'
import { isNullOrUndefined } from '@/util/common-utils' import { isNullOrUndefined } from '@/util/common-utils'
@ -38,6 +39,7 @@ export default function PowerConditionalSelect(props) {
} = props } = props
const [pcsCheck, setPcsCheck] = useRecoilState(pcsCheckState) const [pcsCheck, setPcsCheck] = useRecoilState(pcsCheckState)
const sessionState = useRecoilValue(sessionStore)
const { getMessage } = useMessage() const { getMessage } = useMessage()
const [selectedRow, setSelectedRow] = useState(null) const [selectedRow, setSelectedRow] = useState(null)
const globalLocale = useRecoilValue(globalLocaleStore) const globalLocale = useRecoilValue(globalLocaleStore)
@ -132,7 +134,7 @@ export default function PowerConditionalSelect(props) {
mixMatlNo: item.mixMatlNo, mixMatlNo: item.mixMatlNo,
} }
}) })
getPcsModelList({ pcsMkrCd, pcsSerList, moduleItemList }).then((res) => { getPcsModelList({ pcsMkrCd, pcsSerList, moduleItemList, storeId: sessionState.storeId }).then((res) => {
if (res?.result.code === 200 && res?.data) { if (res?.result.code === 200 && res?.data) {
setModels( setModels(
res.data.map((model) => { res.data.map((model) => {
@ -177,7 +179,7 @@ export default function PowerConditionalSelect(props) {
if (selectedMaker.pcsMkrMultiType === PCS_MKR_MULTI_TYPE.MULTI) { if (selectedMaker.pcsMkrMultiType === PCS_MKR_MULTI_TYPE.MULTI) {
setSelectedModels([...selectedModels, { ...selectedRow, id: uuidv4() }]) setSelectedModels([...selectedModels, { ...selectedRow, id: uuidv4() }])
} else if (!selectedModels.find((m) => m.itemId === selectedRow.itemId)) { } else if (!selectedModels.find((m) => m.itemId === selectedRow.itemId && m.pcsSerCd === selectedRow.pcsSerCd)) {
setSelectedModels([...selectedModels, { ...selectedRow, id: uuidv4() }]) setSelectedModels([...selectedModels, { ...selectedRow, id: uuidv4() }])
} }
setSelectedRow(null) setSelectedRow(null)

View File

@ -1,7 +1,9 @@
import { useAxios } from '@/hooks/useAxios' import { useAxios } from '@/hooks/useAxios'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { useSwal } from '@/hooks/useSwal' import { useSwal } from '@/hooks/useSwal'
import { sessionStore } from '@/store/commonAtom'
import { getQueryString } from '@/util/common-utils' import { getQueryString } from '@/util/common-utils'
import { useRecoilValue } from 'recoil'
/** /**
* 마스터 컨트롤러 * 마스터 컨트롤러
@ -11,6 +13,7 @@ export function useMasterController() {
const { get, post, put } = useAxios() const { get, post, put } = useAxios()
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { swalFire } = useSwal() const { swalFire } = useSwal()
const sessionState = useRecoilValue(sessionStore)
/** /**
* 지붕재 목록 조회 * 지붕재 목록 조회
@ -34,7 +37,7 @@ export function useMasterController() {
return null return null
} }
const paramString = `?${paramArr.map((item) => `arrRoofMatlCd=${item}`).join('&')}` const paramString = `?${paramArr.map((item) => `arrRoofMatlCd=${item}`).join('&')}`
return await get({ url: `/api/v1/master/getModuleTypeItemList${paramString}` }).then((res) => { return await get({ url: `/api/v1/master/getModuleTypeItemList${paramString}&storeId=${sessionState.storeId}` }).then((res) => {
// console.log('🚀🚀 ~ getModuleTypeItemList ~ res:', res) // console.log('🚀🚀 ~ getModuleTypeItemList ~ res:', res)
return res return res
}) })

View File

@ -12,6 +12,7 @@ const trestleReducer = (state, action) => {
switch (action.type) { switch (action.type) {
case 'SET_LENGTH': case 'SET_LENGTH':
case 'SET_RAFT_BASE': case 'SET_RAFT_BASE':
case 'SET_HAJEBICHI':
case 'SET_TRESTLE_MAKER': case 'SET_TRESTLE_MAKER':
case 'SET_CONST_MTHD': case 'SET_CONST_MTHD':
case 'SET_ROOF_BASE': case 'SET_ROOF_BASE':