This commit is contained in:
hyojun.choi 2025-02-24 18:09:39 +09:00
commit d35b73d67c
19 changed files with 116 additions and 3552 deletions

View File

@ -1,22 +0,0 @@
'use server'
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'
const q = req.nextUrl.searchParams.get('q')
const fileNm = req.nextUrl.searchParams.get('fileNm')
const zoom = req.nextUrl.searchParams.get('zoom')
const targetUrl = `https://maps.googleapis.com/maps/api/staticmap?center=${q}&zoom=${zoom}&maptype=satellite&size=640x640&scale=1&key=AIzaSyDO7nVR1N_D2tKy60hgGFavpLaXkHpiHpc`
const decodeUrl = decodeURIComponent(targetUrl)
const response = await fetch(decodeUrl)
const data = await response.arrayBuffer()
const buffer = Buffer.from(data)
await writeImage(fileNm, buffer)
return NextResponse.json({ fileNm: `${fileNm}.png` })
}

View File

@ -1,15 +0,0 @@
'use server'
import { NextResponse } from 'next/server'
import { writeImage } from '@/lib/fileAction'
export async function POST(req) {
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)
await writeImage(fileName, buffer)
return NextResponse.json({ fileNm: `${fileName}.png` })
}

View File

@ -1,25 +0,0 @@
import { NextResponse } from 'next/server'
const defaultData = [
{
id: 1,
name: 'John Doe',
email: 'john.doe@example.com',
},
{
id: 2,
name: 'Jane Lee',
email: 'jane.lee@example.com',
},
]
export async function GET(req, res) {
return NextResponse.json(defaultData)
}
export const POST = async (req, res) => {
const { id, name, email } = await req.json()
const newData = { id, name, email }
console.log('🚀 ~ POST ~ newData:', newData)
return NextResponse.json([...defaultData, newData])
}

View File

@ -1,9 +0,0 @@
import Playground from '@/components/Playground'
export default async function PlaygroundPage() {
return (
<>
<Playground />
</>
)
}

View File

@ -1,13 +0,0 @@
import Hero from '@/components/Hero'
import Roof from '@/components/Roof'
export default async function RoofPage() {
return (
<>
<Hero title="Drawing on canvas 2D Roof" />
<div className="flex flex-col justify-center my-8">
<Roof />
</div>
</>
)
}

View File

@ -1,11 +0,0 @@
import Roof2 from '@/components/Roof2'
export default async function Roof2Page() {
return (
<>
<div className="flex flex-col justify-center my-8 pt-20">
<Roof2 />
</div>
</>
)
}

View File

@ -1,13 +0,0 @@
import Hero from '@/components/Hero'
import Settings from '@/components/Settings'
export default async function SettingsPage() {
return (
<>
<Hero title="Canvas Setting" />
<div className="flex flex-col justify-center my-8">
<Settings />
</div>
</>
)
}

View File

@ -1,273 +0,0 @@
import { useEffect, useRef, useState } from 'react'
import { Button, Checkbox, CheckboxGroup, RadioGroup, Radio, Input } from '@nextui-org/react'
import { useRecoilState, useRecoilValue } from 'recoil'
import { modalContent, modalState } from '@/store/modalAtom'
import { guideLineState, horiGuideLinesState, vertGuideLinesState } from '@/store/canvasAtom'
import { fabric } from 'fabric'
import { ColorPicker, useColor } from 'react-color-palette'
import 'react-color-palette/css'
import { gridColorState } from '@/store/gridAtom'
export default function GridSettingsModal(props) {
const { canvasProps } = props
const [isCustomGridSetting, setIsCustomGridSetting] = useState(true)
const [gridCheckedValue, setGridCheckValue] = useState([])
const [ratioValue, setRatioValue] = useState('1')
const moduleLength = useRef(null) // mm
const customModuleHoriLength = useRef(null)
const customModuleVertLength = useRef(null)
const [open, setOpen] = useRecoilState(modalState)
const [guideLine, setGuideLine] = useRecoilState(guideLineState)
const [horiGuideLines, setHoriGuideLines] = useRecoilState(horiGuideLinesState)
const [vertGuideLines, setVertGuideLines] = useRecoilState(vertGuideLinesState)
const gridSettingArray = []
const gridColor = useRecoilValue(gridColorState)
const [colorPickerShow, setColorPickerShow] = useState(false)
const boxStyle = {
width: '50px',
height: '30px',
border: '1px solid black',
backgroundColor: guideColor.hex,
}
useEffect(() => {
moduleLength.current.value = 90
customModuleHoriLength.current.value = 90
customModuleVertLength.current.value = 90
}, [])
useEffect(() => {
setIsCustomGridSetting(ratioValue !== 'custom')
}, [ratioValue])
const drawGridSettings = () => {
//
if (!(Object.keys(guideLine).length === 0 && guideLine.constructor === Object)) {
gridSettingArray.push(...guideLine)
}
let moduleHoriLength = moduleLength.current.value //
let moduleVertLength = moduleLength.current.value //
if (ratioValue === 'custom') {
moduleHoriLength = customModuleHoriLength.current.value
moduleVertLength = customModuleVertLength.current.value
} else {
moduleHoriLength = moduleHoriLength / ratioValue
moduleVertLength = moduleVertLength / ratioValue
}
if (gridCheckedValue.includes('line')) {
const horizontalLineArray = []
const verticalLineArray = []
for (let i = 0; i < canvasProps.height / moduleVertLength + 1; i++) {
const horizontalLine = new fabric.Line(
[0, i * moduleVertLength - moduleVertLength / 2, canvasProps.width, i * moduleVertLength - moduleVertLength / 2],
{
stroke: gridColor,
strokeWidth: 1,
selectable: true,
lockMovementX: true,
lockMovementY: true,
lockRotation: true,
lockScalingX: true,
lockScalingY: true,
name: 'guideLine',
strokeDashArray: [5, 2],
opacity: 0.3,
direction: 'horizontal',
},
)
canvasProps.add(horizontalLine)
horizontalLineArray.push(horizontalLine)
}
for (let i = 0; i < canvasProps.width / moduleHoriLength + 1; i++) {
const verticalLine = new fabric.Line(
[i * moduleHoriLength - moduleHoriLength / 2, 0, i * moduleHoriLength - moduleHoriLength / 2, canvasProps.height],
{
stroke: gridColor,
strokeWidth: 1,
selectable: true,
lockMovementX: true,
lockMovementY: true,
lockRotation: true,
lockScalingX: true,
lockScalingY: true,
name: 'guideLine',
strokeDashArray: [5, 2],
opacity: 0.3,
direction: 'vertical',
},
)
canvasProps.add(verticalLine)
verticalLineArray.push(verticalLine)
}
canvasProps.renderAll()
const snapDistance = 10
const recoilObj = {
guideMode: 'guideLine',
horizontalLineArray,
verticalLineArray,
moduleVertLength: moduleVertLength,
moduleHoriLength: moduleHoriLength,
}
gridSettingArray.push(recoilObj)
const newHoriGuideLines = [...horiGuideLines]
horizontalLineArray.forEach((line) => {
newHoriGuideLines.push(line)
})
const newVertGuideLines = [...vertGuideLines]
verticalLineArray.forEach((line) => {
newVertGuideLines.push(line)
})
setHoriGuideLines(newHoriGuideLines)
setVertGuideLines(newVertGuideLines)
}
if (gridCheckedValue.includes('dot')) {
const circle = new fabric.Circle({
radius: 2,
fill: 'white',
stroke: guideColor.hex,
strokeWidth: 0.7,
originX: 'center',
originY: 'center',
selectable: false,
lockMovementX: true,
lockMovementY: true,
lockRotation: true,
lockScalingX: true,
lockScalingY: true,
})
const patternSourceCanvas = new fabric.StaticCanvas(null, {
width: moduleHoriLength,
height: moduleVertLength,
})
patternSourceCanvas.add(circle)
circle.set({
left: patternSourceCanvas.width / 2,
top: patternSourceCanvas.height / 2,
})
patternSourceCanvas.renderAll()
const pattern = new fabric.Pattern({
source: patternSourceCanvas.getElement(),
repeat: 'repeat',
})
const backgroundPolygon = new fabric.Polygon(
[
{ x: 0, y: 0 },
{ x: canvasProps.width, y: 0 },
{ x: canvasProps.width, y: canvasProps.height },
{ x: 0, y: canvasProps.height },
],
{
fill: pattern,
selectable: false,
name: 'guideDot',
},
)
canvasProps.add(backgroundPolygon)
backgroundPolygon.sendToBack()
canvasProps.renderAll()
const recoilObj = {
guideMode: 'guideDot',
moduleVertLength: moduleVertLength,
moduleHoriLength: moduleHoriLength,
}
gridSettingArray.push(recoilObj)
}
canvasProps.renderAll()
setGuideLine(gridSettingArray)
}
const removeGuideLines = () => {
if (!(Object.keys(guideLine).length === 0 && guideLine.constructor === Object)) {
const guideLines = canvasProps._objects.filter((obj) => obj.name === 'guideLine' || obj.name === 'guideDot')
guideLines?.forEach((item) => canvasProps.remove(item))
canvasProps.renderAll()
setGuideLine([])
setHoriGuideLines([])
setVertGuideLines([])
} else {
alert('그리드가 없습니다.')
return
}
}
return (
<>
<div>
<div className="flex w-full flex-wrap md:flex-nowrap gap-4">
<label style={{ display: 'block', marginBottom: '5px' }}>
<CheckboxGroup label="그리드 설정" value={gridCheckedValue} defaultChecked={gridCheckedValue} onValueChange={setGridCheckValue}>
<Checkbox value="dot"> 그리드</Checkbox>
<Checkbox value="line">점선 그리드</Checkbox>
</CheckboxGroup>
</label>
</div>
<div className="flex w-full flex-wrap md:flex-nowrap gap-4">
<Input type="number" label="모듈" ref={moduleLength} />
mm
</div>
<div className="flex w-full flex-wrap md:flex-nowrap gap-4">
<RadioGroup label="비율 설정" value={ratioValue} defaultValue={ratioValue} onValueChange={setRatioValue}>
<Radio value="1">원치수</Radio>
<Radio value="2">1/2</Radio>
<Radio value="4">1/4</Radio>
<Radio value="10">1/10</Radio>
<Radio value="custom">임의간격</Radio>
</RadioGroup>
</div>
<div className="flex w-full flex-wrap md:flex-nowrap gap-4">
가이드컬러 <div style={boxStyle} onClick={() => setColorPickerShow(!colorPickerShow)}></div>
</div>
{colorPickerShow && (
<ColorPicker color={guideColor} onChange={setGuideColor} hideInput={['hsv', 'rgb', 'hex']} height={100} hideAlpha={true} />
)}
<div className="flex w-full flex-wrap md:flex-nowrap gap-4">
<Checkbox value="linked" isDisabled={isCustomGridSetting}>
종횡연동
</Checkbox>
</div>
</div>
<div className="flex w-full flex-wrap md:flex-nowrap gap-4">
<Input type="number" label="가로간격" ref={customModuleHoriLength} min={0} isDisabled={isCustomGridSetting} />
mm
</div>
<div className="flex w-full flex-wrap md:flex-nowrap gap-4">
<Input type="number" label="세로간격" ref={customModuleVertLength} min={0} isDisabled={isCustomGridSetting} />
mm
</div>
<div className="flex gap-4 items-center">
<Button size="sm">초기화</Button>
<Button size="sm" color="secondary" onClick={drawGridSettings} isDisabled={gridCheckedValue.length === 0}>
저장
</Button>
<Button size="sm" onClick={() => setOpen(!open)}>
취소
</Button>
<Button size="sm" onClick={() => removeGuideLines()}>
그리드 삭제
</Button>
</div>
</>
)
}

