From 7cc3537709f4035527f86013ee62c7cf5839be3e Mon Sep 17 00:00:00 2001 From: basssy Date: Fri, 25 Oct 2024 09:12:38 +0900 Subject: [PATCH 01/26] =?UTF-8?q?=EB=AC=BC=EA=B1=B4=EB=AA=A9=EB=A1=9D=20?= =?UTF-8?q?=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EC=B2=B4=ED=81=AC=EB=B0=95?= =?UTF-8?q?=EC=8A=A4=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/management/Stuff.jsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/management/Stuff.jsx b/src/components/management/Stuff.jsx index 5771543f..64cd7f33 100644 --- a/src/components/management/Stuff.jsx +++ b/src/components/management/Stuff.jsx @@ -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) { From 4d6c1f3b08b483427fee9f70e9609b1eefdaf61b Mon Sep 17 00:00:00 2001 From: minsik Date: Fri, 25 Oct 2024 10:27:21 +0900 Subject: [PATCH 02/26] =?UTF-8?q?=F0=9F=9A=A8chore:=20Sync=20Sass?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/styles/_grid-detail.scss | 11 ++++++++++- src/styles/_main.scss | 1 - src/styles/_submodal.scss | 17 +++++++++++++++-- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/styles/_grid-detail.scss b/src/styles/_grid-detail.scss index 048a1946..0392bae8 100644 --- a/src/styles/_grid-detail.scss +++ b/src/styles/_grid-detail.scss @@ -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; diff --git a/src/styles/_main.scss b/src/styles/_main.scss index b8783c3b..ea1535b1 100644 --- a/src/styles/_main.scss +++ b/src/styles/_main.scss @@ -252,7 +252,6 @@ .faq-item{ position: relative; margin-bottom: 10px; - cursor: pointer; .faq-item-inner{ display: flex; align-items: center; diff --git a/src/styles/_submodal.scss b/src/styles/_submodal.scss index e5fae9fb..de01ee36 100644 --- a/src/styles/_submodal.scss +++ b/src/styles/_submodal.scss @@ -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; + } } } \ No newline at end of file From 451a3c8ccec0c9107c2b4ce2777220bf9daa0cfa Mon Sep 17 00:00:00 2001 From: basssy Date: Fri, 25 Oct 2024 10:29:40 +0900 Subject: [PATCH 03/26] =?UTF-8?q?=EB=AC=BC=EA=B1=B4=ED=98=84=ED=99=A9=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=ED=99=94=EB=A9=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/management/StuffDetail.jsx | 38 +++++++++++++++++------ src/components/management/StuffHeader.jsx | 9 ++++-- src/locales/ja.json | 1 + src/locales/ko.json | 1 + 4 files changed, 38 insertions(+), 11 deletions(-) diff --git a/src/components/management/StuffDetail.jsx b/src/components/management/StuffDetail.jsx index b96818dc..44b053a6 100644 --- a/src/components/management/StuffDetail.jsx +++ b/src/components/management/StuffDetail.jsx @@ -427,7 +427,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 +438,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 +446,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) }) @@ -888,53 +891,70 @@ export default function StuffDetail() { let errors = {} let fieldNm + //담당자 if (!formData.receiveUser || formData.receiveUser.trim().length === 0) { fieldNm = getMessage('stuff.detail.receiveUser') errors = fieldNm } + //물건명 if (!formData.objectName || formData.objectName.trim().length === 0) { fieldNm = getMessage('stuff.detail.objectStatusId') errors = fieldNm } + //경칭 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 } - + //주소 + if (!formData.address) { + fieldNm = getMessage('stuff.detail.address') + errors = fieldNm + } + //도도부현 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 } + //면조도구분 + if (!formData.surfaceType) { + fieldNm = getMessage('stuff.detail.surfaceType') + errors = fieldNm + } + //설치높이 if (!formData.installHeight) { fieldNm = getMessage('stuff.detail.installHeight') errors = fieldNm } + //계약조건 + if (Object.keys(errors).length > 0) { return alert(getMessage('stuff.detail.save.valierror3', [errors])) } diff --git a/src/components/management/StuffHeader.jsx b/src/components/management/StuffHeader.jsx index d8cade80..0ed58b34 100644 --- a/src/components/management/StuffHeader.jsx +++ b/src/components/management/StuffHeader.jsx @@ -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,15 @@ export default function StuffHeader() {
{getMessage('stuff.detail.header.lastEditDatetime')}
-
{headerData.lastEditDatetime}
+
+ {dayjs(headerData.lastEditDatetime).format('YYYY.MM.DD HH:mm:ss')} ({headerData.lastEditUserName}) +
{getMessage('stuff.detail.header.createDatetime')}
-
{headerData.createDatetime}
+
+ {dayjs(headerData.createDatetime).format('YYYY.MM.DD')} ({headerData.createUserName}) +
) diff --git a/src/locales/ja.json b/src/locales/ja.json index c5522fe2..3b886602 100644 --- a/src/locales/ja.json +++ b/src/locales/ja.json @@ -486,6 +486,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": "都道府県 / 住所 ", diff --git a/src/locales/ko.json b/src/locales/ko.json index 529dc494..25f7998f 100644 --- a/src/locales/ko.json +++ b/src/locales/ko.json @@ -491,6 +491,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": "도도부현 / 주소", From a10d999cc8e7363c4cecba2d862fbd842b69c0ea Mon Sep 17 00:00:00 2001 From: basssy Date: Fri, 25 Oct 2024 11:37:39 +0900 Subject: [PATCH 04/26] =?UTF-8?q?=EB=AC=BC=EA=B1=B4=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=ED=99=94=EB=A9=B4=201=EC=B0=A8=EC=A0=90=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8=EC=8B=9C=201&2=EC=B0=A8=EC=A0=90=20=EC=9E=90=EB=8F=99?= =?UTF-8?q?=EC=99=84=EC=84=B1=20Select=20X=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/management/StuffDetail.jsx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/components/management/StuffDetail.jsx b/src/components/management/StuffDetail.jsx index 44b053a6..e8a25251 100644 --- a/src/components/management/StuffDetail.jsx +++ b/src/components/management/StuffDetail.jsx @@ -484,8 +484,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') @@ -596,8 +598,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') From c4861f95a37d752f12187d64e9b47e6751083e47 Mon Sep 17 00:00:00 2001 From: basssy Date: Fri, 25 Oct 2024 12:27:52 +0900 Subject: [PATCH 05/26] =?UTF-8?q?=EB=B9=84=EB=B0=80=EB=B2=88=ED=98=B8=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20=ED=8C=9D=EC=97=85=20validation=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/main/ChangePasswordPop.jsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/main/ChangePasswordPop.jsx b/src/components/main/ChangePasswordPop.jsx index b5a3f8da..42ab6ef6 100644 --- a/src/components/main/ChangePasswordPop.jsx +++ b/src/components/main/ChangePasswordPop.jsx @@ -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') } }) } From 5f9e3a15b978d6e0345361d9a4a5f63bd7563082 Mon Sep 17 00:00:00 2001 From: leeyongjae Date: Fri, 25 Oct 2024 13:14:22 +0900 Subject: [PATCH 06/26] =?UTF-8?q?[=ED=9A=8C=EC=9B=90]=20=EA=B0=80=EC=9E=85?= =?UTF-8?q?=EC=8B=A0=EC=B2=AD=20=EC=9C=A0=ED=9A=A8=EC=84=B1=20=EA=B2=80?= =?UTF-8?q?=EC=82=AC=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8=20=ED=99=94=EB=A9=B4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/auth/Join.jsx | 238 +++++++++++++++++++++++++++------- src/components/auth/Login.jsx | 10 +- src/locales/ja.json | 1 + src/locales/ko.json | 1 + src/util/common-utils.js | 20 +++ 5 files changed, 222 insertions(+), 48 deletions(-) diff --git a/src/components/auth/Join.jsx b/src/components/auth/Join.jsx index 3412efdb..3f4c1097 100644 --- a/src/components/auth/Join.jsx +++ b/src/components/auth/Join.jsx @@ -1,53 +1,171 @@ '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'), - }, - } + if (joinValidation(formData)) { + 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) + 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) - }) + }) + .catch((error) => { + alert(error.response.data.message) + }) + } } return ( @@ -71,6 +189,7 @@ export default function Join() { + {/* 판매대리점명 */} {getMessage('join.sub1.storeQcastNm')} * @@ -81,14 +200,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} /> + {/* 판매대리점명 후리가나 */} {getMessage('join.sub1.storeQcastNmKana')} * @@ -99,13 +220,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} /> + {/* 우편번호/주소 */} {getMessage('join.sub1.postCd')}/{getMessage('join.sub1.addr')} * @@ -117,9 +240,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} />
@@ -127,14 +252,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} />
+ {/* 전화번호 */} {getMessage('join.sub1.telNo')} * @@ -145,13 +272,16 @@ export default function Join() { type="text" id="telNo" name="telNo" - required className="input-light" placeholder={getMessage('join.sub1.telNo_placeholder')} - > + maxLength={15} + onChange={inputTelNumberCheck} + ref={telNoRef} + /> + {/* FAX 번호 */} {getMessage('join.sub1.fax')} * @@ -162,18 +292,21 @@ export default function Join() { type="text" id="fax" name="fax" - required className="input-light" placeholder={getMessage('join.sub1.fax_placeholder')} - > + maxLength={15} + onChange={inputTelNumberCheck} + ref={faxRef} + /> + {/* 법인번호 */} {getMessage('join.sub1.bizNo')}
- +
@@ -196,44 +329,49 @@ export default function Join() { + {/* 담당자명 */} {getMessage('join.sub2.userNm')} *
- +
+ {/* 담당자명 후리가나 */} {getMessage('join.sub2.userNmKana')}
- +
+ {/* 신청 ID */} {getMessage('join.sub2.userId')} *
- +
+ {/* 이메일 주소 */} {getMessage('join.sub2.email')} *
- +
+ {/* 전화번호 */} {getMessage('join.sub2.telNo')} * @@ -246,11 +384,14 @@ export default function Join() { name="userTelNo" className="input-light" placeholder={getMessage('join.sub2.telNo_placeholder')} - required + maxLength={15} + onChange={inputTelNumberCheck} + ref={userTelNoRef} /> + {/* FAX 번호 */} {getMessage('join.sub2.fax')} * @@ -263,16 +404,19 @@ export default function Join() { name="userFax" className="input-light" placeholder={getMessage('join.sub1.fax_placeholder')} - required + maxLength={15} + onChange={inputTelNumberCheck} + ref={userFaxRef} /> + {/* 부서명 */} {getMessage('join.sub2.category')}
- +
diff --git a/src/components/auth/Login.jsx b/src/components/auth/Login.jsx index 80b34f9f..ef4f92a3 100644 --- a/src/components/auth/Login.jsx +++ b/src/components/auth/Login.jsx @@ -331,7 +331,15 @@ export default function Login() { >
-
- @@ -1198,7 +1223,7 @@ export default function StuffDetail() {
- +
@@ -1227,7 +1252,7 @@ export default function StuffDetail() { })} {/* 라디오끝 */}
- +
- +
-
{getMessage('stuff.detail.btn.addressPop.guide')}
@@ -1368,7 +1394,7 @@ export default function StuffDetail() {
-
+
{prefCodeList?.length > 0 && ( +
@@ -1444,7 +1476,7 @@ export default function StuffDetail() { >
{getMessage('stuff.detail.standardWindSpeedIdSpan')} -
@@ -1463,6 +1495,7 @@ export default function StuffDetail() { onKeyUp={handleKeyUp} value={form.watch('verticalSnowCover') || ''} {...register('verticalSnowCover')} + ref={inputVerticalSnowCoverEl} />
cm @@ -1510,6 +1543,7 @@ export default function StuffDetail() { onKeyUp={handleKeyUp} value={form.watch('installHeight') || ''} {...register('installHeight')} + ref={inputInstallHeightEl} /> m @@ -1544,7 +1578,7 @@ export default function StuffDetail() {
{!isFormValid ? ( - ) : ( @@ -1595,7 +1629,7 @@ export default function StuffDetail() { {/* {detailData?.tempFlg === '1' ? ( */} {objectNo.substring(0, 1) === 'T' ? ( <> - @@ -1609,7 +1643,13 @@ export default function StuffDetail() {
- +
@@ -1638,7 +1678,7 @@ export default function StuffDetail() { })} {/* 상세라디오끝 */}
- +
+
-
{getMessage('stuff.detail.btn.addressPop.guide')}
@@ -1780,7 +1820,7 @@ export default function StuffDetail() {
-
+
{prefCodeList?.length > 0 && ( +
@@ -1860,7 +1906,7 @@ export default function StuffDetail() { >
{getMessage('stuff.detail.standardWindSpeedIdSpan')} -
@@ -1881,6 +1927,7 @@ export default function StuffDetail() { onKeyUp={handleKeyUp} value={form.watch('verticalSnowCover') || ''} {...register('verticalSnowCover')} + ref={inputVerticalSnowCoverEl} /> cm @@ -1932,6 +1979,7 @@ export default function StuffDetail() { onKeyUp={handleKeyUp} value={form.watch('installHeight') || ''} {...register('installHeight')} + ref={inputInstallHeightEl} /> m @@ -2005,13 +2053,13 @@ export default function StuffDetail() {
-
@@ -2020,17 +2068,17 @@ export default function StuffDetail() { <>
{!isFormValid ? ( - ) : ( )}
From 49bb5e436289bedf2c597f3cd32305a31cd71908 Mon Sep 17 00:00:00 2001 From: leeyongjae Date: Fri, 25 Oct 2024 14:53:49 +0900 Subject: [PATCH 09/26] =?UTF-8?q?=EA=B0=80=EC=9E=85=EC=8B=A0=EC=B2=AD=20?= =?UTF-8?q?=EC=8B=9C,=20confirm=20=EB=A9=94=EC=8B=9C=EC=A7=80=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/auth/Join.jsx | 64 +++++++++++++++++++----------------- src/locales/ja.json | 1 + src/locales/ko.json | 1 + 3 files changed, 35 insertions(+), 31 deletions(-) diff --git a/src/components/auth/Join.jsx b/src/components/auth/Join.jsx index 3f4c1097..a9e79750 100644 --- a/src/components/auth/Join.jsx +++ b/src/components/auth/Join.jsx @@ -132,39 +132,41 @@ export default function Join() { const formData = new FormData(e.target) if (joinValidation(formData)) { - 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'), - }, - } + 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'), + }, + } - 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) + 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) - }) + }) + .catch((error) => { + alert(error.response.data.message) + }) + } } } diff --git a/src/locales/ja.json b/src/locales/ja.json index 688cae4c..baa74f41 100644 --- a/src/locales/ja.json +++ b/src/locales/ja.json @@ -446,6 +446,7 @@ "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": "プラン数", diff --git a/src/locales/ko.json b/src/locales/ko.json index 7daef05c..d46e7726 100644 --- a/src/locales/ko.json +++ b/src/locales/ko.json @@ -451,6 +451,7 @@ "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": "플랜 수", From f3f7daf9d698a0f71f5e31eb28053e9a274a0c4f Mon Sep 17 00:00:00 2001 From: yoosangwook Date: Fri, 25 Oct 2024 15:01:25 +0900 Subject: [PATCH 10/26] feat: Add MainSkeleton --- package.json | 1 + src/components/main/MainContents.jsx | 53 +++++++++++++++------------- src/components/ui/Loading.jsx | 5 --- src/components/ui/Loading.module.css | 35 ------------------ src/components/ui/MainSkeleton.jsx | 13 +++++++ yarn.lock | 5 +++ 6 files changed, 48 insertions(+), 64 deletions(-) delete mode 100644 src/components/ui/Loading.jsx delete mode 100644 src/components/ui/Loading.module.css create mode 100644 src/components/ui/MainSkeleton.jsx diff --git a/package.json b/package.json index 303e4864..6c94a282 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/components/main/MainContents.jsx b/src/components/main/MainContents.jsx index d2e38853..fa61b676 100644 --- a/src/components/main/MainContents.jsx +++ b/src/components/main/MainContents.jsx @@ -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() {
-
    - {objectList.map((row) => { - return ( -
  • { - 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()}`) - } - }} - > -
    - {dayjs(row.lastEditDatetime).format('YYYY.MM.DD HH:mm:ss')} - {row.objectNo} - {row.objectName} - {row.saleStoreName} -
    -
  • - ) - })} -
+ {objectList.length > 0 ? ( +
    + {objectList.map((row) => { + return ( +
  • { + 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()}`) + } + }} + > +
    + {dayjs(row.lastEditDatetime).format('YYYY.MM.DD HH:mm:ss')} + {row.objectNo} + {row.objectName} + {row.saleStoreName} +
    +
  • + ) + })} +
+ ) : ( + + )}
diff --git a/src/components/ui/Loading.jsx b/src/components/ui/Loading.jsx deleted file mode 100644 index 59d2170b..00000000 --- a/src/components/ui/Loading.jsx +++ /dev/null @@ -1,5 +0,0 @@ -import style from '@/components/ui/Loading.module.css' - -export default function Loading() { - return -} diff --git a/src/components/ui/Loading.module.css b/src/components/ui/Loading.module.css deleted file mode 100644 index 7b3001f8..00000000 --- a/src/components/ui/Loading.module.css +++ /dev/null @@ -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); - } -} diff --git a/src/components/ui/MainSkeleton.jsx b/src/components/ui/MainSkeleton.jsx new file mode 100644 index 00000000..fa0a48c2 --- /dev/null +++ b/src/components/ui/MainSkeleton.jsx @@ -0,0 +1,13 @@ +import Skeleton from 'react-loading-skeleton' +import 'react-loading-skeleton/dist/skeleton.css' + +export default function MainSkeleton() { + return ( + <> +
+ +
+ + + ) +} diff --git a/yarn.lock b/yarn.lock index bf4e65b8..6e7b5f2c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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" From 32691e4da9d9ab6dbcd00c2749227279b035fac2 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Fri, 25 Oct 2024 15:22:30 +0900 Subject: [PATCH 11/26] =?UTF-8?q?1=EB=8B=A8=EC=9C=84=EB=8F=84=20=EB=82=98?= =?UTF-8?q?=EC=98=A4=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95,=20=EC=A7=80?= =?UTF-8?q?=EB=B6=95=EB=A9=B4=20=ED=95=A0=EB=8B=B9=ED=91=9C=EC=8B=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/fabric/QLine.js | 2 +- src/components/fabric/QPolygon.js | 2 +- src/hooks/common/useCanvasConfigInitialize.js | 2 + src/hooks/common/useRoof.js | 151 ++++++++++++++++++ src/hooks/usePlan.js | 1 + 5 files changed, 156 insertions(+), 2 deletions(-) create mode 100644 src/hooks/common/useRoof.js diff --git a/src/components/fabric/QLine.js b/src/components/fabric/QLine.js index 7b4e2196..a1d56b08 100644 --- a/src/components/fabric/QLine.js +++ b/src/components/fabric/QLine.js @@ -148,7 +148,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) { diff --git a/src/components/fabric/QPolygon.js b/src/components/fabric/QPolygon.js index 76c8dd6c..b28a3b47 100644 --- a/src/components/fabric/QPolygon.js +++ b/src/components/fabric/QPolygon.js @@ -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 diff --git a/src/hooks/common/useCanvasConfigInitialize.js b/src/hooks/common/useCanvasConfigInitialize.js index 2e91fad1..c2775376 100644 --- a/src/hooks/common/useCanvasConfigInitialize.js +++ b/src/hooks/common/useCanvasConfigInitialize.js @@ -6,6 +6,7 @@ import { 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) @@ -15,6 +16,7 @@ export function useCanvasConfigInitialize() { const setDotLineGridSetting = useSetRecoilState(dotLineGridSettingState) const {} = useFont() const {} = useGrid() + const {} = useRoof() useEffect(() => { if (!canvas) return diff --git a/src/hooks/common/useRoof.js b/src/hooks/common/useRoof.js new file mode 100644 index 00000000..8aee0344 --- /dev/null +++ b/src/hooks/common/useRoof.js @@ -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 {} +} diff --git a/src/hooks/usePlan.js b/src/hooks/usePlan.js index 19b9a879..e00c78e8 100644 --- a/src/hooks/usePlan.js +++ b/src/hooks/usePlan.js @@ -39,6 +39,7 @@ export function usePlan() { 'length', 'idx', 'direction', + 'parentDirection', 'lines', 'points', 'lockMovementX', From 1387c8aa0bc9fc9aa824fce33f5653feb8473718 Mon Sep 17 00:00:00 2001 From: Daseul Kim Date: Fri, 25 Oct 2024 16:19:20 +0900 Subject: [PATCH 12/26] =?UTF-8?q?refactor:=20=EC=BA=94=EB=B2=84=EC=8A=A4?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD=EC=82=AC=ED=95=AD=20=ED=99=95=EC=9D=B8=20?= =?UTF-8?q?=EB=B0=A9=EB=B2=95=EC=9D=84=20=EC=8B=A4=EC=8B=9C=EA=B0=84=20?= =?UTF-8?q?=EC=98=A4=EB=B8=8C=EC=A0=9D=ED=8A=B8=20=EC=9D=B4=EB=B2=A4?= =?UTF-8?q?=ED=8A=B8=EB=A5=BC=20=EA=B0=90=EC=A7=80=ED=95=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EC=88=98=EC=A0=95=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/fabric/QPolygon.js | 2 +- src/components/floor-plan/CanvasFrame.jsx | 15 ++++++++-- src/components/floor-plan/CanvasLayout.jsx | 6 ++-- src/hooks/useCanvasEvent.js | 34 ++++++++++++++++++++-- src/hooks/usePlan.js | 32 ++++++++++---------- src/store/canvasAtom.js | 5 ++++ 6 files changed, 71 insertions(+), 23 deletions(-) diff --git a/src/components/fabric/QPolygon.js b/src/components/fabric/QPolygon.js index b28a3b47..31076b69 100644 --- a/src/components/fabric/QPolygon.js +++ b/src/components/fabric/QPolygon.js @@ -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) }) diff --git a/src/components/floor-plan/CanvasFrame.jsx b/src/components/floor-plan/CanvasFrame.jsx index 086e9441..4cc08372 100644 --- a/src/components/floor-plan/CanvasFrame.jsx +++ b/src/components/floor-plan/CanvasFrame.jsx @@ -2,18 +2,20 @@ 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 { currentObjectState, modifiedPlanFlagState } from '@/store/canvasAtom' import { useCanvasEvent } from '@/hooks/useCanvasEvent' import QContextMenu from '@/components/common/context-menu/QContextMenu' import { useCanvasConfigInitialize } from '@/hooks/common/useCanvasConfigInitialize' export default function CanvasFrame({ plan }) { const canvasRef = useRef(null) + const [modifiedPlanFlag, setModifiedPlanFlag] = useRecoilState(modifiedPlanFlagState) const { canvas } = useCanvas('canvas') const { handleZoomClear } = useCanvasEvent() const { contextMenu, currentContextMenu, setCurrentContextMenu, handleClick } = useContextMenu({ @@ -21,7 +23,7 @@ export default function CanvasFrame({ plan }) { handleZoomClear, }, }) - const { checkCanvasObjectEvent, checkUnsavedCanvasPlan } = usePlan() + const { checkCanvasObjectEvent, resetModifiedPlans } = usePlan() const { canvasLoadInit, gridInit } = useCanvasConfigInitialize() const currentObject = useRecoilValue(currentObjectState) @@ -40,8 +42,15 @@ export default function CanvasFrame({ plan }) { } } + useEffect(() => { + if (modifiedPlanFlag && plan?.id) { + checkCanvasObjectEvent(plan.id) + } + }, [modifiedPlanFlag]) + useEffect(() => { loadCanvas() + resetModifiedPlans() }, [plan, canvas]) const onClickContextMenu = (index) => {} diff --git a/src/components/floor-plan/CanvasLayout.jsx b/src/components/floor-plan/CanvasLayout.jsx index 63722a64..ac247f6b 100644 --- a/src/components/floor-plan/CanvasLayout.jsx +++ b/src/components/floor-plan/CanvasLayout.jsx @@ -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) diff --git a/src/hooks/useCanvasEvent.js b/src/hooks/useCanvasEvent.js index bf878c7b..0df650c0 100644 --- a/src/hooks/useCanvasEvent.js +++ b/src/hooks/useCanvasEvent.js @@ -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 diff --git a/src/hooks/usePlan.js b/src/hooks/usePlan.js index e00c78e8..8ed84af4 100644 --- a/src/hooks/usePlan.js +++ b/src/hooks/usePlan.js @@ -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() @@ -90,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) => { @@ -122,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'), @@ -365,7 +367,7 @@ export function usePlan() { plans, modifiedPlans, checkCanvasObjectEvent, - checkUnsavedCanvasPlan, + resetModifiedPlans, saveCanvas, handleCurrentPlan, handleAddPlan, diff --git a/src/store/canvasAtom.js b/src/store/canvasAtom.js index d9878f2f..0f2428ee 100644 --- a/src/store/canvasAtom.js +++ b/src/store/canvasAtom.js @@ -275,6 +275,11 @@ export const modifiedPlansState = atom({ key: 'modifiedPlansState', default: [], }) +// 변경감지 flag +export const modifiedPlanFlagState = atom({ + key: 'modifiedPlanFlagState', + default: false, +}) export const tempGridModeState = atom({ key: 'tempGridModeState', From 45b0063b8b2befba7142116386a85a5f6c5c847c Mon Sep 17 00:00:00 2001 From: basssy Date: Fri, 25 Oct 2024 16:26:07 +0900 Subject: [PATCH 13/26] =?UTF-8?q?=EB=AC=BC=EA=B1=B4=ED=99=94=EB=A9=B4=20,?= =?UTF-8?q?=20=EB=A9=94=EC=9D=B8=ED=99=94=EB=A9=B4=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/main/MainContents.jsx | 10 +++++++--- src/components/management/StuffQGrid.jsx | 5 ++++- .../management/popup/FindAddressPopQGrid.jsx | 6 +++++- src/components/management/popup/PlanRequestPop.jsx | 7 +++++++ .../management/popup/PlanRequestPopQGrid.jsx | 4 ++++ src/components/ui/MainSkeleton.jsx | 4 ++-- src/locales/ja.json | 1 + src/locales/ko.json | 1 + 8 files changed, 31 insertions(+), 7 deletions(-) diff --git a/src/components/main/MainContents.jsx b/src/components/main/MainContents.jsx index fa61b676..9490a868 100644 --- a/src/components/main/MainContents.jsx +++ b/src/components/main/MainContents.jsx @@ -136,7 +136,7 @@ export default function MainContents() { })} ) : ( - + )} @@ -147,7 +147,9 @@ export default function MainContents() {
{recentNoticeList[0]?.title}
{recentNoticeList[0]?.contents}
- ) : null} + ) : ( + + )}
@@ -168,7 +170,9 @@ export default function MainContents() { ) })} - ) : null} + ) : ( + + )} diff --git a/src/components/management/StuffQGrid.jsx b/src/components/management/StuffQGrid.jsx index 33836e79..abbafca4 100644 --- a/src/components/management/StuffQGrid.jsx +++ b/src/components/management/StuffQGrid.jsx @@ -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을 사용하여 렌더링 전반에 걸쳐 일관된 배열 참조를 유지하는 것이 좋습니다 @@ -95,7 +98,7 @@ export default function StuffQGrid(props) { onSelectionChanged={onSelectionChanged} onCellDoubleClicked={onCellDoubleClicked} pagination={isPageable} - overlayNoRowsTemplate={'물건 목록이 없습니다.'} + overlayNoRowsTemplate={`${getMessage('stuff.grid.noData')}`} getRowClass={getRowClass} autoSizeAllColumns={true} /> diff --git a/src/components/management/popup/FindAddressPopQGrid.jsx b/src/components/management/popup/FindAddressPopQGrid.jsx index b9d1bb90..383e1cc7 100644 --- a/src/components/management/popup/FindAddressPopQGrid.jsx +++ b/src/components/management/popup/FindAddressPopQGrid.jsx @@ -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) @@ -46,7 +49,7 @@ export default function FindAddressPopGrid(props) { } return ( -
+
${getMessage('stuff.grid.noData')}`} />
) diff --git a/src/components/management/popup/PlanRequestPop.jsx b/src/components/management/popup/PlanRequestPop.jsx index cde86d6b..bbe0f6c9 100644 --- a/src/components/management/popup/PlanRequestPop.jsx +++ b/src/components/management/popup/PlanRequestPop.jsx @@ -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 (
@@ -301,6 +307,7 @@ export default function PlanRequestPop(props) { type="text" className="input-light" value={schPlanReqNo} + onKeyUp={handleKeyUp} onChange={(e) => { setSchPlanReqNo(e.target.value) }} diff --git a/src/components/management/popup/PlanRequestPopQGrid.jsx b/src/components/management/popup/PlanRequestPopQGrid.jsx index e610a138..6e3a8467 100644 --- a/src/components/management/popup/PlanRequestPopQGrid.jsx +++ b/src/components/management/popup/PlanRequestPopQGrid.jsx @@ -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) @@ -56,6 +59,7 @@ export default function PlanRequestPopQGrid(props) { rowSelection={'singleRow'} pagination={isPageable} onSelectionChanged={onSelectionChanged} + overlayNoRowsTemplate={`${getMessage('stuff.grid.noData')}`} />
) diff --git a/src/components/ui/MainSkeleton.jsx b/src/components/ui/MainSkeleton.jsx index fa0a48c2..e8b64e17 100644 --- a/src/components/ui/MainSkeleton.jsx +++ b/src/components/ui/MainSkeleton.jsx @@ -1,13 +1,13 @@ import Skeleton from 'react-loading-skeleton' import 'react-loading-skeleton/dist/skeleton.css' -export default function MainSkeleton() { +export default function MainSkeleton({ count }) { return ( <>
- + ) } diff --git a/src/locales/ja.json b/src/locales/ja.json index baa74f41..91b6df42 100644 --- a/src/locales/ja.json +++ b/src/locales/ja.json @@ -587,6 +587,7 @@ "stuff.detail.planGridHeader.management": "管理", "stuff.detail.planGrid.btn1": "見積書の照会", "stuff.detail.planGrid.btn2": "Excel", + "stuff.grid.noData": "照会されたデータがありません", "length": "長さ", "height": "高さ", "output": "出力", diff --git a/src/locales/ko.json b/src/locales/ko.json index d46e7726..f21c282e 100644 --- a/src/locales/ko.json +++ b/src/locales/ko.json @@ -592,6 +592,7 @@ "stuff.detail.planGridHeader.management": "관리", "stuff.detail.planGrid.btn1": "견적서 조회", "stuff.detail.planGrid.btn2": "Excel", + "stuff.grid.noData": "조회된 데이터가 없습니다", "length": "길이", "height": "높이", "output": "출력", From c8a27c5f019f2d51de8d03b4e95d73d957a1a7ee Mon Sep 17 00:00:00 2001 From: leeyongjae Date: Fri, 25 Oct 2024 16:32:18 +0900 Subject: [PATCH 14/26] =?UTF-8?q?=EA=B2=8C=EC=8B=9C=EA=B8=80=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=20=ED=8C=9D=EC=97=85=20=EB=8B=A4=EA=B5=AD=EC=96=B4=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/community/modal/BoardDetailModal.jsx | 7 +++++-- src/locales/ja.json | 1 + src/locales/ko.json | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/components/community/modal/BoardDetailModal.jsx b/src/components/community/modal/BoardDetailModal.jsx index ec324966..c1ba4213 100644 --- a/src/components/community/modal/BoardDetailModal.jsx +++ b/src/components/community/modal/BoardDetailModal.jsx @@ -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')}
@@ -55,7 +58,7 @@ export default function BoardDetailModal({ noticeNo, setOpen }) { {boardDetail.listFile && (
-
첨부파일 목록
+
{getMessage('board.sub.fileList')}
{boardDetail.listFile.map((boardFile) => (
From eaa388204536ac7104144f671d7f7d108767606e Mon Sep 17 00:00:00 2001 From: minsik Date: Fri, 25 Oct 2024 17:30:01 +0900 Subject: [PATCH 19/26] =?UTF-8?q?=F0=9F=9A=A8chore:=20Sync=20Sass?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/styles/_canvasside.scss | 20 +++++++++----------- src/styles/_contents.scss | 1 - src/styles/_reset.scss | 6 ++++++ 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/styles/_canvasside.scss b/src/styles/_canvasside.scss index 66bba83d..efb79f02 100644 --- a/src/styles/_canvasside.scss +++ b/src/styles/_canvasside.scss @@ -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{ diff --git a/src/styles/_contents.scss b/src/styles/_contents.scss index c242a96b..e3d8d1ee 100644 --- a/src/styles/_contents.scss +++ b/src/styles/_contents.scss @@ -679,7 +679,6 @@ .infomation-box-wrap{ display: flex; - align-items: center; gap: 10px; .sub-table-box{ flex: 1 ; diff --git a/src/styles/_reset.scss b/src/styles/_reset.scss index b623dbff..525c0488 100644 --- a/src/styles/_reset.scss +++ b/src/styles/_reset.scss @@ -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; + } } } } From 9f30b2e88f4b5cf0f8fc13cf566c49ea208af953 Mon Sep 17 00:00:00 2001 From: minsik Date: Fri, 25 Oct 2024 17:30:43 +0900 Subject: [PATCH 20/26] =?UTF-8?q?=EB=8B=A4=EA=B5=AD=EC=96=B4=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/floor-plan/CanvasFrame.jsx | 26 ++- .../flowDirection/FlowDirectionSetting.jsx | 174 +++++++++--------- .../lineProperty/LinePropertySetting.jsx | 111 +++++------ .../floor-plan/modal/object/DormerOffset.jsx | 12 +- .../floor-plan/modal/object/SizeSetting.jsx | 16 +- src/hooks/useContextMenu.js | 144 ++++++++------- src/locales/ja.json | 60 +++++- src/locales/ko.json | 58 ++++++ 8 files changed, 337 insertions(+), 264 deletions(-) diff --git a/src/components/floor-plan/CanvasFrame.jsx b/src/components/floor-plan/CanvasFrame.jsx index e6261edc..7de221f2 100644 --- a/src/components/floor-plan/CanvasFrame.jsx +++ b/src/components/floor-plan/CanvasFrame.jsx @@ -1,32 +1,25 @@ 'use client' import { useEffect, useRef } from 'react' - 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 { useCanvasEvent } from '@/hooks/useCanvasEvent' import QContextMenu from '@/components/common/context-menu/QContextMenu' import { useCanvasConfigInitialize } from '@/hooks/common/useCanvasConfigInitialize' +import { useRecoilValue } from 'recoil' +import { currentMenuState } from '@/store/canvasAtom' +import { MENU } from '@/common/common' +import PanelBatchStatistics from '@/components/floor-plan/modal/panelBatch/PanelBatchStatistics' export default function CanvasFrame({ plan }) { const canvasRef = useRef(null) const { canvas } = useCanvas('canvas') - const { handleZoomClear } = useCanvasEvent() - const { contextMenu, currentContextMenu, setCurrentContextMenu, handleClick } = useContextMenu({ - externalFn: { - handleZoomClear, - }, - }) + const { contextMenu, handleClick } = useContextMenu() const { checkCanvasObjectEvent, checkUnsavedCanvasPlan } = usePlan() const { canvasLoadInit } = useCanvasConfigInitialize() - const currentObject = useRecoilValue(currentObjectState) - + const currentMenu = useRecoilValue(currentMenuState) useEvent() - const loadCanvas = () => { if (canvas) { canvas?.clear() // 캔버스를 초기화합니다. @@ -43,8 +36,6 @@ export default function CanvasFrame({ plan }) { loadCanvas() }, [plan, canvas]) - const onClickContextMenu = (index) => {} - return (
@@ -68,6 +59,11 @@ export default function CanvasFrame({ plan }) { ))} + {[ + MENU.MODULE_CIRCUIT_SETTING.BASIC_SETTING, + MENU.MODULE_CIRCUIT_SETTING.CIRCUIT_TRESTLE_SETTING, + MENU.MODULE_CIRCUIT_SETTING.PLAN_ORIENTATION, + ].includes(currentMenu) && }
) } diff --git a/src/components/floor-plan/modal/flowDirection/FlowDirectionSetting.jsx b/src/components/floor-plan/modal/flowDirection/FlowDirectionSetting.jsx index ff749a6f..9fa412f9 100644 --- a/src/components/floor-plan/modal/flowDirection/FlowDirectionSetting.jsx +++ b/src/components/floor-plan/modal/flowDirection/FlowDirectionSetting.jsx @@ -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 (
-

面フローの設定

+

{getMessage('modal.shape.flow.direction.setting')}

@@ -26,113 +70,59 @@ export default function FlowDirectionSetting(props) {
-
流れ方向の設定
-
流れ方向を選択してください。
+
{getMessage('modal.flow.direction.setting')}
+
{getMessage('modal.flow.direction.setting.info')}
- - ドン - - 立つ + {getMessage('commons.north')} + {getMessage('commons.east')} + {getMessage('commons.south')} + {getMessage('commons.west')}
-
方位設定
-
シミュレーション計算の方向を指定します。面が向いている方位を選択してください。
+
{getMessage('modal.module.basic.setting.orientation.setting')}
+
{getMessage('modal.shape.flow.direction.setting.orientation.setting.info')}
- - + setType(e.target.value)} /> +
- + setSelectedOrientation(e)} />
- - + setType(e.target.value)} /> +
-
setCompasDeg(180)}> - 13 -
-
setCompasDeg(195)}> - 12 -
-
setCompasDeg(210)}> - 11 -
-
setCompasDeg(225)}> - 10 -
-
setCompasDeg(240)}> - 9 -
-
setCompasDeg(255)}> - 8 -
-
setCompasDeg(270)}> - 7 -
-
setCompasDeg(285)}> - 6 -
-
setCompasDeg(300)}> - 5 -
-
setCompasDeg(315)}> - 4 -
-
setCompasDeg(330)}> - 3 -
-
setCompasDeg(345)}> - 2 -
-
setCompasDeg(0)}> - 1 -
-
setCompasDeg(15)}> - 24 -
-
setCompasDeg(30)}> - 23 -
-
setCompasDeg(45)}> - 22 -
-
setCompasDeg(60)}> - 21 -
-
setCompasDeg(75)}> - 20 -
-
setCompasDeg(90)}> - 19 -
-
setCompasDeg(105)}> - 18 -
-
setCompasDeg(120)}> - 17 -
-
setCompasDeg(135)}> - 16 -
-
setCompasDeg(150)}> - 15 -
-
setCompasDeg(165)}> - 14 -
+ {Array.from({ length: 180 / 15 + 1 }).map((dot, index) => ( +
setCompasDeg(15 * (12 + index))} + > + {13 - index} +
+ ))} + {Array.from({ length: 180 / 15 - 1 }).map((dot, index) => ( +
setCompasDeg(15 * (index + 1))} + > + {24 - index} +
+ ))}
@@ -141,7 +131,7 @@ export default function FlowDirectionSetting(props) {
- +
diff --git a/src/components/floor-plan/modal/lineProperty/LinePropertySetting.jsx b/src/components/floor-plan/modal/lineProperty/LinePropertySetting.jsx index 16871a19..f5dc4b46 100644 --- a/src/components/floor-plan/modal/lineProperty/LinePropertySetting.jsx +++ b/src/components/floor-plan/modal/lineProperty/LinePropertySetting.jsx @@ -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 ( -
+
-

各辺属性の変更

+

{getMessage('contextmenu.line.property.edit')}

- 属性を変更する辺を選択してください。 - 選択した値 [龍丸] + {getMessage('modal.line.property.edit.info')} + + {getMessage('modal.line.property.edit.selected')} [ {selectedProperty?.name} ] +
-
設定
+
{getMessage('setting')}
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
+ {properties.map((property, index) => { + return ( +
+ = 10 ? index + 1 : `0${index + 1}`)} + onChange={(e) => setSelectedProperty(property)} + /> + +
+ ) + })}
- +
diff --git a/src/components/floor-plan/modal/object/DormerOffset.jsx b/src/components/floor-plan/modal/object/DormerOffset.jsx index 442e92df..b4292821 100644 --- a/src/components/floor-plan/modal/object/DormerOffset.jsx +++ b/src/components/floor-plan/modal/object/DormerOffset.jsx @@ -13,26 +13,26 @@ export default function DormerOffset(props) {
-

도머 오프셋

+

{getMessage('contextmenu.dormer.offset')}

-
移動する方向を入力してください
+
{getMessage('modal.dormer.offset.info')}
-

長さ

+

{getMessage('length')}

- +
mm
- +
mm
@@ -46,7 +46,7 @@ export default function DormerOffset(props) {
- +
diff --git a/src/components/floor-plan/modal/object/SizeSetting.jsx b/src/components/floor-plan/modal/object/SizeSetting.jsx index ca7d4773..bdb25821 100644 --- a/src/components/floor-plan/modal/object/SizeSetting.jsx +++ b/src/components/floor-plan/modal/object/SizeSetting.jsx @@ -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 (
-

サイズ変更

+

{getMessage('modal.size.setting')}

@@ -30,11 +28,11 @@ export default function SizeSetting(props) {
- + mm
- + mm
@@ -43,11 +41,11 @@ export default function SizeSetting(props) {
- + mm
- + mm
@@ -62,7 +60,7 @@ export default function SizeSetting(props) {
- +
diff --git a/src/hooks/useContextMenu.js b/src/hooks/useContextMenu.js index bbd5115b..d4e690e7 100644 --- a/src/hooks/useContextMenu.js +++ b/src/hooks/useContextMenu.js @@ -20,16 +20,20 @@ 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 { useMessage } from '@/hooks/useMessage' +import { useCanvasEvent } from '@/hooks/useCanvasEvent' -export function useContextMenu({ externalFn }) { +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 { handleZoomClear } = useCanvasEvent() const currentMenuSetting = (position) => { switch (currentMenu) { case MENU.PLAN_DRAWING: @@ -37,26 +41,26 @@ export function useContextMenu({ externalFn }) { [ { id: 'gridMove', - name: '그리드 이동', + name: getMessage('modal.grid.move'), component: , }, { id: 'gridCopy', - name: '그리드 복사', + name: getMessage('modal.grid.copy'), component: , }, { id: 'gridColorEdit', - name: '그리드 색 변경', + name: getMessage('modal.grid.color.edit'), component: , }, { id: 'remove', - name: '삭제', + name: getMessage('delete'), }, { id: 'removeAll', - name: '전체 삭제', + name: getMessage('delete.all'), }, ], ]) @@ -75,63 +79,61 @@ 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: , }, { id: 'auxiliaryMove', - name: '보조선 이동(M)', + name: `${getMessage('contextmenu.auxiliary.move')}(M)`, component: , }, { id: 'auxiliaryCopy', - name: '보조선 복사(C)', + name: `${getMessage('contextmenu.auxiliary.copy')}(C)`, component: , }, { id: 'auxiliaryRemove', - name: '보조선 삭제(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'), }, ], ]) @@ -146,33 +148,34 @@ export function useContextMenu({ externalFn }) { [ { id: 'sizeEdit', - name: '사이즈 변경', + name: getMessage('contextmenu.size.edit'), }, { id: 'remove', - name: '삭제(D)', + name: `${getMessage('contextmenu.remove')}(D)`, }, { id: 'move', - name: '이동(M)', + name: `${getMessage('contextmenu.move')}(M)`, }, { id: 'copy', - name: '복사(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: , }, { id: 'flowDirectionEdit', - name: '흐름 방향 변경', + name: getMessage('contextmenu.flow.direction.edit'), }, ], ]) @@ -200,7 +203,6 @@ export function useContextMenu({ externalFn }) { }, [currentContextMenu]) useEffect(() => { - console.log('object name', currentObject?.name) if (currentObject?.name) { switch (currentObject.name) { case 'triangleDormer': @@ -209,29 +211,29 @@ export function useContextMenu({ externalFn }) { [ { id: 'sizeEdit', - name: '사이즈 변경', - component: , + name: getMessage('contextmenu.size.edit'), + component: , }, { id: 'dormerRemove', - name: '삭제(D)', + name: `${getMessage('contextmenu.remove')}(D)`, }, { id: 'dormerMove', - name: '이동(M)', + name: `${getMessage('contextmenu.move')}(M)`, }, { id: 'dormerCopy', - name: '복사(C)', + name: `${getMessage('contextmenu.copy')}(C)`, }, { id: 'roofMaterialEdit', - name: '지붕재 변경', + name: getMessage('contextmenu.roof.material.edit'), component: , }, { id: 'dormerOffset', - name: '도머 오프셋', + name: getMessage('contextmenu.dormer.offset'), component: , }, ], @@ -247,32 +249,32 @@ export function useContextMenu({ externalFn }) { }, { id: 'roofMaterialRemove', - name: '삭제(D)', + name: `${getMessage('contextmenu.remove')}(D)`, }, { id: 'roofMaterialMove', - name: '이동(M)', + name: `${getMessage('contextmenu.move')}(M)`, }, { id: 'roofMaterialCopy', - name: '복사(C)', + name: `${getMessage('contextmenu.copy')}(C)`, }, ], [ { id: 'roofMaterialEdit', - name: '지붕재 변경', + name: getMessage('contextmenu.roof.material.edit'), component: , }, { id: 'linePropertyEdit', - name: '각 변 속성 변경', + name: getMessage('contextmenu.line.property.edit'), component: , }, { id: 'flowDirectionEdit', - name: '흐름 뱡향 변경', - component: , + name: getMessage('contextmenu.flow.direction.edit'), + component: , }, ], ]) @@ -282,23 +284,23 @@ export function useContextMenu({ externalFn }) { [ { id: 'sizeEdit', - name: '사이즈 변경', + name: getMessage('contextmenu.size.edit'), }, { id: 'openingRemove', - name: '삭제(D)', + name: `${getMessage('contextmenu.remove')}(D)`, }, { id: 'openingMove', - name: '이동(M)', + name: `${getMessage('contextmenu.move')}(M)`, }, { id: 'openingCopy', - name: '복사(C)', + name: `${getMessage('contextmenu.copy')}(C)`, }, { id: 'openingOffset', - name: '개구 오프셋', + name: getMessage('contextmenu.opening.offset'), }, ], ]) @@ -308,19 +310,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'), }, ], ]) @@ -330,24 +332,24 @@ export function useContextMenu({ externalFn }) { [ { id: 'commonTextRemove', - name: '삭제', + name: getMessage('contextmenu.remove'), }, { id: 'commonTextMove', - name: '이동', + name: getMessage('contextmenu.move'), }, { id: 'commonTextCopy', - name: '복사', + name: getMessage('contextmenu.copy'), }, { id: 'commonTextFontSetting', - name: '폰트 설정', + name: getMessage('contextmenu.font.setting'), component: , }, { id: 'commonTextEdit', - name: '편집', + name: getMessage('contextmenu.edit'), }, ], ]) @@ -357,23 +359,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'), }, ], ]) @@ -383,19 +385,19 @@ export function useContextMenu({ externalFn }) { [ { id: 'dimensionLineRemove', - name: '삭제', + name: getMessage('contextmenu.remove'), }, { id: 'dimensionLineMove', - name: '이동', + name: getMessage('contextmenu.move'), }, { id: 'dimensionAuxiliaryLineEdit', - name: '치수 보조선 변경', + name: getMessage('contextmenu.dimension.auxiliary.line.edit'), }, { id: 'dimensionLineDisplayEdit', - name: '표시 변경', + name: getMessage('contextmenu.display.edit'), component: , }, ], @@ -406,20 +408,20 @@ export function useContextMenu({ externalFn }) { [ { id: 'sizeEdit', - name: '사이즈 변경', + name: getMessage('contextmenu.size.edit'), component: , }, { id: 'remove', - name: '삭제(D)', + name: `${getMessage('contextmenu.remove')}(D)`, }, { id: 'move', - name: '이동(M)', + name: `${getMessage('contextmenu.move')}(M)`, }, { id: 'copy', - name: '복사(C)', + name: `${getMessage('contextmenu.copy')}(C)`, }, ], ]) diff --git a/src/locales/ja.json b/src/locales/ja.json index c5522fe2..81b85161 100644 --- a/src/locales/ja.json +++ b/src/locales/ja.json @@ -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": "太陽光発電システム図面管理サイト", @@ -595,9 +639,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": "直角", diff --git a/src/locales/ko.json b/src/locales/ko.json index 529dc494..12102c3b 100644 --- a/src/locales/ko.json +++ b/src/locales/ko.json @@ -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", @@ -600,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": "직각", From 5465756526515e3e9100c3365ea81c4d9aa0411a Mon Sep 17 00:00:00 2001 From: minsik Date: Fri, 25 Oct 2024 17:30:59 +0900 Subject: [PATCH 21/26] =?UTF-8?q?=ED=8C=A8=EB=84=90=20=EB=B0=B0=EC=B9=98?= =?UTF-8?q?=20=EC=A7=91=EA=B3=84=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../modal/panelBatch/PanelBatchStatistics.jsx | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 src/components/floor-plan/modal/panelBatch/PanelBatchStatistics.jsx diff --git a/src/components/floor-plan/modal/panelBatch/PanelBatchStatistics.jsx b/src/components/floor-plan/modal/panelBatch/PanelBatchStatistics.jsx new file mode 100644 index 00000000..2d36c4eb --- /dev/null +++ b/src/components/floor-plan/modal/panelBatch/PanelBatchStatistics.jsx @@ -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 ( + +
+

{getMessage('modal.panel.batch.statistic')}

+ +
+ + + + + + + + + + + + + +
{getMessage('modal.panel.batch.statistic.roof.shape')}{getMessage('modal.panel.batch.statistic.power.generation.amount')} (kW)
{getMessage('modal.panel.batch.statistic.total')}0.000
+
+
+
+ ) +} From c10d757f6968fef6547420cfb34202dc5b09ad4d Mon Sep 17 00:00:00 2001 From: minsik Date: Fri, 25 Oct 2024 17:38:22 +0900 Subject: [PATCH 22/26] merge --- src/components/floor-plan/CanvasFrame.jsx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/components/floor-plan/CanvasFrame.jsx b/src/components/floor-plan/CanvasFrame.jsx index 0117a871..f9183be8 100644 --- a/src/components/floor-plan/CanvasFrame.jsx +++ b/src/components/floor-plan/CanvasFrame.jsx @@ -8,12 +8,10 @@ import { useCanvas } from '@/hooks/useCanvas' import { useEvent } from '@/hooks/useEvent' import { usePlan } from '@/hooks/usePlan' import { useContextMenu } from '@/hooks/useContextMenu' -import { currentObjectState, modifiedPlanFlagState } 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 { useRecoilValue } from 'recoil' -import { currentMenuState } from '@/store/canvasAtom' import { MENU } from '@/common/common' import PanelBatchStatistics from '@/components/floor-plan/modal/panelBatch/PanelBatchStatistics' @@ -24,9 +22,9 @@ export default function CanvasFrame({ plan }) { const { handleZoomClear } = useCanvasEvent() const { canvasLoadInit, gridInit } = useCanvasConfigInitialize() const currentObject = useRecoilValue(currentObjectState) - + const currentMenu = useRecoilValue(currentMenuState) const { contextMenu, handleClick } = useContextMenu() - const { checkCanvasObjectEvent, checkUnsavedCanvasPlan } = usePlan() + const { checkCanvasObjectEvent, checkUnsavedCanvasPlan, resetModifiedPlans } = usePlan() useEvent() const loadCanvas = () => { From 70b2141a6c0a7572e2864eef99f32c9c1fb7da06 Mon Sep 17 00:00:00 2001 From: basssy Date: Mon, 28 Oct 2024 09:33:50 +0900 Subject: [PATCH 23/26] =?UTF-8?q?=EB=AC=BC=EA=B1=B4=EB=AA=A9=EB=A1=9D,?= =?UTF-8?q?=ED=98=84=ED=99=A9=20=EB=AF=B8=EC=82=AC=EC=9A=A9=20=EC=A3=BC?= =?UTF-8?q?=EC=84=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/management/Stuff.jsx | 20 +++++++++---------- src/components/management/StuffPlanQGrid.jsx | 20 +++++++++---------- src/components/management/StuffQGrid.jsx | 10 +++++----- .../management/popup/FindAddressPopQGrid.jsx | 2 +- .../management/popup/PlanRequestPopQGrid.jsx | 2 +- 5 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/components/management/Stuff.jsx b/src/components/management/Stuff.jsx index 64cd7f33..138a3b85 100644 --- a/src/components/management/Stuff.jsx +++ b/src/components/management/Stuff.jsx @@ -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() @@ -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() { {convertNumberToPriceDecimal(totalCount)}
  • - {getMessage('stuff.search.grid.selected')} - {convertNumberToPriceDecimal(selectedRowDataCount)} + {/* {getMessage('stuff.search.grid.selected')} */} + {/* {convertNumberToPriceDecimal(selectedRowDataCount)} */}
  • @@ -428,7 +428,7 @@ export default function Stuff() {
    - +
    diff --git a/src/components/management/StuffPlanQGrid.jsx b/src/components/management/StuffPlanQGrid.jsx index 108e17a2..1c8b88ba 100644 --- a/src/components/management/StuffPlanQGrid.jsx +++ b/src/components/management/StuffPlanQGrid.jsx @@ -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 (
    { - props.getSelectedRowdata(event.api.getSelectedRows()) - }, []) + // 체크박스 체크시 체크박스 미사용 + // const onSelectionChanged = useCallback((event) => { + // props.getSelectedRowdata(event.api.getSelectedRows()) + // }, []) //더블클릭 const onCellDoubleClicked = useCallback((event) => { @@ -95,7 +95,7 @@ export default function StuffQGrid(props) { isRowSelectable={isRowSelectable} rowSelection={'multiple'} suppressRowClickSelection={true} - onSelectionChanged={onSelectionChanged} + // onSelectionChanged={onSelectionChanged} onCellDoubleClicked={onCellDoubleClicked} pagination={isPageable} overlayNoRowsTemplate={`${getMessage('stuff.grid.noData')}`} diff --git a/src/components/management/popup/FindAddressPopQGrid.jsx b/src/components/management/popup/FindAddressPopQGrid.jsx index 383e1cc7..ba703625 100644 --- a/src/components/management/popup/FindAddressPopQGrid.jsx +++ b/src/components/management/popup/FindAddressPopQGrid.jsx @@ -28,7 +28,7 @@ export default function FindAddressPopGrid(props) { } }, []) - const rowBuffer = 100 + const rowBuffer = 10 useEffect(() => { gridData ? setRowData(gridData) : '' diff --git a/src/components/management/popup/PlanRequestPopQGrid.jsx b/src/components/management/popup/PlanRequestPopQGrid.jsx index 6e3a8467..955a24ff 100644 --- a/src/components/management/popup/PlanRequestPopQGrid.jsx +++ b/src/components/management/popup/PlanRequestPopQGrid.jsx @@ -28,7 +28,7 @@ export default function PlanRequestPopQGrid(props) { } }, []) - const rowBuffer = 100 + const rowBuffer = 20 useEffect(() => { gridData ? setRowData(gridData) : '' From d834e68b239f29dacfacfef064c94aead07efda3 Mon Sep 17 00:00:00 2001 From: minsik Date: Mon, 28 Oct 2024 10:04:43 +0900 Subject: [PATCH 24/26] =?UTF-8?q?contextmenu=20shortcut(=EB=8B=A8=EC=B6=95?= =?UTF-8?q?=ED=82=A4)=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/context-menu/QContextMenu.jsx | 14 +++--- src/components/floor-plan/CanvasFrame.jsx | 14 ++---- src/hooks/useContextMenu.js | 43 ++++++++++++++++++- src/store/contextMenu.js | 11 +++++ 4 files changed, 61 insertions(+), 21 deletions(-) create mode 100644 src/store/contextMenu.js diff --git a/src/components/common/context-menu/QContextMenu.jsx b/src/components/common/context-menu/QContextMenu.jsx index 8e3e2314..d1d73df3 100644 --- a/src/components/common/context-menu/QContextMenu.jsx +++ b/src/components/common/context-menu/QContextMenu.jsx @@ -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) //한번 노출 후 이벤트 삭제 } diff --git a/src/components/floor-plan/CanvasFrame.jsx b/src/components/floor-plan/CanvasFrame.jsx index f9183be8..c2a73b63 100644 --- a/src/components/floor-plan/CanvasFrame.jsx +++ b/src/components/floor-plan/CanvasFrame.jsx @@ -23,7 +23,7 @@ export default function CanvasFrame({ plan }) { const { canvasLoadInit, gridInit } = useCanvasConfigInitialize() const currentObject = useRecoilValue(currentObjectState) const currentMenu = useRecoilValue(currentMenuState) - const { contextMenu, handleClick } = useContextMenu() + const { contextMenu, handleClick, handleKeyup } = useContextMenu() const { checkCanvasObjectEvent, checkUnsavedCanvasPlan, resetModifiedPlans } = usePlan() useEvent() @@ -55,19 +55,11 @@ export default function CanvasFrame({ plan }) {
    - + {contextMenu.map((menus, index) => (
      {menus.map((menu) => ( -
    • { - if (menu.fn) { - menu.fn() - } - handleClick(e, menu) - }} - > +
    • handleClick(e, menu)}> {menu.name}
    • ))} diff --git a/src/hooks/useContextMenu.js b/src/hooks/useContextMenu.js index d4e690e7..c5134e25 100644 --- a/src/hooks/useContextMenu.js +++ b/src/hooks/useContextMenu.js @@ -22,6 +22,7 @@ import LinePropertySetting from '@/components/floor-plan/modal/lineProperty/Line import FlowDirectionSetting from '@/components/floor-plan/modal/flowDirection/FlowDirectionSetting' import { useMessage } from '@/hooks/useMessage' import { useCanvasEvent } from '@/hooks/useCanvasEvent' +import { contextMenuState } from '@/store/contextMenu' export function useContextMenu() { const currentMenu = useRecoilValue(currentMenuState) // 현재 메뉴 @@ -33,6 +34,7 @@ export function useContextMenu() { const { addPopup } = usePopup() const [popupId, setPopupId] = useState(uuidv4()) const [gridColor, setGridColor] = useRecoilState(gridColorState) + const [qContextMenu, setQContextMenu] = useRecoilState(contextMenuState) const { handleZoomClear } = useCanvasEvent() const currentMenuSetting = (position) => { switch (currentMenu) { @@ -112,15 +114,18 @@ export function useContextMenu() { { id: 'auxiliaryMove', name: `${getMessage('contextmenu.auxiliary.move')}(M)`, + shortcut: ['m', 'M'], component: , }, { id: 'auxiliaryCopy', name: `${getMessage('contextmenu.auxiliary.copy')}(C)`, + shortcut: ['c', 'C'], component: , }, { id: 'auxiliaryRemove', + shortcut: ['d', 'D'], name: `${getMessage('contextmenu.auxiliary.remove')}(D)`, }, { @@ -152,14 +157,17 @@ export function useContextMenu() { }, { id: 'remove', + shortcut: ['d', 'D'], name: `${getMessage('contextmenu.remove')}(D)`, }, { id: 'move', + shortcut: ['m', 'M'], name: `${getMessage('contextmenu.move')}(M)`, }, { id: 'copy', + shortcut: ['c', 'C'], name: `${getMessage('contextmenu.copy')}(C)`, }, ], @@ -187,11 +195,29 @@ export function useContextMenu() { } 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(() => { @@ -216,14 +242,17 @@ export function useContextMenu() { }, { id: 'dormerRemove', + shortcut: ['d', 'D'], name: `${getMessage('contextmenu.remove')}(D)`, }, { id: 'dormerMove', + shortcut: ['m', 'M'], name: `${getMessage('contextmenu.move')}(M)`, }, { id: 'dormerCopy', + shortcut: ['c', 'C'], name: `${getMessage('contextmenu.copy')}(C)`, }, { @@ -249,14 +278,17 @@ export function useContextMenu() { }, { id: 'roofMaterialRemove', + shortcut: ['d', 'D'], name: `${getMessage('contextmenu.remove')}(D)`, }, { id: 'roofMaterialMove', + shortcut: ['m', 'M'], name: `${getMessage('contextmenu.move')}(M)`, }, { id: 'roofMaterialCopy', + shortcut: ['c', 'C'], name: `${getMessage('contextmenu.copy')}(C)`, }, ], @@ -288,14 +320,17 @@ export function useContextMenu() { }, { id: 'openingRemove', + shortcut: ['d', 'D'], name: `${getMessage('contextmenu.remove')}(D)`, }, { id: 'openingMove', + shortcut: ['m', 'M'], name: `${getMessage('contextmenu.move')}(M)`, }, { id: 'openingCopy', + shortcut: ['c', 'C'], name: `${getMessage('contextmenu.copy')}(C)`, }, { @@ -413,14 +448,17 @@ export function useContextMenu() { }, { id: 'remove', + shortcut: ['d', 'D'], name: `${getMessage('contextmenu.remove')}(D)`, }, { id: 'move', + shortcut: ['m', 'M'], name: `${getMessage('contextmenu.move')}(M)`, }, { id: 'copy', + shortcut: ['c', 'C'], name: `${getMessage('contextmenu.copy')}(C)`, }, ], @@ -439,5 +477,6 @@ export function useContextMenu() { currentContextMenu, setCurrentContextMenu, handleClick, + handleKeyup, } } diff --git a/src/store/contextMenu.js b/src/store/contextMenu.js new file mode 100644 index 00000000..31b18f53 --- /dev/null +++ b/src/store/contextMenu.js @@ -0,0 +1,11 @@ +import { atom } from 'recoil' + +export const contextMenuState = atom({ + key: 'contextMenuState', + default: { + visible: false, + x: 0, + y: 0, + }, + dangerouslyAllowMutability: true, +}) From f89b249b9899158e038fb017d8935010b208d763 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Mon, 28 Oct 2024 10:37:53 +0900 Subject: [PATCH 25/26] =?UTF-8?q?=EA=B2=BD=EC=82=AC,=20=EA=B0=81=EB=8F=84?= =?UTF-8?q?=20=EC=84=A4=EC=A0=95=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/fabric/QLine.js | 8 +++- src/components/floor-plan/modal/Slope.jsx | 7 +-- .../modal/eavesGable/EavesGableEdit.jsx | 4 +- .../modal/eavesGable/type/Eaves.jsx | 9 ++-- .../modal/eavesGable/type/Gable.jsx | 16 +++++-- .../placementShape/PlacementShapeSetting.jsx | 18 +------ .../roofShape/RoofShapePassivitySetting.jsx | 5 +- .../modal/roofShape/RoofShapeSetting.jsx | 7 ++- .../modal/roofShape/passivity/Eaves.jsx | 9 ++-- .../modal/roofShape/passivity/Gable.jsx | 9 ++-- .../modal/roofShape/type/Direction.jsx | 4 +- .../modal/roofShape/type/Pattern.jsx | 4 +- .../floor-plan/modal/roofShape/type/Ridge.jsx | 4 +- .../floor-plan/modal/roofShape/type/Side.jsx | 7 +-- .../modal/roofShape/type/option/Eaves.jsx | 4 +- .../roofShape/type/option/HipAndGable.jsx | 4 +- .../roofShape/type/option/Jerkinhead.jsx | 12 ++++- src/hooks/common/useCanvasConfigInitialize.js | 26 ++++++++-- src/hooks/roofcover/useEavesGableEdit.js | 6 ++- .../roofcover/useRoofShapePassivitySetting.js | 7 ++- src/hooks/roofcover/useRoofShapeSetting.js | 16 ++++--- src/hooks/useLine.js | 2 + src/hooks/usePolygon.js | 2 + src/store/canvasAtom.js | 48 +++++++++++++++++++ src/store/settingAtom.js | 20 ++++++++ src/util/canvas-util.js | 9 ++++ 26 files changed, 201 insertions(+), 66 deletions(-) diff --git a/src/components/fabric/QLine.js b/src/components/fabric/QLine.js index a1d56b08..37c9a75f 100644 --- a/src/components/fabric/QLine.js +++ b/src/components/fabric/QLine.js @@ -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 } diff --git a/src/components/floor-plan/modal/Slope.jsx b/src/components/floor-plan/modal/Slope.jsx index ffe32af6..42b8be64 100644 --- a/src/components/floor-plan/modal/Slope.jsx +++ b/src/components/floor-plan/modal/Slope.jsx @@ -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 } }) {
      - {getMessage('size.angle')} + {pitchText}
    diff --git a/src/components/floor-plan/modal/eavesGable/EavesGableEdit.jsx b/src/components/floor-plan/modal/eavesGable/EavesGableEdit.jsx index ab8553a2..2b5a7079 100644 --- a/src/components/floor-plan/modal/eavesGable/EavesGableEdit.jsx +++ b/src/components/floor-plan/modal/eavesGable/EavesGableEdit.jsx @@ -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 = { diff --git a/src/components/floor-plan/modal/eavesGable/type/Eaves.jsx b/src/components/floor-plan/modal/eavesGable/type/Eaves.jsx index 80120107..a7d7d466 100644 --- a/src/components/floor-plan/modal/eavesGable/type/Eaves.jsx +++ b/src/components/floor-plan/modal/eavesGable/type/Eaves.jsx @@ -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 ( <>
    @@ -17,9 +20,9 @@ export default function Eaves({ pitchRef, offsetRef, widthRef, radioTypeRef }) { {getMessage('slope')}
    - +
    - + {pitchText}
    diff --git a/src/components/floor-plan/modal/eavesGable/type/Gable.jsx b/src/components/floor-plan/modal/eavesGable/type/Gable.jsx index 23f9c300..999687fd 100644 --- a/src/components/floor-plan/modal/eavesGable/type/Gable.jsx +++ b/src/components/floor-plan/modal/eavesGable/type/Gable.jsx @@ -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 ( <>
    @@ -57,9 +61,15 @@ export default function Gable({ pitchRef, offsetRef, widthRef, radioTypeRef }) { {getMessage('slope')}
    - +
    - + {pitchText}
    diff --git a/src/components/floor-plan/modal/placementShape/PlacementShapeSetting.jsx b/src/components/floor-plan/modal/placementShape/PlacementShapeSetting.jsx index 79a89b88..b635ea86 100644 --- a/src/components/floor-plan/modal/placementShape/PlacementShapeSetting.jsx +++ b/src/components/floor-plan/modal/placementShape/PlacementShapeSetting.jsx @@ -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() diff --git a/src/components/floor-plan/modal/roofShape/RoofShapePassivitySetting.jsx b/src/components/floor-plan/modal/roofShape/RoofShapePassivitySetting.jsx index b9ddd97d..090eec0a 100644 --- a/src/components/floor-plan/modal/roofShape/RoofShapePassivitySetting.jsx +++ b/src/components/floor-plan/modal/roofShape/RoofShapePassivitySetting.jsx @@ -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 = { diff --git a/src/components/floor-plan/modal/roofShape/RoofShapeSetting.jsx b/src/components/floor-plan/modal/roofShape/RoofShapeSetting.jsx index 48058b1b..e60aadc6 100644 --- a/src/components/floor-plan/modal/roofShape/RoofShapeSetting.jsx +++ b/src/components/floor-plan/modal/roofShape/RoofShapeSetting.jsx @@ -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 ( diff --git a/src/components/floor-plan/modal/roofShape/passivity/Eaves.jsx b/src/components/floor-plan/modal/roofShape/passivity/Eaves.jsx index 3a16b5c0..2ba0f3e3 100644 --- a/src/components/floor-plan/modal/roofShape/passivity/Eaves.jsx +++ b/src/components/floor-plan/modal/roofShape/passivity/Eaves.jsx @@ -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 ( <>
    @@ -9,9 +12,9 @@ export default function Eaves({ offsetRef, pitchRef }) { {getMessage('slope')}
    - +
    - + {pitchText}
    diff --git a/src/components/floor-plan/modal/roofShape/passivity/Gable.jsx b/src/components/floor-plan/modal/roofShape/passivity/Gable.jsx index 69b2cf9d..e7d9718d 100644 --- a/src/components/floor-plan/modal/roofShape/passivity/Gable.jsx +++ b/src/components/floor-plan/modal/roofShape/passivity/Gable.jsx @@ -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 ( <>
    @@ -9,9 +12,9 @@ export default function Gable({ offsetRef, pitchRef }) { {getMessage('slope')}
    - +
    - + {pitchText}
    diff --git a/src/components/floor-plan/modal/roofShape/type/Direction.jsx b/src/components/floor-plan/modal/roofShape/type/Direction.jsx index 51faffcb..ef2be366 100644 --- a/src/components/floor-plan/modal/roofShape/type/Direction.jsx +++ b/src/components/floor-plan/modal/roofShape/type/Direction.jsx @@ -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 (
    @@ -12,7 +12,7 @@ export default function Direction({ pitch, setPitch, eavesOffset, setEavesOffset
    onlyNumberWithDotInputChange(e, setPitch)} />
    - {getMessage('size')} + {pitchText}
    diff --git a/src/components/floor-plan/modal/roofShape/type/Pattern.jsx b/src/components/floor-plan/modal/roofShape/type/Pattern.jsx index 0c086fbe..704b3a52 100644 --- a/src/components/floor-plan/modal/roofShape/type/Pattern.jsx +++ b/src/components/floor-plan/modal/roofShape/type/Pattern.jsx @@ -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 (
    @@ -13,7 +13,7 @@ export default function Pattern(props) {
    onlyNumberWithDotInputChange(e, setPitch)} />
    - {getMessage('size')} + {pitchText}
    diff --git a/src/components/floor-plan/modal/roofShape/type/Ridge.jsx b/src/components/floor-plan/modal/roofShape/type/Ridge.jsx index 13dab6f3..538cda3d 100644 --- a/src/components/floor-plan/modal/roofShape/type/Ridge.jsx +++ b/src/components/floor-plan/modal/roofShape/type/Ridge.jsx @@ -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 (
    @@ -15,7 +15,7 @@ export default function Ridge(props) {
    onlyNumberWithDotInputChange(e, setPitch)} />
    - {getMessage('size')} + {pitchText}
    diff --git a/src/components/floor-plan/modal/roofShape/type/Side.jsx b/src/components/floor-plan/modal/roofShape/type/Side.jsx index b2f032ba..487ccd43 100644 --- a/src/components/floor-plan/modal/roofShape/type/Side.jsx +++ b/src/components/floor-plan/modal/roofShape/type/Side.jsx @@ -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() diff --git a/src/components/floor-plan/modal/roofShape/type/option/Eaves.jsx b/src/components/floor-plan/modal/roofShape/type/option/Eaves.jsx index 8d061a7b..cd39a439 100644 --- a/src/components/floor-plan/modal/roofShape/type/option/Eaves.jsx +++ b/src/components/floor-plan/modal/roofShape/type/option/Eaves.jsx @@ -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 })
    onlyNumberWithDotInputChange(e, setPitch)} />
    - {getMessage('size')} + {pitchText}
    diff --git a/src/components/floor-plan/modal/roofShape/type/option/HipAndGable.jsx b/src/components/floor-plan/modal/roofShape/type/option/HipAndGable.jsx index ce4a57cc..a59e94b1 100644 --- a/src/components/floor-plan/modal/roofShape/type/option/HipAndGable.jsx +++ b/src/components/floor-plan/modal/roofShape/type/option/HipAndGable.jsx @@ -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
    onlyNumberWithDotInputChange(e, setPitch)} />
    - {getMessage('size')} + {pitchText}
    diff --git a/src/components/floor-plan/modal/roofShape/type/option/Jerkinhead.jsx b/src/components/floor-plan/modal/roofShape/type/option/Jerkinhead.jsx index be846e94..e1b822e7 100644 --- a/src/components/floor-plan/modal/roofShape/type/option/Jerkinhead.jsx +++ b/src/components/floor-plan/modal/roofShape/type/option/Jerkinhead.jsx @@ -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)} />
    - {getMessage('size')} + {pitchText}
    ) diff --git a/src/hooks/common/useCanvasConfigInitialize.js b/src/hooks/common/useCanvasConfigInitialize.js index c2775376..2b31bbda 100644 --- a/src/hooks/common/useCanvasConfigInitialize.js +++ b/src/hooks/common/useCanvasConfigInitialize.js @@ -1,8 +1,8 @@ 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' @@ -11,9 +11,11 @@ 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() @@ -29,6 +31,24 @@ 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() //화면표시 초기화 } diff --git a/src/hooks/roofcover/useEavesGableEdit.js b/src/hooks/roofcover/useEavesGableEdit.js index e062a77a..92c9b324 100644 --- a/src/hooks/roofcover/useEavesGableEdit.js +++ b/src/hooks/roofcover/useEavesGableEdit.js @@ -1,6 +1,6 @@ import { useEffect, useRef, useState } from 'react' import { useRecoilValue } from 'recoil' -import { canvasState } from '@/store/canvasAtom' +import { canvasState, currentAngleTypeSelector, pitchTextSelector } from '@/store/canvasAtom' import { useMessage } from '@/hooks/useMessage' import { useEvent } from '@/hooks/useEvent' import { LINE_TYPE } from '@/common/common' @@ -28,6 +28,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) @@ -217,5 +219,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 } } diff --git a/src/hooks/roofcover/useRoofShapePassivitySetting.js b/src/hooks/roofcover/useRoofShapePassivitySetting.js index 0f51c900..e8d8dbd9 100644 --- a/src/hooks/roofcover/useRoofShapePassivitySetting.js +++ b/src/hooks/roofcover/useRoofShapePassivitySetting.js @@ -1,4 +1,4 @@ -import { canvasState, currentObjectState } from '@/store/canvasAtom' +import { canvasState, currentAngleTypeSelector, currentObjectState, pitchTextSelector } from '@/store/canvasAtom' import { useRecoilValue } from 'recoil' import { useEffect, useRef, useState } from 'react' import { useLine } from '@/hooks/useLine' @@ -19,6 +19,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 +36,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 }, @@ -216,5 +219,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 } } diff --git a/src/hooks/roofcover/useRoofShapeSetting.js b/src/hooks/roofcover/useRoofShapeSetting.js index 13dfef8b..4899888e 100644 --- a/src/hooks/roofcover/useRoofShapeSetting.js +++ b/src/hooks/roofcover/useRoofShapeSetting.js @@ -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' @@ -17,13 +17,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') @@ -68,8 +71,8 @@ export function useRoofShapeSetting(id) { strokeWidth, }) - addPitchText(line) - line.setViewLengthText(false) + /*addPitchText(line) + line.setViewLengthText(false)*/ } }) canvas.renderAll() @@ -114,12 +117,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]) @@ -629,5 +630,6 @@ export function useRoofShapeSetting(id) { setButtonAct, handleConfirm, handleRollBack, + pitchText, } } diff --git a/src/hooks/useLine.js b/src/hooks/useLine.js index 210c1ac6..5cffa26f 100644 --- a/src/hooks/useLine.js +++ b/src/hooks/useLine.js @@ -97,9 +97,11 @@ export const useLine = () => { left, top, fontSize: 20, + originText: `${attributes.offset ? attributes.offset * 10 : attributes.width * 10}`, fill: 'black', name: 'pitchText', parentId: line.id, + pitch: attributes.pitch, }, ) diff --git a/src/hooks/usePolygon.js b/src/hooks/usePolygon.js index 71ec1549..03b864db 100644 --- a/src/hooks/usePolygon.js +++ b/src/hooks/usePolygon.js @@ -406,9 +406,11 @@ export const usePolygon = () => { 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, diff --git a/src/store/canvasAtom.js b/src/store/canvasAtom.js index 0f2428ee..2eade854 100644 --- a/src/store/canvasAtom.js +++ b/src/store/canvasAtom.js @@ -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', @@ -305,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' ? '寸' : '度' + }, +}) diff --git a/src/store/settingAtom.js b/src/store/settingAtom.js index c6e94029..2432d884 100644 --- a/src/store/settingAtom.js +++ b/src/store/settingAtom.js @@ -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', + }, + ], + }, +}) diff --git a/src/util/canvas-util.js b/src/util/canvas-util.js index 8ed918ca..9f8b9469 100644 --- a/src/util/canvas-util.js +++ b/src/util/canvas-util.js @@ -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} From 7d14f8da5abd740d95a6da10af120ace653bf891 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Mon, 28 Oct 2024 11:34:25 +0900 Subject: [PATCH 26/26] =?UTF-8?q?pitch=20=EC=84=B8=ED=8C=85=20default=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/roofcover/useEavesGableEdit.js | 9 +-- .../roofcover/useRoofShapePassivitySetting.js | 7 ++- src/hooks/roofcover/useRoofShapeSetting.js | 57 +++++++++++-------- src/hooks/useLine.js | 36 +++++++----- src/hooks/usePolygon.js | 10 +++- 5 files changed, 72 insertions(+), 47 deletions(-) diff --git a/src/hooks/roofcover/useEavesGableEdit.js b/src/hooks/roofcover/useEavesGableEdit.js index 92c9b324..b0085019 100644 --- a/src/hooks/roofcover/useEavesGableEdit.js +++ b/src/hooks/roofcover/useEavesGableEdit.js @@ -1,6 +1,6 @@ import { useEffect, useRef, useState } from 'react' import { useRecoilValue } from 'recoil' -import { canvasState, currentAngleTypeSelector, pitchTextSelector } 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) { @@ -107,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, } @@ -128,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, } diff --git a/src/hooks/roofcover/useRoofShapePassivitySetting.js b/src/hooks/roofcover/useRoofShapePassivitySetting.js index e8d8dbd9..ef7a4477 100644 --- a/src/hooks/roofcover/useRoofShapePassivitySetting.js +++ b/src/hooks/roofcover/useRoofShapePassivitySetting.js @@ -1,4 +1,4 @@ -import { canvasState, currentAngleTypeSelector, currentObjectState, pitchTextSelector } 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) { @@ -137,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) { diff --git a/src/hooks/roofcover/useRoofShapeSetting.js b/src/hooks/roofcover/useRoofShapeSetting.js index 4899888e..9aab5da7 100644 --- a/src/hooks/roofcover/useRoofShapeSetting.js +++ b/src/hooks/roofcover/useRoofShapeSetting.js @@ -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) { @@ -37,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) { @@ -71,8 +82,8 @@ export function useRoofShapeSetting(id) { strokeWidth, }) - /*addPitchText(line) - line.setViewLengthText(false)*/ + addPitchText(line) + line.setViewLengthText(false) } }) canvas.renderAll() @@ -182,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, } } @@ -198,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, } } @@ -206,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, } } @@ -227,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, } } @@ -235,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, } } @@ -243,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, } } @@ -251,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, } } @@ -271,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, } } @@ -279,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, } } @@ -287,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, } } @@ -295,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, } } @@ -316,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, } } @@ -324,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, } } @@ -332,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, } } @@ -340,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, } } @@ -437,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) @@ -459,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, } } @@ -482,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, } } @@ -505,7 +516,7 @@ export function useRoofShapeSetting(id) { // 처마 attributes = { type: LINE_TYPE.WALLLINE.EAVES, - pitch: pitch, + pitch: pitchRef.current, offset: eavesOffset / 10, } addPitchText(currentObject) @@ -536,7 +547,7 @@ export function useRoofShapeSetting(id) { // 팔작지붕 attributes = { type: LINE_TYPE.WALLLINE.HIPANDGABLE, - pitch: pitch, + pitch: pitchRef.current, offset: eavesOffset / 10, width: hipAndGableWidth / 10, } @@ -551,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 }) diff --git a/src/hooks/useLine.js b/src/hooks/useLine.js index 5cffa26f..62ec937a 100644 --- a/src/hooks/useLine.js +++ b/src/hooks/useLine.js @@ -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,19 +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, - originText: `${attributes.offset ? attributes.offset * 10 : attributes.width * 10}`, - fill: 'black', - name: 'pitchText', - parentId: line.id, - pitch: attributes.pitch, - }, - ) + 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) } diff --git a/src/hooks/usePolygon.js b/src/hooks/usePolygon.js index 03b864db..c147e2fe 100644 --- a/src/hooks/usePolygon.js +++ b/src/hooks/usePolygon.js @@ -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,7 +403,9 @@ 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,