Merge branch 'dev' into dev-yj
# Conflicts: # src/components/floor-plan/CanvasFrame.jsx # src/hooks/useContextMenu.js
This commit is contained in:
commit
665d00c367
@ -28,6 +28,7 @@
|
||||
"react-draggable": "^4.4.6",
|
||||
"react-hook-form": "^7.53.0",
|
||||
"react-icons": "^5.3.0",
|
||||
"react-loading-skeleton": "^3.5.0",
|
||||
"react-responsive-modal": "^6.4.2",
|
||||
"recoil": "^0.7.7",
|
||||
"sweetalert2": "^11.14.1",
|
||||
|
||||
@ -1,53 +1,173 @@
|
||||
'use client'
|
||||
|
||||
import { useRef } from 'react'
|
||||
import { useAxios } from '@/hooks/useAxios'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import Cookies from 'js-cookie'
|
||||
|
||||
import { isObjectNotEmpty, inputTelNumberCheck, inputNumberCheck } from '@/util/common-utils'
|
||||
|
||||
export default function Join() {
|
||||
const { getMessage } = useMessage()
|
||||
const { promisePost } = useAxios()
|
||||
const router = useRouter()
|
||||
|
||||
const storeQcastNmRef = useRef()
|
||||
const storeQcastNmKanaRef = useRef()
|
||||
const postCdRef = useRef()
|
||||
const addrRef = useRef()
|
||||
const telNoRef = useRef()
|
||||
const faxRef = useRef()
|
||||
const userNmRef = useRef()
|
||||
const userIdRef = useRef()
|
||||
const emailRef = useRef()
|
||||
const userTelNoRef = useRef()
|
||||
const userFaxRef = useRef()
|
||||
|
||||
// 가입 신청 유효성 검사
|
||||
const joinValidation = (formData) => {
|
||||
// 판매대리점 정보 - 판매대리점명
|
||||
const storeQcastNm = formData.get('storeQcastNm')
|
||||
if (!isObjectNotEmpty(storeQcastNm)) {
|
||||
alert(getMessage('common.message.required.data', [getMessage('join.sub1.storeQcastNm')]))
|
||||
storeQcastNmRef.current.focus()
|
||||
return false
|
||||
}
|
||||
|
||||
// 판매대리점 정보 - 판매대리점명 후리가나
|
||||
const storeQcastNmKana = formData.get('storeQcastNmKana')
|
||||
if (!isObjectNotEmpty(storeQcastNmKana)) {
|
||||
alert(getMessage('common.message.required.data', [getMessage('join.sub1.storeQcastNmKana')]))
|
||||
storeQcastNmKanaRef.current.focus()
|
||||
return false
|
||||
}
|
||||
|
||||
// 판매대리점 정보 - 우편번호
|
||||
const postCd = formData.get('postCd')
|
||||
if (!isObjectNotEmpty(postCd)) {
|
||||
alert(getMessage('common.message.required.data', [getMessage('join.sub1.postCd')]))
|
||||
postCdRef.current.focus()
|
||||
return false
|
||||
}
|
||||
|
||||
// 판매대리점 정보 - 주소
|
||||
const addr = formData.get('addr')
|
||||
if (!isObjectNotEmpty(addr)) {
|
||||
alert(getMessage('common.message.required.data', [getMessage('join.sub1.addr')]))
|
||||
addrRef.current.focus()
|
||||
return false
|
||||
}
|
||||
|
||||
// 판매대리점 정보 - 전화번호
|
||||
const telNo = formData.get('telNo')
|
||||
if (!isObjectNotEmpty(telNo)) {
|
||||
alert(getMessage('common.message.required.data', [getMessage('join.sub1.telNo')]))
|
||||
telNoRef.current.focus()
|
||||
return false
|
||||
}
|
||||
|
||||
// 판매대리점 정보 - FAX 번호
|
||||
const fax = formData.get('fax')
|
||||
if (!isObjectNotEmpty(fax)) {
|
||||
alert(getMessage('common.message.required.data', [getMessage('join.sub1.fax')]))
|
||||
faxRef.current.focus()
|
||||
return false
|
||||
}
|
||||
|
||||
// 담당자 정보 - 담당자명
|
||||
const userNm = formData.get('userNm')
|
||||
if (!isObjectNotEmpty(userNm)) {
|
||||
alert(getMessage('common.message.required.data', [getMessage('join.sub2.userNm')]))
|
||||
userNmRef.current.focus()
|
||||
return false
|
||||
}
|
||||
|
||||
// 담당자 정보 - 신청 ID
|
||||
const userId = formData.get('userId')
|
||||
if (!isObjectNotEmpty(userId)) {
|
||||
alert(getMessage('common.message.required.data', [getMessage('join.sub2.userId')]))
|
||||
userIdRef.current.focus()
|
||||
return false
|
||||
}
|
||||
|
||||
// 담당자 정보 - 이메일 주소
|
||||
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/
|
||||
|
||||
const email = formData.get('email')
|
||||
if (!isObjectNotEmpty(email)) {
|
||||
alert(getMessage('common.message.required.data', [getMessage('join.sub2.email')]))
|
||||
emailRef.current.focus()
|
||||
return false
|
||||
} else {
|
||||
// 이메일 정규식 검사
|
||||
if (!emailRegex.test(email)) {
|
||||
alert(getMessage('join.validation.check1', [getMessage('join.sub2.email')]))
|
||||
emailRef.current.focus()
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// 담당자 정보 - 전화번호
|
||||
const userTelNo = formData.get('userTelNo')
|
||||
if (!isObjectNotEmpty(userTelNo)) {
|
||||
alert(getMessage('common.message.required.data', [getMessage('join.sub2.telNo')]))
|
||||
userTelNoRef.current.focus()
|
||||
return false
|
||||
}
|
||||
|
||||
// 담당자 정보 - FAX 번호
|
||||
const userFax = formData.get('userFax')
|
||||
if (!isObjectNotEmpty(userFax)) {
|
||||
alert(getMessage('common.message.required.data', [getMessage('join.sub2.fax')]))
|
||||
userFaxRef.current.focus()
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// 가입 신청
|
||||
const joinProcess = async (e) => {
|
||||
e.preventDefault()
|
||||
const formData = new FormData(e.target)
|
||||
|
||||
const param = {
|
||||
storeQcastNm: formData.get('storeQcastNm'),
|
||||
storeQcastNmKana: formData.get('storeQcastNmKana'),
|
||||
postCd: formData.get('postCd'),
|
||||
addr: formData.get('addr'),
|
||||
telNo: formData.get('telNo'),
|
||||
fax: formData.get('fax'),
|
||||
bizNo: formData.get('bizNo'),
|
||||
userInfo: {
|
||||
userId: formData.get('userId'),
|
||||
userNm: formData.get('userNm'),
|
||||
userNmKana: formData.get('userNmKana'),
|
||||
telNo: formData.get('userTelNo'),
|
||||
fax: formData.get('userFax'),
|
||||
email: formData.get('email'),
|
||||
category: formData.get('category'),
|
||||
},
|
||||
}
|
||||
|
||||
await promisePost({ url: '/api/login/v1.0/user/join', data: param })
|
||||
.then((res) => {
|
||||
if (res) {
|
||||
if (res.data.result.resultCode == 'S') {
|
||||
Cookies.set('joinEmail', formData.get('email'), { expires: 1 })
|
||||
router.push('/join/complete')
|
||||
} else {
|
||||
alert(res.data.result.resultMsg)
|
||||
}
|
||||
if (joinValidation(formData)) {
|
||||
if (confirm(getMessage('join.complete.save.confirm'))) {
|
||||
const param = {
|
||||
storeQcastNm: formData.get('storeQcastNm'),
|
||||
storeQcastNmKana: formData.get('storeQcastNmKana'),
|
||||
postCd: formData.get('postCd'),
|
||||
addr: formData.get('addr'),
|
||||
telNo: formData.get('telNo'),
|
||||
fax: formData.get('fax'),
|
||||
bizNo: formData.get('bizNo'),
|
||||
userInfo: {
|
||||
userId: formData.get('userId'),
|
||||
userNm: formData.get('userNm'),
|
||||
userNmKana: formData.get('userNmKana'),
|
||||
telNo: formData.get('userTelNo'),
|
||||
fax: formData.get('userFax'),
|
||||
email: formData.get('email'),
|
||||
category: formData.get('category'),
|
||||
},
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
alert(error.response.data.message)
|
||||
})
|
||||
|
||||
await promisePost({ url: '/api/login/v1.0/user/join', data: param })
|
||||
.then((res) => {
|
||||
if (res) {
|
||||
if (res.data.result.resultCode == 'S') {
|
||||
Cookies.set('joinEmail', formData.get('email'), { expires: 1 })
|
||||
router.push('/join/complete')
|
||||
} else {
|
||||
alert(res.data.result.resultMsg)
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
alert(error.response.data.message)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
@ -71,6 +191,7 @@ export default function Join() {
|
||||
<col />
|
||||
</colgroup>
|
||||
<tbody>
|
||||
{/* 판매대리점명 */}
|
||||
<tr>
|
||||
<th>
|
||||
{getMessage('join.sub1.storeQcastNm')} <span className="important">*</span>
|
||||
@ -81,14 +202,16 @@ export default function Join() {
|
||||
type="text"
|
||||
id="storeQcastNm"
|
||||
name="storeQcastNm"
|
||||
required
|
||||
alt={getMessage('join.sub1.storeQcastNm')}
|
||||
className="input-light"
|
||||
placeholder={getMessage('join.sub1.storeQcastNm_placeholder')}
|
||||
maxLength={30}
|
||||
ref={storeQcastNmRef}
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{/* 판매대리점명 후리가나 */}
|
||||
<tr>
|
||||
<th>
|
||||
{getMessage('join.sub1.storeQcastNmKana')} <span className="important">*</span>
|
||||
@ -99,13 +222,15 @@ export default function Join() {
|
||||
type="text"
|
||||
id="storeQcastNmKana"
|
||||
name="storeQcastNmKana"
|
||||
required
|
||||
className="input-light"
|
||||
placeholder={getMessage('join.sub1.storeQcastNmKana_placeholder')}
|
||||
maxLength={30}
|
||||
ref={storeQcastNmKanaRef}
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{/* 우편번호/주소 */}
|
||||
<tr>
|
||||
<th>
|
||||
{getMessage('join.sub1.postCd')}/{getMessage('join.sub1.addr')} <span className="important">*</span>
|
||||
@ -117,9 +242,11 @@ export default function Join() {
|
||||
type="text"
|
||||
id="postCd"
|
||||
name="postCd"
|
||||
required
|
||||
className="input-light"
|
||||
placeholder={getMessage('join.sub1.postCd_placeholder')}
|
||||
onChange={inputNumberCheck}
|
||||
maxLength={7}
|
||||
ref={postCdRef}
|
||||
/>
|
||||
</div>
|
||||
<div className="input-wrap" style={{ width: '495px' }}>
|
||||
@ -127,14 +254,16 @@ export default function Join() {
|
||||
type="text"
|
||||
id="addr"
|
||||
name="addr"
|
||||
required
|
||||
className="input-light"
|
||||
placeholder={getMessage('join.sub1.addr_placeholder')}
|
||||
maxLength={50}
|
||||
ref={addrRef}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{/* 전화번호 */}
|
||||
<tr>
|
||||
<th>
|
||||
{getMessage('join.sub1.telNo')} <span className="important">*</span>
|
||||
@ -145,13 +274,16 @@ export default function Join() {
|
||||
type="text"
|
||||
id="telNo"
|
||||
name="telNo"
|
||||
required
|
||||
className="input-light"
|
||||
placeholder={getMessage('join.sub1.telNo_placeholder')}
|
||||
></input>
|
||||
maxLength={15}
|
||||
onChange={inputTelNumberCheck}
|
||||
ref={telNoRef}
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{/* FAX 번호 */}
|
||||
<tr>
|
||||
<th>
|
||||
{getMessage('join.sub1.fax')} <span className="important">*</span>
|
||||
@ -162,18 +294,21 @@ export default function Join() {
|
||||
type="text"
|
||||
id="fax"
|
||||
name="fax"
|
||||
required
|
||||
className="input-light"
|
||||
placeholder={getMessage('join.sub1.fax_placeholder')}
|
||||
></input>
|
||||
maxLength={15}
|
||||
onChange={inputTelNumberCheck}
|
||||
ref={faxRef}
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{/* 법인번호 */}
|
||||
<tr>
|
||||
<th>{getMessage('join.sub1.bizNo')}</th>
|
||||
<td>
|
||||
<div className="input-wrap" style={{ width: '200px' }}>
|
||||
<input type="text" id="bizNo" name="bizNo" className="input-light" />
|
||||
<input type="text" id="bizNo" name="bizNo" className="input-light" maxLength={15} onChange={inputTelNumberCheck} />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@ -196,44 +331,49 @@ export default function Join() {
|
||||
<col />
|
||||
</colgroup>
|
||||
<tbody>
|
||||
{/* 담당자명 */}
|
||||
<tr>
|
||||
<th>
|
||||
{getMessage('join.sub2.userNm')} <span className="important">*</span>
|
||||
</th>
|
||||
<td>
|
||||
<div className="input-wrap" style={{ width: '200px' }}>
|
||||
<input type="text" id="userNm" name="userNm" className="input-light" required />
|
||||
<input type="text" id="userNm" name="userNm" className="input-light" maxLength={20} ref={userNmRef} />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{/* 담당자명 후리가나 */}
|
||||
<tr>
|
||||
<th>{getMessage('join.sub2.userNmKana')}</th>
|
||||
<td>
|
||||
<div className="input-wrap" style={{ width: '200px' }}>
|
||||
<input type="text" id="userNmKana" name="userNmKana" className="input-light" />
|
||||
<input type="text" id="userNmKana" name="userNmKana" maxLength={20} className="input-light" />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{/* 신청 ID */}
|
||||
<tr>
|
||||
<th>
|
||||
{getMessage('join.sub2.userId')} <span className="important">*</span>
|
||||
</th>
|
||||
<td>
|
||||
<div className="input-wrap" style={{ width: '200px' }}>
|
||||
<input type="text" id="userId" name="userId" className="input-light" required />
|
||||
<input type="text" id="userId" name="userId" className="input-light" maxLength={20} ref={userIdRef} />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{/* 이메일 주소 */}
|
||||
<tr>
|
||||
<th>
|
||||
{getMessage('join.sub2.email')} <span className="important">*</span>
|
||||
</th>
|
||||
<td>
|
||||
<div className="input-wrap" style={{ width: '200px' }}>
|
||||
<input type="text" id="email" name="email" className="input-light" required />
|
||||
<input type="text" id="email" name="email" className="input-light" maxLength={30} ref={emailRef} />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{/* 전화번호 */}
|
||||
<tr>
|
||||
<th>
|
||||
{getMessage('join.sub2.telNo')} <span className="important">*</span>
|
||||
@ -246,11 +386,14 @@ export default function Join() {
|
||||
name="userTelNo"
|
||||
className="input-light"
|
||||
placeholder={getMessage('join.sub2.telNo_placeholder')}
|
||||
required
|
||||
maxLength={15}
|
||||
onChange={inputTelNumberCheck}
|
||||
ref={userTelNoRef}
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{/* FAX 번호 */}
|
||||
<tr>
|
||||
<th>
|
||||
{getMessage('join.sub2.fax')} <span className="important">*</span>
|
||||
@ -263,16 +406,19 @@ export default function Join() {
|
||||
name="userFax"
|
||||
className="input-light"
|
||||
placeholder={getMessage('join.sub1.fax_placeholder')}
|
||||
required
|
||||
maxLength={15}
|
||||
onChange={inputTelNumberCheck}
|
||||
ref={userFaxRef}
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{/* 부서명 */}
|
||||
<tr>
|
||||
<th>{getMessage('join.sub2.category')}</th>
|
||||
<td>
|
||||
<div className="input-wrap" style={{ width: '200px' }}>
|
||||
<input type="text" id="category" name="category" className="input-light" />
|
||||
<input type="text" id="category" name="category" className="input-light" maxLength={20} />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@ -331,7 +331,15 @@ export default function Login() {
|
||||
></button>
|
||||
</div>
|
||||
<div className="pwreset-btn-box">
|
||||
<button type="button" className="login-btn light mr5" onClick={() => setPasswordReset(1)}>
|
||||
<button
|
||||
type="button"
|
||||
className="login-btn light mr5"
|
||||
onClick={() => {
|
||||
setPasswordReset(1)
|
||||
setCheckEmail('')
|
||||
setCheckId('')
|
||||
}}
|
||||
>
|
||||
{getMessage('login.init_password.btn.back')}
|
||||
</button>
|
||||
<button type="button" className="login-btn" onClick={initPasswordProcess}>
|
||||
|
||||
@ -1,15 +1,13 @@
|
||||
'use client'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useEffect } from 'react'
|
||||
import '@/styles/contents.scss'
|
||||
import { useRecoilState } from 'recoil'
|
||||
import { contextMenuState } from '@/store/contextMenu'
|
||||
|
||||
export default function QContextMenu(props) {
|
||||
const { contextRef, canvasProps } = props
|
||||
|
||||
// const children = useRecoilValue(modalContent)
|
||||
const [contextMenu, setContextMenu] = useState({ visible: false, x: 0, y: 0 })
|
||||
|
||||
const { contextRef, canvasProps, handleKeyup } = props
|
||||
const [contextMenu, setContextMenu] = useRecoilState(contextMenuState)
|
||||
const activeObject = canvasProps?.getActiveObject() //액티브된 객체를 가져옴
|
||||
|
||||
let contextType = ''
|
||||
|
||||
if (activeObject) {
|
||||
@ -27,7 +25,7 @@ export default function QContextMenu(props) {
|
||||
const handleContextMenu = (e) => {
|
||||
// e.preventDefault() //기존 contextmenu 막고
|
||||
setContextMenu({ visible: true, x: e.pageX, y: e.pageY })
|
||||
// console.log(111, canvasProps)
|
||||
document.addEventListener('keyup', (e) => handleKeyup(e))
|
||||
canvasProps?.upperCanvasEl.removeEventListener('contextmenu', handleContextMenu) //한번 노출 후 이벤트 삭제
|
||||
}
|
||||
|
||||
|
||||
@ -81,11 +81,11 @@ export default function Table({ clsCode }) {
|
||||
{/* 번호 */}
|
||||
{board.rowNumber}
|
||||
</td>
|
||||
<td style={{ textAlign: 'center' }}>
|
||||
<td>
|
||||
{/* 제목 */}
|
||||
<div className="text-frame">
|
||||
<div className="text-overflow">{board.title}</div>
|
||||
{board.attachYn && <span className="clip"></span>}
|
||||
{board.attachYn === 'Y' && <span className="clip"></span>}
|
||||
</div>
|
||||
</td>
|
||||
<td className="al-c">
|
||||
|
||||
@ -3,8 +3,11 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useAxios } from '@/hooks/useAxios'
|
||||
import { handleFileDown } from '@/util/board-utils'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
|
||||
export default function BoardDetailModal({ noticeNo, setOpen }) {
|
||||
const { getMessage } = useMessage()
|
||||
|
||||
// api 조회 관련
|
||||
const { get } = useAxios()
|
||||
const [boardDetail, setBoardDetail] = useState({})
|
||||
@ -46,7 +49,7 @@ export default function BoardDetailModal({ noticeNo, setOpen }) {
|
||||
setOpen(false)
|
||||
}}
|
||||
>
|
||||
닫기
|
||||
{getMessage('board.sub.btn.close')}
|
||||
</button>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
@ -55,7 +58,7 @@ export default function BoardDetailModal({ noticeNo, setOpen }) {
|
||||
|
||||
{boardDetail.listFile && (
|
||||
<dl className="community_detail-file-wrap">
|
||||
<dt>첨부파일 목록</dt>
|
||||
<dt>{getMessage('board.sub.fileList')}</dt>
|
||||
{boardDetail.listFile.map((boardFile) => (
|
||||
<dd key={boardFile.encodeFileNo}>
|
||||
<button type="button" className="down" onClick={() => handleFileDown(boardFile)}>
|
||||
|
||||
@ -12,7 +12,7 @@ export const QLine = fabric.util.createClass(fabric.Line, {
|
||||
idx: 0,
|
||||
area: 0,
|
||||
children: [],
|
||||
initialize: function (points, options, canvas) {
|
||||
initialize: function (points, options, length = 0) {
|
||||
this.callSuper('initialize', points, { ...options, selectable: options.selectable ?? true })
|
||||
if (options.id) {
|
||||
this.id = options.id
|
||||
@ -27,7 +27,11 @@ export const QLine = fabric.util.createClass(fabric.Line, {
|
||||
|
||||
this.idx = options.idx ?? 0
|
||||
this.direction = options.direction ?? getDirectionByPoint({ x: this.x1, y: this.y1 }, { x: this.x2, y: this.y2 })
|
||||
this.setLength()
|
||||
if (length !== 0) {
|
||||
this.length = length
|
||||
} else {
|
||||
this.setLength()
|
||||
}
|
||||
|
||||
this.startPoint = { x: this.x1, y: this.y1 }
|
||||
this.endPoint = { x: this.x2, y: this.y2 }
|
||||
@ -148,7 +152,7 @@ export const QLine = fabric.util.createClass(fabric.Line, {
|
||||
|
||||
getLength() {
|
||||
//10배 곱해진 값 return
|
||||
return Number(this.length.toFixed(0) * 10)
|
||||
return Number(this.length.toFixed(2) * 10)
|
||||
},
|
||||
|
||||
setViewLengthText(bool) {
|
||||
|
||||
@ -172,7 +172,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
||||
addLengthText() {
|
||||
this.canvas
|
||||
?.getObjects()
|
||||
.filter((obj) => obj.name === 'lengthText' && obj.parent === this)
|
||||
.filter((obj) => obj.name === 'lengthText' && obj.parentId === this.id)
|
||||
.forEach((text) => {
|
||||
this.canvas.remove(text)
|
||||
})
|
||||
@ -183,7 +183,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
||||
const end = points[(i + 1) % points.length]
|
||||
const dx = end.x - start.x
|
||||
const dy = end.y - start.y
|
||||
const length = Number(Math.sqrt(dx * dx + dy * dy).toFixed(0)) * 10
|
||||
const length = Number(Math.sqrt(dx * dx + dy * dy).toFixed(2)) * 10
|
||||
|
||||
let midPoint
|
||||
|
||||
|
||||
@ -2,34 +2,29 @@
|
||||
|
||||
import { useEffect, useRef } from 'react'
|
||||
|
||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||
|
||||
import { useCanvas } from '@/hooks/useCanvas'
|
||||
import { useEvent } from '@/hooks/useEvent'
|
||||
import { usePlan } from '@/hooks/usePlan'
|
||||
import { useContextMenu } from '@/hooks/useContextMenu'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { currentObjectState } from '@/store/canvasAtom'
|
||||
import { currentMenuState, currentObjectState, modifiedPlanFlagState } from '@/store/canvasAtom'
|
||||
import { useCanvasEvent } from '@/hooks/useCanvasEvent'
|
||||
import QContextMenu from '@/components/common/context-menu/QContextMenu'
|
||||
import { useCanvasConfigInitialize } from '@/hooks/common/useCanvasConfigInitialize'
|
||||
|
||||
import { useCommonUtils } from '@/hooks/common/useCommonUtils'
|
||||
import { useObjectBatch } from '@/hooks/object/useObjectBatch'
|
||||
import { useSurfaceShapeBatch } from '@/hooks/surface/useSurfaceShapeBatch'
|
||||
import { MENU } from '@/common/common'
|
||||
import PanelBatchStatistics from '@/components/floor-plan/modal/panelBatch/PanelBatchStatistics'
|
||||
|
||||
export default function CanvasFrame({ plan }) {
|
||||
const canvasRef = useRef(null)
|
||||
const [modifiedPlanFlag, setModifiedPlanFlag] = useRecoilState(modifiedPlanFlagState)
|
||||
const { canvas } = useCanvas('canvas')
|
||||
const { handleZoomClear } = useCanvasEvent()
|
||||
const { testAlert } = useCommonUtils({})
|
||||
|
||||
const { contextMenu, currentContextMenu, setCurrentContextMenu, handleClick } = useContextMenu({
|
||||
externalFn: {
|
||||
handleZoomClear,
|
||||
},
|
||||
})
|
||||
const { checkCanvasObjectEvent, checkUnsavedCanvasPlan } = usePlan()
|
||||
const { canvasLoadInit } = useCanvasConfigInitialize()
|
||||
const currentObject = useRecoilValue(currentObjectState)
|
||||
|
||||
const { canvasLoadInit, gridInit } = useCanvasConfigInitialize()
|
||||
const currentMenu = useRecoilValue(currentMenuState)
|
||||
const { contextMenu, handleClick, handleKeyup } = useContextMenu()
|
||||
const { checkCanvasObjectEvent, resetModifiedPlans } = usePlan()
|
||||
useEvent()
|
||||
|
||||
const loadCanvas = () => {
|
||||
@ -37,42 +32,45 @@ export default function CanvasFrame({ plan }) {
|
||||
canvas?.clear() // 캔버스를 초기화합니다.
|
||||
if (plan?.canvasStatus) {
|
||||
canvas?.loadFromJSON(JSON.parse(plan.canvasStatus), function () {
|
||||
canvas?.renderAll() // 캔버스를 다시 그립니다.
|
||||
canvasLoadInit() //config된 상태로 캔버스 객체를 그린다
|
||||
canvas?.renderAll() // 캔버스를 다시 그립니다.
|
||||
})
|
||||
}
|
||||
gridInit()
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
loadCanvas()
|
||||
}, [plan, canvas])
|
||||
if (modifiedPlanFlag && plan?.id) {
|
||||
checkCanvasObjectEvent(plan.id)
|
||||
}
|
||||
}, [modifiedPlanFlag])
|
||||
|
||||
const onClickContextMenu = (index) => {}
|
||||
useEffect(() => {
|
||||
loadCanvas()
|
||||
resetModifiedPlans()
|
||||
}, [plan, canvas])
|
||||
|
||||
return (
|
||||
<div className="canvas-frame">
|
||||
<canvas ref={canvasRef} id="canvas" style={{ position: 'relative' }}></canvas>
|
||||
|
||||
<QContextMenu contextRef={canvasRef} canvasProps={canvas}>
|
||||
<QContextMenu contextRef={canvasRef} canvasProps={canvas} handleKeyup={handleKeyup}>
|
||||
{contextMenu.map((menus, index) => (
|
||||
<ul key={index}>
|
||||
{menus.map((menu) => (
|
||||
<li
|
||||
key={menu.id}
|
||||
onClick={(e) => {
|
||||
if (menu.fn) {
|
||||
menu.fn()
|
||||
}
|
||||
handleClick(e, menu)
|
||||
}}
|
||||
>
|
||||
<li key={menu.id} onClick={(e) => handleClick(e, menu)}>
|
||||
{menu.name}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
))}
|
||||
</QContextMenu>
|
||||
{[
|
||||
MENU.MODULE_CIRCUIT_SETTING.BASIC_SETTING,
|
||||
MENU.MODULE_CIRCUIT_SETTING.CIRCUIT_TRESTLE_SETTING,
|
||||
MENU.MODULE_CIRCUIT_SETTING.PLAN_ORIENTATION,
|
||||
].includes(currentMenu) && <PanelBatchStatistics />}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
'use client'
|
||||
|
||||
import { useContext, useEffect, useState } from 'react'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||
import CanvasFrame from './CanvasFrame'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { useSwal } from '@/hooks/useSwal'
|
||||
import { usePlan } from '@/hooks/usePlan'
|
||||
import { modifiedPlansState } from '@/store/canvasAtom'
|
||||
import { globalLocaleStore } from '@/store/localeAtom'
|
||||
import { SessionContext } from '@/app/SessionProvider'
|
||||
|
||||
@ -13,11 +14,12 @@ export default function CanvasLayout(props) {
|
||||
const { menuNumber } = props
|
||||
const { session } = useContext(SessionContext)
|
||||
const [objectNo, setObjectNo] = useState('test123240822001') // 이후 삭제 필요
|
||||
const [modifiedPlans, setModifiedPlans] = useRecoilState(modifiedPlansState) // 변경된 canvas plan
|
||||
const globalLocaleState = useRecoilValue(globalLocaleStore)
|
||||
|
||||
const { getMessage } = useMessage()
|
||||
const { swalFire } = useSwal()
|
||||
const { plans, modifiedPlans, loadCanvasPlanData, handleCurrentPlan, handleAddPlan, handleDeletePlan } = usePlan()
|
||||
const { plans, loadCanvasPlanData, handleCurrentPlan, handleAddPlan, handleDeletePlan } = usePlan()
|
||||
|
||||
useEffect(() => {
|
||||
loadCanvasPlanData(session.userId, objectNo)
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import WithDraggable from '@/components/common/draggable/WithDraggable'
|
||||
import { globalPitchState } from '@/store/canvasAtom'
|
||||
import { globalPitchState, pitchSelector, pitchTextSelector } from '@/store/canvasAtom'
|
||||
import { useRecoilState } from 'recoil'
|
||||
import { useRef } from 'react'
|
||||
import { usePopup } from '@/hooks/usePopup'
|
||||
@ -8,7 +8,8 @@ import { usePopup } from '@/hooks/usePopup'
|
||||
export default function Slope({ id, pos = { x: 50, y: 230 } }) {
|
||||
const { getMessage } = useMessage()
|
||||
const { closePopup } = usePopup()
|
||||
const [globalPitch, setGlobalPitch] = useRecoilState(globalPitchState)
|
||||
const [globalPitch, setGlobalPitch] = useRecoilState(pitchSelector)
|
||||
const pitchText = useRecoilState(pitchTextSelector)
|
||||
const inputRef = useRef()
|
||||
|
||||
return (
|
||||
@ -29,7 +30,7 @@ export default function Slope({ id, pos = { x: 50, y: 230 } }) {
|
||||
<div className="input-grid mr5">
|
||||
<input type="text" className="input-origin block" defaultValue={globalPitch} ref={inputRef} />
|
||||
</div>
|
||||
<span className="thin">{getMessage('size.angle')}</span>
|
||||
<span className="thin">{pitchText}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid-btn-wrap">
|
||||
|
||||
@ -11,12 +11,13 @@ export default function EavesGableEdit({ id, pos = { x: 50, y: 230 } }) {
|
||||
const { getMessage } = useMessage()
|
||||
const { closePopup } = usePopup()
|
||||
|
||||
const { type, setType, buttonMenu, TYPES, pitchRef, offsetRef, widthRef, radioTypeRef } = useEavesGableEdit(id)
|
||||
const { type, setType, buttonMenu, TYPES, pitchRef, offsetRef, widthRef, radioTypeRef, pitchText } = useEavesGableEdit(id)
|
||||
const eavesProps = {
|
||||
pitchRef,
|
||||
offsetRef,
|
||||
widthRef,
|
||||
radioTypeRef,
|
||||
pitchText,
|
||||
}
|
||||
|
||||
const gableProps = {
|
||||
@ -24,6 +25,7 @@ export default function EavesGableEdit({ id, pos = { x: 50, y: 230 } }) {
|
||||
offsetRef,
|
||||
widthRef,
|
||||
radioTypeRef,
|
||||
pitchText,
|
||||
}
|
||||
|
||||
const wallMergeProps = {
|
||||
|
||||
@ -1,14 +1,17 @@
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import Image from 'next/image'
|
||||
import { useState } from 'react'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { ANGLE_TYPE, currentAngleTypeSelector } from '@/store/canvasAtom'
|
||||
|
||||
export default function Eaves({ pitchRef, offsetRef, widthRef, radioTypeRef }) {
|
||||
export default function Eaves({ pitchRef, offsetRef, widthRef, radioTypeRef, pitchText }) {
|
||||
const { getMessage } = useMessage()
|
||||
const [type, setType] = useState('1')
|
||||
const onChange = (e) => {
|
||||
setType(e.target.value)
|
||||
radioTypeRef.current = e.target.value
|
||||
}
|
||||
const currentAngleType = useRecoilValue(currentAngleTypeSelector)
|
||||
return (
|
||||
<>
|
||||
<div className="outline-wrap">
|
||||
@ -17,9 +20,9 @@ export default function Eaves({ pitchRef, offsetRef, widthRef, radioTypeRef }) {
|
||||
{getMessage('slope')}
|
||||
</span>
|
||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||
<input type="number" className="input-origin block" defaultValue={4} ref={pitchRef} />
|
||||
<input type="number" className="input-origin block" defaultValue={currentAngleType === ANGLE_TYPE.SLOPE ? 4 : 21.8} ref={pitchRef} />
|
||||
</div>
|
||||
<span className="thin">寸</span>
|
||||
<span className="thin">{pitchText}</span>
|
||||
</div>
|
||||
<div className="outline-form">
|
||||
<span className="mr10" style={{ width: '24px' }}>
|
||||
|
||||
@ -1,14 +1,18 @@
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import Image from 'next/image'
|
||||
import { useState } from 'react'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { ANGLE_TYPE, currentAngleTypeSelector } from '@/store/canvasAtom'
|
||||
|
||||
export default function Gable({ pitchRef, offsetRef, widthRef, radioTypeRef }) {
|
||||
export default function Gable({ pitchRef, offsetRef, widthRef, radioTypeRef, pitchText }) {
|
||||
const { getMessage } = useMessage()
|
||||
const [type, setType] = useState('1')
|
||||
const onChange = (e) => {
|
||||
setType(e.target.value)
|
||||
radioTypeRef.current = e.target.value
|
||||
}
|
||||
const currentAngleType = useRecoilValue(currentAngleTypeSelector)
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="outline-wrap">
|
||||
@ -57,9 +61,15 @@ export default function Gable({ pitchRef, offsetRef, widthRef, radioTypeRef }) {
|
||||
{getMessage('slope')}
|
||||
</span>
|
||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||
<input type="text" className="input-origin block" defaultValue={4.5} ref={pitchRef} readOnly={type === '1'} />
|
||||
<input
|
||||
type="text"
|
||||
className="input-origin block"
|
||||
defaultValue={currentAngleType === ANGLE_TYPE.SLOPE ? 4.5 : 20}
|
||||
ref={pitchRef}
|
||||
readOnly={type === '1'}
|
||||
/>
|
||||
</div>
|
||||
<span className="thin">寸</span>
|
||||
<span className="thin">{pitchText}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="eaves-keraba-td">
|
||||
|
||||
@ -1,24 +1,68 @@
|
||||
import WithDraggable from '@/components/common/draggable/withDraggable'
|
||||
import { useState } from 'react'
|
||||
import { useEffect, useState } from 'react'
|
||||
import QSelectBox from '@/components/common/select/QSelectBox'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { contextPopupPositionState } from '@/store/popupAtom'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { usePopup } from '@/hooks/usePopup'
|
||||
|
||||
const SelectOption01 = [{ name: 'M' }, { name: 'M' }, { name: 'M' }, { name: 'M' }]
|
||||
|
||||
export default function FlowDirectionSetting(props) {
|
||||
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
||||
const { id, pos = contextPopupPosition } = props
|
||||
const { id, pos = contextPopupPosition, target } = props
|
||||
const { getMessage } = useMessage()
|
||||
const { closePopup } = usePopup()
|
||||
const [compasDeg, setCompasDeg] = useState(0)
|
||||
const [compasDeg, setCompasDeg] = useState(360)
|
||||
const orientations = [
|
||||
{ name: `${getMessage('commons.south')}`, value: 360 },
|
||||
{ name: `${getMessage('commons.south')}${getMessage('commons.east')}`, value: 315 },
|
||||
{ name: `${getMessage('commons.south')}${getMessage('commons.west')}`, value: 45 },
|
||||
{ name: `${getMessage('commons.east')}`, value: 270 },
|
||||
{ name: `${getMessage('commons.west')}`, value: 90 },
|
||||
{ name: `${getMessage('commons.north')}${getMessage('commons.east')}`, value: 225 },
|
||||
{ name: `${getMessage('commons.north')}${getMessage('commons.west')}`, value: 135 },
|
||||
{ name: `${getMessage('commons.north')}`, value: 180 },
|
||||
]
|
||||
const [selectedOrientation, setSelectedOrientation] = useState(orientations[0])
|
||||
const [type, setType] = useState('0')
|
||||
useEffect(() => {
|
||||
if (target?.angle === 0) {
|
||||
setCompasDeg(360)
|
||||
} else {
|
||||
setCompasDeg(target?.angle ?? 360)
|
||||
}
|
||||
}, [])
|
||||
useEffect(() => {
|
||||
if (type === '0') {
|
||||
setCompasDeg(selectedOrientation.value)
|
||||
}
|
||||
}, [selectedOrientation])
|
||||
|
||||
useEffect(() => {
|
||||
if (type === '1') {
|
||||
if ([15, 345, 360].includes(compasDeg)) {
|
||||
setSelectedOrientation(orientations[0])
|
||||
} else if ([30, 45, 60].includes(compasDeg)) {
|
||||
setSelectedOrientation(orientations[2])
|
||||
} else if ([75, 90, 105].includes(compasDeg)) {
|
||||
setSelectedOrientation(orientations[4])
|
||||
} else if ([120, 135, 150].includes(compasDeg)) {
|
||||
setSelectedOrientation(orientations[6])
|
||||
} else if ([165, 180, 195].includes(compasDeg)) {
|
||||
setSelectedOrientation(orientations[7])
|
||||
} else if ([210, 225, 240].includes(compasDeg)) {
|
||||
setSelectedOrientation(orientations[5])
|
||||
} else if ([255, 270, 285].includes(compasDeg)) {
|
||||
setSelectedOrientation(orientations[3])
|
||||
} else if ([300, 315, 330].includes(compasDeg)) {
|
||||
setSelectedOrientation(orientations[1])
|
||||
}
|
||||
}
|
||||
}, [compasDeg])
|
||||
return (
|
||||
<WithDraggable isShow={true} pos={pos}>
|
||||
<div className={`modal-pop-wrap lx`}>
|
||||
<div className="modal-head">
|
||||
<h1 className="title">面フローの設定 </h1>
|
||||
<h1 className="title">{getMessage('modal.shape.flow.direction.setting')} </h1>
|
||||
<button className="modal-close" onClick={() => closePopup(id)}>
|
||||
닫기
|
||||
</button>
|
||||
@ -26,113 +70,59 @@ export default function FlowDirectionSetting(props) {
|
||||
<div className="modal-body">
|
||||
<div className="drawing-flow-wrap">
|
||||
<div className="discrimination-box">
|
||||
<div className="discrimination-tit mb15">流れ方向の設定</div>
|
||||
<div className="guide">流れ方向を選択してください。</div>
|
||||
<div className="discrimination-tit mb15">{getMessage('modal.flow.direction.setting')}</div>
|
||||
<div className="guide">{getMessage('modal.flow.direction.setting.info')}</div>
|
||||
<div className="object-direction-wrap">
|
||||
<div className="plane-direction">
|
||||
<span className="top">北</span>
|
||||
<span className="right">ドン</span>
|
||||
<span className="bottom">南</span>
|
||||
<span className="left">立つ</span>
|
||||
<span className="top">{getMessage('commons.north')}</span>
|
||||
<button className="plane-btn up"></button>
|
||||
<span className="right">{getMessage('commons.east')}</span>
|
||||
<button className="plane-btn right"></button>
|
||||
<span className="bottom">{getMessage('commons.south')}</span>
|
||||
<button className="plane-btn down act"></button>
|
||||
<span className="left">{getMessage('commons.west')}</span>
|
||||
<button className="plane-btn left"></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="discrimination-box">
|
||||
<div className="discrimination-tit mb15">方位設定</div>
|
||||
<div className="guide">シミュレーション計算の方向を指定します。面が向いている方位を選択してください。</div>
|
||||
<div className="discrimination-tit mb15">{getMessage('modal.module.basic.setting.orientation.setting')}</div>
|
||||
<div className="guide">{getMessage('modal.shape.flow.direction.setting.orientation.setting.info')}</div>
|
||||
<div className="mb-box">
|
||||
<div className="d-check-radio pop mb15">
|
||||
<input type="radio" name="radio01" id="ra01" />
|
||||
<label htmlFor="ra01">8方位に選ぶ</label>
|
||||
<input type="radio" name="radio01" id="ra01" value={0} checked={type === '0'} onChange={(e) => setType(e.target.value)} />
|
||||
<label htmlFor="ra01">{getMessage('modal.shape.flow.direction.setting.orientation.8')}</label>
|
||||
</div>
|
||||
<div className="grid-select ">
|
||||
<QSelectBox title={'M'} options={SelectOption01} />
|
||||
<QSelectBox title={''} options={orientations} value={selectedOrientation} onChange={(e) => setSelectedOrientation(e)} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-box">
|
||||
<div className="d-check-radio pop">
|
||||
<input type="radio" name="radio02" id="ra02" />
|
||||
<label htmlFor="ra02">24方位から選択する (表記は8方位です。)</label>
|
||||
<input type="radio" name="radio01" id="ra02" value={1} checked={type === '1'} onChange={(e) => setType(e.target.value)} />
|
||||
<label htmlFor="ra02">{getMessage('modal.shape.flow.direction.setting.orientation.24')}</label>
|
||||
</div>
|
||||
</div>
|
||||
<div className="compas-box">
|
||||
<div className="compas-box-inner">
|
||||
<div className={`circle ${compasDeg === 180 ? 'act' : ''}`} onClick={() => setCompasDeg(180)}>
|
||||
<i>13</i>
|
||||
</div>
|
||||
<div className={`circle ${compasDeg === 195 ? 'act' : ''}`} onClick={() => setCompasDeg(195)}>
|
||||
<i>12</i>
|
||||
</div>
|
||||
<div className={`circle ${compasDeg === 210 ? 'act' : ''}`} onClick={() => setCompasDeg(210)}>
|
||||
<i>11</i>
|
||||
</div>
|
||||
<div className={`circle ${compasDeg === 225 ? 'act' : ''}`} onClick={() => setCompasDeg(225)}>
|
||||
<i>10</i>
|
||||
</div>
|
||||
<div className={`circle ${compasDeg === 240 ? 'act' : ''}`} onClick={() => setCompasDeg(240)}>
|
||||
<i>9</i>
|
||||
</div>
|
||||
<div className={`circle ${compasDeg === 255 ? 'act' : ''}`} onClick={() => setCompasDeg(255)}>
|
||||
<i>8</i>
|
||||
</div>
|
||||
<div className={`circle ${compasDeg === 270 ? 'act' : ''}`} onClick={() => setCompasDeg(270)}>
|
||||
<i>7</i>
|
||||
</div>
|
||||
<div className={`circle ${compasDeg === 285 ? 'act' : ''}`} onClick={() => setCompasDeg(285)}>
|
||||
<i>6</i>
|
||||
</div>
|
||||
<div className={`circle ${compasDeg === 300 ? 'act' : ''}`} onClick={() => setCompasDeg(300)}>
|
||||
<i>5</i>
|
||||
</div>
|
||||
<div className={`circle ${compasDeg === 315 ? 'act' : ''}`} onClick={() => setCompasDeg(315)}>
|
||||
<i>4</i>
|
||||
</div>
|
||||
<div className={`circle ${compasDeg === 330 ? 'act' : ''}`} onClick={() => setCompasDeg(330)}>
|
||||
<i>3</i>
|
||||
</div>
|
||||
<div className={`circle ${compasDeg === 345 ? 'act' : ''}`} onClick={() => setCompasDeg(345)}>
|
||||
<i>2</i>
|
||||
</div>
|
||||
<div className={`circle ${compasDeg === 0 ? 'act' : ''}`} onClick={() => setCompasDeg(0)}>
|
||||
<i>1</i>
|
||||
</div>
|
||||
<div className={`circle ${compasDeg === 15 ? 'act' : ''}`} onClick={() => setCompasDeg(15)}>
|
||||
<i>24</i>
|
||||
</div>
|
||||
<div className={`circle ${compasDeg === 30 ? 'act' : ''}`} onClick={() => setCompasDeg(30)}>
|
||||
<i>23</i>
|
||||
</div>
|
||||
<div className={`circle ${compasDeg === 45 ? 'act' : ''}`} onClick={() => setCompasDeg(45)}>
|
||||
<i>22</i>
|
||||
</div>
|
||||
<div className={`circle ${compasDeg === 60 ? 'act' : ''}`} onClick={() => setCompasDeg(60)}>
|
||||
<i>21</i>
|
||||
</div>
|
||||
<div className={`circle ${compasDeg === 75 ? 'act' : ''}`} onClick={() => setCompasDeg(75)}>
|
||||
<i>20</i>
|
||||
</div>
|
||||
<div className={`circle ${compasDeg === 90 ? 'act' : ''}`} onClick={() => setCompasDeg(90)}>
|
||||
<i>19</i>
|
||||
</div>
|
||||
<div className={`circle ${compasDeg === 105 ? 'act' : ''}`} onClick={() => setCompasDeg(105)}>
|
||||
<i>18</i>
|
||||
</div>
|
||||
<div className={`circle ${compasDeg === 120 ? 'act' : ''}`} onClick={() => setCompasDeg(120)}>
|
||||
<i>17</i>
|
||||
</div>
|
||||
<div className={`circle ${compasDeg === 135 ? 'act' : ''}`} onClick={() => setCompasDeg(135)}>
|
||||
<i>16</i>
|
||||
</div>
|
||||
<div className={`circle ${compasDeg === 150 ? 'act' : ''}`} onClick={() => setCompasDeg(150)}>
|
||||
<i>15</i>
|
||||
</div>
|
||||
<div className={`circle ${compasDeg === 165 ? 'act' : ''}`} onClick={() => setCompasDeg(165)}>
|
||||
<i>14</i>
|
||||
</div>
|
||||
{Array.from({ length: 180 / 15 + 1 }).map((dot, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={`circle ${compasDeg === 15 * (12 + index) ? 'act' : ''}`}
|
||||
onClick={() => setCompasDeg(15 * (12 + index))}
|
||||
>
|
||||
<i>{13 - index}</i>
|
||||
</div>
|
||||
))}
|
||||
{Array.from({ length: 180 / 15 - 1 }).map((dot, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={`circle ${compasDeg === 15 * (index + 1) ? 'act' : ''}`}
|
||||
onClick={() => setCompasDeg(15 * (index + 1))}
|
||||
>
|
||||
<i>{24 - index}</i>
|
||||
</div>
|
||||
))}
|
||||
<div className="compas">
|
||||
<div className="compas-arr" style={{ transform: `rotate(${compasDeg}deg)` }}></div>
|
||||
</div>
|
||||
@ -141,7 +131,7 @@ export default function FlowDirectionSetting(props) {
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid-btn-wrap">
|
||||
<button className="btn-frame modal act">保存</button>
|
||||
<button className="btn-frame modal act">{getMessage('modal.common.save')}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -3,99 +3,70 @@ import { useRecoilValue } from 'recoil'
|
||||
import { contextPopupPositionState } from '@/store/popupAtom'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { usePopup } from '@/hooks/usePopup'
|
||||
import { useState } from 'react'
|
||||
|
||||
export default function LinePropertySetting(props) {
|
||||
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
||||
const { id, pos = contextPopupPosition } = props
|
||||
const { getMessage } = useMessage()
|
||||
const { closePopup } = usePopup()
|
||||
const properties = [
|
||||
{ name: getMessage('eaves.line'), value: 'eaves' },
|
||||
{ name: getMessage('ridge'), value: 'ridge' },
|
||||
{ name: getMessage('oneside.flow.ridge'), value: 'onesideFlowRidge' },
|
||||
{ name: getMessage('gable'), value: 'gable' },
|
||||
{ name: getMessage('gable.left'), value: 'gableLeft' },
|
||||
{ name: getMessage('gable.right'), value: 'gableRight' },
|
||||
{ name: getMessage('yosemune'), value: 'yosemune' },
|
||||
{ name: getMessage('valley'), value: 'valley' },
|
||||
{ name: getMessage('l.abandon.valley'), value: 'lAbandonValley' },
|
||||
{ name: getMessage('mansard'), value: 'mansard' },
|
||||
{ name: getMessage('wall.merge'), value: 'wallCollection' },
|
||||
{ name: getMessage('wall.merge.type'), value: 'wallCollectionType' },
|
||||
{ name: getMessage('wall.merge.flow'), value: 'wallCollectionFlow' },
|
||||
{ name: getMessage('wall.merge.flow.left'), value: 'wallCollectionFlowLeft' },
|
||||
{ name: getMessage('wall.merge.flow.right'), value: 'wallCollectionFlowRight' },
|
||||
{ name: getMessage('no.setting'), value: 'noSetting' },
|
||||
]
|
||||
const [selectedProperty, setSelectedProperty] = useState(null)
|
||||
return (
|
||||
<WithDraggable isShow={true} pos={pos}>
|
||||
<div className={`modal-pop-wrap r`}>
|
||||
<div className={`modal-pop-wrap r mount`}>
|
||||
<div className="modal-head">
|
||||
<h1 className="title">各辺属性の変更 </h1>
|
||||
<h1 className="title">{getMessage('contextmenu.line.property.edit')} </h1>
|
||||
<button className="modal-close" onClick={() => closePopup(id)}>
|
||||
닫기
|
||||
</button>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<div className="guide">
|
||||
<span className="mb10">属性を変更する辺を選択してください。</span>
|
||||
<span>選択した値 [龍丸]</span>
|
||||
<span className="mb10">{getMessage('modal.line.property.edit.info')}</span>
|
||||
<span>
|
||||
{getMessage('modal.line.property.edit.selected')} [ {selectedProperty?.name} ]
|
||||
</span>
|
||||
</div>
|
||||
<div className="properties-setting-wrap outer">
|
||||
<div className="setting-tit">設定</div>
|
||||
<div className="setting-tit">{getMessage('setting')}</div>
|
||||
<div className="outline-wrap">
|
||||
<div className="radio-grid-wrap">
|
||||
<div className="d-check-radio pop">
|
||||
<input type="radio" name="radio01" id="ra01" />
|
||||
<label htmlFor="ra01">軒先</label>
|
||||
</div>
|
||||
<div className="d-check-radio pop">
|
||||
<input type="radio" name="radio01" id="ra02" />
|
||||
<label htmlFor="ra02">ケラバ</label>
|
||||
</div>
|
||||
<div className="d-check-radio pop">
|
||||
<input type="radio" name="radio01" id="ra03" />
|
||||
<label htmlFor="ra03">龍丸</label>
|
||||
</div>
|
||||
<div className="d-check-radio pop">
|
||||
<input type="radio" name="radio01" id="ra04" />
|
||||
<label htmlFor="ra04">ケラバ左</label>
|
||||
</div>
|
||||
<div className="d-check-radio pop">
|
||||
<input type="radio" name="radio01" id="ra05" />
|
||||
<label htmlFor="ra05">ヨセムネ</label>
|
||||
</div>
|
||||
<div className="d-check-radio pop">
|
||||
<input type="radio" name="radio01" id="ra06" />
|
||||
<label htmlFor="ra06">ケラバ右</label>
|
||||
</div>
|
||||
<div className="d-check-radio pop">
|
||||
<input type="radio" name="radio01" id="ra07" />
|
||||
<label htmlFor="ra07">片側の流れ</label>
|
||||
</div>
|
||||
<div className="d-check-radio pop">
|
||||
<input type="radio" name="radio01" id="ra08" />
|
||||
<label htmlFor="ra08">谷</label>
|
||||
</div>
|
||||
<div className="d-check-radio pop">
|
||||
<input type="radio" name="radio01" id="ra09" />
|
||||
<label htmlFor="ra09">壁取り</label>
|
||||
</div>
|
||||
<div className="d-check-radio pop">
|
||||
<input type="radio" name="radio01" id="ra10" />
|
||||
<label htmlFor="ra10">Lの捨て渓谷</label>
|
||||
</div>
|
||||
<div className="d-check-radio pop">
|
||||
<input type="radio" name="radio01" id="ra11" />
|
||||
<label htmlFor="ra11">壁取り(型)</label>
|
||||
</div>
|
||||
<div className="d-check-radio pop">
|
||||
<input type="radio" name="radio01" id="ra12" />
|
||||
<label htmlFor="ra12">マンサード</label>
|
||||
</div>
|
||||
<div className="d-check-radio pop">
|
||||
<input type="radio" name="radio01" id="ra13" />
|
||||
<label htmlFor="ra13">壁取合(流れ)</label>
|
||||
</div>
|
||||
<div className="d-check-radio pop">
|
||||
<input type="radio" name="radio01" id="ra14" />
|
||||
<label htmlFor="ra14">設定なし</label>
|
||||
</div>
|
||||
<div className="d-check-radio pop">
|
||||
<input type="radio" name="radio01" id="ra15" />
|
||||
<label htmlFor="ra15">壁取合(流れ左)</label>
|
||||
</div>
|
||||
<div className="d-check-radio pop">
|
||||
<input type="radio" name="radio01" id="ra16" />
|
||||
<label htmlFor="ra16">壁取り(流れ右)</label>
|
||||
</div>
|
||||
{properties.map((property, index) => {
|
||||
return (
|
||||
<div className="d-check-radio pop" key={index}>
|
||||
<input
|
||||
type="radio"
|
||||
name="radio01"
|
||||
id={'ra' + (index + 1 >= 10 ? index + 1 : `0${index + 1}`)}
|
||||
onChange={(e) => setSelectedProperty(property)}
|
||||
/>
|
||||
<label htmlFor={'ra' + (index + 1 > 10 ? index + 1 : `0${index + 1}`)}>{property.name}</label>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid-btn-wrap">
|
||||
<button className="btn-frame modal act">保存</button>
|
||||
<button className="btn-frame modal act">{getMessage('modal.common.save')}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -13,26 +13,26 @@ export default function DormerOffset(props) {
|
||||
<WithDraggable isShow={true} pos={pos}>
|
||||
<div className={`modal-pop-wrap xm`}>
|
||||
<div className="modal-head">
|
||||
<h1 className="title">도머 오프셋 </h1>
|
||||
<h1 className="title">{getMessage('contextmenu.dormer.offset')}</h1>
|
||||
<button className="modal-close" onClick={() => closePopup(id)}>
|
||||
닫기
|
||||
</button>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<div className="grid-option-tit">移動する方向を入力してください</div>
|
||||
<div className="grid-option-tit">{getMessage('modal.dormer.offset.info')}</div>
|
||||
<div className="grid-option-wrap">
|
||||
<div className="grid-option-box">
|
||||
<div className="move-form">
|
||||
<p className="mb5">長さ</p>
|
||||
<p className="mb5">{getMessage('length')}</p>
|
||||
<div className="input-move-wrap mb5">
|
||||
<div className="input-move">
|
||||
<input type="text" className="input-origin" defaultValue={910} />
|
||||
<input type="text" className="input-origin" defaultValue={0} />
|
||||
</div>
|
||||
<span>mm</span>
|
||||
</div>
|
||||
<div className="input-move-wrap">
|
||||
<div className="input-move">
|
||||
<input type="text" className="input-origin" defaultValue={910} />
|
||||
<input type="text" className="input-origin" defaultValue={0} />
|
||||
</div>
|
||||
<span>mm</span>
|
||||
</div>
|
||||
@ -46,7 +46,7 @@ export default function DormerOffset(props) {
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid-btn-wrap">
|
||||
<button className="btn-frame modal act">保存</button>
|
||||
<button className="btn-frame modal act">{getMessage('modal.common.save')}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { canvasState } from '@/store/canvasAtom'
|
||||
import WithDraggable from '@/components/common/draggable/WithDraggable'
|
||||
import { usePopup } from '@/hooks/usePopup'
|
||||
import { contextPopupPositionState } from '@/store/popupAtom'
|
||||
@ -11,16 +10,15 @@ import { useState } from 'react'
|
||||
export default function SizeSetting(props) {
|
||||
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
||||
const [settingTarget, setSettingTarget] = useState(1)
|
||||
const { id, pos = contextPopupPosition } = props
|
||||
const { id, pos = contextPopupPosition, target } = props
|
||||
const { getMessage } = useMessage()
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
const { closePopup } = usePopup()
|
||||
|
||||
return (
|
||||
<WithDraggable isShow={true} pos={pos}>
|
||||
<div className={`modal-pop-wrap ssm`}>
|
||||
<div className="modal-head">
|
||||
<h1 className="title">サイズ変更 </h1>
|
||||
<h1 className="title">{getMessage('modal.size.setting')} </h1>
|
||||
<button className="modal-close" onClick={() => closePopup(id)}>
|
||||
닫기
|
||||
</button>
|
||||
@ -30,11 +28,11 @@ export default function SizeSetting(props) {
|
||||
<div className="size-option-top">
|
||||
<div className="size-option-wrap">
|
||||
<div className="size-option mb5">
|
||||
<input type="text" className="input-origin mr5" defaultValue={1000} readOnly />
|
||||
<input type="text" className="input-origin mr5" defaultValue={1000} readOnly value={target?.width * 10 * 2} />
|
||||
<span className="normal-font">mm</span>
|
||||
</div>
|
||||
<div className="size-option">
|
||||
<input type="text" className="input-origin mr5" defaultValue={1000} />
|
||||
<input type="text" className="input-origin mr5" defaultValue={1000} value={target?.width * 10 * 2} />
|
||||
<span className="normal-font">mm</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -43,11 +41,11 @@ export default function SizeSetting(props) {
|
||||
<div className="size-option-side">
|
||||
<div className="size-option-wrap">
|
||||
<div className="size-option mb5">
|
||||
<input type="text" className="input-origin mr5" defaultValue={1000} readOnly />
|
||||
<input type="text" className="input-origin mr5" defaultValue={1000} readOnly value={target?.height * 10} />
|
||||
<span className="normal-font">mm</span>
|
||||
</div>
|
||||
<div className="size-option">
|
||||
<input type="text" className="input-origin mr5" defaultValue={1000} />
|
||||
<input type="text" className="input-origin mr5" defaultValue={1000} value={target?.height * 10} />
|
||||
<span className="normal-font">mm</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -62,7 +60,7 @@ export default function SizeSetting(props) {
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid-btn-wrap">
|
||||
<button className="btn-frame modal act">作成</button>
|
||||
<button className="btn-frame modal act">{getMessage('write')}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -0,0 +1,37 @@
|
||||
import WithDraggable from '@/components/common/draggable/withDraggable'
|
||||
import { useState } from 'react'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
|
||||
export default function PanelBatchStatistics() {
|
||||
const { getMessage } = useMessage()
|
||||
const [isFold, setIsFold] = useState(false)
|
||||
const [pos, setPos] = useState({
|
||||
x: 0,
|
||||
y: 30,
|
||||
})
|
||||
|
||||
return (
|
||||
<WithDraggable isShow={true} handle=".penal-wrap" pos={pos}>
|
||||
<div className={`penal-wrap ${!isFold ? 'act' : ''}`}>
|
||||
<h2>{getMessage('modal.panel.batch.statistic')}</h2>
|
||||
<button className="penal-arr" onClick={() => setIsFold(!isFold)}></button>
|
||||
<div className="penal-table-wrap">
|
||||
<table className="penal-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{getMessage('modal.panel.batch.statistic.roof.shape')}</th>
|
||||
<th>{getMessage('modal.panel.batch.statistic.power.generation.amount')} (kW)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{getMessage('modal.panel.batch.statistic.total')}</td>
|
||||
<td className="al-r">0.000</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</WithDraggable>
|
||||
)
|
||||
}
|
||||
@ -8,6 +8,7 @@ import { useMessage } from '@/hooks/useMessage'
|
||||
import { useAxios } from '@/hooks/useAxios'
|
||||
import { useSwal } from '@/hooks/useSwal'
|
||||
import { usePopup } from '@/hooks/usePopup'
|
||||
import { basicSettingState } from '@/store/settingAtom'
|
||||
|
||||
export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, setShowPlaceShapeModal }) {
|
||||
const [objectNo, setObjectNo] = useState('test123241008001') // 후에 삭제 필요
|
||||
@ -16,22 +17,7 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, set
|
||||
const [selectedRoofMaterial, setSelectedRoofMaterial] = useState(1)
|
||||
const [canvasSetting, setCanvasSetting] = useRecoilState(canvasSettingState)
|
||||
const { closePopup } = usePopup()
|
||||
const [basicSetting, setBasicSettings] = useState({
|
||||
roofSizeSet: 1,
|
||||
roofAngleSet: 'slope',
|
||||
roofs: [
|
||||
{
|
||||
roofApply: true,
|
||||
roofSeq: 1,
|
||||
roofType: 1,
|
||||
roofWidth: 200,
|
||||
roofHeight: 200,
|
||||
roofHajebichi: 200,
|
||||
roofGap: 0,
|
||||
roofLayout: 'parallel',
|
||||
},
|
||||
],
|
||||
})
|
||||
const [basicSetting, setBasicSettings] = useRecoilState(basicSettingState)
|
||||
|
||||
const { getMessage } = useMessage()
|
||||
const { get, post } = useAxios()
|
||||
|
||||
@ -7,18 +7,21 @@ import { useRoofShapePassivitySetting } from '@/hooks/roofcover/useRoofShapePass
|
||||
import { usePopup } from '@/hooks/usePopup'
|
||||
|
||||
export default function RoofShapePassivitySetting({ id, pos = { x: 50, y: 230 } }) {
|
||||
const { handleSave, handleConfirm, handleRollback, buttons, type, setType, TYPES, offsetRef, pitchRef } = useRoofShapePassivitySetting(id)
|
||||
const { handleSave, handleConfirm, handleRollback, buttons, type, setType, TYPES, offsetRef, pitchRef, pitchText } =
|
||||
useRoofShapePassivitySetting(id)
|
||||
const { getMessage } = useMessage()
|
||||
const { closePopup } = usePopup()
|
||||
|
||||
const eavesProps = {
|
||||
offsetRef,
|
||||
pitchRef,
|
||||
pitchText,
|
||||
}
|
||||
|
||||
const gableProps = {
|
||||
offsetRef,
|
||||
pitchRef,
|
||||
pitchText,
|
||||
}
|
||||
|
||||
const shedProps = {
|
||||
|
||||
@ -38,11 +38,12 @@ export default function RoofShapeSetting({ id, pos = { x: 50, y: 230 } }) {
|
||||
buttonMenu,
|
||||
handleConfirm,
|
||||
handleRollBack,
|
||||
pitchText,
|
||||
} = useRoofShapeSetting(id)
|
||||
const { closePopup } = usePopup()
|
||||
|
||||
const ridgeProps = { pitch, setPitch, eavesOffset, setEavesOffset }
|
||||
const patternProps = { pitch, setPitch, eavesOffset, setEavesOffset, gableOffset, setGableOffset }
|
||||
const ridgeProps = { pitch, setPitch, eavesOffset, setEavesOffset, pitchText }
|
||||
const patternProps = { pitch, setPitch, eavesOffset, setEavesOffset, gableOffset, setGableOffset, pitchText }
|
||||
const sideProps = {
|
||||
pitch,
|
||||
setPitch,
|
||||
@ -67,6 +68,7 @@ export default function RoofShapeSetting({ id, pos = { x: 50, y: 230 } }) {
|
||||
buttonMenu,
|
||||
handleConfirm,
|
||||
handleRollBack,
|
||||
pitchText,
|
||||
}
|
||||
|
||||
const directionProps = {
|
||||
@ -78,6 +80,7 @@ export default function RoofShapeSetting({ id, pos = { x: 50, y: 230 } }) {
|
||||
setGableOffset,
|
||||
shedWidth,
|
||||
setShedWidth,
|
||||
pitchText,
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { ANGLE_TYPE, currentAngleTypeSelector } from '@/store/canvasAtom'
|
||||
|
||||
export default function Eaves({ offsetRef, pitchRef }) {
|
||||
export default function Eaves({ offsetRef, pitchRef, pitchText }) {
|
||||
const { getMessage } = useMessage()
|
||||
const currentAngleType = useRecoilValue(currentAngleTypeSelector)
|
||||
return (
|
||||
<>
|
||||
<div className="outline-form mb10">
|
||||
@ -9,9 +12,9 @@ export default function Eaves({ offsetRef, pitchRef }) {
|
||||
{getMessage('slope')}
|
||||
</span>
|
||||
<div className="input-grid mr5">
|
||||
<input type="text" className="input-origin block" defaultValue={4} ref={pitchRef} />
|
||||
<input type="text" className="input-origin block" defaultValue={currentAngleType === ANGLE_TYPE.SLOPE ? 4 : 21.8} ref={pitchRef} />
|
||||
</div>
|
||||
<span className="thin">寸</span>
|
||||
<span className="thin">{pitchText}</span>
|
||||
</div>
|
||||
<div className="outline-form mb10">
|
||||
<span className="mr10" style={{ width: '63px' }}>
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { ANGLE_TYPE, currentAngleTypeSelector } from '@/store/canvasAtom'
|
||||
|
||||
export default function Gable({ offsetRef, pitchRef }) {
|
||||
export default function Gable({ offsetRef, pitchRef, pitchText }) {
|
||||
const { getMessage } = useMessage()
|
||||
const currentAngleType = useRecoilValue(currentAngleTypeSelector)
|
||||
return (
|
||||
<>
|
||||
<div className="outline-form mb10">
|
||||
@ -9,9 +12,9 @@ export default function Gable({ offsetRef, pitchRef }) {
|
||||
{getMessage('slope')}
|
||||
</span>
|
||||
<div className="input-grid mr5">
|
||||
<input type="text" className="input-origin block" defaultValue={4} ref={pitchRef} />
|
||||
<input type="text" className="input-origin block" defaultValue={currentAngleType === ANGLE_TYPE.SLOPE ? 4 : 21.8} ref={pitchRef} />
|
||||
</div>
|
||||
<span className="thin">寸</span>
|
||||
<span className="thin">{pitchText}</span>
|
||||
</div>
|
||||
<div className="outline-form mb10">
|
||||
<span className="mr10" style={{ width: '63px' }}>
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { onlyNumberInputChange, onlyNumberWithDotInputChange } from '@/util/input-utils'
|
||||
|
||||
export default function Direction({ pitch, setPitch, eavesOffset, setEavesOffset, gableOffset, setGableOffset, shedWidth, setShedWidth }) {
|
||||
export default function Direction({ pitch, setPitch, eavesOffset, setEavesOffset, gableOffset, setGableOffset, shedWidth, setShedWidth, pitchText }) {
|
||||
const { getMessage } = useMessage()
|
||||
return (
|
||||
<div className="setting-box">
|
||||
@ -12,7 +12,7 @@ export default function Direction({ pitch, setPitch, eavesOffset, setEavesOffset
|
||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||
<input type="text" className="input-origin block" value={pitch} onChange={(e) => onlyNumberWithDotInputChange(e, setPitch)} />
|
||||
</div>
|
||||
<span className="thin">{getMessage('size')}</span>
|
||||
<span className="thin">{pitchText}</span>
|
||||
</div>
|
||||
<div className="outline-form mb10">
|
||||
<span className="mr10" style={{ width: '60px' }}>
|
||||
|
||||
@ -3,7 +3,7 @@ import { onlyNumberInputChange, onlyNumberWithDotInputChange } from '@/util/inpu
|
||||
|
||||
export default function Pattern(props) {
|
||||
const { getMessage } = useMessage()
|
||||
const { pitch, setPitch, eavesOffset, setEavesOffset, gableOffset, setGableOffset } = props
|
||||
const { pitch, setPitch, eavesOffset, setEavesOffset, gableOffset, setGableOffset, pitchText } = props
|
||||
return (
|
||||
<div className="setting-box">
|
||||
<div className="outline-form mb10">
|
||||
@ -13,7 +13,7 @@ export default function Pattern(props) {
|
||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||
<input type="text" className="input-origin block" value={pitch} onChange={(e) => onlyNumberWithDotInputChange(e, setPitch)} />
|
||||
</div>
|
||||
<span className="thin"> {getMessage('size')}</span>
|
||||
<span className="thin"> {pitchText}</span>
|
||||
</div>
|
||||
<div className="outline-form mb10">
|
||||
<span className="mr10" style={{ width: '60px' }}>
|
||||
|
||||
@ -4,7 +4,7 @@ import { onlyNumberInputChange, onlyNumberWithDotInputChange } from '@/util/inpu
|
||||
|
||||
export default function Ridge(props) {
|
||||
const { getMessage } = useMessage()
|
||||
const { pitch, setPitch, eavesOffset, setEavesOffset } = props
|
||||
const { pitch, setPitch, eavesOffset, setEavesOffset, pitchText } = props
|
||||
|
||||
return (
|
||||
<div className="setting-box">
|
||||
@ -15,7 +15,7 @@ export default function Ridge(props) {
|
||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||
<input type="text" className="input-origin block" value={pitch} onChange={(e) => onlyNumberWithDotInputChange(e, setPitch)} />
|
||||
</div>
|
||||
<span className="thin">{getMessage('size')}</span>
|
||||
<span className="thin">{pitchText}</span>
|
||||
</div>
|
||||
<div className="outline-form">
|
||||
<span className="mr10" style={{ width: '24px' }}>
|
||||
|
||||
@ -32,13 +32,14 @@ export default function Side(props) {
|
||||
buttonMenu,
|
||||
handleConfirm,
|
||||
handleRollBack,
|
||||
pitchText,
|
||||
} = props
|
||||
|
||||
const eavesProps = { pitch, setPitch, eavesOffset, setEavesOffset }
|
||||
const eavesProps = { pitch, setPitch, eavesOffset, setEavesOffset, pitchText }
|
||||
const gableProps = { gableOffset, setGableOffset }
|
||||
const wallProps = { sleeveOffset, setSleeveOffset, hasSleeve, setHasSleeve }
|
||||
const hipAndGableProps = { pitch, setPitch, eavesOffset, setEavesOffset, hipAndGableWidth, setHipAndGableWidth }
|
||||
const jerkinheadProps = { gableOffset, setGableOffset, jerkinHeadWidth, setJerkinHeadWidth, jerkinHeadPitch, setJerkinHeadPitch }
|
||||
const hipAndGableProps = { pitch, setPitch, eavesOffset, setEavesOffset, hipAndGableWidth, setHipAndGableWidth, pitchText }
|
||||
const jerkinheadProps = { gableOffset, setGableOffset, jerkinHeadWidth, setJerkinHeadWidth, jerkinHeadPitch, setJerkinHeadPitch, pitchText }
|
||||
const shedProps = { shedWidth, setShedWidth }
|
||||
|
||||
const { getMessage } = useMessage()
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { onlyNumberInputChange, onlyNumberWithDotInputChange } from '@/util/input-utils'
|
||||
|
||||
export default function Eaves({ pitch, setPitch, eavesOffset, setEavesOffset }) {
|
||||
export default function Eaves({ pitch, setPitch, eavesOffset, setEavesOffset, pitchText }) {
|
||||
const { getMessage } = useMessage()
|
||||
return (
|
||||
<>
|
||||
@ -12,7 +12,7 @@ export default function Eaves({ pitch, setPitch, eavesOffset, setEavesOffset })
|
||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||
<input type="text" className="input-origin block" value={pitch} onChange={(e) => onlyNumberWithDotInputChange(e, setPitch)} />
|
||||
</div>
|
||||
<span className="thin">{getMessage('size')}</span>
|
||||
<span className="thin">{pitchText}</span>
|
||||
</div>
|
||||
<div className="outline-form">
|
||||
<span className="mr10" style={{ width: '24px' }}>
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { onlyNumberInputChange, onlyNumberWithDotInputChange } from '@/util/input-utils'
|
||||
|
||||
export default function HipAndGable({ pitch, setPitch, eavesOffset, setEavesOffset, hipAndGableWidth, setHipAndGableWidth }) {
|
||||
export default function HipAndGable({ pitch, setPitch, eavesOffset, setEavesOffset, hipAndGableWidth, setHipAndGableWidth, pitchText }) {
|
||||
const { getMessage } = useMessage()
|
||||
return (
|
||||
<>
|
||||
@ -12,7 +12,7 @@ export default function HipAndGable({ pitch, setPitch, eavesOffset, setEavesOffs
|
||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||
<input type="text" className="input-origin block" value={pitch} onChange={(e) => onlyNumberWithDotInputChange(e, setPitch)} />
|
||||
</div>
|
||||
<span className="thin">{getMessage('size')}</span>
|
||||
<span className="thin">{pitchText}</span>
|
||||
</div>
|
||||
<div className="outline-form mb10">
|
||||
<span className="mr10" style={{ width: '60px' }}>
|
||||
|
||||
@ -1,7 +1,15 @@
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { onlyNumberInputChange, onlyNumberWithDotInputChange } from '@/util/input-utils'
|
||||
|
||||
export default function Jerkinhead({ gableOffset, setGableOffset, jerkinHeadWidth, setJerkinHeadWidth, jerkinHeadPitch, setJerkinHeadPitch }) {
|
||||
export default function Jerkinhead({
|
||||
gableOffset,
|
||||
setGableOffset,
|
||||
jerkinHeadWidth,
|
||||
setJerkinHeadWidth,
|
||||
jerkinHeadPitch,
|
||||
setJerkinHeadPitch,
|
||||
pitchText,
|
||||
}) {
|
||||
const { getMessage } = useMessage()
|
||||
return (
|
||||
<>
|
||||
@ -35,7 +43,7 @@ export default function Jerkinhead({ gableOffset, setGableOffset, jerkinHeadWidt
|
||||
onChange={(e) => onlyNumberWithDotInputChange(e, setJerkinHeadPitch)}
|
||||
/>
|
||||
</div>
|
||||
<span className="thin">{getMessage('size')}</span>
|
||||
<span className="thin">{pitchText}</span>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
|
||||
@ -62,7 +62,7 @@ export default function ChangePasswordPop() {
|
||||
|
||||
//패스워드 길이수 체크
|
||||
if (checkLength(_password1) > 10) {
|
||||
alert(getMessage('main.popup.login.validate2'))
|
||||
return alert(getMessage('main.popup.login.validate2'))
|
||||
}
|
||||
|
||||
const param = {
|
||||
@ -81,6 +81,8 @@ export default function ChangePasswordPop() {
|
||||
} else {
|
||||
alert(res.result.resultMsg)
|
||||
}
|
||||
} else {
|
||||
console.log('error')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@ import { useRouter } from 'next/navigation'
|
||||
import { globalLocaleStore } from '@/store/localeAtom'
|
||||
import { queryStringFormatter } from '@/util/common-utils'
|
||||
import { sessionStore } from '@/store/commonAtom'
|
||||
import MainSkeleton from '../ui/MainSkeleton'
|
||||
|
||||
export default function MainContents() {
|
||||
const { getMessage } = useMessage()
|
||||
@ -109,30 +110,34 @@ export default function MainContents() {
|
||||
<div className="main-product-list-wrap">
|
||||
<div className="main-product-list">
|
||||
<ProductItem num={1} name={getMessage('main.content.objectList')}>
|
||||
<ul className="recently-list">
|
||||
{objectList.map((row) => {
|
||||
return (
|
||||
<li
|
||||
key={row.objectNo}
|
||||
className="recently-item"
|
||||
onClick={() => {
|
||||
if (row.objectNo.substring(0, 1) === 'R') {
|
||||
router.push(`/management/stuff/detail?objectNo=${row.objectNo.toString()}`)
|
||||
} else {
|
||||
router.push(`/management/stuff/tempdetail?objectNo=${row.objectNo.toString()}`)
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className="item-inner">
|
||||
<span className="time">{dayjs(row.lastEditDatetime).format('YYYY.MM.DD HH:mm:ss')}</span>
|
||||
<span>{row.objectNo}</span>
|
||||
<span>{row.objectName}</span>
|
||||
<span>{row.saleStoreName}</span>
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
{objectList.length > 0 ? (
|
||||
<ul className="recently-list">
|
||||
{objectList.map((row) => {
|
||||
return (
|
||||
<li
|
||||
key={row.objectNo}
|
||||
className="recently-item"
|
||||
onClick={() => {
|
||||
if (row.objectNo.substring(0, 1) === 'R') {
|
||||
router.push(`/management/stuff/detail?objectNo=${row.objectNo.toString()}`)
|
||||
} else {
|
||||
router.push(`/management/stuff/tempdetail?objectNo=${row.objectNo.toString()}`)
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className="item-inner">
|
||||
<span className="time">{dayjs(row.lastEditDatetime).format('YYYY.MM.DD HH:mm:ss')}</span>
|
||||
<span>{row.objectNo}</span>
|
||||
<span>{row.objectName}</span>
|
||||
<span>{row.saleStoreName}</span>
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
) : (
|
||||
<MainSkeleton count={6} />
|
||||
)}
|
||||
</ProductItem>
|
||||
<ProductItem num={2} name={getMessage('main.content.notice')}>
|
||||
<div className="notice-box">
|
||||
@ -142,7 +147,9 @@ export default function MainContents() {
|
||||
<div className="notice-title">{recentNoticeList[0]?.title}</div>
|
||||
<div className="notice-contents">{recentNoticeList[0]?.contents}</div>
|
||||
</>
|
||||
) : null}
|
||||
) : (
|
||||
<MainSkeleton count={5} />
|
||||
)}
|
||||
</div>
|
||||
</ProductItem>
|
||||
</div>
|
||||
@ -163,7 +170,9 @@ export default function MainContents() {
|
||||
)
|
||||
})}
|
||||
</>
|
||||
) : null}
|
||||
) : (
|
||||
<MainSkeleton count={2} />
|
||||
)}
|
||||
</ul>
|
||||
</ProductItem>
|
||||
<ProductItem num={4} name={'Data Download'}>
|
||||
|
||||
@ -33,8 +33,8 @@ export default function Stuff() {
|
||||
const { get } = useAxios(globalLocaleState)
|
||||
const gridRef = useRef()
|
||||
|
||||
const [selectedRowData, setSelectedRowData] = useState([])
|
||||
const [selectedRowDataCount, setSelectedRowDataCount] = useState(0)
|
||||
// const [selectedRowData, setSelectedRowData] = useState([])
|
||||
// const [selectedRowDataCount, setSelectedRowDataCount] = useState(0)
|
||||
|
||||
const router = useRouter()
|
||||
const pathname = usePathname()
|
||||
@ -67,10 +67,10 @@ export default function Stuff() {
|
||||
field: 'lastEditDatetime',
|
||||
minWidth: 200,
|
||||
headerName: getMessage('stuff.gridHeader.lastEditDatetime'),
|
||||
headerCheckboxSelection: true,
|
||||
headerCheckboxSelectionCurrentPageOnly: true, //페이징시 현재 페이지만 체크되도록
|
||||
checkboxSelection: true,
|
||||
showDisabledCheckboxes: true,
|
||||
// headerCheckboxSelection: true,
|
||||
// headerCheckboxSelectionCurrentPageOnly: true, //페이징시 현재 페이지만 체크되도록
|
||||
// checkboxSelection: true,
|
||||
// showDisabledCheckboxes: true,
|
||||
cellStyle: { textAlign: 'center' },
|
||||
valueFormatter: function (params) {
|
||||
if (params.value) {
|
||||
@ -165,11 +165,11 @@ export default function Stuff() {
|
||||
}
|
||||
}
|
||||
|
||||
//그리드 체크박스 선택시
|
||||
const getSelectedRowdata = (data) => {
|
||||
setSelectedRowData(data)
|
||||
setSelectedRowDataCount(data.length)
|
||||
}
|
||||
//그리드 체크박스 선택시 미사용
|
||||
// const getSelectedRowdata = (data) => {
|
||||
// setSelectedRowData(data)
|
||||
// setSelectedRowDataCount(data.length)
|
||||
// }
|
||||
|
||||
//물건삭제
|
||||
// const fnDeleteRowData = (data) => {
|
||||
@ -405,8 +405,8 @@ export default function Stuff() {
|
||||
<span>{convertNumberToPriceDecimal(totalCount)}</span>
|
||||
</li>
|
||||
<li>
|
||||
{getMessage('stuff.search.grid.selected')}
|
||||
<span className="red">{convertNumberToPriceDecimal(selectedRowDataCount)}</span>
|
||||
{/* {getMessage('stuff.search.grid.selected')} */}
|
||||
{/* <span className="red">{convertNumberToPriceDecimal(selectedRowDataCount)}</span> */}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@ -428,7 +428,7 @@ export default function Stuff() {
|
||||
</div>
|
||||
<div className="grid-table-wrap">
|
||||
<div className="q-grid">
|
||||
<StuffQGrid {...gridProps} getSelectedRowdata={getSelectedRowdata} getCellDoubleClicked={getCellDoubleClicked} gridRef={gridRef} />
|
||||
<StuffQGrid {...gridProps} getCellDoubleClicked={getCellDoubleClicked} gridRef={gridRef} />
|
||||
<div className="pagination-wrap">
|
||||
<QPagination pageNo={pageNo} pageSize={pageSize} pagePerBlock={10} totalCount={totalCount} handleChangePage={handleChangePage} />
|
||||
</div>
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
import React, { useState, useEffect, useRef } from 'react'
|
||||
import { useRouter, useSearchParams, usePathname } from 'next/navigation'
|
||||
import { Button } from '@nextui-org/react'
|
||||
import Select from 'react-select'
|
||||
import Select, { components } from 'react-select'
|
||||
import Link from 'next/link'
|
||||
import { useAxios } from '@/hooks/useAxios'
|
||||
import { globalLocaleStore } from '@/store/localeAtom'
|
||||
@ -19,6 +19,13 @@ import { useCommonCode } from '@/hooks/common/useCommonCode'
|
||||
import StuffPlanQGrid from './StuffPlanQGrid'
|
||||
|
||||
export default function StuffDetail() {
|
||||
const inputReceiveUserEl = useRef(null) //담당자ref
|
||||
const inputObjectNameEl = useRef(null) //물건명ref
|
||||
const inputZipNoEl = useRef(null) //우편번호ref
|
||||
const inputAddressEl = useRef(null) //주소ref
|
||||
const inputVerticalSnowCoverEl = useRef(null) //수직적설량ref
|
||||
const inputInstallHeightEl = useRef(null) //설치높이ref
|
||||
|
||||
//공통코드
|
||||
const { commonCode, findCommonCode } = useCommonCode()
|
||||
const [selOptions, setSelOptions] = useState('') //선택한 1차점
|
||||
@ -54,7 +61,7 @@ export default function StuffDetail() {
|
||||
address: '', //주소
|
||||
areaId: '', //발전량시뮬레이션지역id
|
||||
standardWindSpeedId: '', //기준풍속
|
||||
verticalSnowCover: '', //수직적설량NEW
|
||||
verticalSnowCover: '', //수직적설량
|
||||
coldRegionFlg: false, //한랭지대책시행(true : 1 / false : 0)
|
||||
surfaceType: 'III・IV', //면조도구분(III・IV / Ⅱ)
|
||||
saltAreaFlg: false, //염해지역용아이템사용 (true : 1 / false : 0)
|
||||
@ -427,7 +434,6 @@ export default function StuffDetail() {
|
||||
//도도부현 / 주소
|
||||
setPrefValue(detailData.prefId)
|
||||
form.setValue('prefId', detailData.prefId)
|
||||
//prefName ???
|
||||
form.setValue('address', detailData.address)
|
||||
//발전시뮬
|
||||
form.setValue('areaId', detailData.areaId)
|
||||
@ -439,7 +445,7 @@ export default function StuffDetail() {
|
||||
//한랭지대책시행 coldRegionFlg 1이면 true
|
||||
form.setValue('coldRegionFlg', detailData.coldRegionFlg === '1' ? true : false)
|
||||
|
||||
//면조도구분surfaceType
|
||||
//면조도구분 surfaceType null로 내려오면 셋팅 안하고 저장할때 필수값 체크하도록
|
||||
// form.setValue('surfaceType', 'Ⅱ')
|
||||
// form.setValue('surfaceType', 'III・IV')
|
||||
form.setValue('surfaceType', detailData.surfaceType)
|
||||
@ -447,8 +453,12 @@ export default function StuffDetail() {
|
||||
form.setValue('saltAreaFlg', detailData.saltAreaFlg === '1' ? true : false)
|
||||
//설치높이
|
||||
form.setValue('installHeight', detailData.installHeight)
|
||||
//계약조건
|
||||
form.setValue('conType', detailData.conType)
|
||||
//계약조건 null로 내려오면 0으로 디폴트셋팅
|
||||
if (detailData.conType === null) {
|
||||
form.setValue('conType', '0')
|
||||
} else {
|
||||
form.setValue('conType', detailData.conType)
|
||||
}
|
||||
//메모
|
||||
form.setValue('remarks', detailData.remarks)
|
||||
})
|
||||
@ -481,8 +491,10 @@ export default function StuffDetail() {
|
||||
}
|
||||
//1차점 변경 이벤트
|
||||
const onSelectionChange = (key) => {
|
||||
if (key.saleStoreId === selOptions) {
|
||||
return
|
||||
if (isObjectNotEmpty(key)) {
|
||||
if (key.saleStoreId === selOptions) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
const planReqNo = form.watch('planReqNo')
|
||||
@ -593,8 +605,10 @@ export default function StuffDetail() {
|
||||
|
||||
//2차점 변경 이벤트
|
||||
const onSelectionChange2 = (key) => {
|
||||
if (key.saleStoreId === otherSelOptions) {
|
||||
return
|
||||
if (isObjectNotEmpty(key)) {
|
||||
if (key.saleStoreId === otherSelOptions) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
const planReqNo = form.watch('planReqNo')
|
||||
@ -883,56 +897,78 @@ export default function StuffDetail() {
|
||||
form.setValue('areaName', e.prefName)
|
||||
}
|
||||
|
||||
// 저장
|
||||
const onValid = async () => {
|
||||
const formData = form.getValues()
|
||||
|
||||
let errors = {}
|
||||
let fieldNm
|
||||
//담당자
|
||||
if (!formData.receiveUser || formData.receiveUser.trim().length === 0) {
|
||||
fieldNm = getMessage('stuff.detail.receiveUser')
|
||||
errors = fieldNm
|
||||
inputReceiveUserEl.current.focus()
|
||||
}
|
||||
//물건명
|
||||
if (!formData.objectName || formData.objectName.trim().length === 0) {
|
||||
fieldNm = getMessage('stuff.detail.objectStatusId')
|
||||
errors = fieldNm
|
||||
inputObjectNameEl.current.focus()
|
||||
}
|
||||
//경칭
|
||||
if (!formData.objectNameOmit) {
|
||||
fieldNm = getMessage('stuff.detail.objectNameOmit')
|
||||
errors = fieldNm
|
||||
}
|
||||
//1차판매점명
|
||||
if (!formData.saleStoreId) {
|
||||
fieldNm = getMessage('stuff.detail.saleStoreId')
|
||||
errors = fieldNm
|
||||
}
|
||||
|
||||
//우편번호
|
||||
if (!formData.zipNo) {
|
||||
fieldNm = getMessage('stuff.detail.zipNo')
|
||||
errors = fieldNm
|
||||
inputZipNoEl.current.focus()
|
||||
}
|
||||
|
||||
//주소
|
||||
if (!formData.address) {
|
||||
fieldNm = getMessage('stuff.detail.address')
|
||||
errors = fieldNm
|
||||
inputAddressEl.current.focus()
|
||||
}
|
||||
//도도부현
|
||||
if (!formData.prefId || formData.prefId === '0') {
|
||||
fieldNm = getMessage('stuff.detail.prefId')
|
||||
errors = fieldNm
|
||||
}
|
||||
|
||||
//발전시뮬레이션지역
|
||||
if (!formData.areaId) {
|
||||
fieldNm = getMessage('stuff.detail.areaId')
|
||||
errors = fieldNm
|
||||
}
|
||||
|
||||
//기준풍속
|
||||
if (!formData.standardWindSpeedId) {
|
||||
fieldNm = getMessage('stuff.detail.standardWindSpeedId')
|
||||
errors = fieldNm
|
||||
}
|
||||
|
||||
//수직적설량
|
||||
if (!formData.verticalSnowCover) {
|
||||
fieldNm = getMessage('stuff.detail.verticalSnowCover')
|
||||
errors = fieldNm
|
||||
inputVerticalSnowCoverEl.current.focus()
|
||||
}
|
||||
|
||||
//면조도구분
|
||||
if (!formData.surfaceType) {
|
||||
fieldNm = getMessage('stuff.detail.surfaceType')
|
||||
errors = fieldNm
|
||||
}
|
||||
//설치높이
|
||||
if (!formData.installHeight) {
|
||||
fieldNm = getMessage('stuff.detail.installHeight')
|
||||
errors = fieldNm
|
||||
inputInstallHeightEl.current.focus()
|
||||
}
|
||||
|
||||
if (Object.keys(errors).length > 0) {
|
||||
@ -1101,6 +1137,7 @@ export default function StuffDetail() {
|
||||
if (height === '0') {
|
||||
return alert(getMessage('stuff.detail.save.valierror2'))
|
||||
}
|
||||
|
||||
await promisePost({ url: '/api/object/save-object', data: params }).then((res) => {
|
||||
if (res.status === 201) {
|
||||
alert(getMessage('stuff.detail.tempSave.message1'))
|
||||
@ -1129,6 +1166,17 @@ export default function StuffDetail() {
|
||||
input.value = input.value.replace(/[^0-9]/g, '')
|
||||
}
|
||||
|
||||
//자동완성 옵션 없을때 메세지 컴포넌트..
|
||||
const NoOptionsMessage = (props) => {
|
||||
return (
|
||||
<components.NoOptionsMessage {...props}>
|
||||
<span style={{ background: 'red' }} className="custom-css-class">
|
||||
TEXTTTTTTT
|
||||
</span>
|
||||
</components.NoOptionsMessage>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{(editMode === 'NEW' && (
|
||||
@ -1162,7 +1210,7 @@ export default function StuffDetail() {
|
||||
)) ||
|
||||
null}
|
||||
</div>
|
||||
<Button className="btn-origin grey" onClick={onSearchDesignRequestPopOpen}>
|
||||
<Button className="btn-origin grey" onPress={onSearchDesignRequestPopOpen}>
|
||||
{getMessage('stuff.planReqPopup.title')}
|
||||
</Button>
|
||||
</div>
|
||||
@ -1174,7 +1222,7 @@ export default function StuffDetail() {
|
||||
</th>
|
||||
<td>
|
||||
<div className="input-wrap" style={{ width: '500px' }}>
|
||||
<input type="text" className="input-light" {...form.register('receiveUser')} />
|
||||
<input type="text" className="input-light" {...form.register('receiveUser')} ref={inputReceiveUserEl} />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@ -1203,7 +1251,7 @@ export default function StuffDetail() {
|
||||
})}
|
||||
{/* 라디오끝 */}
|
||||
<div className="input-wrap mr5" style={{ width: '545px' }}>
|
||||
<input type="text" className="input-light" {...form.register('objectName')} />
|
||||
<input type="text" className="input-light" {...form.register('objectName')} ref={inputObjectNameEl} />
|
||||
</div>
|
||||
<div className="select-wrap" style={{ width: '120px' }}>
|
||||
<Select
|
||||
@ -1293,6 +1341,7 @@ export default function StuffDetail() {
|
||||
<div className="select-wrap mr5" style={{ width: '567px' }}>
|
||||
<Select
|
||||
id="long-value-select2"
|
||||
// components={{ NoOptionsMessage }}
|
||||
instanceId="long-value-select2"
|
||||
className="react-select-custom"
|
||||
classNamePrefix="custom"
|
||||
@ -1328,9 +1377,9 @@ export default function StuffDetail() {
|
||||
<td>
|
||||
<div className="flx-box">
|
||||
<div className="input-wrap mr5" style={{ width: '200px' }}>
|
||||
<input type="text" className="input-light" disabled value={form.watch('zipNo') || ''} />
|
||||
<input type="text" className="input-light" disabled value={form.watch('zipNo') || ''} ref={inputZipNoEl} />
|
||||
</div>
|
||||
<Button className="btn-origin grey" onClick={onSearchPostNumberPopOpen}>
|
||||
<Button className="btn-origin grey" onPress={onSearchPostNumberPopOpen}>
|
||||
{getMessage('stuff.detail.btn.addressPop')}
|
||||
</Button>
|
||||
<div className="guide">{getMessage('stuff.detail.btn.addressPop.guide')}</div>
|
||||
@ -1344,7 +1393,7 @@ export default function StuffDetail() {
|
||||
</th>
|
||||
<td>
|
||||
<div className="flx-box">
|
||||
<div className="select-wrap" style={{ width: '200px' }}>
|
||||
<div className="select-wrap mr5" style={{ width: '200px' }}>
|
||||
{prefCodeList?.length > 0 && (
|
||||
<Select
|
||||
id="long-value-select3"
|
||||
@ -1364,7 +1413,13 @@ export default function StuffDetail() {
|
||||
)}
|
||||
</div>
|
||||
<div className="input-wrap mr5" style={{ width: '580px' }}>
|
||||
<input type="text" className="input-light" value={form.watch('address') || ''} {...form.register('address')} />
|
||||
<input
|
||||
type="text"
|
||||
className="input-light"
|
||||
value={form.watch('address') || ''}
|
||||
{...form.register('address')}
|
||||
ref={inputAddressEl}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
@ -1420,7 +1475,7 @@ export default function StuffDetail() {
|
||||
></Select>
|
||||
</div>
|
||||
<span className="mr10">{getMessage('stuff.detail.standardWindSpeedIdSpan')}</span>
|
||||
<Button type="button" className="btn-origin grey" onClick={onSearchWindSpeedPopOpen}>
|
||||
<Button type="button" className="btn-origin grey" onPress={onSearchWindSpeedPopOpen}>
|
||||
{getMessage('stuff.detail.btn.windSpeedPop')}
|
||||
</Button>
|
||||
</div>
|
||||
@ -1439,6 +1494,7 @@ export default function StuffDetail() {
|
||||
onKeyUp={handleKeyUp}
|
||||
value={form.watch('verticalSnowCover') || ''}
|
||||
{...register('verticalSnowCover')}
|
||||
ref={inputVerticalSnowCoverEl}
|
||||
/>
|
||||
</div>
|
||||
<span className="mr10">cm</span>
|
||||
@ -1486,6 +1542,7 @@ export default function StuffDetail() {
|
||||
onKeyUp={handleKeyUp}
|
||||
value={form.watch('installHeight') || ''}
|
||||
{...register('installHeight')}
|
||||
ref={inputInstallHeightEl}
|
||||
/>
|
||||
</div>
|
||||
<span>m</span>
|
||||
@ -1520,7 +1577,7 @@ export default function StuffDetail() {
|
||||
</div>
|
||||
<div className="sub-right-footer">
|
||||
{!isFormValid ? (
|
||||
<Button className="btn-origin grey mr5" onClick={onTempSave}>
|
||||
<Button className="btn-origin grey mr5" onPress={onTempSave}>
|
||||
{getMessage('stuff.detail.btn.tempSave')}
|
||||
</Button>
|
||||
) : (
|
||||
@ -1571,7 +1628,7 @@ export default function StuffDetail() {
|
||||
{/* {detailData?.tempFlg === '1' ? ( */}
|
||||
{objectNo.substring(0, 1) === 'T' ? (
|
||||
<>
|
||||
<Button className="btn-origin grey" onClick={onSearchDesignRequestPopOpen}>
|
||||
<Button className="btn-origin grey" onPress={onSearchDesignRequestPopOpen}>
|
||||
{getMessage('stuff.planReqPopup.title')}
|
||||
</Button>
|
||||
</>
|
||||
@ -1585,7 +1642,13 @@ export default function StuffDetail() {
|
||||
</th>
|
||||
<td>
|
||||
<div className="input-wrap" style={{ width: '500px' }}>
|
||||
<input type="text" className="input-light" {...form.register('receiveUser')} value={form.watch('receiveUser') || ''} />
|
||||
<input
|
||||
type="text"
|
||||
className="input-light"
|
||||
{...form.register('receiveUser')}
|
||||
value={form.watch('receiveUser') || ''}
|
||||
ref={inputReceiveUserEl}
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@ -1614,7 +1677,7 @@ export default function StuffDetail() {
|
||||
})}
|
||||
{/* 상세라디오끝 */}
|
||||
<div className="input-wrap mr5" style={{ width: '545px' }}>
|
||||
<input type="text" className="input-light" {...form.register('objectName')} />
|
||||
<input type="text" className="input-light" {...form.register('objectName')} ref={inputObjectNameEl} />
|
||||
</div>
|
||||
<div className="select-wrap" style={{ width: '120px' }}>
|
||||
<Select
|
||||
@ -1739,9 +1802,9 @@ export default function StuffDetail() {
|
||||
<td>
|
||||
<div className="flx-box">
|
||||
<div className="input-wrap mr5" style={{ width: '200px' }}>
|
||||
<input type="text" className="input-light" disabled value={form.watch('zipNo') || ''} />
|
||||
<input type="text" className="input-light" disabled value={form.watch('zipNo') || ''} ref={inputZipNoEl} />
|
||||
</div>
|
||||
<Button className="btn-origin grey" onClick={onSearchPostNumberPopOpen}>
|
||||
<Button className="btn-origin grey" onPress={onSearchPostNumberPopOpen}>
|
||||
{getMessage('stuff.detail.btn.addressPop')}
|
||||
</Button>
|
||||
<div className="guide">{getMessage('stuff.detail.btn.addressPop.guide')}</div>
|
||||
@ -1756,7 +1819,7 @@ export default function StuffDetail() {
|
||||
</th>
|
||||
<td>
|
||||
<div className="flx-box">
|
||||
<div className="select-wrap" style={{ width: '200px' }}>
|
||||
<div className="select-wrap mr5" style={{ width: '200px' }}>
|
||||
{prefCodeList?.length > 0 && (
|
||||
<Select
|
||||
id="long-value-select3"
|
||||
@ -1776,7 +1839,13 @@ export default function StuffDetail() {
|
||||
)}
|
||||
</div>
|
||||
<div className="input-wrap mr5" style={{ width: '580px' }}>
|
||||
<input type="text" className="input-light" value={form.watch('address') || ''} {...form.register('address')} />
|
||||
<input
|
||||
type="text"
|
||||
className="input-light"
|
||||
value={form.watch('address') || ''}
|
||||
{...form.register('address')}
|
||||
ref={inputAddressEl}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
@ -1836,7 +1905,7 @@ export default function StuffDetail() {
|
||||
></Select>
|
||||
</div>
|
||||
<span className="mr10">{getMessage('stuff.detail.standardWindSpeedIdSpan')}</span>
|
||||
<Button type="button" className="btn-origin grey" onClick={onSearchWindSpeedPopOpen}>
|
||||
<Button type="button" className="btn-origin grey" onPress={onSearchWindSpeedPopOpen}>
|
||||
{getMessage('stuff.detail.btn.windSpeedPop')}
|
||||
</Button>
|
||||
</div>
|
||||
@ -1857,6 +1926,7 @@ export default function StuffDetail() {
|
||||
onKeyUp={handleKeyUp}
|
||||
value={form.watch('verticalSnowCover') || ''}
|
||||
{...register('verticalSnowCover')}
|
||||
ref={inputVerticalSnowCoverEl}
|
||||
/>
|
||||
</div>
|
||||
<span className="mr10">cm</span>
|
||||
@ -1908,6 +1978,7 @@ export default function StuffDetail() {
|
||||
onKeyUp={handleKeyUp}
|
||||
value={form.watch('installHeight') || ''}
|
||||
{...register('installHeight')}
|
||||
ref={inputInstallHeightEl}
|
||||
/>
|
||||
</div>
|
||||
<span>m</span>
|
||||
@ -1981,13 +2052,13 @@ export default function StuffDetail() {
|
||||
<div className="sub-right-footer">
|
||||
<Link href="/management/stuff" scroll={false}>
|
||||
<button type="button" className="btn-origin grey mr5">
|
||||
R상세: {getMessage('stuff.detail.btn.moveList')}
|
||||
{getMessage('stuff.detail.btn.moveList')}
|
||||
</button>
|
||||
</Link>
|
||||
<Button type="submit" className="btn-origin navy mr5">
|
||||
R상세:{getMessage('stuff.detail.btn.save')}
|
||||
{getMessage('stuff.detail.btn.save')}
|
||||
</Button>
|
||||
<Button type="button" className="btn-origin grey" onClick={onDelete}>
|
||||
<Button type="button" className="btn-origin grey" onPress={onDelete}>
|
||||
{getMessage('stuff.detail.btn.delete')}
|
||||
</Button>
|
||||
</div>
|
||||
@ -1996,17 +2067,17 @@ export default function StuffDetail() {
|
||||
<>
|
||||
<div className="sub-right-footer">
|
||||
{!isFormValid ? (
|
||||
<Button className="btn-origin grey mr5" onClick={onTempSave}>
|
||||
TEMP상세:{getMessage('stuff.detail.btn.tempSave')}
|
||||
<Button className="btn-origin grey mr5" onPress={onTempSave}>
|
||||
{getMessage('stuff.detail.btn.tempSave')}
|
||||
</Button>
|
||||
) : (
|
||||
<Button type="submit" className="btn-origin navy mr5">
|
||||
TEMP상세:{getMessage('stuff.detail.btn.save')}
|
||||
{getMessage('stuff.detail.btn.save')}
|
||||
</Button>
|
||||
)}
|
||||
<Link href="/management/stuff" scroll={false}>
|
||||
<button type="button" className="btn-origin grey">
|
||||
TEMP상세:{getMessage('stuff.detail.btn.moveList')}
|
||||
{getMessage('stuff.detail.btn.moveList')}
|
||||
</button>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
@ -6,6 +6,7 @@ import { useRouter, useSearchParams } from 'next/navigation'
|
||||
import { globalLocaleStore } from '@/store/localeAtom'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
export default function StuffHeader() {
|
||||
const { getMessage } = useMessage()
|
||||
@ -57,11 +58,17 @@ export default function StuffHeader() {
|
||||
</div>
|
||||
<div className="sub-table-box">
|
||||
<div className="info-title">{getMessage('stuff.detail.header.lastEditDatetime')}</div>
|
||||
<div className="info-inner">{headerData.lastEditDatetime}</div>
|
||||
<div className="info-inner">
|
||||
{headerData?.lastEditDatetime ? `${dayjs(headerData.lastEditDatetime).format('YYYY.MM.DD HH:mm:ss')}` : ''}{' '}
|
||||
{headerData?.lastEditUserName ? `(${headerData.lastEditUserName})` : null}
|
||||
</div>
|
||||
</div>
|
||||
<div className="sub-table-box">
|
||||
<div className="info-title">{getMessage('stuff.detail.header.createDatetime')}</div>
|
||||
<div className="info-inner">{headerData.createDatetime}</div>
|
||||
<div className="info-inner">
|
||||
{headerData?.createDatetime ? `${dayjs(headerData.lastEditDatetime).format('YYYY.MM.DD')}` : ''}{' '}
|
||||
{headerData?.createUserName ? `(${headerData.createUserName})` : null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -6,7 +6,7 @@ export default function StuffPlanQGrid(props) {
|
||||
const { planGridData, planGridColumns, isPageable = true } = props
|
||||
|
||||
const [rowData, setRowData] = useState(null)
|
||||
const [gridApi, setGridApi] = useState(null)
|
||||
// const [gridApi, setGridApi] = useState(null)
|
||||
const [colDefs, setColDefs] = useState(planGridColumns)
|
||||
|
||||
const defaultColDef = useMemo(() => {
|
||||
@ -20,24 +20,24 @@ export default function StuffPlanQGrid(props) {
|
||||
}
|
||||
}, [])
|
||||
|
||||
const rowBuffer = 100
|
||||
const rowBuffer = 10
|
||||
|
||||
useEffect(() => {
|
||||
planGridData ? setRowData(planGridData) : ''
|
||||
}, [planGridData])
|
||||
|
||||
const onGridReady = useCallback(
|
||||
(params) => {
|
||||
setGridApi(params.api)
|
||||
planGridData ? setRowData(planGridData) : ''
|
||||
},
|
||||
[planGridData],
|
||||
)
|
||||
// const onGridReady = useCallback(
|
||||
// (params) => {
|
||||
// setGridApi(params.api)
|
||||
// planGridData ? setRowData(planGridData) : ''
|
||||
// },
|
||||
// [planGridData],
|
||||
// )
|
||||
|
||||
return (
|
||||
<div className="ag-theme-quartz" style={{ height: '100%' }}>
|
||||
<AgGridReact
|
||||
onGridReady={onGridReady}
|
||||
// onGridReady={onGridReady}
|
||||
rowBuffer={rowBuffer}
|
||||
rowData={rowData}
|
||||
columnDefs={colDefs}
|
||||
|
||||
@ -1,11 +1,14 @@
|
||||
import React from 'react'
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { AgGridReact } from 'ag-grid-react'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
|
||||
import 'ag-grid-community/styles/ag-grid.css'
|
||||
import 'ag-grid-community/styles/ag-theme-quartz.css'
|
||||
|
||||
export default function StuffQGrid(props) {
|
||||
const { getMessage } = useMessage()
|
||||
|
||||
const { gridData, gridColumns, isPageable = true, count, gridRef } = props
|
||||
/**
|
||||
* 행 데이터를 설정할 때 useState을 사용하여 렌더링 전반에 걸쳐 일관된 배열 참조를 유지하는 것이 좋습니다
|
||||
@ -59,10 +62,10 @@ export default function StuffQGrid(props) {
|
||||
[count],
|
||||
)
|
||||
|
||||
// 체크박스 체크시
|
||||
const onSelectionChanged = useCallback((event) => {
|
||||
props.getSelectedRowdata(event.api.getSelectedRows())
|
||||
}, [])
|
||||
// 체크박스 체크시 체크박스 미사용
|
||||
// const onSelectionChanged = useCallback((event) => {
|
||||
// props.getSelectedRowdata(event.api.getSelectedRows())
|
||||
// }, [])
|
||||
|
||||
//더블클릭
|
||||
const onCellDoubleClicked = useCallback((event) => {
|
||||
@ -92,10 +95,10 @@ export default function StuffQGrid(props) {
|
||||
isRowSelectable={isRowSelectable}
|
||||
rowSelection={'multiple'}
|
||||
suppressRowClickSelection={true}
|
||||
onSelectionChanged={onSelectionChanged}
|
||||
// onSelectionChanged={onSelectionChanged}
|
||||
onCellDoubleClicked={onCellDoubleClicked}
|
||||
pagination={isPageable}
|
||||
overlayNoRowsTemplate={'<span className="ag-overlay-loading-center">물건 목록이 없습니다.</span>'}
|
||||
overlayNoRowsTemplate={`<span className="ag-overlay-loading-center">${getMessage('stuff.grid.noData')}</span>`}
|
||||
getRowClass={getRowClass}
|
||||
autoSizeAllColumns={true}
|
||||
/>
|
||||
|
||||
@ -1,11 +1,14 @@
|
||||
import React from 'react'
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { AgGridReact } from 'ag-grid-react'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
|
||||
import 'ag-grid-community/styles/ag-grid.css'
|
||||
import 'ag-grid-community/styles/ag-theme-quartz.css'
|
||||
|
||||
export default function FindAddressPopGrid(props) {
|
||||
const { getMessage } = useMessage()
|
||||
|
||||
const { gridData, gridColumns, isPageable = true } = props
|
||||
|
||||
const [rowData, setRowData] = useState(null)
|
||||
@ -25,7 +28,7 @@ export default function FindAddressPopGrid(props) {
|
||||
}
|
||||
}, [])
|
||||
|
||||
const rowBuffer = 100
|
||||
const rowBuffer = 10
|
||||
|
||||
useEffect(() => {
|
||||
gridData ? setRowData(gridData) : ''
|
||||
@ -46,7 +49,7 @@ export default function FindAddressPopGrid(props) {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="ag-theme-quartz" style={{ height: 500 }}>
|
||||
<div className="ag-theme-quartz" style={{ height: 400 }}>
|
||||
<AgGridReact
|
||||
onGridReady={onGridReady}
|
||||
rowBuffer={rowBuffer}
|
||||
@ -56,6 +59,7 @@ export default function FindAddressPopGrid(props) {
|
||||
rowSelection={'singleRow'}
|
||||
pagination={isPageable}
|
||||
onSelectionChanged={onSelectionChanged}
|
||||
overlayNoRowsTemplate={`<span className="ag-overlay-loading-center">${getMessage('stuff.grid.noData')}</span>`}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -253,6 +253,12 @@ export default function PlanRequestPop(props) {
|
||||
}
|
||||
}, [commonCode])
|
||||
|
||||
// 숫자만 입력 가능
|
||||
const handleKeyUp = (e) => {
|
||||
let input = e.target
|
||||
input.value = input.value.replace(/[^0-9]/g, '')
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="modal-popup">
|
||||
<div className="modal-dialog big">
|
||||
@ -301,6 +307,7 @@ export default function PlanRequestPop(props) {
|
||||
type="text"
|
||||
className="input-light"
|
||||
value={schPlanReqNo}
|
||||
onKeyUp={handleKeyUp}
|
||||
onChange={(e) => {
|
||||
setSchPlanReqNo(e.target.value)
|
||||
}}
|
||||
|
||||
@ -1,11 +1,14 @@
|
||||
import React from 'react'
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { AgGridReact } from 'ag-grid-react'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
|
||||
import 'ag-grid-community/styles/ag-grid.css'
|
||||
import 'ag-grid-community/styles/ag-theme-quartz.css'
|
||||
|
||||
export default function PlanRequestPopQGrid(props) {
|
||||
const { getMessage } = useMessage()
|
||||
|
||||
const { gridData, gridColumns, isPageable = true } = props
|
||||
|
||||
const [rowData, setRowData] = useState(null)
|
||||
@ -25,7 +28,7 @@ export default function PlanRequestPopQGrid(props) {
|
||||
}
|
||||
}, [])
|
||||
|
||||
const rowBuffer = 100
|
||||
const rowBuffer = 20
|
||||
|
||||
useEffect(() => {
|
||||
gridData ? setRowData(gridData) : ''
|
||||
@ -56,6 +59,7 @@ export default function PlanRequestPopQGrid(props) {
|
||||
rowSelection={'singleRow'}
|
||||
pagination={isPageable}
|
||||
onSelectionChanged={onSelectionChanged}
|
||||
overlayNoRowsTemplate={`<span className="ag-overlay-loading-center">${getMessage('stuff.grid.noData')}</span>`}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -1,5 +0,0 @@
|
||||
import style from '@/components/ui/Loading.module.css'
|
||||
|
||||
export default function Loading() {
|
||||
return <span className={style.loader}></span>
|
||||
}
|
||||
@ -1,35 +0,0 @@
|
||||
.loader {
|
||||
position: relative;
|
||||
font-size: 48px;
|
||||
letter-spacing: 6px;
|
||||
}
|
||||
.loader:before {
|
||||
content: 'Loading';
|
||||
color: #fff;
|
||||
}
|
||||
.loader:after {
|
||||
content: '';
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background-color: #ff3d00;
|
||||
background-image: radial-gradient(circle 2px, #fff4 100%, transparent 0), radial-gradient(circle 1px, #fff3 100%, transparent 0);
|
||||
background-position:
|
||||
14px -4px,
|
||||
12px -1px;
|
||||
border-radius: 50%;
|
||||
position: absolute;
|
||||
margin: auto;
|
||||
top: -5px;
|
||||
right: 66px;
|
||||
transform-origin: center bottom;
|
||||
animation: fillBaloon 1s ease-in-out infinite alternate;
|
||||
}
|
||||
|
||||
@keyframes fillBaloon {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
}
|
||||
100% {
|
||||
transform: scale(3);
|
||||
}
|
||||
}
|
||||
13
src/components/ui/MainSkeleton.jsx
Normal file
13
src/components/ui/MainSkeleton.jsx
Normal file
@ -0,0 +1,13 @@
|
||||
import Skeleton from 'react-loading-skeleton'
|
||||
import 'react-loading-skeleton/dist/skeleton.css'
|
||||
|
||||
export default function MainSkeleton({ count }) {
|
||||
return (
|
||||
<>
|
||||
<div className="mb-6">
|
||||
<Skeleton height={`50px`} />
|
||||
</div>
|
||||
<Skeleton count={count} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -1,20 +1,24 @@
|
||||
import { useEffect } from 'react'
|
||||
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
|
||||
import { roofDisplaySelector, settingModalFirstOptionsState } from '@/store/settingAtom'
|
||||
import { canvasState, dotLineGridSettingState } from '@/store/canvasAtom'
|
||||
import { setSurfaceShapePattern } from '@/util/canvas-util'
|
||||
import { basicSettingState, roofDisplaySelector, settingModalFirstOptionsState } from '@/store/settingAtom'
|
||||
import { canvasState, dotLineGridSettingState, pitchText, pitchTextSelector } from '@/store/canvasAtom'
|
||||
import { getChonByDegree, getDegreeByChon, setSurfaceShapePattern } from '@/util/canvas-util'
|
||||
import { useFont } from '@/hooks/common/useFont'
|
||||
import { useGrid } from '@/hooks/common/useGrid'
|
||||
import { globalFontAtom } from '@/store/fontAtom'
|
||||
import { useRoof } from '@/hooks/common/useRoof'
|
||||
|
||||
export function useCanvasConfigInitialize() {
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
const [settingModalFirstOptions, setSettingModalFirstOptions] = useRecoilState(settingModalFirstOptionsState)
|
||||
const [basicSetting, setBasicSettings] = useRecoilState(basicSettingState)
|
||||
const roofDisplay = useRecoilValue(roofDisplaySelector)
|
||||
const setGlobalFonts = useSetRecoilState(globalFontAtom)
|
||||
const setDotLineGridSetting = useSetRecoilState(dotLineGridSettingState)
|
||||
const pitchText = useRecoilValue(pitchTextSelector)
|
||||
const {} = useFont()
|
||||
const {} = useGrid()
|
||||
const {} = useRoof()
|
||||
|
||||
useEffect(() => {
|
||||
if (!canvas) return
|
||||
@ -27,10 +31,34 @@ export function useCanvasConfigInitialize() {
|
||||
canvas.renderAll()
|
||||
}, [roofDisplay])
|
||||
|
||||
useEffect(() => {
|
||||
if (!canvas) return
|
||||
const texts = canvas.getObjects().filter((obj) => obj.name === 'pitchText' || obj.name === 'flowText')
|
||||
if (basicSetting.roofAngleSet === 'slope') {
|
||||
texts.forEach((obj) => {
|
||||
obj.set({ text: `${obj.originText}-∠${obj.pitch}${pitchText}` })
|
||||
})
|
||||
}
|
||||
|
||||
if (basicSetting.roofAngleSet === 'flat') {
|
||||
texts.forEach((obj) => {
|
||||
obj.set({ text: `${obj.originText}-∠${getDegreeByChon(obj.pitch)}${pitchText}` })
|
||||
})
|
||||
}
|
||||
|
||||
canvas.renderAll()
|
||||
}, [basicSetting])
|
||||
|
||||
const canvasLoadInit = () => {
|
||||
roofInit() //화면표시 초기화
|
||||
}
|
||||
|
||||
const gridInit = () => {
|
||||
setDotLineGridSetting((prev) => {
|
||||
return { ...prev, INTERVAL: { ...prev.INTERVAL } }
|
||||
})
|
||||
}
|
||||
|
||||
//치수표시, 화면표시, 글꼴등 초기화
|
||||
const roofInit = () => {
|
||||
setSettingModalFirstOptions((prev) => {
|
||||
@ -46,10 +74,7 @@ export function useCanvasConfigInitialize() {
|
||||
})
|
||||
return { ...prev, option1, option2, dimensionDisplay }
|
||||
})
|
||||
|
||||
setDotLineGridSetting((prev) => {
|
||||
return { ...prev }
|
||||
})
|
||||
gridInit()
|
||||
|
||||
setGlobalFonts((prev) => {
|
||||
const commonText = { ...prev.commonText }
|
||||
@ -61,5 +86,5 @@ export function useCanvasConfigInitialize() {
|
||||
})
|
||||
}
|
||||
|
||||
return { canvasLoadInit }
|
||||
return { canvasLoadInit, gridInit }
|
||||
}
|
||||
|
||||
151
src/hooks/common/useRoof.js
Normal file
151
src/hooks/common/useRoof.js
Normal file
@ -0,0 +1,151 @@
|
||||
import { canvasState } from '@/store/canvasAtom'
|
||||
import { allocDisplaySelector, roofDisplaySelector } from '@/store/settingAtom'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { useEffect } from 'react'
|
||||
|
||||
export function useRoof() {
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
const allocDisplay = useRecoilValue(allocDisplaySelector)
|
||||
const roofDisplay = useRecoilValue(roofDisplaySelector)
|
||||
|
||||
useEffect(() => {
|
||||
if (!canvas) return
|
||||
canvas
|
||||
.getObjects()
|
||||
.filter((polygon) => polygon.name === 'roof')
|
||||
.forEach((polygon) => {
|
||||
if (allocDisplay) {
|
||||
setSurfaceShapePattern(polygon, roofDisplay.column)
|
||||
} else {
|
||||
polygon.set('fill', null)
|
||||
}
|
||||
})
|
||||
canvas.renderAll()
|
||||
}, [allocDisplay])
|
||||
|
||||
const setSurfaceShapePattern = (polygon, mode = 'onlyBorder') => {
|
||||
const ratio = window.devicePixelRatio || 1
|
||||
|
||||
let width = 265 / 10
|
||||
let height = 150 / 10
|
||||
let roofStyle = 2
|
||||
const inputPatternSize = { width: width, height: height } //임시 사이즈
|
||||
const patternSize = { ...inputPatternSize } // 입력된 값을 뒤집기 위해
|
||||
|
||||
if (polygon.direction === 'east' || polygon.direction === 'west') {
|
||||
//세로형이면 width height를 바꿈
|
||||
;[patternSize.width, patternSize.height] = [inputPatternSize.height, patternSize.width]
|
||||
}
|
||||
|
||||
// 패턴 소스를 위한 임시 캔버스 생성
|
||||
const patternSourceCanvas = document.createElement('canvas')
|
||||
patternSourceCanvas.width = polygon.width * ratio
|
||||
patternSourceCanvas.height = polygon.height * ratio
|
||||
const ctx = patternSourceCanvas.getContext('2d')
|
||||
let offset = roofStyle === 1 ? 0 : patternSize.width / 2
|
||||
|
||||
const rows = Math.floor(patternSourceCanvas.height / patternSize.height)
|
||||
const cols = Math.floor(patternSourceCanvas.width / patternSize.width)
|
||||
|
||||
ctx.strokeStyle = mode === 'allPainted' ? 'black' : 'green'
|
||||
ctx.lineWidth = mode === 'allPainted' ? 1 : 0.4
|
||||
ctx.fillStyle = mode === 'allPainted' ? 'rgba(0, 159, 64, 0.7)' : 'white'
|
||||
|
||||
if (polygon.direction === 'east' || polygon.direction === 'west') {
|
||||
offset = roofStyle === 1 ? 0 : patternSize.height / 2
|
||||
for (let col = 0; col <= cols; col++) {
|
||||
const x = col * patternSize.width
|
||||
const yStart = 0
|
||||
const yEnd = patternSourceCanvas.height
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(x, yStart) // 선 시작점
|
||||
ctx.lineTo(x, yEnd) // 선 끝점
|
||||
ctx.stroke()
|
||||
if (mode === 'allPainted') {
|
||||
ctx.fillRect(x, yStart, patternSize.width, yEnd - yStart)
|
||||
}
|
||||
|
||||
for (let row = 0; row <= rows; row++) {
|
||||
const y = row * patternSize.height + (col % 2 === 0 ? 0 : offset)
|
||||
const xStart = col * patternSize.width
|
||||
const xEnd = xStart + patternSize.width
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(xStart, y) // 선 시작점
|
||||
ctx.lineTo(xEnd, y) // 선 끝점
|
||||
ctx.stroke()
|
||||
if (mode === 'allPainted') {
|
||||
ctx.fillRect(xStart, y, xEnd - xStart, patternSize.height)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (let row = 0; row <= rows; row++) {
|
||||
const y = row * patternSize.height
|
||||
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(0, y) // 선 시작점
|
||||
ctx.lineTo(patternSourceCanvas.width, y) // 선 끝점
|
||||
ctx.stroke()
|
||||
if (mode === 'allPainted') {
|
||||
ctx.fillRect(0, y, patternSourceCanvas.width, patternSize.height)
|
||||
}
|
||||
|
||||
for (let col = 0; col <= cols; col++) {
|
||||
const x = col * patternSize.width + (row % 2 === 0 ? 0 : offset)
|
||||
const yStart = row * patternSize.height
|
||||
const yEnd = yStart + patternSize.height
|
||||
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(x, yStart) // 선 시작점
|
||||
ctx.lineTo(x, yEnd) // 선 끝점
|
||||
ctx.stroke()
|
||||
if (mode === 'allPainted') {
|
||||
ctx.fillRect(x, yStart, patternSize.width, yEnd - yStart)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const hachingPatternSourceCanvas = document.createElement('canvas')
|
||||
|
||||
if (mode === 'lineHatch') {
|
||||
hachingPatternSourceCanvas.width = polygon.width * ratio
|
||||
hachingPatternSourceCanvas.height = polygon.height * ratio
|
||||
|
||||
const ctx1 = hachingPatternSourceCanvas.getContext('2d')
|
||||
|
||||
const gap = 10
|
||||
|
||||
ctx1.strokeStyle = 'green' // 선 색상
|
||||
ctx1.lineWidth = 0.3 // 선 두께
|
||||
|
||||
for (let x = 0; x < hachingPatternSourceCanvas.width + hachingPatternSourceCanvas.height; x += gap) {
|
||||
ctx1.beginPath()
|
||||
ctx1.moveTo(x, 0) // 선 시작점
|
||||
ctx1.lineTo(0, x) // 선 끝점
|
||||
ctx1.stroke()
|
||||
}
|
||||
}
|
||||
|
||||
const combinedPatternCanvas = document.createElement('canvas')
|
||||
combinedPatternCanvas.width = polygon.width * ratio
|
||||
combinedPatternCanvas.height = polygon.height * ratio
|
||||
const combinedCtx = combinedPatternCanvas.getContext('2d')
|
||||
|
||||
// 첫 번째 패턴을 그린 후 두 번째 패턴을 덧입힘
|
||||
combinedCtx.drawImage(patternSourceCanvas, 0, 0)
|
||||
combinedCtx.drawImage(hachingPatternSourceCanvas, 0, 0)
|
||||
|
||||
// 패턴 생성
|
||||
const pattern = new fabric.Pattern({
|
||||
source: combinedPatternCanvas,
|
||||
repeat: 'repeat',
|
||||
})
|
||||
|
||||
polygon.set('fill', null)
|
||||
polygon.set('fill', pattern)
|
||||
polygon.canvas?.renderAll()
|
||||
}
|
||||
|
||||
return {}
|
||||
}
|
||||
@ -33,7 +33,7 @@ export function useFirstOption() {
|
||||
optionName = ['outerLine', 'wallLine']
|
||||
break
|
||||
case 'gridDisplay': //그리드 표시
|
||||
optionName = ['lindGrid', 'dotGrid']
|
||||
optionName = ['lineGrid', 'dotGrid', 'adsorptionPoint', 'tempGrid']
|
||||
break
|
||||
case 'lineDisplay': //지붕선 표시
|
||||
optionName = ['roof', 'roofBase']
|
||||
@ -45,7 +45,7 @@ export function useFirstOption() {
|
||||
optionName = ['7']
|
||||
break
|
||||
case 'flowDisplay': //흐름방향 표시
|
||||
optionName = ['arrow']
|
||||
optionName = ['arrow', 'flowText']
|
||||
break
|
||||
case 'trestleDisplay': //가대 표시
|
||||
optionName = ['8']
|
||||
@ -66,6 +66,8 @@ export function useFirstOption() {
|
||||
//obj.set({ visible: !obj.visible })
|
||||
})
|
||||
|
||||
canvas.renderAll()
|
||||
|
||||
// console.log(
|
||||
// 'optionName',
|
||||
// optionName,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { canvasState } from '@/store/canvasAtom'
|
||||
import { ANGLE_TYPE, canvasState, currentAngleTypeSelector, pitchTextSelector } from '@/store/canvasAtom'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { useEvent } from '@/hooks/useEvent'
|
||||
import { LINE_TYPE } from '@/common/common'
|
||||
@ -9,6 +9,7 @@ import { useMode } from '@/hooks/useMode'
|
||||
import { outerLineFixState } from '@/store/outerLineAtom'
|
||||
import { useSwal } from '@/hooks/useSwal'
|
||||
import { usePopup } from '@/hooks/usePopup'
|
||||
import { getChonByDegree } from '@/util/canvas-util'
|
||||
|
||||
// 처마.케라바 변경
|
||||
export function useEavesGableEdit(id) {
|
||||
@ -28,6 +29,8 @@ export function useEavesGableEdit(id) {
|
||||
const { swalFire } = useSwal()
|
||||
|
||||
const { drawRoofPolygon } = useMode()
|
||||
const currentAngleType = useRecoilValue(currentAngleTypeSelector)
|
||||
const pitchText = useRecoilValue(pitchTextSelector)
|
||||
|
||||
const pitchRef = useRef(null)
|
||||
const offsetRef = useRef(null)
|
||||
@ -105,13 +108,13 @@ export function useEavesGableEdit(id) {
|
||||
if (radioTypeRef.current === '1') {
|
||||
attributes = {
|
||||
type: LINE_TYPE.WALLLINE.EAVES,
|
||||
pitch: pitchRef.current.value,
|
||||
pitch: currentAngleType === ANGLE_TYPE.SLOPE ? pitchRef.current.value : getChonByDegree(pitchRef.current.value),
|
||||
offset: offsetRef.current.value / 10,
|
||||
}
|
||||
} else {
|
||||
attributes = {
|
||||
type: LINE_TYPE.WALLLINE.HIPANDGABLE,
|
||||
pitch: pitchRef.current.value,
|
||||
pitch: currentAngleType === ANGLE_TYPE.SLOPE ? pitchRef.current.value : getChonByDegree(pitchRef.current.value),
|
||||
offset: offsetRef.current.value / 10,
|
||||
width: widthRef.current.value / 10,
|
||||
}
|
||||
@ -126,7 +129,7 @@ export function useEavesGableEdit(id) {
|
||||
} else {
|
||||
attributes = {
|
||||
type: LINE_TYPE.WALLLINE.JERKINHEAD,
|
||||
pitch: pitchRef.current.value,
|
||||
pitch: currentAngleType === ANGLE_TYPE.SLOPE ? pitchRef.current.value : getChonByDegree(pitchRef.current.value),
|
||||
offset: offsetRef.current.value / 10,
|
||||
width: widthRef.current.value / 10,
|
||||
}
|
||||
@ -217,5 +220,5 @@ export function useEavesGableEdit(id) {
|
||||
canvas?.renderAll()
|
||||
}
|
||||
|
||||
return { type, setType, buttonMenu, TYPES, pitchRef, offsetRef, widthRef, radioTypeRef }
|
||||
return { type, setType, buttonMenu, TYPES, pitchRef, offsetRef, widthRef, radioTypeRef, pitchText }
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { canvasState, currentObjectState } from '@/store/canvasAtom'
|
||||
import { ANGLE_TYPE, canvasState, currentAngleTypeSelector, currentObjectState, pitchTextSelector } from '@/store/canvasAtom'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { useLine } from '@/hooks/useLine'
|
||||
@ -10,6 +10,7 @@ import { usePolygon } from '@/hooks/usePolygon'
|
||||
import { outerLineFixState } from '@/store/outerLineAtom'
|
||||
import { useSwal } from '@/hooks/useSwal'
|
||||
import { usePopup } from '@/hooks/usePopup'
|
||||
import { getChonByDegree } from '@/util/canvas-util'
|
||||
|
||||
//지붕형상 수동 설정
|
||||
export function useRoofShapePassivitySetting(id) {
|
||||
@ -19,6 +20,8 @@ export function useRoofShapePassivitySetting(id) {
|
||||
SHED: 'shed',
|
||||
}
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
const currentAngleType = useRecoilValue(currentAngleTypeSelector)
|
||||
const pitchText = useRecoilValue(pitchTextSelector)
|
||||
const { getMessage } = useMessage()
|
||||
const { showLine, hideLine, addPitchTextsByOuterLines } = useLine()
|
||||
const { swalFire } = useSwal()
|
||||
@ -34,6 +37,7 @@ export function useRoofShapePassivitySetting(id) {
|
||||
const isFix = useRef(false)
|
||||
const initLines = useRef([])
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
|
||||
const { closePopup } = usePopup()
|
||||
const buttons = [
|
||||
{ id: 1, name: getMessage('eaves'), type: TYPES.EAVES },
|
||||
@ -134,12 +138,12 @@ export function useRoofShapePassivitySetting(id) {
|
||||
attributes = {
|
||||
type: LINE_TYPE.WALLLINE.EAVES,
|
||||
offset,
|
||||
pitch: pitchRef.current.value,
|
||||
pitch: currentAngleType === ANGLE_TYPE.SLOPE ? pitchRef.current.value : getChonByDegree(pitchRef.current.value),
|
||||
}
|
||||
} else if (type === TYPES.GABLE) {
|
||||
attributes = {
|
||||
type: LINE_TYPE.WALLLINE.GABLE,
|
||||
pitch: pitchRef.current.value,
|
||||
pitch: currentAngleType === ANGLE_TYPE.SLOPE ? pitchRef.current.value : getChonByDegree(pitchRef.current.value),
|
||||
offset,
|
||||
}
|
||||
} else if (type === TYPES.SHED) {
|
||||
@ -216,5 +220,5 @@ export function useRoofShapePassivitySetting(id) {
|
||||
canvas.renderAll()
|
||||
}
|
||||
|
||||
return { handleSave, handleConfirm, buttons, type, setType, TYPES, offsetRef, pitchRef, handleRollback }
|
||||
return { handleSave, handleConfirm, buttons, type, setType, TYPES, offsetRef, pitchRef, handleRollback, pitchText }
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { useRecoilValue, useSetRecoilState } from 'recoil'
|
||||
import { canvasState, currentMenuState, currentObjectState } from '@/store/canvasAtom'
|
||||
import { ANGLE_TYPE, canvasState, currentAngleTypeSelector, currentMenuState, currentObjectState, pitchTextSelector } from '@/store/canvasAtom'
|
||||
import { LINE_TYPE } from '@/common/common'
|
||||
import { usePolygon } from '@/hooks/usePolygon'
|
||||
import { useMode } from '@/hooks/useMode'
|
||||
@ -9,6 +9,7 @@ import { useLine } from '@/hooks/useLine'
|
||||
import { outerLineFixState } from '@/store/outerLineAtom'
|
||||
import { useSwal } from '@/hooks/useSwal'
|
||||
import { usePopup } from '@/hooks/usePopup'
|
||||
import { getChonByDegree } from '@/util/canvas-util'
|
||||
|
||||
// 지붕형상 설정
|
||||
export function useRoofShapeSetting(id) {
|
||||
@ -17,13 +18,16 @@ export function useRoofShapeSetting(id) {
|
||||
const { swalFire } = useSwal()
|
||||
const { getMessage } = useMessage()
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
const currentAngleType = useRecoilValue(currentAngleTypeSelector)
|
||||
const pitchText = useRecoilValue(pitchTextSelector)
|
||||
const { addPolygonByLines } = usePolygon()
|
||||
const [pitch, setPitch] = useState(4)
|
||||
|
||||
const [pitch, setPitch] = useState(currentAngleType === ANGLE_TYPE.SLOPE ? 4 : 21.8) // 경사
|
||||
const [eavesOffset, setEavesOffset] = useState(500) // 처마출폭
|
||||
const [gableOffset, setGableOffset] = useState(300) // 케라바출폭
|
||||
const [sleeveOffset, setSleeveOffset] = useState(300) // 소매출폭
|
||||
const [jerkinHeadWidth, setJerkinHeadWidth] = useState(800) // 반절처 폭
|
||||
const [jerkinHeadPitch, setJerkinHeadPitch] = useState(4.5) // 반절처 경사
|
||||
const [jerkinHeadPitch, setJerkinHeadPitch] = useState(currentAngleType === ANGLE_TYPE.SLOPE ? 4.5 : 20) // 반절처 경사
|
||||
const [hipAndGableWidth, setHipAndGableWidth] = useState(800) // 팔작지붕 폭
|
||||
const [shedWidth, setShedWidth] = useState(300) // 한쪽흐름 폭
|
||||
const [hasSleeve, setHasSleeve] = useState('0')
|
||||
@ -34,9 +38,19 @@ export function useRoofShapeSetting(id) {
|
||||
const setCurrentMenu = useSetRecoilState(currentMenuState)
|
||||
const outerLineFix = useRecoilValue(outerLineFixState)
|
||||
|
||||
const pitchRef = useRef(null)
|
||||
const jerkinHeadPitchRef = useRef(null)
|
||||
|
||||
const history = useRef([])
|
||||
const { closePopup } = usePopup()
|
||||
|
||||
useEffect(() => {
|
||||
pitchRef.current = currentAngleType === ANGLE_TYPE.SLOPE ? pitch : getChonByDegree(pitch)
|
||||
}, [pitch])
|
||||
useEffect(() => {
|
||||
jerkinHeadPitchRef.current = currentAngleType === ANGLE_TYPE.SLOPE ? jerkinHeadPitch : getChonByDegree(jerkinHeadPitch)
|
||||
}, [jerkinHeadPitch])
|
||||
|
||||
useEffect(() => {
|
||||
const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
|
||||
if (!outerLineFix || outerLines.length === 0) {
|
||||
@ -114,12 +128,10 @@ export function useRoofShapeSetting(id) {
|
||||
|
||||
canvas?.renderAll()
|
||||
}
|
||||
setPitch(4)
|
||||
setEavesOffset(500)
|
||||
setGableOffset(300)
|
||||
setSleeveOffset(300)
|
||||
setJerkinHeadWidth(800)
|
||||
setJerkinHeadPitch(4.5)
|
||||
setHipAndGableWidth(800)
|
||||
setShedWidth(300)
|
||||
}, [shapeNum])
|
||||
@ -181,7 +193,7 @@ export function useRoofShapeSetting(id) {
|
||||
if (line.direction === 'bottom') {
|
||||
line.attributes = {
|
||||
offset: eavesOffset / 10,
|
||||
pitch: pitch,
|
||||
pitch: pitchRef.current,
|
||||
type: LINE_TYPE.WALLLINE.EAVES,
|
||||
}
|
||||
}
|
||||
@ -197,7 +209,7 @@ export function useRoofShapeSetting(id) {
|
||||
if (line.direction === 'top') {
|
||||
line.attributes = {
|
||||
offset: eavesOffset / 10,
|
||||
pitch: pitch,
|
||||
pitch: pitchRef.current,
|
||||
type: LINE_TYPE.WALLLINE.EAVES,
|
||||
}
|
||||
}
|
||||
@ -205,7 +217,7 @@ export function useRoofShapeSetting(id) {
|
||||
if (line.direction === 'bottom') {
|
||||
line.attributes = {
|
||||
offset: shedWidth / 10,
|
||||
pitch: pitch,
|
||||
pitch: pitchRef.current,
|
||||
type: LINE_TYPE.WALLLINE.SHED,
|
||||
}
|
||||
}
|
||||
@ -226,7 +238,7 @@ export function useRoofShapeSetting(id) {
|
||||
if (line.direction === 'top') {
|
||||
line.attributes = {
|
||||
offset: eavesOffset / 10,
|
||||
pitch: pitch,
|
||||
pitch: pitchRef.current,
|
||||
type: LINE_TYPE.WALLLINE.EAVES,
|
||||
}
|
||||
}
|
||||
@ -234,7 +246,7 @@ export function useRoofShapeSetting(id) {
|
||||
if (line.direction === 'bottom') {
|
||||
line.attributes = {
|
||||
offset: shedWidth / 10,
|
||||
pitch: pitch,
|
||||
pitch: pitchRef.current,
|
||||
type: LINE_TYPE.WALLLINE.SHED,
|
||||
}
|
||||
}
|
||||
@ -242,7 +254,7 @@ export function useRoofShapeSetting(id) {
|
||||
if (line.direction === 'bottom') {
|
||||
line.attributes = {
|
||||
offset: eavesOffset / 10,
|
||||
pitch: pitch,
|
||||
pitch: pitchRef.current,
|
||||
type: LINE_TYPE.WALLLINE.EAVES,
|
||||
}
|
||||
}
|
||||
@ -250,7 +262,7 @@ export function useRoofShapeSetting(id) {
|
||||
if (line.direction === 'top') {
|
||||
line.attributes = {
|
||||
offset: shedWidth / 10,
|
||||
pitch: pitch,
|
||||
pitch: pitchRef.current,
|
||||
type: LINE_TYPE.WALLLINE.SHED,
|
||||
}
|
||||
}
|
||||
@ -270,7 +282,7 @@ export function useRoofShapeSetting(id) {
|
||||
if (line.direction === 'right') {
|
||||
line.attributes = {
|
||||
offset: eavesOffset / 10,
|
||||
pitch: pitch,
|
||||
pitch: pitchRef.current,
|
||||
type: LINE_TYPE.WALLLINE.EAVES,
|
||||
}
|
||||
}
|
||||
@ -278,7 +290,7 @@ export function useRoofShapeSetting(id) {
|
||||
if (line.direction === 'left') {
|
||||
line.attributes = {
|
||||
offset: shedWidth / 10,
|
||||
pitch: pitch,
|
||||
pitch: pitchRef.current,
|
||||
type: LINE_TYPE.WALLLINE.SHED,
|
||||
}
|
||||
}
|
||||
@ -286,7 +298,7 @@ export function useRoofShapeSetting(id) {
|
||||
if (line.direction === 'left') {
|
||||
line.attributes = {
|
||||
offset: eavesOffset / 10,
|
||||
pitch: pitch,
|
||||
pitch: pitchRef.current,
|
||||
type: LINE_TYPE.WALLLINE.EAVES,
|
||||
}
|
||||
}
|
||||
@ -294,7 +306,7 @@ export function useRoofShapeSetting(id) {
|
||||
if (line.direction === 'right') {
|
||||
line.attributes = {
|
||||
offset: shedWidth / 10,
|
||||
pitch: pitch,
|
||||
pitch: pitchRef.current,
|
||||
type: LINE_TYPE.WALLLINE.SHED,
|
||||
}
|
||||
}
|
||||
@ -315,7 +327,7 @@ export function useRoofShapeSetting(id) {
|
||||
if (line.direction === 'left') {
|
||||
line.attributes = {
|
||||
offset: eavesOffset / 10,
|
||||
pitch: pitch,
|
||||
pitch: pitchRef.current,
|
||||
type: LINE_TYPE.WALLLINE.EAVES,
|
||||
}
|
||||
}
|
||||
@ -323,7 +335,7 @@ export function useRoofShapeSetting(id) {
|
||||
if (line.direction === 'right') {
|
||||
line.attributes = {
|
||||
offset: shedWidth / 10,
|
||||
pitch: pitch,
|
||||
pitch: pitchRef.current,
|
||||
type: LINE_TYPE.WALLLINE.SHED,
|
||||
}
|
||||
}
|
||||
@ -331,7 +343,7 @@ export function useRoofShapeSetting(id) {
|
||||
if (line.direction === 'right') {
|
||||
line.attributes = {
|
||||
offset: eavesOffset / 10,
|
||||
pitch: pitch,
|
||||
pitch: pitchRef.current,
|
||||
type: LINE_TYPE.WALLLINE.EAVES,
|
||||
}
|
||||
}
|
||||
@ -339,7 +351,7 @@ export function useRoofShapeSetting(id) {
|
||||
if (line.direction === 'left') {
|
||||
line.attributes = {
|
||||
offset: shedWidth / 10,
|
||||
pitch: pitch,
|
||||
pitch: pitchRef.current,
|
||||
type: LINE_TYPE.WALLLINE.SHED,
|
||||
}
|
||||
}
|
||||
@ -436,7 +448,7 @@ export function useRoofShapeSetting(id) {
|
||||
outerLines.forEach((line) => {
|
||||
line.attributes = {
|
||||
offset: eavesOffset / 10,
|
||||
pitch: pitch,
|
||||
pitch: pitchRef.current,
|
||||
type: LINE_TYPE.WALLLINE.EAVES,
|
||||
}
|
||||
// hideLine(line)
|
||||
@ -458,7 +470,7 @@ export function useRoofShapeSetting(id) {
|
||||
} else if (line.direction === 'top' || line.direction === 'bottom') {
|
||||
line.attributes = {
|
||||
offset: eavesOffset / 10,
|
||||
pitch: pitch,
|
||||
pitch: pitchRef.current,
|
||||
type: LINE_TYPE.WALLLINE.EAVES,
|
||||
}
|
||||
}
|
||||
@ -481,7 +493,7 @@ export function useRoofShapeSetting(id) {
|
||||
} else if (line.direction === 'left' || line.direction === 'right') {
|
||||
line.attributes = {
|
||||
offset: eavesOffset / 10,
|
||||
pitch: pitch,
|
||||
pitch: pitchRef.current,
|
||||
type: LINE_TYPE.WALLLINE.EAVES,
|
||||
}
|
||||
}
|
||||
@ -504,7 +516,7 @@ export function useRoofShapeSetting(id) {
|
||||
// 처마
|
||||
attributes = {
|
||||
type: LINE_TYPE.WALLLINE.EAVES,
|
||||
pitch: pitch,
|
||||
pitch: pitchRef.current,
|
||||
offset: eavesOffset / 10,
|
||||
}
|
||||
addPitchText(currentObject)
|
||||
@ -535,7 +547,7 @@ export function useRoofShapeSetting(id) {
|
||||
// 팔작지붕
|
||||
attributes = {
|
||||
type: LINE_TYPE.WALLLINE.HIPANDGABLE,
|
||||
pitch: pitch,
|
||||
pitch: pitchRef.current,
|
||||
offset: eavesOffset / 10,
|
||||
width: hipAndGableWidth / 10,
|
||||
}
|
||||
@ -550,7 +562,7 @@ export function useRoofShapeSetting(id) {
|
||||
type: LINE_TYPE.WALLLINE.JERKINHEAD,
|
||||
offset: gableOffset / 10,
|
||||
width: jerkinHeadWidth / 10,
|
||||
pitch: jerkinHeadPitch,
|
||||
pitch: jerkinHeadPitchRef.current,
|
||||
}
|
||||
addPitchText(currentObject)
|
||||
selectedLine.set({ strokeWidth: 4 })
|
||||
@ -629,5 +641,6 @@ export function useRoofShapeSetting(id) {
|
||||
setButtonAct,
|
||||
handleConfirm,
|
||||
handleRollBack,
|
||||
pitchText,
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,7 +12,6 @@ import { defineQPloygon } from '@/util/qpolygon-utils'
|
||||
import { writeImage } from '@/lib/canvas'
|
||||
import { useCanvasEvent } from '@/hooks/useCanvasEvent'
|
||||
import { useAxios } from '@/hooks/useAxios'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { useFont } from '@/hooks/common/useFont'
|
||||
|
||||
export function useCanvas(id) {
|
||||
@ -93,8 +92,6 @@ export function useCanvas(id) {
|
||||
canvas.getObjects().length > 0 && canvas?.clear()
|
||||
// settings for all canvas in the app
|
||||
fabric.Object.prototype.transparentCorners = false
|
||||
fabric.Object.prototype.id = uuidv4()
|
||||
fabric.Object.prototype.uuid = uuidv4()
|
||||
fabric.Object.prototype.selectable = true
|
||||
fabric.Object.prototype.lockMovementX = true
|
||||
fabric.Object.prototype.lockMovementY = true
|
||||
|
||||
@ -1,7 +1,15 @@
|
||||
import { useState } from 'react'
|
||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { canvasSizeState, canvasState, canvasZoomState, currentObjectState, fontFamilyState, fontSizeState } from '@/store/canvasAtom'
|
||||
import {
|
||||
canvasSizeState,
|
||||
canvasState,
|
||||
canvasZoomState,
|
||||
currentObjectState,
|
||||
fontFamilyState,
|
||||
fontSizeState,
|
||||
modifiedPlanFlagState,
|
||||
} from '@/store/canvasAtom'
|
||||
import { QPolygon } from '@/components/fabric/QPolygon'
|
||||
|
||||
// 캔버스에 필요한 이벤트
|
||||
@ -13,6 +21,7 @@ export function useCanvasEvent() {
|
||||
const fontSize = useRecoilValue(fontSizeState)
|
||||
const fontFamily = useRecoilValue(fontFamilyState)
|
||||
const [canvasZoom, setCanvasZoom] = useRecoilState(canvasZoomState)
|
||||
const [modifiedPlanFlag, setModifiedPlanFlag] = useRecoilState(modifiedPlanFlagState)
|
||||
|
||||
// 기본적인 이벤트 필요시 추가
|
||||
const attachDefaultEventOnCanvas = () => {
|
||||
@ -34,14 +43,32 @@ export function useCanvasEvent() {
|
||||
onChange: (e) => {
|
||||
const target = e.target
|
||||
|
||||
if (target.name !== 'mouseLine') {
|
||||
if (!modifiedPlanFlag) {
|
||||
setModifiedPlanFlag((prev) => !prev)
|
||||
}
|
||||
}
|
||||
|
||||
if (target) {
|
||||
target.uuid = uuidv4()
|
||||
// settleDown(target)
|
||||
}
|
||||
},
|
||||
addEvent: (e) => {
|
||||
const target = e.target
|
||||
|
||||
if (!target.id) {
|
||||
target.id = uuidv4()
|
||||
}
|
||||
if (!target.uuid) {
|
||||
target.uuid = uuidv4()
|
||||
}
|
||||
|
||||
if (target.name !== 'mouseLine') {
|
||||
if (!modifiedPlanFlag) {
|
||||
setModifiedPlanFlag((prev) => !prev)
|
||||
}
|
||||
}
|
||||
|
||||
if (target.type === 'QPolygon' || target.type === 'QLine') {
|
||||
const textObjs = canvas?.getObjects().filter((obj) => obj.name === 'lengthText')
|
||||
textObjs.forEach((obj) => {
|
||||
@ -141,6 +168,9 @@ export function useCanvasEvent() {
|
||||
})*/
|
||||
|
||||
target.on('moving', (e) => {
|
||||
target.uuid = uuidv4()
|
||||
setModifiedPlanFlag((prev) => !prev)
|
||||
|
||||
if (target.parentDirection === 'left' || target.parentDirection === 'right') {
|
||||
const minX = target.minX
|
||||
const maxX = target.maxX
|
||||
|
||||
@ -20,20 +20,25 @@ import DimensionLineSetting from '@/components/floor-plan/modal/dimensionLine/Di
|
||||
import RoofAllocationSetting from '@/components/floor-plan/modal/roofAllocation/RoofAllocationSetting'
|
||||
import LinePropertySetting from '@/components/floor-plan/modal/lineProperty/LinePropertySetting'
|
||||
import FlowDirectionSetting from '@/components/floor-plan/modal/flowDirection/FlowDirectionSetting'
|
||||
import { useCommonUtils } from './common/useCommonUtils'
|
||||
import { canvasState } from '@/store/canvasAtom'
|
||||
|
||||
export function useContextMenu({ externalFn }) {
|
||||
import { useCommonUtils } from './common/useCommonUtils'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { useCanvasEvent } from '@/hooks/useCanvasEvent'
|
||||
import { contextMenuState } from '@/store/contextMenu'
|
||||
|
||||
export function useContextMenu() {
|
||||
const currentMenu = useRecoilValue(currentMenuState) // 현재 메뉴
|
||||
const setContextPopupPosition = useSetRecoilState(contextPopupPositionState) // 현재 메뉴
|
||||
const [contextMenu, setContextMenu] = useState([[]]) // 메뉴.object 별 context menu
|
||||
const [currentContextMenu, setCurrentContextMenu] = useState(null) // 선택한 contextMenu
|
||||
const currentObject = useRecoilValue(currentObjectState)
|
||||
const { getMessage } = useMessage()
|
||||
const { addPopup } = usePopup()
|
||||
const [popupId, setPopupId] = useState(uuidv4())
|
||||
const [gridColor, setGridColor] = useRecoilState(gridColorState)
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
const { deleteObject, moveObject, copyObject, editText, changeDimensionExtendLine } = useCommonUtils({})
|
||||
const [qContextMenu, setQContextMenu] = useRecoilState(contextMenuState)
|
||||
const { handleZoomClear } = useCanvasEvent()
|
||||
|
||||
const currentMenuSetting = (position) => {
|
||||
switch (currentMenu) {
|
||||
@ -42,26 +47,26 @@ export function useContextMenu({ externalFn }) {
|
||||
[
|
||||
{
|
||||
id: 'gridMove',
|
||||
name: '그리드 이동',
|
||||
name: getMessage('modal.grid.move'),
|
||||
component: <GridMove id={popupId} />,
|
||||
},
|
||||
{
|
||||
id: 'gridCopy',
|
||||
name: '그리드 복사',
|
||||
name: getMessage('modal.grid.copy'),
|
||||
component: <GridCopy id={popupId} />,
|
||||
},
|
||||
{
|
||||
id: 'gridColorEdit',
|
||||
name: '그리드 색 변경',
|
||||
name: getMessage('modal.grid.color.edit'),
|
||||
component: <ColorPickerModal id={popupId} color={gridColor} setColor={setGridColor} />,
|
||||
},
|
||||
{
|
||||
id: 'remove',
|
||||
name: '삭제',
|
||||
name: getMessage('delete'),
|
||||
},
|
||||
{
|
||||
id: 'removeAll',
|
||||
name: '전체 삭제',
|
||||
name: getMessage('delete.all'),
|
||||
},
|
||||
],
|
||||
])
|
||||
@ -80,63 +85,64 @@ export function useContextMenu({ externalFn }) {
|
||||
[
|
||||
{
|
||||
id: 'refresh',
|
||||
name: '새로고침',
|
||||
fn: () => {
|
||||
externalFn.handleZoomClear()
|
||||
},
|
||||
name: getMessage('refresh'),
|
||||
fn: () => handleZoomClear(),
|
||||
},
|
||||
{
|
||||
id: 'roofMaterialPlacement',
|
||||
name: '지붕재 배치',
|
||||
name: getMessage('contextmenu.roof.material.placement'),
|
||||
},
|
||||
{
|
||||
id: 'roofMaterialRemove',
|
||||
name: '지붕재 삭제',
|
||||
name: getMessage('contextmenu.roof.material.remove'),
|
||||
},
|
||||
{
|
||||
id: 'roofMaterialRemoveAll',
|
||||
name: '지붕재 전체 삭제',
|
||||
name: getMessage('contextmenu.roof.material.remove.all'),
|
||||
},
|
||||
{
|
||||
id: 'selectMove',
|
||||
name: '선택・이동',
|
||||
name: getMessage('contextmenu.select.move'),
|
||||
},
|
||||
{
|
||||
id: 'wallLineRemove',
|
||||
name: '외벽선 삭제',
|
||||
name: getMessage('contextmenu.wallline.remove'),
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
id: 'sizeEdit',
|
||||
name: '사이즈 변경',
|
||||
name: getMessage('contextmenu.size.edit'),
|
||||
component: <AuxiliarySize id={popupId} />,
|
||||
},
|
||||
{
|
||||
id: 'auxiliaryMove',
|
||||
name: '보조선 이동(M)',
|
||||
name: `${getMessage('contextmenu.auxiliary.move')}(M)`,
|
||||
shortcut: ['m', 'M'],
|
||||
component: <AuxiliaryMove id={popupId} />,
|
||||
},
|
||||
{
|
||||
id: 'auxiliaryCopy',
|
||||
name: '보조선 복사(C)',
|
||||
name: `${getMessage('contextmenu.auxiliary.copy')}(C)`,
|
||||
shortcut: ['c', 'C'],
|
||||
component: <AuxiliaryCopy id={popupId} />,
|
||||
},
|
||||
{
|
||||
id: 'auxiliaryRemove',
|
||||
name: '보조선 삭제(D)',
|
||||
shortcut: ['d', 'D'],
|
||||
name: `${getMessage('contextmenu.auxiliary.remove')}(D)`,
|
||||
},
|
||||
{
|
||||
id: 'auxiliaryVerticalBisector',
|
||||
name: '보조선 수직이등분선',
|
||||
name: getMessage('contextmenu.auxiliary.vertical.bisector'),
|
||||
},
|
||||
{
|
||||
id: 'auxiliaryCut',
|
||||
name: '보조선 절삭',
|
||||
name: getMessage('contextmenu.auxiliary.cut'),
|
||||
},
|
||||
{
|
||||
id: 'auxiliaryRemoveAll',
|
||||
name: '보조선 전체 삭제',
|
||||
name: getMessage('contextmenu.auxiliary.remove.all'),
|
||||
},
|
||||
],
|
||||
])
|
||||
@ -151,33 +157,37 @@ export function useContextMenu({ externalFn }) {
|
||||
[
|
||||
{
|
||||
id: 'sizeEdit',
|
||||
name: '사이즈 변경',
|
||||
name: getMessage('contextmenu.size.edit'),
|
||||
},
|
||||
{
|
||||
id: 'remove',
|
||||
name: '삭제(D)',
|
||||
shortcut: ['d', 'D'],
|
||||
name: `${getMessage('contextmenu.remove')}(D)`,
|
||||
},
|
||||
{
|
||||
id: 'move',
|
||||
name: '이동(M)',
|
||||
shortcut: ['m', 'M'],
|
||||
name: `${getMessage('contextmenu.move')}(M)`,
|
||||
},
|
||||
{
|
||||
id: 'copy',
|
||||
name: '복사(C)',
|
||||
shortcut: ['c', 'C'],
|
||||
name: `${getMessage('contextmenu.copy')}(C)`,
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
id: 'roofMaterialEdit',
|
||||
name: '지붕재 변경',
|
||||
name: getMessage('contextmenu.roof.material.edit'),
|
||||
},
|
||||
{
|
||||
id: 'linePropertyEdit',
|
||||
name: '각 변 속성 변경',
|
||||
name: getMessage('contextmenu.line.property.edit'),
|
||||
component: <LinePropertySetting id={popupId} />,
|
||||
},
|
||||
{
|
||||
id: 'flowDirectionEdit',
|
||||
name: '흐름 방향 변경',
|
||||
name: getMessage('contextmenu.flow.direction.edit'),
|
||||
},
|
||||
],
|
||||
])
|
||||
@ -189,11 +199,29 @@ export function useContextMenu({ externalFn }) {
|
||||
}
|
||||
|
||||
const handleClick = (e, menu) => {
|
||||
if (menu?.fn) {
|
||||
menu.fn()
|
||||
}
|
||||
setContextPopupPosition({
|
||||
x: e.clientX,
|
||||
y: e.clientY,
|
||||
x: e?.clientX,
|
||||
y: e?.clientY,
|
||||
})
|
||||
setCurrentContextMenu(menu)
|
||||
setQContextMenu({ ...qContextMenu, visible: false })
|
||||
}
|
||||
|
||||
const handleKeyup = (e) => {
|
||||
let menu = null
|
||||
|
||||
for (let i = 0; i < contextMenu.length; i++) {
|
||||
const temp = contextMenu[i].filter((menu) => {
|
||||
return menu.shortcut?.includes(e.key)
|
||||
})
|
||||
|
||||
if (temp.length > 0) menu = temp
|
||||
}
|
||||
|
||||
handleClick(null, menu)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
@ -205,7 +233,6 @@ export function useContextMenu({ externalFn }) {
|
||||
}, [currentContextMenu])
|
||||
|
||||
useEffect(() => {
|
||||
console.log('object name', currentObject)
|
||||
if (currentObject?.name) {
|
||||
switch (currentObject.name) {
|
||||
case 'triangleDormer':
|
||||
@ -214,29 +241,32 @@ export function useContextMenu({ externalFn }) {
|
||||
[
|
||||
{
|
||||
id: 'sizeEdit',
|
||||
name: '사이즈 변경',
|
||||
component: <SizeSetting id={popupId} />,
|
||||
name: getMessage('contextmenu.size.edit'),
|
||||
component: <SizeSetting id={popupId} target={currentObject} />,
|
||||
},
|
||||
{
|
||||
id: 'dormerRemove',
|
||||
name: '삭제(D)',
|
||||
shortcut: ['d', 'D'],
|
||||
name: `${getMessage('contextmenu.remove')}(D)`,
|
||||
},
|
||||
{
|
||||
id: 'dormerMove',
|
||||
name: '이동(M)',
|
||||
shortcut: ['m', 'M'],
|
||||
name: `${getMessage('contextmenu.move')}(M)`,
|
||||
},
|
||||
{
|
||||
id: 'dormerCopy',
|
||||
name: '복사(C)',
|
||||
shortcut: ['c', 'C'],
|
||||
name: `${getMessage('contextmenu.copy')}(C)`,
|
||||
},
|
||||
{
|
||||
id: 'roofMaterialEdit',
|
||||
name: '지붕재 변경',
|
||||
name: getMessage('contextmenu.roof.material.edit'),
|
||||
component: <RoofMaterialSetting id={popupId} />,
|
||||
},
|
||||
{
|
||||
id: 'dormerOffset',
|
||||
name: '도머 오프셋',
|
||||
name: getMessage('contextmenu.dormer.offset'),
|
||||
component: <DormerOffset id={popupId} />,
|
||||
},
|
||||
],
|
||||
@ -252,32 +282,35 @@ export function useContextMenu({ externalFn }) {
|
||||
},
|
||||
{
|
||||
id: 'roofMaterialRemove',
|
||||
name: '삭제(D)',
|
||||
shortcut: ['d', 'D'],
|
||||
name: `${getMessage('contextmenu.remove')}(D)`,
|
||||
},
|
||||
{
|
||||
id: 'roofMaterialMove',
|
||||
name: '이동(M)',
|
||||
shortcut: ['m', 'M'],
|
||||
name: `${getMessage('contextmenu.move')}(M)`,
|
||||
},
|
||||
{
|
||||
id: 'roofMaterialCopy',
|
||||
name: '복사(C)',
|
||||
shortcut: ['c', 'C'],
|
||||
name: `${getMessage('contextmenu.copy')}(C)`,
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
id: 'roofMaterialEdit',
|
||||
name: '지붕재 변경',
|
||||
name: getMessage('contextmenu.roof.material.edit'),
|
||||
component: <RoofAllocationSetting id={popupId} />,
|
||||
},
|
||||
{
|
||||
id: 'linePropertyEdit',
|
||||
name: '각 변 속성 변경',
|
||||
name: getMessage('contextmenu.line.property.edit'),
|
||||
component: <LinePropertySetting id={popupId} />,
|
||||
},
|
||||
{
|
||||
id: 'flowDirectionEdit',
|
||||
name: '흐름 뱡향 변경',
|
||||
component: <FlowDirectionSetting id={popupId} />,
|
||||
name: getMessage('contextmenu.flow.direction.edit'),
|
||||
component: <FlowDirectionSetting id={popupId} target={currentObject} />,
|
||||
},
|
||||
],
|
||||
])
|
||||
@ -287,23 +320,26 @@ export function useContextMenu({ externalFn }) {
|
||||
[
|
||||
{
|
||||
id: 'sizeEdit',
|
||||
name: '사이즈 변경',
|
||||
name: getMessage('contextmenu.size.edit'),
|
||||
},
|
||||
{
|
||||
id: 'openingRemove',
|
||||
name: '삭제(D)',
|
||||
shortcut: ['d', 'D'],
|
||||
name: `${getMessage('contextmenu.remove')}(D)`,
|
||||
},
|
||||
{
|
||||
id: 'openingMove',
|
||||
name: '이동(M)',
|
||||
shortcut: ['m', 'M'],
|
||||
name: `${getMessage('contextmenu.move')}(M)`,
|
||||
},
|
||||
{
|
||||
id: 'openingCopy',
|
||||
name: '복사(C)',
|
||||
shortcut: ['c', 'C'],
|
||||
name: `${getMessage('contextmenu.copy')}(C)`,
|
||||
},
|
||||
{
|
||||
id: 'openingOffset',
|
||||
name: '개구 오프셋',
|
||||
name: getMessage('contextmenu.opening.offset'),
|
||||
},
|
||||
],
|
||||
])
|
||||
@ -313,19 +349,19 @@ export function useContextMenu({ externalFn }) {
|
||||
[
|
||||
{
|
||||
id: 'lengthTextRemove',
|
||||
name: '삭제',
|
||||
name: getMessage('contextmenu.remove'),
|
||||
},
|
||||
{
|
||||
id: 'lengthTextMove',
|
||||
name: '이동',
|
||||
name: getMessage('contextmenu.move'),
|
||||
},
|
||||
{
|
||||
id: 'lengthTextAuxiliaryLineEdit',
|
||||
name: '치수 보조선 변경',
|
||||
name: getMessage('contextmenu.dimension.auxiliary.line.edit'),
|
||||
},
|
||||
{
|
||||
id: 'displayEdit',
|
||||
name: '표시 변경',
|
||||
name: getMessage('contextmenu.display.edit'),
|
||||
},
|
||||
],
|
||||
])
|
||||
@ -335,27 +371,27 @@ export function useContextMenu({ externalFn }) {
|
||||
[
|
||||
{
|
||||
id: 'commonTextRemove',
|
||||
name: '삭제',
|
||||
name: getMessage('contextmenu.remove'),
|
||||
fn: () => deleteObject(),
|
||||
},
|
||||
{
|
||||
id: 'commonTextMove',
|
||||
name: '이동',
|
||||
name: getMessage('contextmenu.move'),
|
||||
fn: () => moveObject(),
|
||||
},
|
||||
{
|
||||
id: 'commonTextCopy',
|
||||
name: '복사',
|
||||
name: getMessage('contextmenu.copy'),
|
||||
fn: () => copyObject(),
|
||||
},
|
||||
{
|
||||
id: 'commonTextFontSetting',
|
||||
name: '폰트 설정',
|
||||
name: getMessage('contextmenu.font.setting'),
|
||||
component: <FontSetting id={popupId} type={'commonText'} />,
|
||||
},
|
||||
{
|
||||
id: 'commonTextEdit',
|
||||
name: '편집',
|
||||
name: getMessage('contextmenu.edit'),
|
||||
fn: () => editText(),
|
||||
},
|
||||
],
|
||||
@ -366,23 +402,23 @@ export function useContextMenu({ externalFn }) {
|
||||
[
|
||||
{
|
||||
id: 'gridMove',
|
||||
name: '그리드 이동',
|
||||
name: getMessage('modal.grid.move'),
|
||||
},
|
||||
{
|
||||
id: 'gridCopy',
|
||||
name: '그리드 복사',
|
||||
name: getMessage('modal.grid.copy'),
|
||||
},
|
||||
{
|
||||
id: 'gridColorEdit',
|
||||
name: '그리드 색 변경',
|
||||
name: getMessage('contextmenu.grid.color.edit'),
|
||||
},
|
||||
{
|
||||
id: 'remove',
|
||||
name: '삭제',
|
||||
name: getMessage('contextmenu.remove'),
|
||||
},
|
||||
{
|
||||
id: 'removeAll',
|
||||
name: '전체 삭제',
|
||||
name: getMessage('contextmenu.remove.all'),
|
||||
},
|
||||
],
|
||||
])
|
||||
@ -392,22 +428,22 @@ export function useContextMenu({ externalFn }) {
|
||||
[
|
||||
{
|
||||
id: 'dimensionLineRemove',
|
||||
name: '삭제',
|
||||
name: getMessage('contextmenu.remove'),
|
||||
fn: () => deleteObject(),
|
||||
},
|
||||
{
|
||||
id: 'dimensionLineMove',
|
||||
name: '이동',
|
||||
name: getMessage('contextmenu.move'),
|
||||
fn: () => moveObject(),
|
||||
},
|
||||
{
|
||||
id: 'dimensionAuxiliaryLineEdit',
|
||||
name: '치수 보조선 변경',
|
||||
name: getMessage('contextmenu.dimension.auxiliary.line.edit'),
|
||||
fn: () => changeDimensionExtendLine(),
|
||||
},
|
||||
{
|
||||
id: 'dimensionLineDisplayEdit',
|
||||
name: '표시 변경',
|
||||
name: getMessage('contextmenu.display.edit'),
|
||||
component: <DimensionLineSetting id={popupId} />,
|
||||
},
|
||||
],
|
||||
@ -418,20 +454,23 @@ export function useContextMenu({ externalFn }) {
|
||||
[
|
||||
{
|
||||
id: 'sizeEdit',
|
||||
name: '사이즈 변경',
|
||||
name: getMessage('contextmenu.size.edit'),
|
||||
component: <SizeSetting id={popupId} />,
|
||||
},
|
||||
{
|
||||
id: 'remove',
|
||||
name: '삭제(D)',
|
||||
shortcut: ['d', 'D'],
|
||||
name: `${getMessage('contextmenu.remove')}(D)`,
|
||||
},
|
||||
{
|
||||
id: 'move',
|
||||
name: '이동(M)',
|
||||
shortcut: ['m', 'M'],
|
||||
name: `${getMessage('contextmenu.move')}(M)`,
|
||||
},
|
||||
{
|
||||
id: 'copy',
|
||||
name: '복사(C)',
|
||||
shortcut: ['c', 'C'],
|
||||
name: `${getMessage('contextmenu.copy')}(C)`,
|
||||
},
|
||||
],
|
||||
])
|
||||
@ -449,5 +488,6 @@ export function useContextMenu({ externalFn }) {
|
||||
currentContextMenu,
|
||||
setCurrentContextMenu,
|
||||
handleClick,
|
||||
handleKeyup,
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { canvasState, fontFamilyState, fontSizeState } from '@/store/canvasAtom'
|
||||
import { ANGLE_TYPE, canvasState, currentAngleTypeSelector, fontFamilyState, fontSizeState } from '@/store/canvasAtom'
|
||||
import { QLine } from '@/components/fabric/QLine'
|
||||
import { getChonByDegree, getDegreeByChon } from '@/util/canvas-util'
|
||||
|
||||
export const useLine = () => {
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
const fontSize = useRecoilValue(fontSizeState)
|
||||
const fontFamily = useRecoilValue(fontFamilyState)
|
||||
const currentAngleType = useRecoilValue(currentAngleTypeSelector)
|
||||
|
||||
const addLine = (points = [], options) => {
|
||||
const line = new QLine(points, {
|
||||
@ -77,6 +79,11 @@ export const useLine = () => {
|
||||
|
||||
let left, top
|
||||
|
||||
const textStr =
|
||||
currentAngleType === ANGLE_TYPE.SLOPE
|
||||
? `${attributes.offset ? attributes.offset * 10 : attributes.width * 10}${attributes.pitch ? '-∠' + attributes.pitch : ''}`
|
||||
: `${attributes.offset ? attributes.offset * 10 : attributes.width * 10}${attributes.pitch ? '-∠' + getDegreeByChon(attributes.pitch) : ''}`
|
||||
|
||||
if (direction === 'top') {
|
||||
left = (startPoint.x + endPoint.x) / 2
|
||||
top = (startPoint.y + endPoint.y) / 2 - 50
|
||||
@ -91,17 +98,20 @@ export const useLine = () => {
|
||||
top = (startPoint.y + endPoint.y) / 2 - 30
|
||||
}
|
||||
|
||||
const text = new fabric.Text(
|
||||
`${attributes.offset ? attributes.offset * 10 : attributes.width * 10}${attributes.pitch ? '-∠' + attributes.pitch : ''}`,
|
||||
{
|
||||
left,
|
||||
top,
|
||||
fontSize: 20,
|
||||
fill: 'black',
|
||||
name: 'pitchText',
|
||||
parentId: line.id,
|
||||
},
|
||||
)
|
||||
if (!attributes.pitch) {
|
||||
return
|
||||
}
|
||||
|
||||
const text = new fabric.Text(`${textStr}`, {
|
||||
left,
|
||||
top,
|
||||
fontSize: 20,
|
||||
originText: `${attributes.offset ? attributes.offset * 10 : attributes.width * 10}`,
|
||||
fill: 'black',
|
||||
name: 'pitchText',
|
||||
parentId: line.id,
|
||||
pitch: attributes.pitch,
|
||||
})
|
||||
|
||||
canvas.add(text)
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useRecoilState } from 'recoil'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { canvasState, currentCanvasPlanState, initCanvasPlansState, plansState, modifiedPlansState } from '@/store/canvasAtom'
|
||||
import { v4 as uuidv4, validate as isValidUUID } from 'uuid'
|
||||
import { canvasState, currentCanvasPlanState, initCanvasPlansState, plansState, modifiedPlansState, modifiedPlanFlagState } from '@/store/canvasAtom'
|
||||
import { useAxios } from '@/hooks/useAxios'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { useSwal } from '@/hooks/useSwal'
|
||||
@ -14,6 +14,7 @@ export function usePlan() {
|
||||
const [initCanvasPlans, setInitCanvasPlans] = useRecoilState(initCanvasPlansState) // DB에 저장된 plan
|
||||
const [plans, setPlans] = useRecoilState(plansState) // 전체 plan (DB에 저장된 plan + 저장 안된 새로운 plan)
|
||||
const [modifiedPlans, setModifiedPlans] = useRecoilState(modifiedPlansState) // 변경된 canvas plan
|
||||
const [modifiedPlanFlag, setModifiedPlanFlag] = useRecoilState(modifiedPlanFlagState) // 캔버스 실시간 오브젝트 이벤트 감지 flag
|
||||
|
||||
const { swalFire } = useSwal()
|
||||
const { getMessage } = useMessage()
|
||||
@ -39,6 +40,7 @@ export function usePlan() {
|
||||
'length',
|
||||
'idx',
|
||||
'direction',
|
||||
'parentDirection',
|
||||
'lines',
|
||||
'points',
|
||||
'lockMovementX',
|
||||
@ -89,30 +91,29 @@ export function usePlan() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 캔버스에서 발생하는 실시간 오브젝트 이벤트를 감지하여 수정 여부를 판단
|
||||
* 캔버스에서 발생하는 실시간 오브젝트 이벤트를 감지하여 수정 여부를 확인 후 관리
|
||||
*/
|
||||
const checkCanvasObjectEvent = (e, planId) => {
|
||||
const checkCanvasObjectEvent = (planId) => {
|
||||
if (!modifiedPlans.some((modifiedPlan) => modifiedPlan === planId) && checkModifiedCanvasPlan(planId)) {
|
||||
setModifiedPlans([...modifiedPlans, planId])
|
||||
setModifiedPlans((prev) => [...prev, planId])
|
||||
setModifiedPlanFlag(false)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 현재 캔버스 상태와 DB에 저장된 캔버스 상태를 비교하여 수정 여부를 판단
|
||||
*/
|
||||
const checkModifiedCanvasPlan = (planId) => {
|
||||
const canvasStatus = currentCanvasData()
|
||||
const initPlanData = initCanvasPlans.find((plan) => plan.id === planId)
|
||||
|
||||
if (!initPlanData) {
|
||||
if (isValidUUID(planId)) {
|
||||
// 새로운 캔버스
|
||||
return JSON.parse(canvasStatus).objects.length > 0
|
||||
} else {
|
||||
// 저장된 캔버스
|
||||
// 각각 object들의 uuid 목록을 추출하여 비교
|
||||
const canvasObjsUuids = getObjectUuids(JSON.parse(canvasStatus).objects)
|
||||
const initPlanData = initCanvasPlans.find((plan) => plan.id === planId)
|
||||
const dbObjsUuids = getObjectUuids(JSON.parse(initPlanData.canvasStatus).objects)
|
||||
return canvasObjsUuids.length !== dbObjsUuids.length || !canvasObjsUuids.every((id, index) => id === dbObjsUuids[index])
|
||||
return canvasObjsUuids.length !== dbObjsUuids.length || !canvasObjsUuids.every((uuid, index) => uuid === dbObjsUuids[index])
|
||||
}
|
||||
}
|
||||
const getObjectUuids = (objects) => {
|
||||
@ -121,10 +122,12 @@ export function usePlan() {
|
||||
.map((obj) => obj.uuid)
|
||||
.sort()
|
||||
}
|
||||
/**
|
||||
* 캔버스에 저장되지 않은 변경사항이 있는 경우 저장 여부를 확인 후 저장
|
||||
*/
|
||||
const checkUnsavedCanvasPlan = async () => {
|
||||
|
||||
const resetModifiedPlans = () => {
|
||||
setModifiedPlans([])
|
||||
setModifiedPlanFlag(false)
|
||||
}
|
||||
const checkUnsavedCanvasPlan = (str) => {
|
||||
if (modifiedPlans.length > 0) {
|
||||
swalFire({
|
||||
text: `${currentCanvasPlan.name} ` + getMessage('plan.message.confirm.save'),
|
||||
@ -364,7 +367,7 @@ export function usePlan() {
|
||||
plans,
|
||||
modifiedPlans,
|
||||
checkCanvasObjectEvent,
|
||||
checkUnsavedCanvasPlan,
|
||||
resetModifiedPlans,
|
||||
saveCanvas,
|
||||
handleCurrentPlan,
|
||||
handleAddPlan,
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { canvasState, fontFamilyState, fontSizeState } from '@/store/canvasAtom'
|
||||
import { ANGLE_TYPE, canvasState, currentAngleTypeSelector, fontFamilyState, fontSizeState, pitchTextSelector } from '@/store/canvasAtom'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { fabric } from 'fabric'
|
||||
import { getDirectionByPoint } from '@/util/canvas-util'
|
||||
import { getDegreeByChon, getDirectionByPoint } from '@/util/canvas-util'
|
||||
import { QPolygon } from '@/components/fabric/QPolygon'
|
||||
import { isSamePoint } from '@/util/qpolygon-utils'
|
||||
import { flowDisplaySelector } from '@/store/settingAtom'
|
||||
@ -12,6 +12,8 @@ export const usePolygon = () => {
|
||||
const isFlowDisplay = useRecoilValue(flowDisplaySelector)
|
||||
const flowFontOptions = useRecoilValue(fontSelector('flowText'))
|
||||
const lengthTextFontOptions = useRecoilValue(fontSelector('lengthText'))
|
||||
const currentAngleType = useRecoilValue(currentAngleTypeSelector)
|
||||
const pitchText = useRecoilValue(pitchTextSelector)
|
||||
|
||||
const addPolygon = (points, options) => {
|
||||
const polygon = new QPolygon(points, {
|
||||
@ -401,14 +403,18 @@ export const usePolygon = () => {
|
||||
|
||||
const addTextByArrows = (arrows, txt, canvas) => {
|
||||
arrows.forEach((arrow, index) => {
|
||||
const text = new fabric.Text(`${txt}${index + 1} (${arrow.pitch}寸)`, {
|
||||
const textStr = `${txt}${index + 1} (${currentAngleType === ANGLE_TYPE.SLOPE ? arrow.pitch : getDegreeByChon(arrow.pitch)}${pitchText})`
|
||||
|
||||
const text = new fabric.Text(`${textStr}`, {
|
||||
fontSize: flowFontOptions.fontSize.value,
|
||||
fill: flowFontOptions.fontColor.value,
|
||||
fontFamily: flowFontOptions.fontFamily.value,
|
||||
fontWeight: flowFontOptions.fontWeight.value,
|
||||
pitch: arrow.pitch,
|
||||
originX: 'center',
|
||||
originY: 'center',
|
||||
name: 'flowText',
|
||||
originText: `${txt}${index + 1}`,
|
||||
selectable: false,
|
||||
left: arrow.stickeyPoint.x,
|
||||
top: arrow.stickeyPoint.y,
|
||||
|
||||
@ -192,6 +192,8 @@
|
||||
"modal.grid.copy.info": "間隔を設定し、コピー方向を選択します",
|
||||
"modal.grid.copy.length": "長さ",
|
||||
"modal.grid.copy.save": "保存",
|
||||
"modal.grid.color.edit": "그리드 색 변경(JA)",
|
||||
"modal.dormer.offset.info": "移動する方向を入力してください",
|
||||
"modal.common.save": "保存",
|
||||
"modal.common.add": "追加",
|
||||
"modal.common.prev": "以前",
|
||||
@ -261,12 +263,54 @@
|
||||
"modal.placement.surface.setting.diagonal.length": "斜めの長さ",
|
||||
"modal.color.picker.title": "色の設定",
|
||||
"modal.color.picker.default.color": "基本色",
|
||||
"modal.size.setting": "サイズ変更",
|
||||
"modal.shape.flow.direction.setting": "面フローの設定",
|
||||
"modal.shape.flow.direction.setting.orientation.setting.info": "シミュレーション計算の方向を指定します。面が向いている方位を選択してください。",
|
||||
"modal.shape.flow.direction.setting.orientation.8": "8方位に選ぶ",
|
||||
"modal.shape.flow.direction.setting.orientation.24": "24方位から選択する (表記は8方位です。)",
|
||||
"modal.panel.batch.statistic": "パネル配置集計",
|
||||
"modal.panel.batch.statistic.roof.shape": "屋根面",
|
||||
"modal.panel.batch.statistic.power.generation.amount": "発電量",
|
||||
"modal.panel.batch.statistic.total": "合計",
|
||||
"modal.flow.direction.setting": "流れ方向の設定",
|
||||
"modal.flow.direction.setting.info": "流れ方向を選択してください。",
|
||||
"plan.message.confirm.save": "PLAN을 저장하시겠습니까?",
|
||||
"plan.message.confirm.copy": "PLAN을 복사하시겠습니까?",
|
||||
"plan.message.confirm.delete": "PLAN을 삭제하시겠습니까?",
|
||||
"plan.message.save": "저장되었습니다.",
|
||||
"plan.message.delete": "삭제되었습니다.",
|
||||
"setting": "設定",
|
||||
"delete": "삭제(JA)",
|
||||
"delete.all": "전체 삭제(JA)",
|
||||
"refresh": "새로고침(JA)",
|
||||
"contextmenu.roof.material.placement": "지붕재 배치(JA)",
|
||||
"contextmenu.roof.material.edit": "지붕재 변경(JA)",
|
||||
"contextmenu.roof.material.remove": "지붕재 삭제(JA)",
|
||||
"contextmenu.roof.material.remove.all": "지붕재 전체 삭제(JA)",
|
||||
"contextmenu.dormer.offset": "도머 오프셋(JA)",
|
||||
"contextmenu.select.move": "선택・이동(JA)",
|
||||
"contextmenu.wallline.remove": "외벽선 삭제(JA)",
|
||||
"contextmenu.size.edit": "サイズ変更",
|
||||
"contextmenu.auxiliary.move": "보조선 이동(JA)",
|
||||
"contextmenu.auxiliary.copy": "보조선 복사(JA)",
|
||||
"contextmenu.auxiliary.remove": "보조선 삭제(JA)",
|
||||
"contextmenu.auxiliary.vertical.bisector": "보조선 수직이등분선(JA)",
|
||||
"contextmenu.auxiliary.cut": "보조선 절삭(JA)",
|
||||
"contextmenu.auxiliary.remove.all": "보조선 전체 삭제(JA)",
|
||||
"contextmenu.line.property.edit": "各辺属性の変更",
|
||||
"modal.line.property.edit.info": "属性を変更する辺を選択してください。",
|
||||
"modal.line.property.edit.selected": "選択した値",
|
||||
"contextmenu.flow.direction.edit": "흐름 방향 변경(JA)",
|
||||
"contextmenu.font.setting": "폰트 설정(JA)",
|
||||
"contextmenu.grid.color.edit": "그리드 색 변경(JA)",
|
||||
"contextmenu.dimension.auxiliary.line.edit": "치수 보조선 변경(JA)",
|
||||
"contextmenu.display.edit": "표시 변경(JA)",
|
||||
"contextmenu.opening.offset": "개구 오프셋(JA)",
|
||||
"contextmenu.remove": "삭제(JA)",
|
||||
"contextmenu.remove.all": "전체 삭제(JA)",
|
||||
"contextmenu.move": "이동(JA)",
|
||||
"contextmenu.copy": "복사(JA)",
|
||||
"contextmenu.edit": "편집(JA)",
|
||||
"common.message.no.data": "No data",
|
||||
"common.message.no.dataDown": "ダウンロードするデータがありません",
|
||||
"common.message.noData": "表示するデータがありません",
|
||||
@ -357,7 +401,7 @@
|
||||
"common.require": "必須",
|
||||
"commons.west": "立つ",
|
||||
"commons.east": "ドン",
|
||||
"commons.south": "立つ",
|
||||
"commons.south": "南",
|
||||
"commons.north": "北",
|
||||
"site.name": "Q.CAST III",
|
||||
"site.sub_name": "太陽光発電システム図面管理サイト",
|
||||
@ -376,6 +420,7 @@
|
||||
"board.sub.total": "全体",
|
||||
"board.sub.fileList": "添付ファイル一覧",
|
||||
"board.sub.updDt": "更新日",
|
||||
"board.sub.btn.close": "閉じる",
|
||||
"myinfo.title": "マイプロフィール",
|
||||
"myinfo.info.userId": "ユーザーID",
|
||||
"myinfo.info.nameKana": "担当者名ふりがな",
|
||||
@ -445,6 +490,8 @@
|
||||
"join.complete.title": "Q.CAST3 ログインID発行申請完了",
|
||||
"join.complete.contents": "※ 申請したIDが承認されると、担当者情報に入力されたメールアドレスにログイン案内メールが送信されます。",
|
||||
"join.complete.email_comment": "担当者メールアドレス",
|
||||
"join.validation.check1": "{0} の形式を確認してください。",
|
||||
"join.complete.save.confirm": "Hanwha Japan担当者にID承認を要請されると、これ以上情報を修正できません。 本当にリクエストしますか?",
|
||||
"stuff.gridHeader.lastEditDatetime": "更新日時",
|
||||
"stuff.gridHeader.objectNo": "品番",
|
||||
"stuff.gridHeader.planTotCnt": "プラン数",
|
||||
@ -486,6 +533,7 @@
|
||||
"stuff.detail.saleStoreId": "一次販売店名/ID",
|
||||
"stuff.detail.otherSaleStoreId": "二次販売店名/ID",
|
||||
"stuff.detail.zipNo": "郵便番号 ",
|
||||
"stuff.detail.address": "住所 ",
|
||||
"stuff.detail.btn.addressPop": "住所検索",
|
||||
"stuff.detail.btn.addressPop.guide": "※ 郵便番号7桁を入力した後、アドレス検索ボタンをクリックしてください",
|
||||
"stuff.detail.prefId": "都道府県 / 住所 ",
|
||||
@ -584,6 +632,7 @@
|
||||
"stuff.detail.planGridHeader.management": "管理",
|
||||
"stuff.detail.planGrid.btn1": "見積書の照会",
|
||||
"stuff.detail.planGrid.btn2": "Excel",
|
||||
"stuff.grid.noData": "照会されたデータがありません",
|
||||
"length": "長さ",
|
||||
"height": "高さ",
|
||||
"output": "出力",
|
||||
@ -595,9 +644,23 @@
|
||||
"size": "寸",
|
||||
"size.angle": "寸(度)",
|
||||
"eaves": "軒",
|
||||
"eaves.line": "軒先",
|
||||
"gable": "ケラバ",
|
||||
"gable.left": "ケラバ左",
|
||||
"gable.right": "ケラバ右",
|
||||
"ridge": "龍丸",
|
||||
"oneside.flow.ridge": "片側の流れ",
|
||||
"yosemune": "ヨセムネ",
|
||||
"valley": "谷",
|
||||
"l.abandon.valley": "Lの捨て渓谷",
|
||||
"mansard": "マンサード",
|
||||
"wall": "壁",
|
||||
"wall.merge": "壁取り",
|
||||
"wall.merge.type": "壁取り(型)",
|
||||
"wall.merge.flow": "壁取合(流れ)",
|
||||
"wall.merge.flow.left": "壁取合(流れ左)",
|
||||
"wall.merge.flow.right": "壁取り(流れ右)",
|
||||
"no.setting": "설정없음",
|
||||
"hajebichi": "ハゼビーチ",
|
||||
"straight.line": "直線",
|
||||
"right.angle": "直角",
|
||||
|
||||
@ -196,6 +196,8 @@
|
||||
"modal.grid.copy.info": "간격을 설정하고 복사 방향을 선택하십시오",
|
||||
"modal.grid.copy.length": "길이",
|
||||
"modal.grid.copy.save": "저장",
|
||||
"modal.grid.color.edit": "그리드 색 변경",
|
||||
"modal.dormer.offset.info": "이동할 거리와 방향을 입력해주세요.",
|
||||
"modal.common.save": "저장",
|
||||
"modal.common.add": "추가",
|
||||
"modal.common.prev": "이전",
|
||||
@ -266,12 +268,54 @@
|
||||
"modal.placement.surface.setting.diagonal.length": "대각선 길이",
|
||||
"modal.color.picker.title": "색 설정",
|
||||
"modal.color.picker.default.color": "기본색상",
|
||||
"modal.size.setting": "사이즈 변경",
|
||||
"modal.shape.flow.direction.setting": "면 흐름 설정",
|
||||
"modal.shape.flow.direction.setting.orientation.setting.info": "시뮬레이션 계산용 방위를 지정합니다. 면이 향하고 있는 방위를 선택해 주세요.",
|
||||
"modal.shape.flow.direction.setting.orientation.8": "8방위로 선택한다.",
|
||||
"modal.shape.flow.direction.setting.orientation.24": "24방위로 선택한다.(표기는 8방위입니다.)",
|
||||
"modal.panel.batch.statistic": "패널 배치 집계",
|
||||
"modal.panel.batch.statistic.roof.shape": "지붕면",
|
||||
"modal.panel.batch.statistic.power.generation.amount": "발전량",
|
||||
"modal.panel.batch.statistic.total": "합계",
|
||||
"modal.flow.direction.setting": "흐름 방향 설정",
|
||||
"modal.flow.direction.setting.info": "흐름방향을 선택하세요.",
|
||||
"plan.message.confirm.save": "PLAN을 저장하시겠습니까?",
|
||||
"plan.message.confirm.copy": "PLAN을 복사하시겠습니까?",
|
||||
"plan.message.confirm.delete": "PLAN을 삭제하시겠습니까?",
|
||||
"plan.message.save": "저장되었습니다.",
|
||||
"plan.message.delete": "삭제되었습니다.",
|
||||
"setting": "설정",
|
||||
"delete": "삭제",
|
||||
"delete.all": "전체 삭제",
|
||||
"refresh": "새로고침",
|
||||
"contextmenu.roof.material.placement": "지붕재 배치",
|
||||
"contextmenu.roof.material.edit": "지붕재 변경",
|
||||
"contextmenu.roof.material.remove": "지붕재 삭제",
|
||||
"contextmenu.roof.material.remove.all": "지붕재 전체 삭제",
|
||||
"contextmenu.dormer.offset": "도머 오프셋",
|
||||
"contextmenu.select.move": "선택・이동",
|
||||
"contextmenu.wallline.remove": "외벽선 삭제",
|
||||
"contextmenu.size.edit": "사이즈 변경",
|
||||
"contextmenu.auxiliary.move": "보조선 이동",
|
||||
"contextmenu.auxiliary.copy": "보조선 복사",
|
||||
"contextmenu.auxiliary.remove": "보조선 삭제",
|
||||
"contextmenu.auxiliary.vertical.bisector": "보조선 수직이등분선",
|
||||
"contextmenu.auxiliary.cut": "보조선 절삭",
|
||||
"contextmenu.auxiliary.remove.all": "보조선 전체 삭제",
|
||||
"contextmenu.line.property.edit": "각 변 속성 변경",
|
||||
"modal.line.property.edit.info": "속성을 변경할 변을 선택해주세요.",
|
||||
"modal.line.property.edit.selected": "선택한 값",
|
||||
"contextmenu.flow.direction.edit": "흐름 방향 변경",
|
||||
"contextmenu.font.setting": "폰트 설정",
|
||||
"contextmenu.grid.color.edit": "그리드 색 변경",
|
||||
"contextmenu.dimension.auxiliary.line.edit": "치수 보조선 변경",
|
||||
"contextmenu.display.edit": "표시 변경",
|
||||
"contextmenu.opening.offset": "개구 오프셋",
|
||||
"contextmenu.remove": "삭제",
|
||||
"contextmenu.remove.all": "전체 삭제",
|
||||
"contextmenu.move": "이동",
|
||||
"contextmenu.copy": "복사",
|
||||
"contextmenu.edit": "편집",
|
||||
"common.message.no.data": "No data",
|
||||
"common.message.no.dataDown": "No data to download",
|
||||
"common.message.noData": "No data to display",
|
||||
@ -381,6 +425,7 @@
|
||||
"board.sub.total": "전체",
|
||||
"board.sub.fileList": "첨부파일 목록",
|
||||
"board.sub.updDt": "업데이트",
|
||||
"board.sub.btn.close": "닫기",
|
||||
"myinfo.title": "My profile",
|
||||
"myinfo.info.userId": "사용자ID",
|
||||
"myinfo.info.nameKana": "담당자명 후리가나",
|
||||
@ -450,6 +495,8 @@
|
||||
"join.complete.title": "Q.CAST3 로그인ID 발행신청 완료",
|
||||
"join.complete.contents": "※ 신청한 ID가 승인되면, 담당자 정보에 입력한 이메일 주소로 로그인 관련 안내 메일이 전송됩니다.",
|
||||
"join.complete.email_comment": "담당자 이메일 주소",
|
||||
"join.validation.check1": "{0} 형식을 확인해주세요.",
|
||||
"join.complete.save.confirm": "Hanwha Japan 담당자에게 ID승인이 요청되면 더 이상 정보를 수정할 수 없습니다. 정말로 요청하시겠습니까?",
|
||||
"stuff.gridHeader.lastEditDatetime": "갱신일시",
|
||||
"stuff.gridHeader.objectNo": "물건번호",
|
||||
"stuff.gridHeader.planTotCnt": "플랜 수",
|
||||
@ -491,6 +538,7 @@
|
||||
"stuff.detail.saleStoreId": "1차 판매점명 / ID",
|
||||
"stuff.detail.otherSaleStoreId": "2차 판매점명 / ID",
|
||||
"stuff.detail.zipNo": "우편번호",
|
||||
"stuff.detail.address": "주소",
|
||||
"stuff.detail.btn.addressPop": "주소검색",
|
||||
"stuff.detail.btn.addressPop.guide": "※ 주소검색 버튼을 클릭한 후, 도도부현 정보를 선택해주십시오.",
|
||||
"stuff.detail.prefId": "도도부현 / 주소",
|
||||
@ -589,6 +637,7 @@
|
||||
"stuff.detail.planGridHeader.management": "관리",
|
||||
"stuff.detail.planGrid.btn1": "견적서 조회",
|
||||
"stuff.detail.planGrid.btn2": "Excel",
|
||||
"stuff.grid.noData": "조회된 데이터가 없습니다",
|
||||
"length": "길이",
|
||||
"height": "높이",
|
||||
"output": "출력",
|
||||
@ -600,9 +649,23 @@
|
||||
"size": "치수",
|
||||
"size.angle": "寸(度)",
|
||||
"eaves": "처마",
|
||||
"eaves.line": "처마선",
|
||||
"gable": "케라바",
|
||||
"gable.left": "케라바 왼쪽",
|
||||
"gable.right": "케라바 오른쪽",
|
||||
"ridge": "용마루",
|
||||
"oneside.flow.ridge": "한쪽흐름 용마루",
|
||||
"yosemune": "요세무네",
|
||||
"valley": "골짜기",
|
||||
"l.abandon.valley": "L의 버림 계곡",
|
||||
"mansard": "멘사드",
|
||||
"wall": "벽",
|
||||
"wall.merge": "벽취합",
|
||||
"wall.merge.type": "벽취합(형)",
|
||||
"wall.merge.flow": "벽취합(흐름)",
|
||||
"wall.merge.flow.left": "벽취합(흐름 왼쪽)",
|
||||
"wall.merge.flow.right": "벽취합(흐름 오른쪽)",
|
||||
"no.setting": "설정없음",
|
||||
"hajebichi": "하제비치",
|
||||
"straight.line": "직선",
|
||||
"right.angle": "직각",
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
import { atom, selector } from 'recoil'
|
||||
import { MENU } from '@/common/common'
|
||||
import { outerLineFixState, outerLinePointsState } from '@/store/outerLineAtom'
|
||||
import { getChonByDegree, getDegreeByChon } from '@/util/canvas-util'
|
||||
import { basicSettingState } from '@/store/settingAtom'
|
||||
|
||||
export const canvasState = atom({
|
||||
key: 'canvasState',
|
||||
@ -275,6 +277,11 @@ export const modifiedPlansState = atom({
|
||||
key: 'modifiedPlansState',
|
||||
default: [],
|
||||
})
|
||||
// 변경감지 flag
|
||||
export const modifiedPlanFlagState = atom({
|
||||
key: 'modifiedPlanFlagState',
|
||||
default: false,
|
||||
})
|
||||
|
||||
export const tempGridModeState = atom({
|
||||
key: 'tempGridModeState',
|
||||
@ -300,3 +307,49 @@ export const globalPitchState = atom({
|
||||
key: 'globalPitch',
|
||||
default: 4,
|
||||
})
|
||||
|
||||
export const pitchSelector = selector({
|
||||
key: 'pitchSelector',
|
||||
get: ({ get }) => {
|
||||
const globalPitch = get(globalPitchState)
|
||||
const basicSettingStateValue = get(basicSettingState)
|
||||
const roofAngleSet = basicSettingStateValue.roofAngleSet
|
||||
if (roofAngleSet === 'slope') {
|
||||
return globalPitch
|
||||
} else {
|
||||
return getDegreeByChon(globalPitch)
|
||||
}
|
||||
},
|
||||
set: ({ get, set }, newValue) => {
|
||||
const basicSettingStateValue = get(basicSettingState)
|
||||
const roofAngleSet = basicSettingStateValue.roofAngleSet
|
||||
console.log(newValue)
|
||||
if (roofAngleSet === 'slope') {
|
||||
set(globalPitchState, newValue)
|
||||
} else {
|
||||
set(globalPitchState, getChonByDegree(newValue))
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
export const ANGLE_TYPE = {
|
||||
SLOPE: 'slope',
|
||||
FLAT: 'flat',
|
||||
}
|
||||
|
||||
export const currentAngleTypeSelector = selector({
|
||||
key: 'currentAngleTypeSelector',
|
||||
get: ({ get }) => {
|
||||
const basicSettingStateValue = get(basicSettingState)
|
||||
return basicSettingStateValue.roofAngleSet
|
||||
},
|
||||
})
|
||||
|
||||
export const pitchTextSelector = selector({
|
||||
key: 'pitchTextSelector',
|
||||
get: ({ get }) => {
|
||||
const basicSettingStateValue = get(basicSettingState)
|
||||
const roofAngleSet = basicSettingStateValue.roofAngleSet
|
||||
return roofAngleSet === 'slope' ? '寸' : '度'
|
||||
},
|
||||
})
|
||||
|
||||
11
src/store/contextMenu.js
Normal file
11
src/store/contextMenu.js
Normal file
@ -0,0 +1,11 @@
|
||||
import { atom } from 'recoil'
|
||||
|
||||
export const contextMenuState = atom({
|
||||
key: 'contextMenuState',
|
||||
default: {
|
||||
visible: false,
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
dangerouslyAllowMutability: true,
|
||||
})
|
||||
@ -157,3 +157,23 @@ export const roofDisplaySelector = selector({
|
||||
},
|
||||
dangerouslyAllowMutability: true,
|
||||
})
|
||||
|
||||
export const basicSettingState = atom({
|
||||
key: 'basicSettingState',
|
||||
default: {
|
||||
roofSizeSet: 1,
|
||||
roofAngleSet: 'slope',
|
||||
roofs: [
|
||||
{
|
||||
roofApply: true,
|
||||
roofSeq: 1,
|
||||
roofType: 1,
|
||||
roofWidth: 200,
|
||||
roofHeight: 200,
|
||||
roofHajebichi: 200,
|
||||
roofGap: 0,
|
||||
roofLayout: 'parallel',
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
@ -4,30 +4,28 @@
|
||||
top: 200px;
|
||||
left: 50px;
|
||||
z-index: 999999;
|
||||
display: flex;
|
||||
width: 237px;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
background-color: #fff;
|
||||
border: 1px solid #DFDFDF;
|
||||
padding: 0 34px 0 10px;
|
||||
padding: 0 10px 0 10px;
|
||||
border-radius: 2px;
|
||||
box-shadow: 0px 7px 14px 0px rgba(0, 0, 0, 0.05);
|
||||
cursor: pointer;
|
||||
&::before{
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 12px;
|
||||
transform: translateY(-50%);
|
||||
width: 10px;
|
||||
height: 6px;
|
||||
.penal-arr{
|
||||
flex: none;
|
||||
width: 24px;
|
||||
height: 100%;
|
||||
background: url(../../public/static/images/canvas/penal_arr.svg)no-repeat center;
|
||||
background-size: cover;
|
||||
background-size: 10px 6px;
|
||||
}
|
||||
h2{
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: #3D3D3D;
|
||||
flex: 1;
|
||||
}
|
||||
.penal-table-wrap{
|
||||
display: none;
|
||||
@ -69,7 +67,7 @@
|
||||
h2{
|
||||
color: #fff;
|
||||
}
|
||||
&::before{
|
||||
.penal-arr{
|
||||
background: url(../../public/static/images/canvas/penal_arr_white.svg)no-repeat center;
|
||||
}
|
||||
.penal-table-wrap{
|
||||
|
||||
@ -679,7 +679,6 @@
|
||||
|
||||
.infomation-box-wrap{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
.sub-table-box{
|
||||
flex: 1 ;
|
||||
|
||||
@ -8,6 +8,8 @@
|
||||
--ag-header-height: 40px;
|
||||
--ag-header-foreground-color: white;
|
||||
--ag-header-background-color: #5D6A76;
|
||||
--ag-row-hover-color: #ECF0F4;
|
||||
|
||||
// --ag-header-cell-hover-background-color: rgb(80, 40, 140);
|
||||
--ag-header-cell-moving-background-color: #5D6A76;
|
||||
.ag-root-wrapper{
|
||||
@ -41,8 +43,16 @@
|
||||
}
|
||||
}
|
||||
.ag-cell{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 13px;
|
||||
color: #45576F;
|
||||
line-height: 1.4 !important;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
.block{
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
.ag-icon-desc::before,
|
||||
.ag-icon-asc::before,
|
||||
@ -94,7 +104,6 @@
|
||||
justify-content: center;
|
||||
background-color: #fff;
|
||||
border: 1px solid #94A0AD;
|
||||
background-color: transparent;
|
||||
border-radius: 2px;
|
||||
font-size: 13px;
|
||||
color: #94A0AD;
|
||||
|
||||
@ -252,7 +252,6 @@
|
||||
.faq-item{
|
||||
position: relative;
|
||||
margin-bottom: 10px;
|
||||
cursor: pointer;
|
||||
.faq-item-inner{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@ -482,6 +482,9 @@ input[type=text]{
|
||||
}
|
||||
&:read-only{
|
||||
color: #AAA;
|
||||
&:focus{
|
||||
border: 1px solid #323234;
|
||||
}
|
||||
}
|
||||
&.plane{
|
||||
font-family: 'Noto Sans JP', sans-serif;
|
||||
@ -509,6 +512,9 @@ input[type=text]{
|
||||
&:read-only{
|
||||
background-color: #FAFAFA;
|
||||
color: #999999;
|
||||
&:focus{
|
||||
border-color: #eee;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -316,11 +316,24 @@
|
||||
}
|
||||
}
|
||||
.community_detail-inner{
|
||||
padding-top: 20px;
|
||||
padding-bottom: 20px;
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
font-size: 13px;
|
||||
font-weight: 400;
|
||||
color: #45576F;
|
||||
line-height: 26px;
|
||||
word-break: keep-all;
|
||||
&::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
background-color: transparent;
|
||||
}
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background-color: #C1CCD7;
|
||||
}
|
||||
&::-webkit-scrollbar-track {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -262,6 +262,15 @@ export const getDegreeByChon = (chon) => {
|
||||
return Number((radians * (180 / Math.PI)).toFixed(2))
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export const getChonByDegree = (degree) => {
|
||||
// tan(theta) = height / base
|
||||
const radians = (degree * Math.PI) / 180
|
||||
return Number(Number(Math.tan(radians) * 10).toFixed(1))
|
||||
}
|
||||
|
||||
/**
|
||||
* 두 점 사이의 방향을 반환합니다.
|
||||
* @param a {fabric.Object}
|
||||
|
||||
@ -66,3 +66,23 @@ export const convertNumberToPriceDecimal = (value) => {
|
||||
else if (value === 0) return 0
|
||||
else return ''
|
||||
}
|
||||
|
||||
// 전화번호, FAX 번호 숫자 or '-'만 입력 체크
|
||||
export const inputTelNumberCheck = (e) => {
|
||||
const input = e.target
|
||||
if (/^[\d-]*$/g.test(input.value)) {
|
||||
input.value = input.value
|
||||
} else {
|
||||
input.value = input.value.replace(/[^\d-]/g, '')
|
||||
}
|
||||
}
|
||||
|
||||
// 숫자만 입력 체크
|
||||
export const inputNumberCheck = (e) => {
|
||||
const input = e.target
|
||||
if (/^[\d]*$/g.test(input.value)) {
|
||||
input.value = input.value
|
||||
} else {
|
||||
input.value = input.value.replace(/[^\d]/g, '')
|
||||
}
|
||||
}
|
||||
|
||||
@ -5884,6 +5884,11 @@ react-is@^16.13.1, react-is@^16.7.0:
|
||||
resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz"
|
||||
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
|
||||
|
||||
react-loading-skeleton@^3.5.0:
|
||||
version "3.5.0"
|
||||
resolved "https://registry.yarnpkg.com/react-loading-skeleton/-/react-loading-skeleton-3.5.0.tgz#da2090355b4dedcad5c53cb3f0ed364e3a76d6ca"
|
||||
integrity sha512-gxxSyLbrEAdXTKgfbpBEFZCO/P153DnqSCQau2+o6lNy1jgMRr2MmRmOzMmyrwSaSYLRB8g7b0waYPmUjz7IhQ==
|
||||
|
||||
react-onclickoutside@^6.13.0:
|
||||
version "6.13.1"
|
||||
resolved "https://registry.npmjs.org/react-onclickoutside/-/react-onclickoutside-6.13.1.tgz"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user