View File

@ -1,21 +0,0 @@
import Link from 'next/link'
export default function Headers() {
return (
<div className="w-full">
<nav className="container relative flex flex-wrap items-center justify-between mx-auto p-8">
<Link href="/" className="font-bold text-3xl">
Home
</Link>
<div className="space-x-4 text-xl">
<Link href="/intro">Intro</Link>
<Link href="/playground">Playground</Link>
<Link href="/initSettingsModal">Basic Settings</Link>
<Link href="/settings">Canvas Settings</Link>
<Link href="/roof">Roof</Link>
<Link href="/roof2">Roof2</Link>
</div>
</nav>
</div>
)
}

View File

@ -1,7 +0,0 @@
export default function Hero(props) {
return (
<div className="pt-48 flex justify-center">
<h1 className="text-4xl archivo-black-regular">{props.title}</h1>
</div>
)
}

View File

@ -1,253 +0,0 @@
'use client'
import { useEffect, useState, memo, useCallback } from 'react'
import { Button, Checkbox, CheckboxGroup, RadioGroup, Radio, Input, Select, SelectItem } from '@nextui-org/react'
import { useRecoilState, useRecoilValue } from 'recoil'
import { modalContent, modalState } from '@/store/modalAtom'
import { canvasSettingState } from '@/store/canvasAtom'
import { useAxios } from '@/hooks/useAxios'
export default function InitSettingsModal(props) {
const [objectNo, setObjectNo] = useState('test123240909003') //
const [open, setOpen] = useRecoilState(modalState)
const [canvasSetting, setCanvasSetting] = useRecoilState(canvasSettingState)
const [roofMaterials, setRoofMaterials] = useState([])
const [basicSetting, setBasicSettings] = useState({
roofDrawingSet: '1',
roofSizeSet: '1',
roofAngleSet: 'slope',
roofs: [{ roofSeq: '1', roofType: '3', roofWidth: '200', roofHeight: '200', roofGap: '0', roofLayout: 'parallel' }],
})
const modelProps = {
open,
setOpen,
}
const { get, post } = useAxios()
useEffect(() => {
get({ url: `/api/canvas-management/canvas-basic-settings/by-object/${objectNo}` }).then((res) => {
if (res.length == 0) return
// 'roofs'
const roofsRow = res.map((item) => {
return {
roofDrawingSet: String(item.roofDrawingSet),
roofSizeSet: String(item.roofSizeSet),
roofAngleSet: item.roofAngleSet,
}
})
const roofsArray = res.some((item) => !item.roofSeq)
? // roofsArray
[{ roofSeq: '1', roofType: '3', roofWidth: '200', roofHeight: '200', roofGap: '0', roofLayout: 'parallel' }]
: res.map((item) => ({
roofSeq: String(item.roofSeq),
roofType: String(item.roofType),
roofWidth: String(item.roofWidth),
roofHeight: String(item.roofHeight),
roofGap: String(item.roofGap),
roofLayout: item.roofLayout,
}))
// 'roofs' patternData
const patternData = {
roofDrawingSet: roofsRow[0].roofDrawingSet, //
roofSizeSet: roofsRow[0].roofSizeSet, //
roofAngleSet: roofsRow[0].roofAngleSet, //
roofs: roofsArray, // roofs
}
//
setBasicSettings({ ...patternData })
})
if (!(Object.keys(canvasSetting).length === 0 && canvasSetting.constructor === Object)) {
setBasicSettings({ ...canvasSetting })
}
}, [])
//
const handleBasicSetting = (event) => {
const newBasicSetting = { ...basicSetting, [event.target.name]: event.target.value }
setBasicSettings(newBasicSetting)
}
//
const handleRoofSettings = (id, event) => {
// roofs
const updatedRoofs = [...basicSetting.roofs]
// roofSeq id
const index = updatedRoofs.findIndex((roof) => roof.roofSeq === id)
if (index !== -1) {
//
updatedRoofs[index] = {
...updatedRoofs[index],
[event.target.name]: event.target.value,
}
//
setBasicSettings((prevState) => ({
...prevState,
roofs: updatedRoofs,
}))
}
}
//
const submitCanvasConfig = async () => {
if (!objectNo) {
alert('object_no를 입력하세요.')
return
}
const patternData = {
objectNo,
roofDrawingSet: basicSetting.roofDrawingSet,
roofSizeSet: basicSetting.roofSizeSet,
roofAngleSet: basicSetting.roofAngleSet,
roofMaterialsAddList: basicSetting.roofs,
}
await post({ url: `/api/canvas-management/canvas-basic-settings`, data: patternData })
//Recoil
setCanvasSetting({ ...basicSetting })
//
//await handleSelect()
}
return (
<>
<div className="container mx-auto mt-10 p-6 bg-white shadow-lg rounded-lg">
<div className="text-lg font-semibold mb-4">배치면 초기설정</div>
<div className="mb-6">
<div className="flex space-x-4">
<RadioGroup
label="도면 작성방법"
name="roofDrawingSet"
orientation="horizontal"
value={basicSetting.roofDrawingSet}
onChange={handleBasicSetting}
>
<Radio value="1">치수 입력에 의한 물건작성</Radio>
</RadioGroup>
</div>
</div>
<div className="mb-6">
<div className="flex space-x-4">
<RadioGroup
label="치수 입력방법"
name="roofSizeSet"
orientation="horizontal"
value={basicSetting.roofSizeSet}
onChange={handleBasicSetting}
>
<Radio value="1">복사도 입력</Radio>
<Radio value="2">실측값 입력</Radio>
<Radio value="3">육지붕</Radio>
</RadioGroup>
</div>
</div>
<div className="mb-6">
<div className="flex space-x-4">
<RadioGroup
label="지붕각도 설정"
name="roofAngleSet"
orientation="horizontal"
value={basicSetting.roofAngleSet}
onChange={handleBasicSetting}
>
<Radio value="slope">경사</Radio>
<Radio value="angle">각도</Radio>
</RadioGroup>
</div>
</div>
<RadioGroup label="지붕재 추가(단위 : mm)" />
{/* Roofs Array Rendering */}
{basicSetting.roofs &&
basicSetting.roofs.map((roof, index) => {
return (
<div key={index} className="mb-4 flex flex-wrap items-center space-x-4" style={{ border: '1px solid black' }}>
<span> 타입 : </span>
<Select
aria-label="roofMaterial"
className={'w-52'}
name="roofType"
onChange={(e) => handleRoofSettings(roof.roofSeq, e)}
items={roofMaterials}
defaultSelectedKeys={roof.roofType ? [roof.roofType] : []}
selectedKeys={roof.roofType}
value={roof.roofType}
>
{(roofMaterial) => (
<SelectItem key={roofMaterial.id} value={roofMaterial.id}>
{roofMaterial.name}
</SelectItem>
)}
</Select>
<span> 너비 : </span>
<Input
type="text"
name="roofWidth"
placeholder="너비"
value={roof.roofWidth}
className="w-24"
onChange={(e) => handleRoofSettings(roof.roofSeq, e)}
/>
mm
<span> 높이 : </span>
<Input
type="text"
name="roofHeight"
placeholder="높이"
value={roof.roofHeight}
className="w-24"
onChange={(e) => handleRoofSettings(roof.roofSeq, e)}
/>
mm
<span> 서까래 간격 : </span>
<Input
type="text"
name="roofGap"
placeholder="간격"
value={roof.roofGap}
className="w-24"
onChange={(e) => handleRoofSettings(roof.roofSeq, e)}
/>
mm
<div className="flex space-x-4">
<RadioGroup
orientation="horizontal"
name="roofLayout"
value={roof.roofLayout}
defaultValue="parallel"
onChange={(e) => handleRoofSettings(roof.roofSeq, e)}
>
<Radio value="parallel">병렬식</Radio>
<Radio value="cascade">계단식</Radio>
</RadioGroup>
</div>
</div>
)
})}
<div className="flex gap-4 items-right">
<Button size="sm" color="secondary" onClick={submitCanvasConfig}>
저장
</Button>
<Button size="sm" onClick={() => setOpen(!open)}>
취소
</Button>
<input type="text" placeholder="Object No 입력" value={objectNo} onChange={(e) => setObjectNo(e.target.value)} />
</div>
</div>
</>
)
}

View File

