From eeb2ba6c17b753bc976c4ad8739fddd29f102723 Mon Sep 17 00:00:00 2001 From: minsik Date: Tue, 3 Dec 2024 13:05:41 +0900 Subject: [PATCH 1/4] =?UTF-8?q?=ED=8C=8C=EC=9D=BC=20=EB=B6=88=EB=9F=AC?= =?UTF-8?q?=EC=98=A4=EA=B8=B0=20=EB=8B=A4=EA=B5=AD=EC=96=B4=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/locales/ja.json | 5 +++++ src/locales/ko.json | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/src/locales/ja.json b/src/locales/ja.json index 23903120..0409bee0 100644 --- a/src/locales/ja.json +++ b/src/locales/ja.json @@ -341,6 +341,7 @@ "modal.panel.column.insert.info": "挿入する方向を選択してください。", "modal.panel.column.insert.type.left": "左挿入", "modal.panel.column.insert.type.right": "右挿入", + "modal.image.load.size.rotate": "サイズ調整と回転", "contextmenu.column.insert": "列の挿入", "contextmenu.row.move": "단 이동(JA)", "contextmenu.row.copy": "단 복사(JA)", @@ -483,9 +484,13 @@ "common.message.writeToConfirm": "作成解除を実行しますか?", "common.message.password.init.success": "パスワード [{0}] に初期化されました。", "common.message.no.edit.save": "この文書は変更できません。", + "common.load": "ファイルの追加", "common.input.file": "ファイルを読み込む", "common.input.file.load": "ファイルの追加", + "common.input.image.load": "이미지 불러오기", + "common.input.address.load": "アドレスを読み込む", "common.require": "必須", + "common.finish": "完了", "common.ok": "確認", "commons.west": "立つ", "commons.east": "ドン", diff --git a/src/locales/ko.json b/src/locales/ko.json index 0cf7217b..2adcdb28 100644 --- a/src/locales/ko.json +++ b/src/locales/ko.json @@ -348,6 +348,7 @@ "modal.panel.column.insert.info": "삽입할 방향을 선택해주세요.", "modal.panel.column.insert.type.left": "왼쪽 삽입", "modal.panel.column.insert.type.right": "오른쪽 삽입", + "modal.image.load.size.rotate": "크기 조절 및 회전", "contextmenu.row.move": "단 이동", "contextmenu.row.copy": "단 복사", "contextmenu.row.remove": "단 삭제", @@ -492,9 +493,13 @@ "common.message.writeToConfirm": "작성 해제를 실행하시겠습니까?", "common.message.password.init.success": "비밀번호 [{0}]로 초기화 되었습니다.", "common.message.no.edit.save": "This document cannot be changed.", + "common.load": "불러오기", "common.input.file": "파일 불러오기", "common.input.file.load": "불러오기", + "common.input.image.load": "이미지 불러오기", + "common.input.address.load": "주소 불러오기", "common.require": "필수", + "common.finish": "완료", "common.ok": "확인", "commons.west": "서", "commons.east": "동", From 57ce17a9323985bb9f15de5b294f865c1b96fa23 Mon Sep 17 00:00:00 2001 From: minsik Date: Tue, 3 Dec 2024 13:08:29 +0900 Subject: [PATCH 2/4] =?UTF-8?q?-=20=ED=8C=8C=EC=9D=BC=20=EC=A0=80=EC=9E=A5?= =?UTF-8?q?=20=EA=B3=B5=ED=86=B5=20=EC=BD=94=EB=93=9C=20=EC=A0=81=EC=9A=A9?= =?UTF-8?q?=20-=20=ED=8C=8C=EC=9D=BC=20=EC=82=AD=EC=A0=9C=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EC=B6=94=EA=B0=80=20-=20=EB=8B=A4=EA=B5=AD?= =?UTF-8?q?=EC=96=B4=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/api/html2canvas/route.js | 10 +-- src/app/api/image-upload/route.js | 20 ++--- src/components/floor-plan/modal/ImgLoad.jsx | 31 ++++---- src/hooks/common/useRefFiles.js | 81 ++++++++++++++++++--- src/hooks/usePlan.js | 9 +++ src/lib/fileAction.js | 62 ++++++++++++---- 6 files changed, 155 insertions(+), 58 deletions(-) diff --git a/src/app/api/html2canvas/route.js b/src/app/api/html2canvas/route.js index ada6c54a..21f93c82 100644 --- a/src/app/api/html2canvas/route.js +++ b/src/app/api/html2canvas/route.js @@ -3,6 +3,7 @@ import fs from 'fs/promises' import { NextResponse } from 'next/server' +import { writeImage, writeImageBuffer } from '@/lib/fileAction' export async function GET(req) { const path = 'public/plan-map-images' @@ -15,14 +16,7 @@ export async function GET(req) { const response = await fetch(decodeUrl) const data = await response.arrayBuffer() const buffer = Buffer.from(data) - - try { - await fs.readdir(path) - } catch { - await fs.mkdir(path) - } finally { - await fs.writeFile(`${path}/${fileNm}.png`, buffer) - } + await writeImage(fileNm, buffer) return NextResponse.json({ fileNm: `${fileNm}.png` }) } diff --git a/src/app/api/image-upload/route.js b/src/app/api/image-upload/route.js index e817bd3b..92cf7da4 100644 --- a/src/app/api/image-upload/route.js +++ b/src/app/api/image-upload/route.js @@ -3,23 +3,25 @@ import fs from 'fs/promises' import { NextResponse } from 'next/server' +import { writeImage } from '@/lib/fileAction' export async function POST(req) { const path = 'public/plan-bg-images' const formData = await req.formData() const file = formData.get('file') + const fileName = formData.get('fileName') const arrayBuffer = await file.arrayBuffer() const buffer = Buffer.from(arrayBuffer) // const buffer = new Uint8Array(arrayBuffer) + await writeImage(fileName, buffer) + // try { + // await fs.readdir(path) + // } catch { + // await fs.mkdir(path) + // } finally { + // await fs.writeFile(`${path}/${fileName}`, buffer) + // } - try { - await fs.readdir(path) - } catch { - await fs.mkdir(path) - } finally { - await fs.writeFile(`${path}/${file.name}`, buffer) - } - - return NextResponse.json({ fileNm: `${file.name}` }) + return NextResponse.json({ fileNm: `${fileName}` }) } diff --git a/src/components/floor-plan/modal/ImgLoad.jsx b/src/components/floor-plan/modal/ImgLoad.jsx index c95d62a9..d5724972 100644 --- a/src/components/floor-plan/modal/ImgLoad.jsx +++ b/src/components/floor-plan/modal/ImgLoad.jsx @@ -22,18 +22,20 @@ export default function ImgLoad() { setMapPositionAddress, handleFileDelete, handleMapImageDown, + handleAddressDelete, } = useRefFiles() const { currentCanvasPlan } = usePlan() const { getMessage } = useMessage() const { floorPlanState, setFloorPlanState } = useContext(FloorPlanContext) const handleModal = () => { + console.log(floorPlanState) setFloorPlanState({ ...floorPlanState, refFileModalOpen: false }) } useEffect(() => { - console.log('🚀 ~ ImgLoad ~ floorPlanState.refFileModalOpen:', floorPlanState.refFileModalOpen) - console.log('🚀 ~ ImgLoad ~ currentCanvasPlan:', currentCanvasPlan) + // console.log('🚀 ~ ImgLoad ~ floorPlanState.refFileModalOpen:', floorPlanState.refFileModalOpen) + // console.log('🚀 ~ ImgLoad ~ currentCanvasPlan:', currentCanvasPlan) }, [floorPlanState.refFileModalOpen]) useEffect(() => { @@ -50,7 +52,7 @@ export default function ImgLoad() {
- サイズ調整と回転 + {getMessage('modal.image.load.size.rotate')}
diff --git a/src/hooks/common/useRefFiles.js b/src/hooks/common/useRefFiles.js index 05f60afe..5997b399 100644 --- a/src/hooks/common/useRefFiles.js +++ b/src/hooks/common/useRefFiles.js @@ -5,7 +5,7 @@ import { v4 as uuidv4 } from 'uuid' import { useSwal } from '@/hooks/useSwal' import { useAxios } from '../useAxios' import { currentCanvasPlanState } from '@/store/canvasAtom' -import { convertDwgToPng, writeImageBuffer } from '@/lib/fileAction' +import { convertDwgToPng, removeImage, writeImageBuffer } from '@/lib/fileAction' export function useRefFiles() { const converterUrl = process.env.NEXT_PUBLIC_CONVERTER_API_URL @@ -19,17 +19,51 @@ export function useRefFiles() { const { get, promisePut, promisePost } = useAxios() // const { currentCanvasPlan, setCurrentCanvasPlan } = usePlan() + useEffect(() => { + console.log('refImage', refImage) + }, [refImage]) + + useEffect(() => { + if (refFileMethod === '1') { + // 파일 불러오기 + setMapPositionAddress('') + } else { + setRefImage(null) + } + }, [refFileMethod]) /** * 파일 불러오기 버튼 컨트롤 * @param {*} file */ const handleRefFile = (file) => { - setRefImage(file) + console.log('handleRefFile', file) + console.log('refImage', refImage) + if (file && ['image/png', 'image/jpg', 'image/jpeg', 'image/bmp', 'image/gif'].includes(file.type)) { + // setRefImage(file) + file.name.split('.').pop() === 'dwg' ? handleUploadConvertRefFile(file) : handleUploadImageRefFile(file) + } else { + swalFire({ + text: '이미지가 아닙니다.', + type: 'alert', + icon: 'error', + }) + } + if (refImage) { + swalFire({ + text: '파일을 변경하시겠습니까?', + type: 'confirm', + confirmFn: () => { + setRefImage(file) + file.name.split('.').pop() === 'dwg' ? handleUploadConvertRefFile(file) : handleUploadImageRefFile(file) + }, + }) + } + /** * 파일 확장자가 dwg일 경우 변환하여 이미지로 저장 * 파일 확장자가 이미지일 경우 이미지 저장 */ - file.name.split('.').pop() === 'dwg' ? handleUploadConvertRefFile(file) : handleUploadImageRefFile(file) + // handleUploadRefFile(file) } @@ -37,15 +71,34 @@ export function useRefFiles() { * 파일 삭제 */ const handleFileDelete = () => { - setRefImage(null) - setCurrentCanvasPlan((prev) => ({ ...prev, bgFileName: null })) + swalFire({ + text: '삭제하시겠습니까?', + type: 'confirm', + confirmFn: () => { + setRefImage(null) + setCurrentCanvasPlan((prev) => ({ ...prev, bgFileName: null })) + removeImage(currentCanvasPlan.id).then((res) => { + console.log(res) + }) + }, + }) } /** * 주소 삭제 */ const handleAddressDelete = () => { - setCurrentCanvasPlan((prev) => ({ ...prev, mapPositionAddress: null })) + swalFire({ + text: '삭제하시겠습니까?', + type: 'confirm', + confirmFn: () => { + setMapPositionAddress('') + setCurrentCanvasPlan((prev) => ({ ...prev, mapPositionAddress: null })) + removeImage(currentCanvasPlan.id).then((res) => { + console.log(res) + }) + }, + }) } /** @@ -56,9 +109,10 @@ export function useRefFiles() { return } - const res = await get({ url: `http://localhost:3000/api/html2canvas?q=${queryRef.current.value}&fileNm=${uuidv4()}&zoom=20` }) - console.log('🚀 ~ handleMapImageDown ~ res:', res) - setCurrentCanvasPlan((prev) => ({ ...prev, bgImageName: res.fileNm, mapPositionAddress: queryRef.current.value })) + const res = await get({ url: `http://localhost:3000/api/html2canvas?q=${queryRef.current.value}&fileNm=${currentCanvasPlan.id}&zoom=20` }) + // console.log('🚀 ~ handleMapImageDown ~ res:', res) + console.log('currentCanvasPlan', currentCanvasPlan) + setCurrentCanvasPlan((prev) => ({ ...prev, bgImageName: currentCanvasPlan.id, mapPositionAddress: queryRef.current.value })) } /** @@ -66,9 +120,10 @@ export function useRefFiles() { * @param {*} file */ const handleUploadImageRefFile = async (file) => { - console.log('🚀 ~ handleUploadImageRefFile ~ file:', file) + // console.log('🚀 ~ handleUploadImageRefFile ~ file:', file) const formData = new FormData() formData.append('file', file) + formData.append('fileName', currentCanvasPlan.id) const response = await fetch('http://localhost:3000/api/image-upload', { method: 'POST', @@ -76,7 +131,8 @@ export function useRefFiles() { }) const result = await response.json() - console.log('🚀 ~ handleUploadImageRefFile ~ res:', result) + setRefImage(file) + // console.log('🚀 ~ handleUploadImageRefFile ~ res:', result) // writeImageBuffer(file) } @@ -92,6 +148,7 @@ export function useRefFiles() { .then((res) => { convertDwgToPng(res.data.Files[0].FileName, res.data.Files[0].FileData) swalFire({ text: '파일 변환 성공' }) + setRefImage(res.data.Files[0].FileData) }) .catch((err) => { swalFire({ text: '파일 변환 실패', icon: 'error' }) @@ -110,7 +167,7 @@ export function useRefFiles() { * 현재 플랜이 변경되면 플랜 상태 저장 */ useEffect(() => { - console.log('🚀 ~ useRefFiles ~ currentCanvasPlan:', currentCanvasPlan) + // console.log('🚀 ~ useRefFiles ~ currentCanvasPlan:', currentCanvasPlan) // const handleCurrentPlan = async () => { // await promisePut({ url: '/api/canvas-management/canvas-statuses', data: currentCanvasPlan }).then((res) => { // console.log('🚀 ~ awaitpromisePut ~ res:', res) diff --git a/src/hooks/usePlan.js b/src/hooks/usePlan.js index 6b1336a5..3f3ad1a0 100644 --- a/src/hooks/usePlan.js +++ b/src/hooks/usePlan.js @@ -6,6 +6,7 @@ import { useAxios } from '@/hooks/useAxios' import { useMessage } from '@/hooks/useMessage' import { useSwal } from '@/hooks/useSwal' import { SAVE_KEY } from '@/common/common' +import { readImage } from '@/lib/fileAction' export function usePlan() { const [planNum, setPlanNum] = useState(0) @@ -204,6 +205,7 @@ export function usePlan() { userId: userId, imageName: 'image_name', // api 필수항목이여서 임시로 넣음, 이후 삭제 필요 objectNo: currentCanvasPlan.objectNo, + mapPositionAddress: currentCanvasPlan.mapPositionAddress, canvasStatus: canvasToDbFormat(canvasStatus), } await promisePost({ url: '/api/canvas-management/canvas-statuses', data: planData }) @@ -233,6 +235,7 @@ export function usePlan() { const putCanvasStatus = async (canvasStatus) => { const planData = { id: currentCanvasPlan.id, + mapPositionAddress: currentCanvasPlan.mapPositionAddress, canvasStatus: canvasToDbFormat(canvasStatus), } await promisePut({ url: '/api/canvas-management/canvas-statuses', data: planData }) @@ -285,6 +288,7 @@ export function usePlan() { updateCurrentPlan(newCurrentId) } } + const updateCurrentPlan = (newCurrentId) => { setPlans((plans) => plans.map((plan) => { @@ -295,8 +299,13 @@ export function usePlan() { useEffect(() => { setCurrentCanvasPlan(plans.find((plan) => plan.isCurrent) || null) setSelectedPlan(plans.find((plan) => plan.isCurrent)) + // setBgImage() }, [plans]) + const setBgImage = () => { + readImage(selectedPlan?.id) + } + /** * 새로운 plan 생성 * 현재 plan의 데이터가 있을 경우 복제 여부를 확인 diff --git a/src/lib/fileAction.js b/src/lib/fileAction.js index d09473a8..6f88758a 100644 --- a/src/lib/fileAction.js +++ b/src/lib/fileAction.js @@ -4,6 +4,7 @@ import fs from 'fs/promises' const CAD_FILE_PATH = 'public/cad-images' const IMAGE_FILE_PATH = 'public/plan-bg-images' +const FILE_PATH = 'public/plan-images' /** * 파일 변환 & 저장 @@ -39,25 +40,54 @@ const writeImageBase64 = async (title, data) => { return fs.writeFile(`${IMAGE_FILE_PATH}/${title}.png`, data, 'base64') } -/** - * 이미지 저장 - * Buffer 형식으로 저장 - * @param {*} title - * @param {*} data - * @returns - */ -const writeImageBuffer = async (file) => { - // 해당 경로에 Directory 가 없다면 생성 +// /** +// * 이미지 저장 +// * Buffer 형식으로 저장 +// * @param {*} title +// * @param {*} data +// * @returns +// */ +// const writeImageBuffer = async (file) => { +// // 해당 경로에 Directory 가 없다면 생성 +// try { +// await fs.readdir(IMAGE_FILE_PATH) +// } catch { +// await fs.mkdir(IMAGE_FILE_PATH) +// } +// +// const arrayBuffer = await fileURLToPath.arrayBuffer() +// const buffer = new Uint8Array(arrayBuffer) +// +// return fs.writeFile(`${IMAGE_FILE_PATH}/${file.fileName}`, buffer) +// } + +const writeImage = async (fileName, file) => { try { - await fs.readdir(IMAGE_FILE_PATH) + await fs.readdir(FILE_PATH) } catch { - await fs.mkdir(IMAGE_FILE_PATH) + await fs.mkdir(FILE_PATH) } - const arrayBuffer = await fileURLToPath.arrayBuffer() - const buffer = new Uint8Array(arrayBuffer) - - return fs.writeFile(`${IMAGE_FILE_PATH}/${file.fileName}`, buffer) + return fs.writeFile(`${FILE_PATH}/${fileName}.png`, file) } -export { convertDwgToPng, writeImageBase64, writeImageBuffer } +const readImage = async (fileName) => { + await fs + .readFile(`${FILE_PATH}/${fileName}.png`) + .then((res) => { + console.log('readImage-then', res) + }) + .catch((e) => { + console.log('readImage-catch', e) + }) +} + +const removeImage = async (fileName) => { + try { + await fs.rm(`${FILE_PATH}/${fileName}.png`) + } catch (e) { + console.log(e) + } +} + +export { convertDwgToPng, writeImageBase64, writeImage, readImage, removeImage } From d0eee3cb60db3cd9640b7cd152aed60c1e4eda24 Mon Sep 17 00:00:00 2001 From: minsik Date: Tue, 3 Dec 2024 13:08:46 +0900 Subject: [PATCH 3/4] =?UTF-8?q?-=20=EC=A3=BC=EC=84=9D=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/floor-plan/modal/grid/GridCopy.jsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/floor-plan/modal/grid/GridCopy.jsx b/src/components/floor-plan/modal/grid/GridCopy.jsx index 86f79df7..2f768406 100644 --- a/src/components/floor-plan/modal/grid/GridCopy.jsx +++ b/src/components/floor-plan/modal/grid/GridCopy.jsx @@ -17,8 +17,7 @@ export default function GridCopy(props) { const currentObject = useRecoilValue(currentObjectState) const { copy } = useGrid() const handleApply = () => { - // copy(currentObject, ) - copy(currentObject, ['↑', '←'].includes(arrow) ? Number(length) * -1 : Number(length)) + copy(currentObject, ['↑', '←'].includes(arrow) ? +length * -1 : +length) } return ( From 23264bd1e08992a413ac6f060ac1543f02182d54 Mon Sep 17 00:00:00 2001 From: minsik Date: Tue, 3 Dec 2024 14:36:20 +0900 Subject: [PATCH 4/4] =?UTF-8?q?=F0=9F=9A=A8chore:=20Sync=20Sass?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/static/images/canvas/ico-flx05.svg | 5 +++ public/static/images/canvas/return-btn.svg | 3 ++ src/styles/_contents.scss | 52 ++++++++++++++++++++++ 3 files changed, 60 insertions(+) create mode 100644 public/static/images/canvas/ico-flx05.svg create mode 100644 public/static/images/canvas/return-btn.svg diff --git a/public/static/images/canvas/ico-flx05.svg b/public/static/images/canvas/ico-flx05.svg new file mode 100644 index 00000000..350aee07 --- /dev/null +++ b/public/static/images/canvas/ico-flx05.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/static/images/canvas/return-btn.svg b/public/static/images/canvas/return-btn.svg new file mode 100644 index 00000000..f33f417b --- /dev/null +++ b/public/static/images/canvas/return-btn.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/styles/_contents.scss b/src/styles/_contents.scss index e72637e4..8d3b1408 100644 --- a/src/styles/_contents.scss +++ b/src/styles/_contents.scss @@ -139,6 +139,7 @@ &.ico02{background-image: url(../../public/static/images/canvas/ico-flx02.svg);} &.ico03{background-image: url(../../public/static/images/canvas/ico-flx03.svg);} &.ico04{background-image: url(../../public/static/images/canvas/ico-flx04.svg);} + &.ico05{background-image: url(../../public/static/images/canvas/ico-flx05.svg);} } .name{ font-size: 12px; @@ -827,6 +828,47 @@ &:last-child{ margin-bottom: 0; } + .file-item-wrap{ + display: flex; + align-items: center; + gap: 30px; + .return-wrap{ + display: flex; + align-items: center; + } + .return{ + padding: 0; + font-size: 13px; + color: #B0BCCD; + text-decoration: line-through; + } + .return-btn{ + flex: none; + position: relative; + top: 0; + left: 0; + transform: none; + display: flex; + align-items: center; + height: 24px; + padding: 0 9px; + margin-left: 10px; + background: none; + border: 1px solid #B0BCCD; + border-radius: 2px; + font-size: 12px; + color: #B0BCCD; + font-weight: 500; + .return-ico{ + display: block; + width: 14px; + height: 14px; + background: url(../../public/static/images/canvas/return-btn.svg)no-repeat center; + background-size: contain; + margin-right: 5px; + } + } + } } } } @@ -877,6 +919,16 @@ &.act{ background-color: #F7F9FA; } + .special-note-check-box{ + display: flex; + align-items: center; + .check-name{ + font-size: 13px; + color: #45576F; + cursor: pointer; + line-height: 1.3; + } + } } }