@ -1,919 +0,0 @@
'use client'
import { useRef, useState, useEffect, useContext } from 'react'
import Image from 'next/image'
import { useRecoilState } from 'recoil'
import { v4 as uuidv4 } from 'uuid'
import { FaAnglesUp } from 'react-icons/fa6'
import { FaAnglesDown } from 'react-icons/fa6'
import { Button } from '@nextui-org/react'
import ColorPicker from './common/color-picker/ColorPicker'
import { cadFileNameState, googleMapFileNameState, useCadFileState, useGoogleMapFileState } from '@/store/canvasAtom'
import { useAxios } from '@/hooks/useAxios'
import { useMessage } from '@/hooks/useMessage'
import { useMasterController } from '@/hooks/common/useMasterController'
import { useSwal } from '@/hooks/useSwal'
import { convertDwgToPng } from '@/lib/cadAction'
import { GlobalDataContext } from '@/app/GlobalDataProvider'
import QInput from './common/input/Qinput'
import QSelect from './common/select/QSelect'
import QPagination from './common/pagination/QPagination'
import QSelectBox from './common/select/QSelectBox'
import SampleReducer from './sample/SampleReducer'
import styles from './playground.module.css'
import useSWR from 'swr'
import useSWRMutation from 'swr/mutation'
import { useCanvasPopupStatusController } from '@/hooks/common/useCanvasPopupStatusController'
import { canvasPopupStatusStore } from '@/store/canvasPopupStatusAtom'
import { moduleSelectionDataPlanListState } from '@/store/selectedModuleOptions'
import { useRouter } from 'next/navigation'
import { QcastContext } from '@/app/QcastProvider'
export default function Playground() {
const [useCadFile, setUseCadFile] = useRecoilState(useCadFileState)
const [cadFileName, setCadFileName] = useRecoilState(cadFileNameState)
const [useGoogleMapFile, setUseGoogleMapFile] = useRecoilState(useGoogleMapFileState)
const [googleMapFileName, setGoogleMapFileName] = useRecoilState(googleMapFileNameState)
const fileRef = useRef(null)
const queryRef = useRef(null)
const [zoom, setZoom] = useState(20)
const { get, promiseGet, post, promisePost, getFetcher, postFetcher } = useAxios()
const testVar = process.env.NEXT_PUBLIC_TEST
const converterUrl = process.env.NEXT_PUBLIC_CONVERTER_API_URL
const { getMessage } = useMessage()
const { swalFire } = useSwal()
const { getRoofMaterialList, getModuleTypeItemList, getTrestleList, getConstructionList, getTrestleDetailList } = useMasterController()
const [color, setColor] = useState('#ff0000')
const [textInput, setTextInput] = useState('')
const [numberInput, setNumberInput] = useState('')
const [radioInput, setRadioInput] = useState('')
const [checkboxInput, setCheckboxInput] = useState([])
const [selectedValue, setSelectedValue] = useState('')
const [users, setUsers] = useState([])
const { managementState, setManagementState, managementStateLoaded } = useContext(GlobalDataContext)
const router = useRouter()
const { setIsGlobalLoading } = useContext(QcastContext)
useEffect(() => {
setIsGlobalLoading(false)
}, [])
useEffect(() => {
console.log('textInput:', textInput)
}, [textInput])
useEffect(() => {
console.log('numberInput:', numberInput)
}, [numberInput])
useEffect(() => {
console.log('radioInput:', radioInput)
}, [radioInput])
useEffect(() => {
console.log('checkboxInput:', checkboxInput)
}, [checkboxInput])
useEffect(() => {
console.log('selectedValue:', selectedValue)
}, [selectedValue])
const handleUsers = async () => {
// const users = await get('/api/user/find-all')
const params = {
url: '/api/user/find-all',
}
const users = await get(params)
console.log('users', users)
}
const handleConvert = async () => {
console.log('file', fileRef.current.files[0])
const formData = new FormData()
formData.append('file', fileRef.current.files[0])
await promisePost({ url: converterUrl, data: formData })
.then((res) => {
console.log('response: ', res)
convertDwgToPng(res.data.Files[0].FileName, res.data.Files[0].FileData)
setUseCadFile(true)
setCadFileName(res.data.Files[0].FileName)
swalFire({ text: '파일 변환 완료' })
})
.catch((err) => {
console.error(err)
swalFire({ text: '파일 변환 실패' })
})
}
const handleDownImage = async (fileName = '') => {
const fileNm = fileName === '' ? uuidv4() : fileName
const queryString = queryRef.current.value === '' ? '서울시 서대문구 연세로5다길 22-3 발리빌라 3층' : queryRef.current.value
const res = await get({ url: `http://localhost:3000/api/html2canvas?q=${queryString}&fileNm=${fileNm}&zoom=${zoom}` })
console.log('res', res)
setGoogleMapFileName(res.fileNm)
swalFire({ text: '이미지 저장 완료' })
setUseGoogleMapFile(true)
}
const handleZoom = async (type) => {
if (type === 'up') {
setZoom((prevState) => prevState + 1)
} else {
setZoom((prevState) => prevState - 1)
}
await handleDownImage()
}
const data = [
{
id: 1,
author: 'SWYOO',
contents: '버튼 정리(템플릿 적용)',
date: '2024.07.16',
},
{
id: 2,
author: 'SWYOO',
contents: 'README.md 파일 이미지 경로 수정',
date: '2024.07.17',
},
{
id: 3,
author: 'SWYOO',
contents: '',
date: '',
},
]
const handleSwalAlert = () => {
swalFire({
text: '알림 테스트입니다.',
})
}
const paginationProps = {
pageNo: 1,
pageSize: 10,
pagePerBlock: 10,
totalCount: 26,
handleChangePage: (page) => {
console.log('page', page)
},
}
useEffect(() => {
console.log('users:', users)
}, [users])
const codes = [
{
clHeadCd: '203800',
clCode: 'HEI_455',
clCodeNm: '세로 455mm이하',
clPriority: 1,
name: '세로 455mm이하',
id: 'HEI_455',
},
{
clHeadCd: '203800',
clCode: 'HEI_500',
clCodeNm: '세로 500mm이하',
clPriority: 2,
name: '세로 500mm이하',
id: 'HEI_500',
},
{
clHeadCd: '203800',
clCode: 'HEI_606',
clCodeNm: '세로 606mm이하',
clPriority: 3,
name: '세로 606mm이하',
id: 'HEI_606',
},
{
clHeadCd: '203800',
clCode: 'WID_606',
clCodeNm: '가로 606mm이하',
clPriority: 4,
name: '가로 606mm이하',
id: 'WID_606',
},
{
clHeadCd: '203800',
clCode: 'ETC',
clCodeNm: '기타',
clPriority: 5,
name: '기타',
id: 'ETC',
},
]
const [myData, setMyData] = useState({
roofMatlCd: 'ROOF_ID_WA_53A',
roofMatlNm: '화와 A',
roofMatlNmJp: '和瓦A',
widAuth: 'R',
widBase: '265.000',
lenAuth: 'R',
lenBase: '235.000',
roofPchAuth: null,
roofPchBase: null,
raftAuth: 'C',
raftBaseCd: 'HEI_455',
id: 'ROOF_ID_WA_53A',
name: '화와 A',
selected: true,
nameJp: '和瓦A',
length: 235,
width: 265,
layout: 'P',
hajebichi: null,
})
const handleChangeMyData = () => {
setMyData({ ...myData, raftBaseCd: 'HEI_500' })
}
const [myData2, setMyData2] = useState({})
const handleChangeMyData2 = () => {
setMyData2({
roofMatlCd: 'ROOF_ID_WA_53A',
roofMatlNm: '화와 A',
roofMatlNmJp: '和瓦A',
widAuth: 'R',
widBase: '265.000',
lenAuth: 'R',
lenBase: '235.000',
roofPchAuth: null,
roofPchBase: null,
raftAuth: 'C',
raftBaseCd: 'HEI_455',
id: 'ROOF_ID_WA_53A',
name: '화와 A',
selected: true,
nameJp: '和瓦A',
length: 235,
width: 265,
layout: 'P',
hajebichi: null,
})
}
// const [callFlag, setCallFlag] = useState(false)
// const { data: tutoData, error, isLoading } = useSWR('http://localhost:8080/api/tutorial', getFetcher)
// const { data: tutoData, error, isLoading } = useSWR(callFlag ? 'http://localhost:8080/api/tutorial' : null, getFetcher)
// const { trigger, isMutating } = useSWRMutation('http://localhost:8080/api/tutorial', postFetcher)
// if (isLoading) {
// return <div>Loading...</div>
// }
// if (error) {
// return <div>Error...</div>
// }
// const [moduleSelectionDataPlanListStore, setModuleSelectionDataPlanListStore] = useRecoilState(moduleSelectionDataPlanListState)
// useEffect(() => {
// console.log('🚀 ~ Playground ~ moduleSelectionDataPlanListStore:', moduleSelectionDataPlanListStore)
// }, [moduleSelectionDataPlanListStore])
// const { trigger: canvasPopupStatusTrigger } = useCanvasPopupStatusController({ objectNo: 'R201T01241120001', planNo: 2, popupType: 2 })
return (
<>
<div className="container mx-auto p-4 m-4 border">
<div className={styles.test}> 영역은 테스트입니다.</div>
<div className="my-2">
<button
className="btn-frame deepgray"
onClick={() => {
getRoofMaterialList()
}}
>
지붕재 목록 조회 API 호출
</button>{' '}
<button
className="btn-frame deepgray"
onClick={() => {
getModuleTypeItemList(['ROOF_ID_HIRA_SEME', 'ROOF_ID_ROOGA'])
}}
>
모듈 타입별 아이템 목록 조회 API 호출
</button>{' '}
<button
className="btn-frame deepgray"
onClick={() => {
getTrestleList({ moduleTpCd: '', roofMatlCd: '', raftBaseCd: '', trestleMkrCd: '', constMthdCd: '', roofBaseCd: '' }) //
}}
>
가대 목록 조회 API 호출
</button>{' '}
<button
className="btn-frame deepgray"
onClick={() => {
getConstructionList({
//
moduleTpCd: 'testData_1',
roofMatlCd: 'testData_2',
trestleMkrCd: 'testData_3',
constMthdCd: 'testData_4',
roofBaseCd: 'testData_5',
illuminationTp: 'testData_6',
instHt: 'testData_7',
stdWindSpeed: 'testData_8',
stdSnowLd: 'testData_9',
inclCd: 'testData_10',
raftBaseCd: '',
roofPitch: 30,
})
}}
>
시공법 목록 조회 API 호출
</button>{' '}
<button
className="btn-frame deepgray"
onClick={() => {
getTrestleDetailList({
//
moduleTpCd: 'testData_1',
roofMatlCd: 'testData_2',
trestleMkrCd: 'testData_3',
constMthdCd: 'testData_4',
roofBaseCd: 'testData_5',
illuminationTp: 'testData_6',
instHt: 'testData_7',
stdWindSpeed: 'testData_8',
stdSnowLd: 'testData_9',
inclCd: 'testData_10',
constTp: 'testData_11',
mixMatlNo: 30,
roofPitch: 0,
})
}}
>
가대 상세 조회 API 호출
</button>
</div>
<div className="m-2">
<button
className="btn-frame deepgray"
onClick={() => {
setTextInput('')
}}
>
QInput TextInput DATA RESET
</button>
<QInput type="text" placeholder="placeholder" value={textInput} onChange={setTextInput} />
<QInput type="text" placeholder="read only" value={textInput} onChange={setTextInput} readOnly="true" />
<br />
<button
className="btn-frame deepgray"
onClick={() => {
setNumberInput('')
}}
>
QInput NumberInput DATA RESET
</button>
<QInput type="number" placeholder="placeholder" value={numberInput} onChange={setNumberInput} />
<QInput type="number" placeholder="read only" value={numberInput} onChange={setNumberInput} readOnly="true" />
<br />
<button
className="btn-frame deepgray"
onClick={() => {
setRadioInput('')
}}
>
QInput Radio DATA RESET
</button>
<QInput
type="radio"
value={radioInput}
onChange={setRadioInput}
options={[
{ id: 'r01', value: 'option1', name: 'Option 1' },
{ id: 'r02', value: 'option2', name: 'Option 2' },
]}
/>
<br />
<button
className="btn-frame deepgray"
onClick={() => {
setCheckboxInput([])
}}
>
QInput Checkbox DATA RESET
</button>
<QInput
type="checkbox"
value={checkboxInput}
onChange={setCheckboxInput}
options={[
{ id: 'c01', value: 'checkbox1', name: 'Checkbox 1' },
{ id: 'c02', value: 'checkbox2', name: 'Checkbox 2' },
]}
/>
</div>
<div className="m-2">
<br />
<button
className="btn-frame deepgray"
onClick={() => {
setSelectedValue([])
}}
>
QSelect DATA RESET
</button>
<QSelect
value={selectedValue}
onChange={setSelectedValue}
// placeholder=" "
options={[
{ id: 's01', value: 'cat', name: '고양이' },
{ id: 's02', value: 'dog', name: '개' },
{ id: 's03', value: 'lion', name: '사자' },
{ id: 's04', value: 'tiger', name: '호랑이' },
]}
/>
<QSelect
value={selectedValue}
onChange={setSelectedValue}
placeholder="동물을 선택하세요"
options={[
{ id: 's01', value: 'cat', name: '고양이' },
{ id: 's02', value: 'dog', name: '개' },
{ id: 's03', value: 'lion', name: '사자' },
{ id: 's04', value: 'tiger', name: '호랑이' },
]}
disabled="true"
/>
<QSelect
value={selectedValue}
onChange={setSelectedValue}
placeholder="동물을 선택하세요"
options={[
{ id: 's01', value: 'cat', name: '고양이' },
{ id: 's02', value: 'dog', name: '개' },
{ id: 's03', value: 'lion', name: '사자' },
{ id: 's04', value: 'tiger', name: '호랑이' },
]}
dark="true"
/>
</div>
<div className="w-full bg-orange-300 m-2">{testVar}</div>
<div>
<div className="m-2">
<Button onClick={handleUsers}>Button</Button>
</div>
</div>
<div className="test">
<p className="text-white">Sass 테스트입니다.</p>
</div>
<div dangerouslySetInnerHTML={{ __html: getMessage('welcome', ['<span style="color: red">test</span>']) }}></div>
<div>
<h1>React ColorPicker</h1>
<ColorPicker color={color} setColor={setColor} />
<div className="p-4">{color}</div>
</div>
<div>
<h1 className="text-2xl">캐드 파일 이미지 사용</h1>
<input type="file" name="file" ref={fileRef} />
<div>
<Button onClick={handleConvert}>Convert</Button>
</div>
</div>
<div>
<h1 className="text-2xl">구글 이미지 사용</h1>
<input type="text" ref={queryRef} className="w-80 border-medium my-2" />
<div>
<Button onClick={handleDownImage}>Google map Download to Image</Button>
</div>
{useGoogleMapFile && (
<>
<div className="my-2">
<p className="text-lg">Zoom Controller : {zoom}</p>
<Button startContent={<FaAnglesUp />} className="mx-2" onClick={() => handleZoom('up')}></Button>
<Button startContent={<FaAnglesDown />} className="mx-2" onClick={() => handleZoom('down')}></Button>
</div>
<div className="my-2">
<Image src={`/mapImages/${googleMapFileName}`} width={640} height={640} />
</div>
</>
)}
</div>
<div className="my-2">
<Button onClick={() => swalFire({ text: 'alert 테스트입니다.' })}>Sweetalert - alert</Button>
</div>
<div className="my-2">
<Button onClick={() => swalFire({ text: 'alert 아이콘 테스트입니다.', icon: 'error' })}>Sweetalert - alert - error</Button>
</div>
<div className="my-2">
<Button
onClick={() =>
swalFire({
html: `confirm 테스트입니다.<br />당신은 바보입니까?`,
type: 'confirm',
confirmFn: () => {
alert('test')
},
})
}
>
Sweetalert - confirm
</Button>
</div>
<div className="my-2">
<QPagination {...paginationProps} />
</div>
<div className="my-2">
<Button
onClick={() => {
promiseGet({ url: 'http://localhost:8080/api/user' }).then((res) => setUsers(res.data))
}}
>
axios get test
</Button>
</div>
<div className="my-2">
<Button
onClick={() => {
const result = promisePost({
url: 'http://localhost:8080/api/user',
data: {
firstName: 'Yoo',
lastName: 'Sangwook',
email: 'yoo1757@naver.com',
age: 46,
},
}).then((res) => console.log('res', res))
}}
>
axios post test
</Button>
</div>
<div className="my-2">
<QSelectBox options={codes} value={myData} sourceKey="id" targetKey="raftBaseCd" showKey="clCodeNm" />
</div>
<div className="my-2">
<Button onClick={handleChangeMyData}>QSelectBox value change!!</Button>
</div>
<div className="my-2">
<QSelectBox options={codes} value={myData2} sourceKey="id" targetKey="raftBaseCd" showKey="clCodeNm" />
</div>
<div className="my-2">
<Button onClick={handleChangeMyData2}>QSelectBox dynamic data bind change!!</Button>
</div>
<div className="my-2">
<QSelectBox title="초기값 테스트" options={[]} value={{}} sourceKey="id" targetKey="raftBaseCd" showKey="clCodeNm" />
</div>
<div className="my-2">
<SampleReducer />
</div>
<div className="my-2">
<Button onClick={() => setManagementState({ ...managementState, objectNo: '1234567890' })}>GlobalDataProvider 테스트</Button>
</div>
<div className="my-2">
<Button onClick={() => setManagementState({})}>GlobalDataProvider 초기화</Button>
</div>
{/* <div className="my-2">
<p>{managementStateLoaded?.objectNo}</p>
</div> */}
<div className="my-2">
<Button onClick={() => swalFire({ text: 'alert 테스트입니다.', type: 'alert', confirmFn: () => console.log('Alert!!!') })}>
Sweetalert - alert
</Button>
</div>
{/* <div className="my-2">
{tutoData &&
tutoData.map((item) => (
<div key={item.id}>
{item.name} / {item.email}
</div>
))}
</div>
<div className="my-2">
<Button onClick={() => setCallFlag(true)}>getFetcher call</Button>
</div>
<div className="my-2">
<Button disabled={isMutating} onClick={() => trigger({ id: 3, name: 'seulda kim', email: 'seulda.kim@interplug.co.kr' })}>
insert data
</Button>
</div> */}
<div className="my-2">
<Button
onClick={() => {
canvasPopupStatusTrigger({
common: {
illuminationTp: '3',
instHt: '10',
stdWindSpeed: 'WL_32',
stdSnowLd: '5',
moduleTpCd: 'A1',
moduleItemId: '106796',
},
roofConstructions: [
{
roofIndex: 0,
addRoof: {
roofMatlCd: 'ROOF_ID_WA_53A',
roofMatlNm: '일본기와 A',
roofMatlNmJp: '和瓦A',
widAuth: 'R',
widBase: '265.000',
lenAuth: 'R',
lenBase: '235.000',
roofPchAuth: null,
roofPchBase: null,
raftAuth: 'C',
raftBaseCd: 'HEI_455',
id: 'ROOF_ID_WA_53A',
name: '일본기와 A',
selected: true,
index: 0,
nameJp: '和瓦A',
length: 235,
width: 265,
raft: 'HEI_455',
layout: 'P',
hajebichi: 0,
pitch: 7,
angle: 21.8,
roofSizeSet: '1',
roofAngleSet: 'slope',
},
trestle: {
moduleTpCd: 'A1',
moduleTpCdNm: 'A1type',
moduleTpCdJp: 'A1type',
roofMatlCd: 'ROOF_ID_WA_53A',
roofMatlCdNm: '일본기와 A',
roofMatlCdJp: '和瓦A',
trestleMkrCd: 'ROOF_TECHRI',
trestleMkrCdNm: '지붕 기술 연구소',
trestleMkrCdJp: '屋根技術研究所',
constMthdCd: 'CST026',
constMthdCdNm: 'YG 앵커 랙 있음',
constMthdCdJp: 'YGアンカー ラック有り',
roofBaseCd: 'RFB001',
roofBaseCdNm: '구조용 합판 9mm 이상',
roofBaseCdJp: '構造用合板9mm以上',
rackYn: null,
priority: 1,
},
construction: {
constTp: 'WORK_LV_ID_1',
constTpNm: '표준 시공',
constTpJp: '標準施工',
constPossYn: 'Y',
plvrYn: 'Y',
cvrYn: 'Y',
cvrLmtRow: 9999,
snowGdPossYn: 'Y',
roofIndex: 0,
setupCover: true,
setupSnowCover: true,
selectedIndex: 1,
},
},
{
roofIndex: 1,
addRoof: {
roofMatlCd: 'ROOF_ID_WA_53B',
roofMatlNm: '일본기와 B',
roofMatlNmJp: '和瓦B',
widAuth: 'R',
widBase: '275.000',
lenAuth: 'R',
lenBase: '225.000',
roofPchAuth: null,
roofPchBase: null,
raftAuth: 'C',
raftBaseCd: 'HEI_455',
id: 'ROOF_ID_WA_53B',
name: '일본기와 B',
selected: true,
index: 1,
nameJp: '和瓦B',
length: 225,
width: 275,
raft: 'HEI_455',
layout: 'P',
hajebichi: 0,
pitch: 5,
angle: 21.8,
roofSizeSet: '1',
roofAngleSet: 'slope',
},
trestle: {
moduleTpCd: 'A1',
moduleTpCdNm: 'A1type',
moduleTpCdJp: 'A1type',
roofMatlCd: 'ROOF_ID_WA_53B',
roofMatlCdNm: '일본기와 B',
roofMatlCdJp: '和瓦B',
trestleMkrCd: 'DAIDO HUNT',
trestleMkrCdNm: '다이도 헌트',
trestleMkrCdJp: 'ダイドーハント',
constMthdCd: 'CST016',
constMthdCdNm: '지지 기와Ⅱ-B 랙 있음',
constMthdCdJp: '支持瓦Ⅱ-B ラック有り',
roofBaseCd: 'RFB002',
roofBaseCdNm: 'OSB12mm 이상',
roofBaseCdJp: 'OSB12mm以上',
rackYn: null,
priority: 95,
},
construction: {
constTp: 'WORK_LV_ID_1',
constTpNm: '표준 시공',
constTpJp: '標準施工',
constPossYn: 'Y',
plvrYn: 'Y',
cvrYn: 'Y',
cvrLmtRow: 9999,
snowGdPossYn: 'Y',
roofIndex: 1,
setupCover: false,
setupSnowCover: true,
selectedIndex: 1,
},
},
{
roofIndex: 2,
addRoof: {
roofMatlCd: 'ROOF_ID_HIRA_C',
roofMatlNm: '평판기와 C',
roofMatlNmJp: '平板瓦C',
widAuth: 'R',
widBase: '305.000',
lenAuth: 'R',
lenBase: '280.000',
roofPchAuth: null,
roofPchBase: null,
raftAuth: 'C',
raftBaseCd: 'HEI_455',
id: 'ROOF_ID_HIRA_C',
name: '평판기와 C',
selected: true,
index: 2,
nameJp: '平板瓦C',
length: 280,
width: 305,
raft: 'HEI_455',
layout: 'P',
hajebichi: 0,
pitch: 4,
angle: 21.8,
roofSizeSet: '1',
roofAngleSet: 'slope',
},
trestle: {
moduleTpCd: 'A1',
moduleTpCdNm: 'A1type',
moduleTpCdJp: 'A1type',
roofMatlCd: 'ROOF_ID_HIRA_C',
roofMatlCdNm: '평판기와 C',
roofMatlCdJp: '平板瓦C',
trestleMkrCd: 'ROOF_TECHRI',
trestleMkrCdNm: '지붕 기술 연구소',
trestleMkrCdJp: '屋根技術研究所',
constMthdCd: 'CST034',
constMthdCdNm: '지지 기와 C 랙 있음',
constMthdCdJp: '支持瓦C ラック有り',
roofBaseCd: 'RFB001',
roofBaseCdNm: '구조용 합판 9mm 이상',
roofBaseCdJp: '構造用合板9mm以上',
rackYn: null,
priority: 122,
},
construction: {
constTp: 'WORK_LV_ID_1',
constTpNm: '표준 시공',
constTpJp: '標準施工',
constPossYn: 'Y',
plvrYn: 'Y',
cvrYn: 'Y',
cvrLmtRow: 9999,
snowGdPossYn: 'Y',
roofIndex: 2,
setupCover: false,
setupSnowCover: false,
selectedIndex: 1,
},
},
{
roofIndex: 3,
addRoof: {
roofMatlCd: 'ROOF_ID_HIRA_D',
roofMatlNm: '평판기와 D',
roofMatlNmJp: '平板瓦D',
widAuth: 'R',
widBase: '305.000',
lenAuth: 'R',
lenBase: '280.000',
roofPchAuth: null,
roofPchBase: null,
raftAuth: 'C',
raftBaseCd: 'HEI_455',
id: 'ROOF_ID_HIRA_D',
name: '평판기와 D',
selected: true,
index: 3,
nameJp: '平板瓦D',
length: 280,
width: 305,
raft: 'HEI_455',
layout: 'P',
hajebichi: 0,
pitch: 8,
angle: 21.8,
roofSizeSet: '1',
roofAngleSet: 'slope',
},
trestle: {
moduleTpCd: 'A1',
moduleTpCdNm: 'A1type',
moduleTpCdJp: 'A1type',
roofMatlCd: 'ROOF_ID_HIRA_D',
roofMatlCdNm: '평판기와 D',
roofMatlCdJp: '平板瓦D',
trestleMkrCd: 'DAIDO HUNT',
trestleMkrCdNm: '다이도 헌트',
trestleMkrCdJp: 'ダイドーハント',
constMthdCd: 'CST018',
constMthdCdNm: '지지 기와Ⅱ-D 랙 있음',
constMthdCdJp: '支持瓦Ⅱ-D ラック有り',
roofBaseCd: 'RFB002',
roofBaseCdNm: 'OSB12mm 이상',
roofBaseCdJp: 'OSB12mm以上',
rackYn: null,
priority: 203,
},
construction: {
constTp: 'WORK_LV_ID_3',
constTpNm: '강화 시공',
constTpJp: '強化施工',
constPossYn: 'Y',
plvrYn: 'Y',
cvrYn: 'Y',
cvrLmtRow: 9999,
snowGdPossYn: 'Y',
roofIndex: 3,
setupCover: false,
setupSnowCover: false,
selectedIndex: 2,
},
},
],
module: {
itemId: '106796',
itemNm: 'Q.TRON M-G2.4+ 430',
goodsNo: 'Q.TRON M-G2.4+ 430',
itemTp: 'A1',
mixMatlNo: null,
mixItemTpYn: 'N',
itemList: [
{
itemId: '106796',
itemNm: 'Q.TRON M-G2.4+ 430',
goodsNo: 'Q.TRON M-G2.4+ 430',
itemTp: 'A1',
color: '#BEF781',
longAxis: '1722.000',
shortAxis: '1134.000',
thickness: '30.000',
wpOut: '430',
mixMatlNo: null,
},
],
name: 'Q.TRON M-G2.4+ 430',
},
})
}}
>
Test Data insert
</Button>
</div>
<div className="my-2">
<Button
onClick={() => {
const params = {
pid: 1,
objectNo: 'RT01250131002',
}
router.push(`/floor-plan/estimate/5?pid=${params.pid}&objectNo=${params.objectNo}`)
}}
>
견적서 페이지 이동
</Button>
</div>
</div>
</>
)
}

View File

@ -1,351 +0,0 @@
'use client'
import { useEffect } from 'react'
import { addDistanceTextToPolygon, getDistance } from '@/util/canvas-util'
import { useCanvas } from '@/hooks/useCanvas'
import { fabric } from 'fabric'
import { v4 as uuidv4 } from 'uuid'
export default function Roof() {
const {
canvas,
addShape,
handleUndo,
handleRedo,
handleClear,
handleCopy,
handleDelete,
handleSave,
handlePaste,
handleRotate,
attachCustomControlOnPolygon,
saveImage,
handleFlip,
} = useCanvas('canvas')
useEffect(() => {
let circle = new fabric.Circle({
radius: 40,
fill: 'rgba(200, 0, 0, 0.3)',
originX: 'center',
originY: 'center',
})
let text = new fabric.Textbox('AJLoveChina', {
originX: 'center',
originY: 'center',
textAlign: 'center',
fontSize: 12,
})
let group = new fabric.Group([circle, text], {
left: 100,
top: 100,
originX: 'center',
originY: 'center',
})
group.on('mousedblclick', () => {
// textForEditing is temporary obj,
// and will be removed after editing
console.log(text.type)
let textForEditing = new fabric.Textbox(text.text, {
originX: 'center',
originY: 'center',
textAlign: text.textAlign,
fontSize: text.fontSize,
left: group.left,
top: group.top,
})
// hide group inside text
text.visible = false
// note important, text cannot be hidden without this
group.addWithUpdate()
textForEditing.visible = true
// do not give controls, do not allow move/resize/rotation on this
textForEditing.hasConstrols = false
// now add this temporary obj to canvas
canvas.add(textForEditing)
canvas.setActiveObject(textForEditing)
// make the cursor showing
textForEditing.enterEditing()
textForEditing.selectAll()
// editing:exited means you click outside of the textForEditing
textForEditing.on('editing:exited', () => {
let newVal = textForEditing.text
let oldVal = text.text
// then we check if text is changed
if (newVal !== oldVal) {
text.set({
text: newVal,
visible: true,
})
// comment before, you must call this
group.addWithUpdate()
// we do not need textForEditing anymore
textForEditing.visible = false
canvas?.remove(textForEditing)
// optional, buf for better user experience
canvas?.setActiveObject(group)
}
})
})
canvas?.add(group)
}, [canvas])
const addRect = () => {
const rect = new fabric.Rect({
height: 200,
width: 200,
top: 10,
left: 10,
opacity: 0.4,
fill: randomColor(),
stroke: 'red',
name: uuidv4(),
})
addShape(rect)
}
const addHorizontalLine = () => {
const { x1, y1, x2, y2 } = { x1: 20, y1: 100, x2: 220, y2: 100 }
/**
* 시작X,시작Y,도착X,도착Y 좌표
*/
const horizontalLine = new fabric.Line([x1, y1, x2, y2], {
name: uuidv4(),
stroke: 'red',
strokeWidth: 3,
selectable: true,
})
const text = new fabric.Text(getDistance(x1, y1, x2, y2).toString(), {
fontSize: 20,
left: (x2 - x1) / 2,
top: y1 - 20,
})
const group = new fabric.Group([horizontalLine, text], {
left: 20,
top: 20,
})
// addShape(horizontalLine)
addShape(group)
console.log(JSON.stringify(canvas))
}
const addVerticalLine = () => {
const verticalLine = new fabric.Line([10, 10, 10, 100], {
name: uuidv4(),
stroke: 'red',
strokeWidth: 3,
selectable: true,
})
addShape(verticalLine)
}
const addTriangle = () => {
const triangle = new fabric.Triangle({
name: uuidv4(),
top: 50,
left: 50,
width: 100,
stroke: randomColor(),
strokeWidth: 3,
})
addShape(triangle)
}
const addTrapezoid = () => {
const trapezoid = new fabric.Polygon(
[
{ x: 100, y: 100 }, //
{ x: 500, y: 100 }, //
{ x: 750, y: 700 }, //
{ x: 250, y: 400 }, //
],
{
name: uuidv4(),
stroke: 'red',
opacity: 0.4,
strokeWidth: 3,
selectable: true,
objectCaching: false,
},
)
attachCustomControlOnPolygon(trapezoid)
const group = addDistanceTextToPolygon(trapezoid)
addGroupClickEvent(group)
canvas?.add(group)
canvas?.renderAll()
}
// group group object
function addGroupClickEvent(group) {
group.on('selected', (e) => {
console.log(e)
})
group.on('mousedblclick', (e) => {
// textForEditing is temporary obj,
// and will be removed after editing
const pointer = canvas?.getPointer(e.e) //
let minDistance = Infinity
let closestTextbox = null
const groupPoint = group.getCenterPoint()
group.getObjects().forEach(function (object) {
if (object.type === 'textbox') {
// TextBox
const objectCenter = object.getCenterPoint() // TextBox
const dx = objectCenter.x + groupPoint.x - pointer.x
const dy = objectCenter.y + groupPoint.y - pointer.y
const distance = Math.sqrt(dx * dx + dy * dy) // TextBox
if (distance < minDistance) {
// TextBox
minDistance = distance
closestTextbox = object
}
}
})
let textForEditing = new fabric.Textbox(closestTextbox.text, {
originX: 'center',
originY: 'center',
textAlign: closestTextbox.textAlign,
fontSize: closestTextbox.fontSize,
left: closestTextbox.left + groupPoint.x,
top: closestTextbox.top + groupPoint.y,
})
// hide group inside text
closestTextbox.visible = false
// note important, text cannot be hidden without this
group.addWithUpdate()
textForEditing.visible = true
// do not give controls, do not allow move/resize/rotation on this
textForEditing.hasConstrols = false
// now add this temporary obj to canvas
canvas?.add(textForEditing)
canvas?.setActiveObject(textForEditing)
// make the cursor showing
textForEditing?.enterEditing()
textForEditing?.selectAll()
// editing:exited means you click outside of the textForEditing
textForEditing?.on('editing:exited', () => {
let newVal = textForEditing.text
// then we check if text is changed
closestTextbox.set({
text: newVal,
visible: true,
})
// comment before, you must call this
group.addWithUpdate()
// we do not need textForEditing anymore
textForEditing.visible = false
canvas?.remove(textForEditing)
// optional, buf for better user experience
canvas?.setActiveObject(group)
})
})
}
// IText polygon
function addTextModifiedEvent(text, polygon, index) {
text.on('editing:exited', function () {})
}
const randomColor = () => {
return '#' + Math.round(Math.random() * 0xffffff).toString(16)
}
return (
<>
<div className="flex justify-center my-8 w-full">
<button className="w-30 mx-2 p-2 rounded bg-blue-500 text-white" onClick={addRect}>
ADD RECTANGLE
</button>
<button className="w-30 mx-2 p-2 rounded bg-blue-500 text-white" onClick={addHorizontalLine}>
ADD HORIZONTAL LINE
</button>
<button className="w-30 mx-2 p-2 rounded bg-blue-500 text-white" onClick={addVerticalLine}>
ADD VERTICALITY LINE
</button>
<button className="w-30 mx-2 p-2 rounded bg-blue-500 text-white" onClick={addTriangle}>
ADD TRIANGLE
</button>
<button className="w-30 mx-2 p-2 rounded bg-blue-500 text-white" onClick={addTrapezoid}>
ADD TRAPEZOID
</button>
<button className="w-30 mx-2 p-2 rounded bg-black text-white" onClick={handleCopy}>
COPY shape
</button>
<button className="w-30 mx-2 p-2 rounded bg-red-500 text-white" onClick={handleDelete}>
DELETE
</button>
<button className="w-30 mx-2 p-2 rounded bg-red-500 text-white" onClick={handleClear}>
CLEAR
</button>
<button className="w-30 mx-2 p-2 rounded bg-green-500 text-white" onClick={handleUndo}>
UNDO
</button>
<button className="w-30 mx-2 p-2 rounded bg-green-300 text-white" onClick={handleRedo}>
REDO
</button>
<button className="w-30 mx-2 p-2 rounded bg-black text-white" onClick={handleSave}>
저장
</button>
<button className="w-30 mx-2 p-2 rounded bg-black text-white" onClick={handlePaste}>
붙여넣기
</button>
<button className="w-30 mx-2 p-2 rounded bg-black text-white" onClick={() => handleRotate()}>
45 회전
</button>
<button
className="w-30 mx-2 p-2 rounded bg-black text-white"
onClick={() => {
saveImage('제목')
}}
>
이미지 저장
</button>
<button className="w-30 mx-2 p-2 rounded bg-black text-white" onClick={handleFlip}>
도형반전
</button>
</div>
<div
className="flex justify-center"
style={{
border: '1px solid',
width: 1000,
height: 1000,
margin: 'auto',
}}
>
<canvas id="canvas" />
</div>
</>
)
}

File diff suppressed because it is too large Load Diff

View File

@ -1,324 +0,0 @@
'use client'
import React, { useEffect, useState } from 'react'
import { Button } from '@nextui-org/react'
import { useAxios } from '@/hooks/useAxios'
import { useRecoilState } from 'recoil'
import { customSettingsState } from '@/store/canvasAtom'
import { modalContent, modalState } from '@/store/modalAtom'
import ColorPicker from './common/color-picker/ColorPicker'
export default function Settings() {
const [objectNo, setObjectNo] = useState('test123240829010')
const [error, setError] = useState(null)
const [customSettings, setCustomSettings] = useRecoilState(customSettingsState)
const [color, setColor] = useState('#ff0000')
const [open, setOpen] = useRecoilState(modalState)
const [contents, setContent] = useRecoilState(modalContent)
const { get, post } = useAxios()
const handleSavePopup = () => {
console.log('color ', color)
}
const handleClosePopup = () => {
setContent('')
setOpen(false)
console.log('colorSetting ', color)
}
const colorSetting = (
<>
<br />
<h1>React ColorPicker</h1>
<ColorPicker color={color} setColor={setColor} />
<div className="p-4">{color}</div>
<div>
<button onClick={handleSavePopup}>저장</button> <p />
<button onClick={handleClosePopup}>취소</button>
</div>
</>
)
const customStyles = {
overlay: {
backgroundColor: 'rgba(0,0,0,0.5)',
},
content: {
width: '300px',
height: '400px',
margin: 'auto',
borderRadius: '4px',
boxShadow: '0 2px 4px rgba(0,0,0,0.2)',
padding: '20px',
},
}
//
const [settings, setSettings] = useState({
display1: Array(11).fill(false), // 1
display2: Array(3).fill(false), // 2
rangeSetting: 0, //
gridSettings: Array(5).fill(false), //
})
const gridItems = {
display1: [
'할당 표시',
'도면 표시',
'그리드 표시',
'문자 표시',
'흐름방향 표시',
'복도치수 표시',
'실제치수 표시',
'치수 표시 없음',
'가대 표시',
'좌표 표시',
'도면전환 표시',
],
display2: ['테두리만', '라인해치', 'All Painted'],
rangeSetting: ['극소', '소', '중', '대'],
gridSettings: ['임의 그리드', '실선 그리드', '점 그리드', '그리드 색 설정', '흡착점 추가'],
}
//
useEffect(() => {
if (!objectNo) {
alert('object_no를 입력하세요.')
}
}, [])
//
const handleToggle = (type, index) => {
// ' '
if (type === 'gridSettings' && gridItems.gridSettings[index] === '실선 그리드') {
//openGridPopup()
}
// ' '
if (type === 'gridSettings' && gridItems.gridSettings[index] === '그리드 색 설정') {
//setSelectedGridSetting(gridItems.gridSettings[index])
//setIsPopupOpen(true)
//return prevSettings //
setOpen(true)
setContent({ ...colorSetting })
}
setSettings((prevSettings) => {
// prevSettings[type] ,
let updated = Array.isArray(prevSettings[type]) ? [...prevSettings[type]] : []
if (type === 'rangeSetting') {
return { ...prevSettings, [type]: index }
}
updated[index] = updated[index] === false ? true : false
return { ...prevSettings, [type]: updated }
})
}
// ' '
const openGridPopup = () => {
const popupWidth = 500
const popupHeight = 300
//
const left = window.innerWidth / 2 - popupWidth / 2
const top = window.innerHeight / 2 - popupHeight / 2
//
window
.open
//'./components/intro', // URL
//'_blank', //
//`width=${popupWidth},height=${popupHeight},top=${top},left=${left}`, //
()
}
// Canvas Setting
const handleSelect = async () => {
try {
if (!objectNo) {
alert('object_no를 입력하세요.')
return
}
const res = await get({ url: `/api/canvas-management/canvas-settings/by-object/${objectNo}` })
//
if (!res) {
console.warn('조회 결과가 없습니다.')
//
setSettings({
display1: Array(11).fill(false), // 1
display2: Array(3).fill(false), // 2
rangeSetting: 0, //
gridSettings: Array(5).fill(false), //
})
alert('조회된 데이터가 없습니다. 기본 설정이 적용됩니다.')
return //
}
const data = {
display1: [
res.assignDisplay,
res.drawDisplay,
res.gridDisplay,
res.charDisplay,
res.flowDisplay,
res.hallwayDimenDisplay,
res.actualDimenDisplay,
res.noDimenDisplay,
res.trestleDisplay,
res.coordiDisplay,
res.drawConverDisplay,
],
display2: [res.onlyBorder, res.lineHatch, res.allPainted],
rangeSetting: res.adsorpRangeSetting,
gridSettings: [res.randomGrid, res.solidGrid, res.dotGrid, res.gridColorSet, res.adsorpPointAdd],
}
//
setSettings({
display1: data.display1,
display2: data.display2,
rangeSetting: data.rangeSetting,
gridSettings: data.gridSettings,
})
} catch (error) {
console.error('Data fetching error:', error)
}
}
// Canvas Setting
const handleSubmit = async () => {
if (!objectNo) {
alert('object_no를 입력하세요.')
return
}
const patternData = {
objectNo,
assignDisplay: settings.display1[0],
drawDisplay: settings.display1[1],
gridDisplay: settings.display1[2],
charDisplay: settings.display1[3],
flowDisplay: settings.display1[4],
hallwayDimenDisplay: settings.display1[5],
actualDimenDisplay: settings.display1[6],
noDimenDisplay: settings.display1[7],
trestleDisplay: settings.display1[8],
coordiDisplay: settings.display1[9],
drawConverDisplay: settings.display1[10],
onlyBorder: settings.display2[0],
lineHatch: settings.display2[1],
allPainted: settings.display2[2],
adsorpRangeSetting: settings.rangeSetting,
randomGrid: settings.gridSettings[0],
solidGrid: settings.gridSettings[1],
dotGrid: settings.gridSettings[2],
gridColorSet: settings.gridSettings[3],
adsorpPointAdd: settings.gridSettings[4],
}
console.log('patternData', patternData)
await post({ url: `/api/canvas-management/canvas-settings`, data: patternData })
//Recoil
setCustomSettings({ ...patternData })
//
await handleSelect()
}
return (
<>
<div className="container mx-auto p-4 m-4 border">
<div align="right">
<input type="text" placeholder="Object No 입력" value={objectNo} onChange={(e) => setObjectNo(e.target.value)} />
<Button onClick={handleSelect}>조회</Button>
<Button onClick={handleSubmit}>저장</Button>
</div>
<div className="container mx-auto p-4 m-4 border">
<h1>[디스플레이 설정]</h1>
<h1>* 도면에 표시할 항목을 클릭하면 적용 됩니다.</h1>
<div className="grid-container2">
{gridItems.display1.map((item, index) => (
<div
key={index}
className={`grid-item ${settings.display1[index] === true ? 'selected' : 'unselected'}`}
onClick={() => handleToggle('display1', index)}
>
{settings.display1[index]} {item}
</div>
))}
</div>
<br />
<h1>* 화면 표시</h1>
<div className="grid-container3">
{gridItems.display2.map((item, index) => (
<div
key={index}
className={`grid-item ${settings.display2[index] === true ? 'selected' : 'unselected'}`}
onClick={() => handleToggle('display2', index)}
>
{settings.display2[index]} {item}
</div>
))}
</div>
<h1>[글꼴/도면크기 설정]</h1>
<h1>* 글꼴 크기 변경</h1>
<div className="grid-container2">
<div className="grid-item">문자 글꼴 변경</div>
<div className="grid-item">흐름방향 글꼴 변경</div>
<div className="grid-item">치수 글꼴 변경</div>
<div className="grid-item">회로번호 글꼴 변경</div>
</div>
``
<h1>* 흡착 범위 설정</h1>
<div className="grid-container4">
{gridItems.rangeSetting.map((item, index) => (
<div
key={index}
className={`grid-item ${settings.rangeSetting === index ? 'selected' : 'unselected'}`}
onClick={() => handleToggle('rangeSetting', index)}
>
{item}
</div>
))}
</div>
<div className="grid-container3">
<div className="grid-item">치수선 설정</div>
<div className="grid-item">도면 크기 설정</div>
<div className="grid-item">흡착점 ON</div>
</div>
<h1>[그리드 설정]</h1>
<div>
<ColorPicker color={color} setColor={setColor} />
<div className="p-4">{color}</div>
</div>
<div className="grid-container2">
{gridItems.gridSettings.map((item, index) => (
<div
key={index}
className={`grid-item ${settings.gridSettings[index] === true ? 'selected' : 'unselected'}`}
onClick={() => handleToggle('gridSettings', index)}
>
{settings.gridSettings[index]} {item}
</div>
))}
</div>
</div>
</div>
</>
)
}

View File

@ -63,20 +63,21 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla
/**
* 지붕재 초기값
*/
const defaultRoofSettings = {
roofSizeSet: '1', //
roofAngleSet: 'slope', //
const DEFAULT_ROOF_SETTINGS = {
roofSizeSet: '1',
roofAngleSet: 'slope',
angle: 21.8,
hajebichi: '',
hajebichi: null,
id: 'ROOF_ID_WA_53A',
index: 0,
layout: ROOF_MATERIAL_LAYOUT.PARALLEL,
lenAuth: 'R',
lenBase: '235.000',
length: '235',
length: 235,
name: '일본기와 A',
nameJp: '和瓦A',
pitch: 4,
planNo: planNo,
raft: '',
raftAuth: 'C',
raftBaseCd: 'HEI_455',
@ -89,12 +90,12 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla
selected: true,
widAuth: 'R',
widBase: '265.000',
width: '265',
width: 265,
}
useEffect(() => {
/**
* 메뉴에서 배치면초기설정 선택 조회
* 메뉴에서 배치면초기설정 선택 조회 화면 오픈
*/
if (openPoint && openPoint === 'canvasMenus') fetchBasicSettings(planNo, openPoint)
}, [])
@ -103,18 +104,16 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla
if (addedRoofs.length > 0) {
const raftCodeList = findCommonCode('203800')
setRaftCodes(raftCodeList)
/**
* 데이터 설정 확인 데이터가 없으면 기본 데이터 설정
*/
if (addedRoofs.length > 0) {
setCurrentRoof({ ...addedRoofs[0], planNo: basicSetting.planNo })
} else {
setCurrentRoof(defaultRoofSettings)
}
setCurrentRoof({ ...addedRoofs[0], planNo: planNo, roofSizeSet: String(basicSetting.roofSizeSet), roofAngleSet: basicSetting.roofAngleSet })
} else {
/** 데이터 설정 확인 후 데이터가 없으면 기본 데이터 설정 */
setCurrentRoof({ ...DEFAULT_ROOF_SETTINGS })
}
}, [addedRoofs])
/**
* 배치면초기설정 정보 변경 basicSettings 설정
*/
useEffect(() => {
if (!currentRoof) return
setBasicSettings({
@ -256,7 +255,7 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla
id={item.id}
name={item.name}
value={item.value}
checked={currentRoof?.roofSizeSet === item.value}
checked={String(currentRoof?.roofSizeSet) === item.value}
onChange={(e) => setCurrentRoof({ ...currentRoof, roofSizeSet: e.target.value })}
/>
<label htmlFor={item.id}>{getMessage(item.message)}</label>
@ -279,7 +278,7 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla
id={item.id}
name={item.name}
value={item.value}
checked={currentRoof?.roofAngleSet === item.value}
checked={String(currentRoof?.roofAngleSet) === item.value}
onChange={(e) => setCurrentRoof({ ...currentRoof, roofAngleSet: e.target.value })}
/>
<label htmlFor={item.id}>{getMessage(item.message)}</label>

View File

@ -1,4 +0,0 @@
.test {
@apply bg-red-500;
@apply text-2xl;
}

View File

@ -2,10 +2,6 @@ import { useAxios } from '@/hooks/useAxios'
import { useMessage } from '@/hooks/useMessage'
import { useSwal } from '@/hooks/useSwal'
import { getQueryString } from '@/util/common-utils'
import { trestleRequest, constructionRequest, trestleDetailRequest } from '@/models/apiModels'
import { POST } from '@/app/api/image-upload/route'
import { canvasState } from '@/store/canvasAtom'
import { useRecoilValue } from 'recoil'
/**
* 마스터 컨트롤러

View File

@ -43,21 +43,19 @@ import { v4 as uuidv4 } from 'uuid'
const defaultDotLineGridSetting = {
INTERVAL: {
type: 2, // 1: 가로,세로 간격 수동, 2: 비율 간격
type: 2 /* 1: 가로,세로 간격 수동, 2: 비율 간격 */,
ratioInterval: 910,
verticalInterval: 910,
horizontalInterval: 910,
dimension: 1, // 치수
dimension: 1 /* 치수 */,
},
DOT: false,
LINE: false,
}
// let previousRoofMaterialsYn = 'N' // 지붕재 select 정보 비교 후 변경된 것이 없으면 1회만 실행
export function useCanvasSetting() {
const canvas = useRecoilValue(canvasState)
/* canvas가 null이 아닐 때에만 getObjects 호출 */
/** canvas가 null이 아닐 때에만 getObjects 호출 */
const canvasObjects = canvas ? canvas.getObjects() : []
const [correntObjectNo, setCorrentObjectNo] = useRecoilState(correntObjectNoState)
@ -119,14 +117,14 @@ export function useCanvasSetting() {
const [type, setType] = useRecoilState(menuTypeState)
const setCurrentMenu = useSetRecoilState(currentMenuState)
const resetModuleSelectionData = useResetRecoilState(moduleSelectionDataState) //다음으로 넘어가는 최종 데이터
const resetSelectedModules = useResetRecoilState(selectedModuleState) //선택된 모듈
const resetModuleSelectionData = useResetRecoilState(moduleSelectionDataState) /* 다음으로 넘어가는 최종 데이터 */
const resetSelectedModules = useResetRecoilState(selectedModuleState) /* 선택된 모듈 */
const { trigger: moduleSelectedDataTrigger } = useCanvasPopupStatusController(2)
const [raftCodes, setRaftCodes] = useState([]) // 서까래 정보
const [raftCodes, setRaftCodes] = useState([]) /* 서까래 정보 */
const { findCommonCode } = useCommonCode()
const [currentRoof, setCurrentRoof] = useState(null) // 현재 선택된 지붕재 정보
const [currentRoof, setCurrentRoof] = useState(null) /* 현재 선택된 지붕재 정보 */
const { addPopup } = usePopup()
const [popupId, setPopupId] = useState(uuidv4())
@ -144,7 +142,7 @@ export function useCanvasSetting() {
useEffect(() => {
const tempFetchRoofMaterials = !fetchRoofMaterials
/* 초 1회만 실행하도록 처리 */
/** 초 1회만 실행하도록 처리 */
setFetchRoofMaterials(tempFetchRoofMaterials)
if (tempFetchRoofMaterials) {
addRoofMaterials()
@ -176,7 +174,23 @@ export function useCanvasSetting() {
angle: item.angle ? parseInt(item.angle) : 21.8,
}))
setRoofMaterials(roofLists)
const selectedRoofMaterial = roofLists[0]
}
useEffect(() => {
if (addedRoofs.length > 0 && addedRoofs[0].planNo === basicSetting.planNo) {
const raftCodeList = findCommonCode('203800')
setRaftCodes(raftCodeList)
setCurrentRoof({
...addedRoofs[0],
planNo: addedRoofs[0].planNo,
roofSizeSet: String(basicSetting.roofSizeSet),
roofAngleSet: basicSetting.roofAngleSet,
})
}
}, [addedRoofs])
useEffect(() => {
const selectedRoofMaterial = roofMaterials[0]
if (addedRoofs.length === 0) {
const newAddedRoofs = []
@ -184,19 +198,7 @@ export function useCanvasSetting() {
setAddedRoofs(newAddedRoofs)
}
setBasicSettings({ ...basicSetting, selectedRoofMaterial: selectedRoofMaterial })
}
/**
* 배치면 초기설정 화면이 열리지 않아도 데이터 set 하기 위해서 추가
*/
useEffect(() => {
if (addedRoofs.length > 0) {
const raftCodeList = findCommonCode('203800')
setRaftCodes(raftCodeList)
setCurrentRoof({ ...addedRoofs[0] })
}
}, [addedRoofs])
}, [roofMaterials])
useEffect(() => {
if (!canvas) {
@ -339,7 +341,7 @@ export function useCanvasSetting() {
}
})
/* 데이터 존재 시 화면 닫기(메뉴/저장 클릭 시 제외) */
/** 데이터 존재 시 화면 닫기(메뉴/저장 클릭 시 제외) */
if (openPoint !== 'canvasMenus' && openPoint !== 'basicSettingSave') {
//closePopup(popupId)
closeAll()
@ -369,9 +371,9 @@ export function useCanvasSetting() {
},
]
/* 메뉴에서 배치면 초기설정 클릭 시 실행하지 않음 */
/** 메뉴에서 배치면 초기설정 클릭 시 실행하지 않음 */
if (openPoint === null) {
/* 배치면 초기설정 미저장 상태이면 화면 열기 */
/** 배치면 초기설정 미저장 상태이면 화면 열기 */
const placementInitialProps = {
id: popupId,
pos: {
@ -385,7 +387,7 @@ export function useCanvasSetting() {
}
}
/* 데이터 설정 */
/** 데이터 설정 */
const addRoofs = []
for (let i = 0; i < roofsArray.length; i++) {
roofMaterials?.map((material) => {
@ -440,6 +442,21 @@ export function useCanvasSetting() {
// setCanvasSetting({ ...basicSetting })
}
/**
* 저장/복사저장 지붕 크기에 따른 메뉴 설정
*/
const setMenuByRoofSize = (roofSizeSet) => {
if (['2', '3'].includes(String(roofSizeSet))) {
setMenuNumber(3)
setType('surface')
setCurrentMenu(MENU.BATCH_CANVAS.BATCH_DRAWING)
} else {
setMenuNumber(2)
setType('outline')
setCurrentMenu(MENU.ROOF_COVERING.EXTERIOR_WALL_LINE)
}
}
/**
* 기본설정(PlacementShapeSetting) 저장
*/
@ -471,31 +488,23 @@ export function useCanvasSetting() {
await post({ url: `/api/canvas-management/canvas-basic-settings`, data: patternData }).then((res) => {
swalFire({ text: getMessage(res.returnMessage) })
/* BasicSettings Recoil 설정 */
/** BasicSettings Recoil 설정 */
setBasicSettings({ ...params })
})
/* CanvasSetting Recoil 설정 - roofSizeSet을 문자열로 변환 */
/** CanvasSetting Recoil 설정 - roofSizeSet을 문자열로 변환 */
setCanvasSetting({
...basicSetting,
roofSizeSet: String(params.roofSizeSet),
})
/* 메뉴 설정 */
if (['2', '3'].includes(params.roofSizeSet)) {
setMenuNumber(3)
setType('surface')
setCurrentMenu(MENU.BATCH_CANVAS.BATCH_DRAWING)
} else {
setMenuNumber(2)
setType('outline')
setCurrentMenu(MENU.ROOF_COVERING.EXTERIOR_WALL_LINE)
}
/** 메뉴 설정 */
setMenuByRoofSize(params.roofSizeSet)
/* 배치면초기설정 조회 */
/** 배치면초기설정 조회 */
fetchBasicSettings(params.planNo, 'basicSettingSave')
/* 모듈 선택 데이터 초기화 */
/** 모듈 선택 데이터 초기화 */
resetModuleSelectionData()
moduleSelectedDataTrigger({ common: {}, module: {}, roofConstructions: [] })
const isModuleExist = canvas.getObjects().some((obj) => obj.name === POLYGON_TYPE.MODULE)
@ -536,27 +545,19 @@ export function useCanvasSetting() {
swalFire({ text: getMessage(res.returnMessage) })
})
/* CanvasSetting Recoil 설정 - roofSizeSet을 문자열로 변환 */
/** CanvasSetting Recoil 설정 - roofSizeSet을 문자열로 변환 */
setCanvasSetting({
...basicSetting,
roofSizeSet: String(params.roofSizeSet),
})
/* 메뉴 설정 */
if (['2', '3'].includes(params?.roofSizeSet)) {
setMenuNumber(3)
setType('surface')
setCurrentMenu(MENU.BATCH_CANVAS.BATCH_DRAWING)
} else {
setMenuNumber(2)
setType('outline')
setCurrentMenu(MENU.ROOF_COVERING.EXTERIOR_WALL_LINE)
}
/** 메뉴 설정 */
setMenuByRoofSize(params.roofSizeSet)
/* 배치면초기설정 조회 */
/** 배치면초기설정 조회 */
fetchBasicSettings(Number(params.planNo), 'basicSettingSave')
/* 모듈 선택 데이터 초기화 */
/** 모듈 선택 데이터 초기화 */
resetModuleSelectionData()
moduleSelectedDataTrigger({ common: {}, module: {}, roofConstructions: [] })
const isModuleExist = canvas.getObjects().some((obj) => obj.name === POLYGON_TYPE.MODULE)
@ -582,24 +583,16 @@ export function useCanvasSetting() {
const optionData4 = settingModalSecondOptions.option4.map((item) => ({ ...item, selected: res[item.column] }))
const optionData5 = settingModalFirstOptions.dimensionDisplay.map((item) => ({ ...item }))
/**
* 흡착점 ON/OFF
*/
/** 흡착점 ON/OFF */
setAdsorptionPointMode({ ...adsorptionPointMode, adsorptionPoint: res.adsorpPoint })
/**
* 치수선 설정
*/
/** 치수선 설정 */
setDimensionLineSettings({ ...dimensionLineSettings, pixel: res.originPixel, color: res.originColor })
/**
* 도면크기 설정
*/
/** 도면크기 설정 */
setPlanSizeSettingMode({ ...planSizeSettingMode, originHorizon: res.originHorizon, originVertical: res.originVertical })
/**
* 데이터 설정
*/
/** 데이터 설정 */
setSettingModalFirstOptions({
...settingModalFirstOptions,
option1: optionData1,
@ -614,45 +607,35 @@ export function useCanvasSetting() {
const fontPatternData = {
commonText: {
/**
* 문자 글꼴 조회 데이터
*/
/** 문자 글꼴 조회 데이터 */
fontFamily: getFonts(res.wordFont),
fontWeight: getFontStyles(res.wordFontStyle),
fontSize: getFontSizes(res.wordFontSize),
fontColor: getFontColors(res.wordFontColor),
},
flowText: {
/**
* 흐름방향 글꼴 조회 데이터
*/
/** 흐름방향 글꼴 조회 데이터 */
fontFamily: getFonts(res.flowFont),
fontWeight: getFontStyles(res.flowFontStyle),
fontSize: getFontSizes(res.flowFontSize),
fontColor: getFontColors(res.flowFontColor),
},
dimensionLineText: {
/**
* 치수 글꼴 조회 데이터
*/
/** 치수 글꼴 조회 데이터 */
fontFamily: getFonts(res.dimensioFont),
fontWeight: getFontStyles(res.dimensioFontStyle),
fontSize: getFontSizes(res.dimensioFontSize),
fontColor: getFontColors(res.dimensioFontColor),
},
circuitNumberText: {
/**
* 회로번호 글꼴 조회 데이터
*/
/** 회로번호 글꼴 조회 데이터 */
fontFamily: getFonts(res.circuitNumFont),
fontWeight: getFontStyles(res.circuitNumFontStyle),
fontSize: getFontSizes(res.circuitNumFontSize),
fontColor: getFontColors(res.circuitNumFontColor),
},
lengthText: {
/**
* 치수선 글꼴 조회 데이터
*/
/** 치수선 글꼴 조회 데이터 */
fontFamily: getFonts(res.lengthFont),
fontWeight: getFontStyles(res.lengthFontStyle),
fontSize: getFontSizes(res.lengthFontSize),
@ -660,14 +643,10 @@ export function useCanvasSetting() {
},
}
/**
* 조회된 글꼴 데이터 set
*/
/** 조회된 글꼴 데이터 set */
setGlobalFont(fontPatternData)
/**
* / 그리드
*/
/** 점/선 그리드 */
const patternData = {
INTERVAL: {
type: res.gridType,
@ -682,47 +661,31 @@ export function useCanvasSetting() {
setDotLineGridSettingState(patternData)
/**
* 그리드 설정
*/
/** 그리드 색 설정 */
setGridColor(res.gridColor)
} else {
//조회된 글꼴 데이터가 없는 경우 (데이터 초기화)
/** 조회된 글꼴 데이터가 없는 경우 (데이터 초기화) */
/**
* 흡착점 ON/OFF
*/
/** 흡착점 ON/OFF */
setAdsorptionPointMode({ ...adsorptionPointMode, adsorptionPoint: false })
/**
* 치수선 설정
*/
/** 치수선 설정 */
resetDimensionLineSettings()
/**
* 도면크기 설정
*/
/** 도면크기 설정 */
resetPlanSizeSettingMode()
/**
* 데이터 설정
*/
/** 데이터 설정 */
resetSettingModalFirstOptions()
resetSettingModalSecondOptions()
/**
* 데이터 초기화
*/
/** 데이터 초기화 */
resetGlobalFont()
/**
* / 그리드
*/
/** 점/선 그리드 */
setDotLineGridSettingState({ ...defaultDotLineGridSetting })
/**
* 그리드 설정
*/
/** 그리드 색 설정 */
setGridColor('#FF0000')
}
@ -736,9 +699,7 @@ export function useCanvasSetting() {
* CanvasSetting 옵션 클릭 저장
*/
const onClickOption2 = async () => {
/**
* 서버에 전송할 데이터
*/
/** 서버에 전송할 데이터 */
const dataToSend = {
firstOption1: option1.map((item) => ({
column: item.column,
@ -759,13 +720,9 @@ export function useCanvasSetting() {
})),
}
const patternData = {
/**
* 견적서 번호
*/
/** 견적서 번호 */
objectNo: correntObjectNo,
/**
* 디스플레이 설정(다중)
*/
/** 디스플레이 설정(다중) */
allocDisplay: dataToSend.firstOption1[0].selected,
outlineDisplay: dataToSend.firstOption1[1].selected,
gridDisplay: dataToSend.firstOption1[2].selected,
@ -776,85 +733,62 @@ export function useCanvasSetting() {
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
*/
/** 흡착점 ON/OFF */
adsorpPoint: adsorptionPointMode.adsorptionPoint,
//??: adsorptionRange, 사용여부 확인 필요
/**
* 글꼴 설정
*/
//문자 글꼴
/** 문자 글꼴 설정 */
wordFont: globalFont.commonText.fontFamily?.value ?? 'MS PGothic',
wordFontStyle: globalFont.commonText.fontWeight?.value ?? 'normal',
wordFontSize: globalFont.commonText.fontSize?.value ?? 16,
wordFontColor: globalFont.commonText.fontColor?.value ?? 'black',
/**
* 흐름방향 글꼴
*/
/** 흐름방향 글꼴 설정 */
flowFont: globalFont.flowText.fontFamily?.value ?? 'MS PGothic',
flowFontStyle: globalFont.flowText.fontWeight?.value ?? 'normal',
flowFontSize: globalFont.flowText.fontSize?.value ?? 16,
flowFontColor: globalFont.flowText.fontColor?.value ?? 'black',
/**
* 치수 글꼴
*/
/** 치수 글꼴 설정 */
dimensioFont: globalFont.dimensionLineText.fontFamily?.value ?? 'MS PGothic',
dimensioFontStyle: globalFont.dimensionLineText.fontWeight?.value ?? 'normal',
dimensioFontSize: globalFont.dimensionLineText.fontSize?.value ?? 16,
dimensioFontColor: globalFont.dimensionLineText.fontColor?.value ?? 'black',
/**
* 회로번호 글꼴
*/
/** 회로번호 글꼴 설정 */
circuitNumFont: globalFont.circuitNumberText.fontFamily?.value ?? 'MS PGothic',
circuitNumFontStyle: globalFont.circuitNumberText.fontWeight?.value ?? 'normal',
circuitNumFontSize: globalFont.circuitNumberText.fontSize?.value ?? 16,
circuitNumFontColor: globalFont.circuitNumberText.fontColor?.value ?? 'black',
/**
* 치수선 글꼴
*/
/** 치수선 글꼴 설정 */
lengthFont: globalFont.lengthText.fontFamily?.value ?? 'MS PGothic',
lengthFontStyle: globalFont.lengthText.fontWeight?.value ?? 'normal',
lengthFontSize: globalFont.lengthText.fontSize?.value ?? 16,
lengthFontColor: globalFont.lengthText.fontColor?.value ?? 'black',
/**
* 치수선 설정
*/
/** 치수선 설정 */
originPixel: dimensionLineSettings.pixel,
originColor: dimensionLineSettings.color,
/**
* 도면크기 설정
*/
/** 도면크기 설정 */
originHorizon: planSizeSettingMode.originHorizon,
originVertical: planSizeSettingMode.originVertical,
/** 점/선 그리드 */
dotGridDisplay: dotLineGridSetting.DOT,
lineGridDisplay: dotLineGridSetting.LINE,
gridType: dotLineGridSetting.INTERVAL.type,
@ -863,6 +797,7 @@ export function useCanvasSetting() {
gridRatio: dotLineGridSetting.INTERVAL.ratioInterval / 10,
gridDimen: dotLineGridSetting.INTERVAL.dimension,
/** 그리드 색 설정 */
gridColor: gridColor,
}
@ -875,13 +810,10 @@ export function useCanvasSetting() {
.then((res) => {
//swalFire({ text: getMessage(res.returnMessage) })
/**
* Canvas 디스플레이 설정 해당 옵션 적용
*/
/** Canvas 디스플레이 설정 시 해당 옵션 적용 */
frontSettings()
/**
* 저장 재조회
*/
/** 저장 후 재조회 */
fetchSettings()
})
.catch((error) => {
@ -912,8 +844,8 @@ export function useCanvasSetting() {
*/
/**
* 옵션명
* 옵션상태
* 옵션명 optionName
* 옵션상태 optionSelected
*/
let optionName
let optionSelected
@ -948,15 +880,11 @@ export function useCanvasSetting() {
optionName = ['backGroundImage']
break
case 'totalDisplay':
/**
* 작업할 필요 없음
*/
/** 작업할 필요 없음 */
optionName = []
break
}
/**
* 표시 선택 상태(true/false)
*/
/** 표시 선택 상태(true/false)*/
optionSelected = option1[i].selected
canvasObjects