Compare commits

..

333 Commits

Author SHA1 Message Date
a652d09b8d [1036] : [プランをコピー/移動する時の自動保存について]
[작업내용] : 탭간 이동, 복사시 저장 여부 확인 로직 추가
2025-05-13 10:46:26 +09:00
4b8287579c Merge branch 'dev' into feature/dev-yj 2025-05-12 13:49:29 +09:00
f03d35ec8d [1037] : [モジュール1枚を削除する時の表示]
[작업내용] : 모듈 선택시 아웃라인 두께 조정
2025-05-12 11:11:09 +09:00
56b8917345 [1014] : [819の追加 レイアウト指定配置の自動配置]
[작업내용] : 멀티모듈일 경우에 북면 모듈이 같이 계산되는 로직 수정
2025-05-09 15:48:08 +09:00
김민식
dbae3380d7 Merge remote-tracking branch 'origin/qcast-pub' into dev 2025-05-09 15:36:55 +09:00
김민식
35708f65e9 지붕재별 가대정보 저장 로직수정 2025-05-09 15:36:28 +09:00
391fe39a2d [1014] : [819の追加 レイアウト指定配置の自動配置]
[작업내용] : 하단방향 오류 수정
2025-05-09 15:04:21 +09:00
김민식
d1af28987b Merge remote-tracking branch 'origin/qcast-pub' into dev 2025-05-09 14:19:47 +09:00
김민식
b8dad62116 [929] : [翻訳変更]
[작업내용] : 다국어 수정 및 validation 로직 추가
2025-05-09 14:19:05 +09:00
6949236960 [1014] : [819の追加 レイアウト指定配置の自動配置]
[작업내용] : 북면설치 모듈일 경우, 북면과, 북면이 아닐때 분기 처리
2025-05-08 18:02:41 +09:00
e961648d85 [1014] : [819の追加 レイアウト指定配置の自動配置]
[작업내용] : 멀티모듈일때 북면 모듈 포함일 경우 북면이 아닌 면에 설치되는 오류 수정
2025-05-08 16:55:39 +09:00
cbde82b0ab Merge branch 'dev' into feature/dev-yj-layout 2025-05-08 15:16:41 +09:00
김민식
b2065e7699 [1030] : [【HANASYS DESIGN】文字修正]
[작업내용] : 다국어 수정
2025-05-08 14:48:05 +09:00
김민식
c0c3295b04 [1027] : [【HANASYS DESIGN】横葺に使用する金具の離隔について]
[작업내용] : length 변경시 하위 필드 초기화
2025-05-08 13:21:54 +09:00
cbdbade89f Merge pull request '939 - 배치면 그리기에 흡착점 추가' (#34) from feature/ysCha into dev
Reviewed-on: #34
2025-05-08 10:13:16 +09:00
704fe887f1 939 - 배치면 그리기에 흡착점 추가 2025-05-08 10:09:45 +09:00
47245e438c [1014] : [819の追加 レイアウト指定配置の自動配置]
[작업내용] : 불필요 소스 삭제
2025-05-08 09:48:31 +09:00
0fc7ed857d [공통] : [선택면 최상위 이동]
[작업내용] : canvas 객체 선택시 선택된 객체 최상위 레이어으로 이동
2025-05-07 17:56:36 +09:00
f3b8ee40fa [1014] : [819の追加 レイアウト指定配置の自動配置]
[작업내용] : 문구 수정 및 alert icon 정리
2025-05-07 16:56:28 +09:00
3a66920104 Merge branch 'dev' into feature/dev-yj-layout 2025-05-07 15:19:25 +09:00
d244ba3b97 [1014] : [819の追加 レイアウト指定配置の自動配置]
[작업내용] : 고도화 단수 열수 자동 배치 작업
2025-05-07 15:18:59 +09:00
d6b0e4994a # [1014] : 819の追加 レイアウト指定配置の自動配置
# [작업내용] : http://1.248.227.176:43333/issues/1014
2025-05-07 14:47:08 +09:00
김민식
f846644145 모듈 간격 영역 추가 2025-05-07 11:34:22 +09:00
김민식
655f0781a2 🚨chore: Sync Sass 2025-05-07 11:34:03 +09:00
c01ed83b40 Merge branch 'dev' into feature/dev-yj-layout 2025-05-02 17:01:22 +09:00
김민식
76dd1aaa43 Merge remote-tracking branch 'origin/qcast-pub' into dev 2025-05-02 16:43:15 +09:00
김민식
9a800d307b - 다설시공 다국어 변경
- trestle에서 참조하는 지붕정보에 참조하는 정보중 raftBaseCd -> raft 로 변경
2025-05-02 16:42:38 +09:00
e3df21f827 http://1.248.227.176:43333/issues/1022 상단 유틸리티 title 추가 2025-05-02 16:16:23 +09:00
f5c098546c Merge branch 'dev' into feature/dev-yj-layout 2025-05-02 15:50:37 +09:00
08722e8b51 모듈 레이아웃 설치 작업중 2025-05-02 15:50:27 +09:00
김민식
7f1ef1ace4 Merge remote-tracking branch 'origin/qcast-pub' into dev 2025-05-02 10:01:59 +09:00
김민식
f52fe72d2a 시공법 버튼명 api 데이터로 수정 2025-05-02 10:01:26 +09:00
d5bba49ca9 console.log 삭제 2025-05-02 09:23:59 +09:00
234e7f1cf7 Merge branch 'dev' into feature/dev-yj-layout 2025-04-29 15:58:16 +09:00
c40ba2586f Merge pull request '886 - 견적서 정가 다운로드 : 0 => "open"' (#33) from feature/ysCha into dev
Reviewed-on: #33
2025-04-29 15:04:53 +09:00
fc8e85b76c 886 - 견적서 정가 다운로드 : 0 => "open" 2025-04-29 15:02:19 +09:00
ed27f2ed93 모듈 자동 레이아웃 설치 validate 작업중 2025-04-29 13:09:46 +09:00
1c7b81c99f 모듈 자동 레이아웃 수정 2025-04-28 14:09:58 +09:00
fe957102d9 모듈 자동 레이아웃 설치 2025-04-28 13:20:35 +09:00
034bfb374e Merge pull request '1019 - ja, ko 변경' (#32) from feature/ysCha into dev
Reviewed-on: #32
2025-04-25 14:47:44 +09:00
71d3a33bf4 1019 - ja, ko 변경 2025-04-25 14:46:00 +09:00
0f079080a1 Merge pull request '1018 - 캔버스팝업상태 복사저장, 조회 : unescapeString() if => while 변경' (#31) from feature/ysCha into dev
Reviewed-on: #31
2025-04-25 14:05:14 +09:00
9bf961441b 1018 - 캔버스팝업상태 복사저장, 조회 : unescapeString() if => while 변경 2025-04-25 14:02:35 +09:00
3b96344943 index 세팅 추가 2025-04-24 13:30:58 +09:00
4be94e41a2 Merge pull request '1006 - ja 추가변경' (#30) from feature/ysCha into dev
Reviewed-on: #30
2025-04-24 11:36:19 +09:00
bd62bf7028 1006 - ja 추가변경 2025-04-24 11:34:59 +09:00
93c8bad986 Merge pull request '1012 - top select box link name 변경' (#29) from feature/ysCha into dev
Reviewed-on: #29
2025-04-24 10:48:54 +09:00
203ddfbdf3 1012 - top select box link name 변경 2025-04-24 10:39:47 +09:00
b11424e70e 육지붕일 경우 값 고정 2025-04-24 10:29:36 +09:00
49d85d437b Merge branch 'dev' into feature/dev-yj 2025-04-24 10:27:42 +09:00
88e2c8ed05 Merge pull request '1003 - calcLineActualSize() degree가 undefined => 0 처리' (#28) from feature/ysCha into dev
Reviewed-on: #28
2025-04-24 09:51:20 +09:00
6b56af6119 1003 - calcLineActualSize() degree가 undefined => 0 처리
showLine 함수의 canvas.getObjects().find()가 undefined => Optional chaining (?.) 처리
2025-04-24 09:45:36 +09:00
287ce5c85a 보조선 이동, 복사 시 나누기 10 적용 2025-04-23 17:09:33 +09:00
737fcf0b30 Merge pull request '986 - 시공사번호 추가, 시공사(userId) 데이터만 조회' (#27) from feature/ysCha into dev
Reviewed-on: #27
2025-04-23 16:16:42 +09:00
f3dfc50f6e 986 - 시공사번호 추가, 시공사(userId) 데이터만 조회 2025-04-23 16:13:07 +09:00
5b74a8ee6b Merge branch 'dev' into feature/dev-yj 2025-04-23 15:45:43 +09:00
김민식
77e34d849d Merge remote-tracking branch 'origin/qcast-pub' into dev 2025-04-23 15:44:32 +09:00
김민식
b6549e06b2 setManagementState 삭제 2025-04-23 15:44:05 +09:00
abe0ddb731 Merge branch 'dev' into feature/dev-yj
# Conflicts:
#	src/components/floor-plan/modal/circuitTrestle/CircuitTrestleSetting.jsx
#	src/components/floor-plan/modal/circuitTrestle/step/type/PassivityCircuitAllocation.jsx
2025-04-23 15:05:56 +09:00
4a8428f9f7 #1008 보조선 관련 오류 수정 2025-04-23 13:39:32 +09:00
김민식
7ff0178b19 roofConstruction -> roofConstructions 수정 2025-04-23 11:11:53 +09:00
김민식
6d310a6a32 Merge remote-tracking branch 'origin/qcast-pub' into dev 2025-04-23 10:55:30 +09:00
김민식
ebd84967f2 moduleSelectionDataLoad 삭제 및 setData 로직 수정 2025-04-23 10:54:58 +09:00
8de8416160 managementStateLoaded삭제 2025-04-23 10:10:57 +09:00
82c8b25895 Update ecosystem configuration for production deployment 2025-04-23 10:02:13 +09:00
김민식
2b81cc192e Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into qcast-pub 2025-04-22 17:09:41 +09:00
김민식
028fa7f9bf LocalStorage -> API 변경 2025-04-22 17:09:18 +09:00
e0d0eaf017 Merge pull request '991-견적서 pricing 버튼 클릭, confirm 노출' (#26) from feature/ysCha into dev
Reviewed-on: #26
2025-04-22 13:57:12 +09:00
04d49e6aa6 991-견적서 pricing 버튼 클릭, confirm 노출 2025-04-22 13:54:40 +09:00
e938feec2f Merge pull request '1006-오타수정 및 변경' (#25) from feature/ysCha into dev
Reviewed-on: #25
2025-04-22 13:24:00 +09:00
d4a83c4ec5 1006-오타수정 및 변경 2025-04-22 13:21:30 +09:00
3d8e8dfed1 Merge pull request 'feature/ysCha' (#24) from feature/ysCha into dev
Reviewed-on: #24
2025-04-21 13:15:43 +09:00
ac4449f3f3 993 - HANASYS設計 => HANASYS DESIGN 변경및 메인 로고 디자인 변경 2025-04-21 13:13:26 +09:00
159bbf2e6e 993 - HANASYS設計 => HANASYS DESIGN 변경및 메인 로고 디자인 변경 2025-04-21 13:12:43 +09:00
김민식
98d5af6d50 Merge remote-tracking branch 'origin/qcast-pub' into dev 2025-04-21 11:33:47 +09:00
김민식
11a7061e0a roof 변경했을때 setupCover 값 안 넣는 버그 수정 2025-04-21 11:33:08 +09:00
61d8c083b2 Merge pull request '975 - 케이블 선택 버그 수정' (#22) from feature/ysCha into dev
Reviewed-on: #22
2025-04-18 16:41:58 +09:00
479f5d342a 975 - 케이블 선택 버그 수정 2025-04-18 16:40:52 +09:00
da162ac259 모듈,회로구성 => 모듈/가대 설정 메뉴에서 roof 선택 안되도록 수정 2025-04-18 16:03:52 +09:00
김민식
c0b38a00b3 Merge remote-tracking branch 'origin/qcast-pub' into dev 2025-04-18 14:52:55 +09:00
김민식
bee4ee3e74 다국어 개행문자 추가 2025-04-18 14:52:29 +09:00
김민식
0fb3a0bb2a Merge remote-tracking branch 'origin/qcast-pub' into dev 2025-04-18 14:41:33 +09:00
김민식
b12b7aaa3a input type text-> number 수정 2025-04-18 14:41:11 +09:00
김민식
cb3e5dfc47 Merge remote-tracking branch 'origin/qcast-pub' into dev 2025-04-18 14:39:30 +09:00
김민식
77b473c0a1 input type text-> number 수정 2025-04-18 14:39:07 +09:00
김민식
253ddc258a Merge remote-tracking branch 'origin/qcast-pub' into dev 2025-04-18 14:25:14 +09:00
김민식
cb960baa0c Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev 2025-04-18 14:25:08 +09:00
김민식
51a2782d3c plan 추가 로직 삼항연산자 -> if 로 변경 2025-04-18 14:24:46 +09:00
d1ea6229a2 치수표시 selector로 변경 2025-04-18 11:44:58 +09:00
김민식
e717c28758 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev
# Conflicts:
#	README.md
2025-04-18 10:23:33 +09:00
a08c609caa Merge branch 'dev' into feature/yj-layoutSetup 2025-04-18 09:24:53 +09:00
76938af446 지붕면 할당 시에도 실치수 적용 추가 2025-04-17 16:40:40 +09:00
a3b62db4fa 지붕덮개 메뉴 - 복도치수 적용
그 외 메뉴 - 실치수 적용
2025-04-17 16:35:06 +09:00
김민식
21dcdfb3ef 다국어 수정 2025-04-17 14:22:58 +09:00
김민식
29ccf72c5e 수동으로 방위설정 안되던 버그 수정 2025-04-17 14:13:05 +09:00
김민식
4c219ddce9 validation 방식 수정 2025-04-17 14:12:50 +09:00
김민식
ed721f36d0 Merge branch 'feature/yj-layoutSetup' of https://git.hanasys.jp/qcast3/qcast-front into qcast-pub 2025-04-17 14:12:16 +09:00
김민식
30414568e0 다국어 추가 2025-04-17 14:11:55 +09:00
06812b77af Merge branch 'dev' into feature/yj-layoutSetup 2025-04-16 17:17:50 +09:00
bcc91e7609 반대 2025-04-16 15:53:54 +09:00
bdcacd5542 변별로 설정 후 한쪽흐름과 같이 변 속성을 선택할 경우 처리 추가 2025-04-16 15:39:48 +09:00
51e1f70c24 Merge pull request '견적서 저장시 체크' (#21) from feature/qcast-886 into dev
Reviewed-on: #21
2025-04-15 18:08:55 +09:00
3c14f5de52 견적서 저장시 체크 2025-04-15 18:08:05 +09:00
8f4e53c759 #982 표시 문자 변경 2025-04-14 18:14:49 +09:00
a1871e105f Merge pull request '975- 아이템(양단케이블) 타입 코드 추가에 따른 아이템 목록 수정' (#20) from feature/ysCha into dev
Reviewed-on: #20
2025-04-14 15:03:34 +09:00
ed69943dc6 975- 아이템(양단케이블) 타입 코드 추가에 따른 아이템 목록 수정 2025-04-14 15:01:18 +09:00
김민식
2897dc26e3 Orientation에서 nextstep시 compas 매개변수 추가 2025-04-11 16:41:28 +09:00
61f1036a24 변별로 설정 - 한쪽흐름 출폭 적용 추가 2025-04-11 10:58:04 +09:00
김민식
08a29aec26 cvrCheck, snowCheck 수정 2025-04-10 14:23:43 +09:00
김민식
a742fc1f9c Merge remote-tracking branch 'origin/qcast-pub' into feature/yj-layoutSetup
# Conflicts:
#	src/hooks/module/useModuleTrestle.js
2025-04-10 13:55:13 +09:00
김민식
cf900c3e92 lenBase -> length 로 수정 2025-04-10 13:54:05 +09:00
김민식
e2b8bc19b1 lenBase 버그 수정 2025-04-10 13:33:42 +09:00
김민식
14aa4227f4 Merge branch 'feature/yj-layoutSetup' of https://git.hanasys.jp/qcast3/qcast-front into qcast-pub 2025-04-10 13:24:41 +09:00
김민식
af63891df2 지붕재 수정시 모듈.가대 정보 초기화 2025-04-10 13:24:29 +09:00
070e9d933f api 오류 시 처리 추가 2025-04-10 11:36:44 +09:00
e67fc749f4 마우스 휠 줌 이벤트 보정 2025-04-10 10:55:00 +09:00
김민식
adf9febc8c Merge branch 'feature/yj-layoutSetup' of https://git.hanasys.jp/qcast3/qcast-front into feature/yj-layoutSetup 2025-04-10 10:54:29 +09:00
김민식
089ffed7af 키 명 수정 2025-04-10 10:54:21 +09:00
김민식
990de88caf 키 값 수정 2025-04-10 10:51:59 +09:00
d53d7f4220 Merge branch 'dev' into feature/yj-layoutSetup 2025-04-10 10:48:13 +09:00
김민식
134becaa93 중복코드 제거 2025-04-10 10:47:00 +09:00
97f0749f5c Merge pull request '견적서 오픈플래그 텍스트' (#19) from feature/qcast-886 into dev
Reviewed-on: #19
2025-04-10 10:46:57 +09:00
김민식
74b3c6dac7 처마력 커버 설치여부 , 적설방지금구설치 여부 값 추가 2025-04-10 10:46:01 +09:00
82210a99c9 견적서 오픈플래그 텍스트 2025-04-10 10:45:36 +09:00
76693640cf Merge branch 'dev' into feature/yj-layoutSetup 2025-04-09 17:39:13 +09:00
546133c27e 오류 문구 수정 2025-04-09 17:29:37 +09:00
8662a37177 # 974 - 지붕형상 설정 전 팝업 제거 2025-04-09 17:22:55 +09:00
defebb21d7 chore: 내부 개발서버용 실행 스크립트 추가 2025-04-09 17:20:05 +09:00
11cf3b8403 chore: 내부 개발 서버 기동을 위한 실행 파일 추가
- 추후에 제거 예정
2025-04-09 17:18:58 +09:00
c869e9d5a3 육지붕 수정 2025-04-09 16:56:43 +09:00
김민식
e71c655d05 Merge branch 'feature/yj-layoutSetup' of https://git.hanasys.jp/qcast3/qcast-front into feature/yj-layoutSetup 2025-04-09 14:36:18 +09:00
김민식
29e8ca25a5 BasicSetting 하단 버튼 리펙터링 2025-04-09 14:35:59 +09:00
0673567900 Merge branch 'feature/yj-layoutSetup' of https://git.hanasys.jp/qcast3/qcast-front into feature/yj-layoutSetup 2025-04-09 14:13:58 +09:00
c42e244f24 좌표가 음수일때 보정 로직 추가 2025-04-09 14:13:55 +09:00
김민식
e940884312 육지붕일때 moduleSelectionData 에 margin 추가 2025-04-09 13:34:34 +09:00
115544edb4 번역추가 2025-04-09 11:04:10 +09:00
김민식
9e9bd8f9c4 withDraggable spinner 추가 2025-04-09 10:39:57 +09:00
2da7ea0c78 Merge branch 'dev' into feature/yj-layoutSetup 2025-04-08 17:43:46 +09:00
00a201704b trestleDetail 작업 2025-04-08 17:43:19 +09:00
62517870b7 Merge pull request 'QCast Front #886 견적서 OPEN 제품 단가필드에 OPEN 텍스트 추가' (#18) from feature/qcast-886 into dev
Reviewed-on: #18
2025-04-08 16:33:41 +09:00
7ff52a7b19 QCast Front #886 견적서 OPEN 제품 단가필드에 OPEN 텍스트 추가 2025-04-08 16:33:06 +09:00
김민식
597aaa6d42 orientation module validation 추가 2025-04-08 16:14:57 +09:00
김민식
468980abbc roofConstruction 첫번째 trestleDetail null로 들어가는 버그 수정 2025-04-08 15:54:31 +09:00
김민식
91ccfcb20e DB에서 불러온 값 roof에 적용 2025-04-08 15:16:44 +09:00
김민식
c35f3c1adb moduleSelectionData trestleDetail 없는 버그 수정 2025-04-08 14:19:03 +09:00
김민식
468c7fda6f moduleSelectionData 수정 2025-04-08 13:03:23 +09:00
김민식
9976ff59d6 Merge remote-tracking branch 'origin/qcast-pub' into feature/yj-layoutSetup 2025-04-07 18:22:01 +09:00
김민식
f548506179 moduleSelectionData 수정 2025-04-07 18:21:31 +09:00
d6d626f2d7 양단 케이블 파라미터 추가 2025-04-07 14:35:23 +09:00
c0cbcd18a2 Merge branch 'dev' into feature/yj-layoutSetup 2025-04-04 16:58:07 +09:00
2e86c793c3 Merge pull request '955-견적서 2차 SAP 판매점 노출' (#17) from feature/ysCha into dev
Reviewed-on: #17
2025-04-04 16:39:05 +09:00
d38acba7c9 955-견적서 2차 SAP 판매점 노출 2025-04-04 16:34:09 +09:00
김민식
f3a0504d0b Merge branch 'feature/yj-layoutSetup' of https://git.hanasys.jp/qcast3/qcast-front into qcast-pub 2025-04-04 13:21:11 +09:00
b8f05a6ff0 fix: promise.all 형태로 변경 2025-04-04 13:15:21 +09:00
0a493884c6 Merge branch 'feature/yj-layoutSetup' of https://git.hanasys.jp/qcast3/qcast-front into feature/yj-layoutSetup 2025-04-04 12:48:58 +09:00
김민식
a316664b20 addRoof, index 데이터 추가 2025-04-04 12:47:50 +09:00
804911af3e 모듈 혼합 수 동적 대응 2025-04-04 12:38:49 +09:00
김민식
59f9377cc9 trestleDetail 호출시 trestle 데이터 추가 2025-04-04 12:28:42 +09:00
김민식
15cb2cf270 roofIndex 추가 2025-04-04 11:32:32 +09:00
김민식
1cad8eaf76 Merge remote-tracking branch 'origin/qcast-pub' into feature/yj-layoutSetup 2025-04-04 10:58:38 +09:00
김민식
9400ee7707 trestle 데이터 버그 수정 2025-04-04 10:58:02 +09:00
22f8cd3fa7 모듈 선택 초기화 수정 2025-04-04 10:18:52 +09:00
e872df680d 모듈 설치면 초기화 기능 2025-04-03 17:09:17 +09:00
김민식
02a0e4a67f Merge remote-tracking branch 'origin/qcast-pub' into feature/yj-layoutSetup 2025-04-03 16:32:13 +09:00
김민식
d524b33f56 모듈 관련 데이터 수정시 roofs 데이터 초기화 버그 수정 2025-04-03 16:31:12 +09:00
e7b50ca642 Merge branch 'feature/yj-layoutSetup' of https://git.hanasys.jp/qcast3/qcast-front into feature/module-grouping
# Conflicts:
#	src/locales/ja.json
2025-04-03 16:30:36 +09:00
3f40725647 3중 멀티모듈 대응 2025-04-03 16:28:58 +09:00
aa2685e558 레이아웃설치 수정 2025-04-03 14:33:54 +09:00
f32e772141 모듈 표 추가 2025-04-03 13:36:46 +09:00
7d76929689 고도화 작업 레이아웃 모듈 설치 기능 2025-04-03 11:23:37 +09:00
김민식
6c6e5845ef 적설량, 풍속 등 수정했을때 설정한 roof 초기화 2025-04-03 10:22:48 +09:00
김민식
001143954e QSelectbox value 없을때 title 부분 수정 2025-04-03 10:22:14 +09:00
265593f6dd 모듈 그룹화 작업중 2025-04-02 17:36:25 +09:00
김민식
ce7c90eb6f Merge branch 'feature/yj-layoutSetup' of https://git.hanasys.jp/qcast3/qcast-front into feature/yj-layoutSetup
# Conflicts:
#	src/components/floor-plan/modal/basic/BasicSetting.jsx
#	src/components/floor-plan/modal/basic/step/Trestle.jsx
2025-04-02 17:04:29 +09:00
2e60e848c8 basicsetting 수정 2025-04-02 17:01:37 +09:00
김민식
51aaf342fa Merge remote-tracking branch 'origin/qcast-pub' into feature/yj-layoutSetup
# Conflicts:
#	src/components/floor-plan/modal/basic/BasicSetting.jsx
2025-04-02 16:57:48 +09:00
김민식
82632b962e Trestle 데이터 추가 2025-04-02 16:55:23 +09:00
김민식
bfc7b3fe32 Revert "Trestle 데이터 추가 수정"
This reverts commit 648c2e208a89c0080e49d9c570ac2b3700374b2a.
2025-04-02 16:54:21 +09:00
김민식
648c2e208a Trestle 데이터 추가 수정 2025-04-02 16:54:04 +09:00
d386c42c08 Merge branch 'qcast-pub' into dev 2025-04-02 16:39:19 +09:00
ee6f62b4ba chore: nextjs hotfix 로 버전 수정 2025-04-02 16:32:19 +09:00
de8ba00e55 chore: Update startscript to run development server on port 5000 2025-04-02 16:17:52 +09:00
김민식
ebbf010e6e Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev
# Conflicts:
#	README.md
2025-04-02 16:08:42 +09:00
b7cbc3ec47 test: deploy test 2025-04-02 16:00:55 +09:00
8c33611440 Merge branch 'dev' into feature/yj-layoutSetup
# Conflicts:
#	src/components/floor-plan/modal/basic/BasicSetting.jsx
2025-04-02 15:21:15 +09:00
김민식
d8e43b0d81 BasicSetting 버그 수정 2025-04-02 15:19:00 +09:00
cb87d6cd23 Merge branch 'dev' into feature/yj-layoutSetup 2025-04-02 14:51:54 +09:00
김민식
8ba79d6a06 Merge remote-tracking branch 'origin/qcast-pub' into dev 2025-04-02 14:50:45 +09:00
김민식
ba06fde9cc useModuleTrestle 추가 2025-04-02 14:50:20 +09:00
1588b8cda7 Merge branch 'dev' into feature/yj-layoutSetup 2025-04-02 14:48:26 +09:00
김민식
9c1df779e4 Merge remote-tracking branch 'origin/qcast-pub' into dev 2025-04-02 14:47:53 +09:00
김민식
2dbbd3a957 roofAtom 추가 2025-04-02 14:47:21 +09:00
김민식
d165267ec1 🚨chore: Sync Sass 2025-04-02 14:47:09 +09:00
da8e0856cb Merge branch 'dev' into feature/yj-layoutSetup
# Conflicts:
#	src/components/floor-plan/modal/basic/BasicSetting.jsx
#	src/locales/ja.json
2025-04-02 14:46:08 +09:00
김민식
78ce43969a Merge remote-tracking branch 'origin/qcast-pub' into dev 2025-04-02 14:23:23 +09:00
김민식
fe10ecf476 PlacementShapeSetting merge 2025-04-02 14:22:15 +09:00
김민식
b16174ec0d Merge remote-tracking branch 'origin/qcast-pub' into dev
# Conflicts:
#	src/components/floor-plan/modal/placementShape/PlacementShapeSetting.jsx
#	src/locales/ja.json
2025-04-02 14:08:21 +09:00
김민식
964d9bdcc7 basicsetting 로직 수정 2025-04-02 13:56:03 +09:00
김민식
4b6f0b1b06 plan tab 좌측 objectNo 영역 추가 2025-04-02 13:54:44 +09:00
김민식
9df16cad02 모듈 열/단 버퍼 추가 2025-04-02 13:54:06 +09:00
김민식
8354508a2b 다국어 추가 2025-04-02 13:53:34 +09:00
김민식
ced0eb9fc6 🚨chore: Sync Sass 2025-04-02 13:53:24 +09:00
655fef744c 모듈 선택 표시 2025-04-02 11:12:23 +09:00
0a5b6ce132 test: deploy test 2025-04-01 17:59:57 +09:00
d6b9634a89 레이아웃 설치 validate 추가 2025-04-01 17:18:38 +09:00
66f1293b95 Merge pull request '955-견적서 상품검색할때 이미지명(형명)도 같이 표시' (#15) from feature/ysCha into dev
Reviewed-on: #15
2025-04-01 17:14:43 +09:00
167740f33d 955-견적서 상품검색할때 이미지명(형명)도 같이 표시 2025-04-01 17:12:02 +09:00
87ef010ae6 레이아웃 문구 추가 2025-04-01 15:40:20 +09:00
7d37deb048 모듈 설치 페이지 변경 2025-04-01 15:31:12 +09:00
97ca91aa29 Merge branch 'dev' into feature/yj-layoutSetup
# Conflicts:
#	src/hooks/module/useModuleBasicSetting.js
2025-04-01 13:26:08 +09:00
be54df76f7 통합테스트(Integration Test) #956 이동 기능 수정 2025-04-01 13:19:12 +09:00
c2fbc83485 Merge branch 'dev' into feature/dev-yj 2025-04-01 13:07:31 +09:00
09e985e9b5 Merge branch 'dev' into feature/yj-layoutSetup
# Conflicts:
#	src/locales/ja.json
#	src/locales/ko.json
2025-03-31 14:57:32 +09:00
d10642d6d5 메뉴에서 안쓰는 내용 제거 2025-03-31 13:50:06 +09:00
826739fb0e 혼합 가능 여부 확인 validate 추가 2025-03-31 10:44:56 +09:00
482b6b5477 수동 설치 혼합일 경우 설치 여부 확인 2025-03-31 10:20:45 +09:00
3509e5fbb6 Update startscript to run development server on port 5000 2025-03-28 17:13:46 +09:00
a5fb44ad36 Merge branch 'dev' into feature/dev-yj 2025-03-28 15:25:20 +09:00
c88a07a227 모듈 남쪽설치 치조시 좌측으로 붙는 오류 수정 2025-03-28 15:25:03 +09:00
5982a2aa10 Merge pull request '그리드 테이블 경계표시 추가' (#14) from feature/ysCha into dev
Reviewed-on: #14
2025-03-28 15:15:40 +09:00
3fd81e771d 그리드 테이블 경계표시 추가 2025-03-28 15:13:05 +09:00
c9a53b7159 Merge pull request '#944 물건번호 생성시 시작번호 R->S 변경에따른 주석정리' (#13) from feature/qcast-944 into dev
Reviewed-on: #13
2025-03-28 13:29:13 +09:00
24ba8bfb14 #944 물건번호 생성시 시작번호 R->S 변경에따른 주석정리 2025-03-28 13:27:23 +09:00
b82901271d Merge pull request 'HANASYS 개선건 #950' (#12) from feature/qcast-950 into dev
Reviewed-on: #12
2025-03-28 12:38:58 +09:00
25957a37e1 HANASYS 개선건 #950 2025-03-28 12:37:54 +09:00
83f0eef9eb Merge pull request 'HANASYS 개선건 #947' (#11) from feature/qcast-947 into dev
Reviewed-on: #11
2025-03-28 12:29:13 +09:00
46f90aff1f HANASYS 개선건 #947 2025-03-28 12:27:59 +09:00
11f69a01bb 번역 수정 2025-03-28 11:15:12 +09:00
c8f70e0746 번역 수정 2025-03-28 10:58:34 +09:00
2bb1b71a0b 레이아웃 설치 작업 2025-03-28 09:40:19 +09:00
de5901492b 기존 저장된 지붕재 index 수정 2025-03-27 15:17:42 +09:00
6f5b70342d - pcs 모델 추가 버그 수정 2025-03-27 14:41:29 +09:00
b88299b78d Merge branch 'dev' into feature/yj-layoutSetup 2025-03-27 14:11:40 +09:00
63d8ae092c 레이아웃 모듈 설치 validate 추가 2025-03-27 14:11:23 +09:00
7b1c9b681e Merge pull request '견적서::주문분류에서 이미등록된 YJSS 노출' (#10) from feature/ysCha into dev
Reviewed-on: #10
2025-03-27 13:41:25 +09:00
25e8dcc050 견적서::주문분류에서 이미등록된 YJSS 노출 2025-03-27 13:38:15 +09:00
3027f47d5d chore: nextjs 버전 fix & pm2 실행 스크립트 수정 2025-03-27 13:33:29 +09:00
1f723c9ce7 fix: 임포트 구문 추가 2025-03-27 11:05:30 +09:00
5e9c22a928 Merge pull request '📌 feat: Implement usePlan hook for managing floor plan state and interactions, including canvas data handling, plan creation, deletion, and context management.' (#9) from feature/plan-add-dont-dbclick into dev
Reviewed-on: #9
2025-03-27 10:37:14 +09:00
54d06f7d51 가로, 세로 선 없을 경우 return 2025-03-27 10:34:40 +09:00
7f402d5b45 수동 설정 시 text 밖으로 수정 2025-03-27 10:34:15 +09:00
2473cfac17 📌 feat: Implement usePlan hook for managing floor plan state and interactions, including canvas data handling, plan creation, deletion, and context management. 2025-03-27 10:33:34 +09:00
e293d5dfde Merge pull request 'feature/ysCha' (#8) from feature/ysCha into dev
Reviewed-on: #8
2025-03-27 09:58:44 +09:00
cf9acde872 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/ysCha
# Conflicts:
#	src/locales/ja.json
2025-03-26 17:00:11 +09:00
93b645e9e8 지붕면 할당 전 제거 시 오류 수정 2025-03-26 15:08:37 +09:00
25778a099f 팝업 닫고 시작 2025-03-26 11:10:42 +09:00
ba9a49501c 지붕면 할당 => 마지막으로 추가된 지붕재 자동 선택 2025-03-26 10:59:29 +09:00
a2d192084b usePlan 내부 currentObject 초기화 제거 2025-03-26 10:09:40 +09:00
7fcaaece53 Merge branch 'dev' into feature/yj-layoutSetup 2025-03-25 16:08:16 +09:00
cdaeab1d42 Merge branch 'dev' into feature/dev-yj 2025-03-25 16:07:54 +09:00
47e3ae7d29 배치면 타입 상하 반전 추가 2025-03-25 16:07:15 +09:00
yjnoh
5597f8ad70 지붕라인 색칠하기 2025-03-25 15:48:18 +09:00
65ec3d5153 지붕면 할당 시 오류 수정 2025-03-25 15:12:01 +09:00
yjnoh
a7e9aba26c Merge branch 'dev' into feature/yj-layoutSetup
# Conflicts:
#	src/locales/ja.json
#	src/locales/ko.json
2025-03-25 15:09:40 +09:00
yjnoh
592275c0de 배치면 도형 재배치 및 처마,용마루,케라바 타입 변경 로직 변경 2025-03-25 14:49:07 +09:00
5e27ab282c Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev 2025-03-25 11:11:15 +09:00
75549b66b5 지붕재 변경 시 canvas 저장 추가 2025-03-25 11:10:56 +09:00
yjnoh
437d552b3a 배치면 방향 수정 2025-03-25 11:05:54 +09:00
yjnoh
c631c6344e 레이아웃 설치 작업중 2025-03-24 17:07:19 +09:00
85d9aca6d3 병렬수 0 이상인 경우에만 추가 2025-03-24 14:50:06 +09:00
yjnoh
0ec917b09f 레이어 팝업 수정 2025-03-24 14:07:14 +09:00
cha
82e3527432 견적서::주문분류에서 storePriceList의 pkgRank의 값이 없으면 YJSS 선택불가, 기등록은 변경 가능 2025-03-24 13:27:30 +09:00
yjnoh
7ec9854173 Merge branch 'dev' into feature/dev-yj 2025-03-24 11:26:42 +09:00
yjnoh
b458b4e853 캔버스 확시대 오브젝트 클릭 수정 2025-03-24 11:26:26 +09:00
1ba9853a32 #935 전반/글자표기 2025-03-24 11:06:35 +09:00
f1d976521d # 932 - pcs 한국어 기재 수정 2025-03-24 10:55:53 +09:00
yjnoh
dc8f033e9f validation 수정 2025-03-24 10:49:56 +09:00
yjnoh
943fd16e4b 클릭시 배치면 데이터 validation 추가 2025-03-24 10:23:35 +09:00
yjnoh
70c706341e Merge branch 'dev' into feature/yj-layoutSetup 2025-03-24 09:35:53 +09:00
yjnoh
c704207d2f 레이아웃설치 validation 2025-03-24 09:35:21 +09:00
a7ddfacdd2 Merge pull request '견적서 복사 미사용 파라미터 정리' (#7) from feature/qcast-925 into dev
Reviewed-on: #7
2025-03-24 09:28:27 +09:00
b51dacf421 견적서 복사 미사용 파라미터 정리 2025-03-24 09:27:28 +09:00
f25dac0ae3 Merge pull request '견적특이사항 상품 pkgYn 대응' (#6) from feature/qcast-925 into dev
Reviewed-on: #6
2025-03-21 17:48:24 +09:00
c99deaf93f 견적특이사항 상품 pkgYn 대응 2025-03-21 17:47:41 +09:00
8d65765daf Merge pull request '견적특이사항 공통코드 PROD 사용유무 변경에 따른 대응..' (#5) from feature/qcast-925 into dev
Reviewed-on: #5
2025-03-21 16:04:37 +09:00
2f8ca712c9 견적특이사항 공통코드 PROD 사용유무 변경에 따른 대응.. 2025-03-21 16:03:48 +09:00
yjnoh
9285caf422 Merge branch 'dev' into feature/yj-layoutSetup
# Conflicts:
#	src/locales/ja.json
2025-03-21 15:55:58 +09:00
f2470b346c feat: 핫키 이벤트 등록시 구문 수정 2025-03-21 14:23:46 +09:00
1a5f78a970 Merge pull request '견적서 복사 실패 메세지' (#4) from feature/qcast-925 into dev
Reviewed-on: #4
2025-03-21 13:27:21 +09:00
5979555bcb 견적서 복사 실패 메세지 2025-03-21 13:26:14 +09:00
26047df3c8 feat: 핫키 이벤트 등록 샘플 추가 2025-03-21 11:28:16 +09:00
yjnoh
12936ec1f9 Merge branch 'dev' into feature/dev-yj 2025-03-21 11:24:08 +09:00
yjnoh
1bb92a975e 마우스 우클릭 이벤트 조정 2025-03-21 11:23:58 +09:00
f2a083f022 지붕재 할당 수정 추가 2025-03-21 10:23:46 +09:00
786c35e656 intersection 검색 범위 수정 2025-03-21 09:57:51 +09:00
yjnoh
88bcf27bfb 수정 2025-03-20 18:09:40 +09:00
yjnoh
617afb8b1f Merge branch 'dev' into feature/dev-yj-surface 2025-03-20 18:00:30 +09:00
yjnoh
414d6fa0c5 처마 용마루 방향에 따른 타입 작업 2025-03-20 17:59:58 +09:00
6919dac8f1 getCurrentPoints로 수정 2025-03-20 16:48:35 +09:00
yjnoh
79d873c135 진짜 처마면인지 확인하는 로직 추가 2025-03-20 16:00:02 +09:00
yjnoh
0e8ce8b2e2 Merge branch 'dev' into feature/dev-yj-surface 2025-03-20 15:30:47 +09:00
yjnoh
c4d17d2147 배치면 라인 타입 팝업 삭제 및 로직 수정 2025-03-20 15:30:03 +09:00
e75db5ace1 Merge remote-tracking branch 'origin/dev' into dev 2025-03-20 14:18:26 +09:00
8848713c72 Merge pull request '견적서 일본어 변경 누락분 재반영' (#3) from feature/qcast-925 into dev
Reviewed-on: #3
2025-03-20 14:17:30 +09:00
39d48e61f3 test: push test 2025-03-20 13:48:07 +09:00
e624aa0de0 메시지 내용 수정 2025-03-20 13:42:07 +09:00
e7410e5373 견적서 일본어 변경 누락분 재반영 2025-03-20 13:35:06 +09:00
05dd069e53 modified 시 lines 위치 새로 수정 2025-03-20 10:02:09 +09:00
yjnoh
ad14bf091f Merge branch 'dev' into dev-yj-layoutSetup 2025-03-19 17:49:28 +09:00
yjnoh
c34a7fc54d 수동시 치조 여부에 따라 모듈 가운데 흡착 2025-03-19 17:49:17 +09:00
ac015123cd Merge pull request '#925 견적서 상세 가격표시 옵션 & 프라이싱 호출 파라미터 변경' (#2) from feature/qcast-925 into dev
Reviewed-on: #2
2025-03-19 17:40:55 +09:00
f7fe0f6528 #925 견적서 상세 가격표시 옵션 & 프라이싱 호출 파라미터 변경 2025-03-19 17:40:13 +09:00
6b76108d8b Merge pull request '자동로그인 id 입력 수정 #913' (#1) from feature/qcast-913 into dev
Reviewed-on: #1
2025-03-19 16:23:45 +09:00
BOOK-BKT8UBVE0A\dhfkd
590040fa1d 자동로그인 id 입력 수정 #913 2025-03-19 16:23:19 +09:00
yjnoh
9bb72bfa3a 그림자 제외 처리 2025-03-19 13:38:35 +09:00
yjnoh
8ea6f43ddb Merge branch 'dev' into dev-yj-layoutSetup
# Conflicts:
#	src/hooks/module/useModuleBasicSetting.js
#	src/locales/ja.json
#	src/locales/ko.json
2025-03-19 13:38:16 +09:00
yjnoh
325c2c1cc0 번역수정 2025-03-19 13:28:20 +09:00
yjnoh
521bfd4303 반각만 입력가능 문구 추가 2025-03-19 11:10:12 +09:00
cha
b136bc213c Merge branch 'dev' of https://git.jetbrains.space/nalpari/q-cast-iii/qcast-front into feature/ysCha 2025-03-19 10:00:40 +09:00
84e8af50b8 QSelectbox key 중복 제거 2025-03-18 15:55:11 +09:00
yjnoh
8a5bd9f505 단수배치 작업 2025-03-18 15:44:41 +09:00
11438773a1 견적서 상세 헤더메뉴 문서다운로드 버튼 명 수정 2025-03-18 13:28:58 +09:00
2e762537fc #918 견적서 상세화면 견적일 -> 견적작성일 메세지 변경 2025-03-18 13:24:58 +09:00
57446fa6d8 주택 원복 2025-03-18 12:54:36 +09:00
1fa02de62f 지붕면 할당 순서 수정 2025-03-18 11:33:45 +09:00
06fa1766d6 파라미터 없는 경우 같은 포인트 검사 못함 2025-03-18 10:56:37 +09:00
63297328ed
Merge Q-CAST-III-MR-783: YJSS 금액 2025-03-18 01:30:08 +00:00
7c15da2b4c YJSS 금액 2025-03-18 10:29:26 +09:00
3432d64a3c 8각 안나눠지는 현상 작업 추가 2025-03-18 10:23:42 +09:00
9be21fc2b2 번역 수정 #914 2025-03-18 09:52:14 +09:00
yjnoh
c467fc7fa8 흡착점 작업 2025-03-18 09:36:11 +09:00
yjnoh
63db694efa 단수지정배치 작업중 2025-03-17 18:36:39 +09:00
yoosangwook
f915dab239 chore: ecosystem 추가 2025-03-17 18:25:28 +09:00
김민식
16423de079 배치면 초기설정 다국어 적용 2025-03-17 15:44:12 +09:00
김민식
e99a1a3854 배치면 초기설정 화면 다국어 추가 2025-03-17 15:43:57 +09:00
김민식
8ca01757c9 🚨chore: Sync Sass 2025-03-17 15:43:37 +09:00
yjnoh
15ff2989f4 Merge branch 'dev' into dev-yj-layoutSetup 2025-03-17 15:03:27 +09:00
yjnoh
83b27582f7 레이아웃 작업 2025-03-17 15:01:25 +09:00
yjnoh
9e66b3ef7d 레이아웃 설치 동서남북 작업 2025-03-17 11:33:44 +09:00
yjnoh
ae2171c633 레이아웃 설치 2025-03-14 18:35:13 +09:00
yjnoh
f9766a7d21 Merge branch 'dev' into dev-yj-layoutSetup
# Conflicts:
#	src/components/floor-plan/modal/basic/BasicSetting.jsx
#	src/hooks/module/useModuleBasicSetting.js
2025-03-14 18:22:24 +09:00
yjnoh
83038a7c81 Merge branch 'dev' into dev-yj-layoutSetup 2025-03-12 17:27:25 +09:00
yjnoh
3df466a717 레이아웃 설치 데모 작업 2025-03-12 16:44:41 +09:00
89 changed files with 6158 additions and 1653 deletions

3
.gitmessage.txt Normal file
View File

@ -0,0 +1,3 @@
[일감번호] : [제목]
[작업내용] :

View File

@ -34,3 +34,5 @@ You can check out [the Next.js GitHub repository](https://github.com/vercel/next
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
deploy test

13
dev.ecosystem.config.js Normal file
View File

@ -0,0 +1,13 @@
module.exports = {
apps: [
{
name: 'qcast-front-development',
script: 'node_modules/next/dist/bin/next',
instances: 1,
exec_mode: 'fork',
env: {
NODE_ENV: 'development',
},
},
],
}

13
ecosystem.config.js Normal file
View File

@ -0,0 +1,13 @@
module.exports = {
apps: [
{
name: 'qcast-front-production',
script: 'node_modules/next/dist/bin/next',
instances: 2,
exec_mode: 'cluster',
env: {
NODE_ENV: 'production',
},
},
],
}

View File

@ -5,7 +5,8 @@
"scripts": { "scripts": {
"dev": "next dev", "dev": "next dev",
"build": "next build", "build": "next build",
"start": "next start -p 5000", "start:cluster1": "next start -p 5000",
"start:cluster2": "next start -p 5001",
"start:dev": "next start -p 5010", "start:dev": "next start -p 5010",
"lint": "next lint", "lint": "next lint",
"serve": "node server.js" "serve": "node server.js"
@ -23,7 +24,7 @@
"js-cookie": "^3.0.5", "js-cookie": "^3.0.5",
"mathjs": "^13.0.2", "mathjs": "^13.0.2",
"mssql": "^11.0.1", "mssql": "^11.0.1",
"next": "14.2.21", "next": "14.2.26",
"next-international": "^1.2.4", "next-international": "^1.2.4",
"react": "^18", "react": "^18",
"react-chartjs-2": "^5.2.0", "react-chartjs-2": "^5.2.0",

View File

@ -0,0 +1,3 @@
<svg width="16" height="14" viewBox="0 0 16 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8 0L15.7942 13.5H0.205771L8 0Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 160 B

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="12" r="8.25" stroke="#101010" stroke-width="1.5"/>
<path d="M7.94995 16.5C10.0485 14.302 13.9289 14.1986 16.05 16.5M14.2455 9.75C14.2455 10.9926 13.2367 12 11.9923 12C10.7479 12 9.73912 10.9926 9.73912 9.75C9.73912 8.50736 10.7479 7.5 11.9923 7.5C13.2367 7.5 14.2455 8.50736 14.2455 9.75Z" stroke="#101010" stroke-width="1.5" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 474 B

View File

@ -1,21 +1,13 @@
'use client' 'use client'
import { createContext, useEffect, useState } from 'react' import { createContext, useState } from 'react'
import { useLocalStorage } from 'usehooks-ts'
export const GlobalDataContext = createContext(null) export const GlobalDataContext = createContext(null)
const GlobalDataProvider = ({ children }) => { const GlobalDataProvider = ({ children }) => {
const [managementState, setManagementState] = useState(null) const [managementState, setManagementState] = useState(null)
const [managementStateLoaded, setManagementStateLoaded] = useLocalStorage('managementStateLoaded', null)
useEffect(() => { return <GlobalDataContext.Provider value={{ managementState, setManagementState }}>{children}</GlobalDataContext.Provider>
if (managementState !== null) {
setManagementStateLoaded(managementState)
}
}, [managementState])
return <GlobalDataContext.Provider value={{ managementState, setManagementState, managementStateLoaded }}>{children}</GlobalDataContext.Provider>
} }
export default GlobalDataProvider export default GlobalDataProvider

View File

@ -21,8 +21,8 @@ import GlobalLoadingProvider from './GlobalLoadingProvider'
* 서버 컴포넌트에 한해서 개별로 설정할 있음 * 서버 컴포넌트에 한해서 개별로 설정할 있음
*/ */
export const metadata = { export const metadata = {
title: 'HANASYS設計', title: 'HANASYS DESIGN',
description: 'HANASYS設計', description: 'HANASYS DESIGN',
} }
/** /**
@ -58,6 +58,7 @@ export default async function RootLayout({ children }) {
pwdInitYn: session.pwdInitYn, pwdInitYn: session.pwdInitYn,
custCd: session.custCd, custCd: session.custCd,
isLoggedIn: session.isLoggedIn, isLoggedIn: session.isLoggedIn,
builderNo: session.builderNo
} }
} }
if (!headerPathname.includes('/login') && !session.isLoggedIn) { if (!headerPathname.includes('/login') && !session.isLoggedIn) {

View File

@ -125,6 +125,11 @@ export const TRESTLE_MATERIAL = {
BRACKET: 'bracket', BRACKET: 'bracket',
} }
export const MODULE_SETUP_TYPE = {
LAYOUT: 'layout',
AUTO: 'auto',
}
export const SAVE_KEY = [ export const SAVE_KEY = [
'selectable', 'selectable',
'name', 'name',
@ -203,6 +208,7 @@ export const SAVE_KEY = [
'fontWeight', 'fontWeight',
'dormerAttributes', 'dormerAttributes',
'toFixed', 'toFixed',
'isSortedPoints',
] ]
export const OBJECT_PROTOTYPE = [fabric.Line.prototype, fabric.Polygon.prototype, fabric.Triangle.prototype, fabric.Group.prototype] export const OBJECT_PROTOTYPE = [fabric.Line.prototype, fabric.Polygon.prototype, fabric.Triangle.prototype, fabric.Group.prototype]

View File

@ -2,26 +2,111 @@
import { useState } from 'react' import { useState } from 'react'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { setSession, login } from '@/lib/authActions'
import { sessionStore } from '@/store/commonAtom'
import { useRecoilState } from 'recoil'
import { useAxios } from '@/hooks/useAxios'
import { globalLocaleStore } from '@/store/localeAtom'
import { useRouter } from 'next/navigation'
import GlobalSpinner from '@/components/common/spinner/GlobalSpinner' import GlobalSpinner from '@/components/common/spinner/GlobalSpinner'
export default function AutoLoginPage() { export default function AutoLoginPage({ autoLoginParam }) {
const [isLoading, setIsLoading] = useState(true) const router = useRouter()
const [isLoading, setIsLoading] = useState(autoLoginParam === 'Y' ? false : true)
const [globalLocaleState, setGlbalLocaleState] = useRecoilState(globalLocaleStore)
const { promisePost } = useAxios(globalLocaleState)
const { getMessage } = useMessage() const { getMessage } = useMessage()
const [userId, setUserId] = useState('')
const [sessionState, setSessionState] = useRecoilState(sessionStore)
const [idFocus, setIdFocus] = useState(false)
const loginProcess = async () => {
setIsLoading(true)
await promisePost({ url: '/api/login/v1.0/user', data: { loginId: userId } }).then((response) => {
setIsLoading(false)
if (response.data) {
const res = response.data
const result = { ...res, storeLvl: res.groupId === '60000' ? '1' : '2', pwdInitYn: 'Y' }
setSession(result)
setSessionState(result)
login()
} else {
alert(getMessage('login.fail'))
router.push('/login?autoLoginParam1=Y')
}
})
}
return ( return (
<> <>
{isLoading && <GlobalSpinner />} {isLoading && <GlobalSpinner />}
<div className="login-input-frame"> {autoLoginParam !== 'Y' ? (
<div className="login-frame-tit "> <>
<span>{getMessage('site.name')}</span> <div className="login-input-frame">
{getMessage('site.sub_name')} <div className="login-frame-tit ">
</div> <span>{getMessage('site.name')}</span>
<div className="login-input-wrap"> {getMessage('site.sub_name')}
<div className="login-area id" style={{ fontWeight: 'bolder' }}> </div>
{getMessage('login.auto.page.text')} <div className="login-input-wrap">
<div className="login-area id" style={{ fontWeight: 'bolder' }}>
{getMessage('login.auto.page.text')}
</div>
</div>
</div> </div>
</div> </>
</div> ) : (
<>
<div className="login-input-frame">
<form
onSubmit={(e) => {
e.preventDefault()
loginProcess()
}}
className="space-y-6"
>
<div className="login-frame-tit">
<span>{getMessage('site.name')}</span>
{getMessage('site.sub_name')}
</div>
<div className="login-input-wrap">
<div className={`login-area id ${idFocus ? 'focus' : ''}`}>
<input
type="text"
className="login-input"
id="userId"
name="id"
required
value={userId}
placeholder={getMessage('login.id.placeholder')}
onChange={(e) => {
setUserId(e.target.value)
}}
onFocus={() => setIdFocus(true)}
onBlur={() => setIdFocus(false)}
/>
<button
type="button"
className="id-delete"
onClick={(e) => {
setUserId('')
}}
></button>
</div>
<div className="login-btn-box">
<button type="submit" className="login-btn">
{getMessage('login')}
</button>
</div>
</div>
</form>
</div>
</>
)}
</> </>
) )
} }

View File

@ -25,7 +25,9 @@ export default function Login() {
useEffect(() => { useEffect(() => {
if (autoLoginParam) { if (autoLoginParam) {
autoLoginProcess(autoLoginParam) if (autoLoginParam !== 'Y') {
autoLoginProcess(autoLoginParam)
}
} }
// console.log('🚀 ~ checkSession ~ checkSession():', checkSession()) // console.log('🚀 ~ checkSession ~ checkSession():', checkSession())
@ -334,7 +336,7 @@ export default function Login() {
</div> </div>
</> </>
)} )}
{autoLoginParam && <AutoLogin />} {autoLoginParam && <AutoLogin autoLoginParam={autoLoginParam} />}
</div> </div>
<div className="login-copyright">COPYRIGHT©2024 Hanwha Japan All Rights Reserved.</div> <div className="login-copyright">COPYRIGHT©2024 Hanwha Japan All Rights Reserved.</div>
</div> </div>

View File

@ -6,29 +6,19 @@ import { contextMenuListState, contextMenuState } from '@/store/contextMenu'
import { useTempGrid } from '@/hooks/useTempGrid' import { useTempGrid } from '@/hooks/useTempGrid'
import { useContextMenu } from '@/hooks/useContextMenu' import { useContextMenu } from '@/hooks/useContextMenu'
import { useEvent } from '@/hooks/useEvent' import { useEvent } from '@/hooks/useEvent'
import { canvasState } from '@/store/canvasAtom' import { canvasState, currentObjectState } from '@/store/canvasAtom'
export default function QContextMenu(props) { export default function QContextMenu(props) {
const canvas = useRecoilValue(canvasState) const canvas = useRecoilValue(canvasState)
const { contextRef, canvasProps } = props const { contextRef, canvasProps } = props
const [contextMenu, setContextMenu] = useRecoilState(contextMenuState) const [contextMenu, setContextMenu] = useRecoilState(contextMenuState)
const contextMenuList = useRecoilValue(contextMenuListState) const contextMenuList = useRecoilValue(contextMenuListState)
const activeObject = canvasProps?.getActiveObject() // const currentObject = useRecoilValue(currentObjectState)
const { tempGridMode, setTempGridMode } = useTempGrid() const { tempGridMode, setTempGridMode } = useTempGrid()
const { handleKeyup } = useContextMenu() const { handleKeyup } = useContextMenu()
const { addDocumentEventListener, removeDocumentEvent } = useEvent() const { addDocumentEventListener, removeDocumentEvent } = useEvent()
// const { addDocumentEventListener, removeDocumentEvent } = useContext(EventContext) // const { addDocumentEventListener, removeDocumentEvent } = useContext(EventContext)
let contextType = ''
if (activeObject) {
if (activeObject.initOptions && activeObject.initOptions.name) {
//
if (activeObject.initOptions?.name?.indexOf('guide') > -1) {
contextType = 'surface' //
}
}
}
const getYPosition = (e) => { const getYPosition = (e) => {
const contextLength = contextMenuList.reduce((acc, cur, index) => { const contextLength = contextMenuList.reduce((acc, cur, index) => {
return acc + cur.length return acc + cur.length
@ -36,11 +26,13 @@ export default function QContextMenu(props) {
return e?.clientY - (contextLength * 25 + contextMenuList.length * 2 * 17) return e?.clientY - (contextLength * 25 + contextMenuList.length * 2 * 17)
} }
useEffect(() => { const handleContextMenu = (e) => {
if (!contextRef.current) return // e.preventDefault() // contextmenu
if (currentObject) {
const isArray = currentObject.hasOwnProperty('arrayData')
if (isArray && currentObject.arrayData.length === 0) return
const handleContextMenu = (e) => {
e.preventDefault() // contextmenu
if (tempGridMode) return if (tempGridMode) return
const position = { const position = {
x: window.innerWidth / 2 < e.pageX ? e.pageX - 240 : e.pageX, x: window.innerWidth / 2 < e.pageX ? e.pageX - 240 : e.pageX,
@ -48,21 +40,24 @@ export default function QContextMenu(props) {
} }
setContextMenu({ visible: true, ...position, currentMousePos: canvasProps.getPointer(e) }) setContextMenu({ visible: true, ...position, currentMousePos: canvasProps.getPointer(e) })
addDocumentEventListener('keyup', document, handleKeyup) addDocumentEventListener('keyup', document, handleKeyup)
canvasProps?.upperCanvasEl.removeEventListener('contextmenu', handleContextMenu) //
} }
}
const handleClick = (e) => { const handleClick = (e) => {
// e.preventDefault() // e.preventDefault()
setContextMenu({ ...contextMenu, visible: false })
}
const handleOutsideClick = (e) => {
// e.preventDefault()
if (contextMenu.visible) {
setContextMenu({ ...contextMenu, visible: false }) setContextMenu({ ...contextMenu, visible: false })
removeDocumentEvent('keyup')
} }
}
const handleOutsideClick = (e) => { useEffect(() => {
// e.preventDefault() if (!contextRef.current) return
if (contextMenu.visible) {
setContextMenu({ ...contextMenu, visible: false })
removeDocumentEvent('keyup')
}
}
canvasProps?.upperCanvasEl.addEventListener('contextmenu', handleContextMenu) canvasProps?.upperCanvasEl.addEventListener('contextmenu', handleContextMenu)
document.addEventListener('click', handleClick) document.addEventListener('click', handleClick)
@ -72,43 +67,9 @@ export default function QContextMenu(props) {
removeDocumentEvent('keyup') removeDocumentEvent('keyup')
document.removeEventListener('click', handleClick) document.removeEventListener('click', handleClick)
document.removeEventListener('click', handleOutsideClick) document.removeEventListener('click', handleOutsideClick)
canvasProps?.upperCanvasEl.removeEventListener('contextmenu', handleContextMenu) //
} }
}, [contextRef, contextMenuList]) }, [contextRef, contextMenuList, currentObject])
const handleObjectMove = () => {
activeObject.set({
lockMovementX: false, // X
lockMovementY: false, // Y
})
canvasProps?.on('object:modified', function (e) {
activeObject.set({
lockMovementX: true, // X
lockMovementY: true, // Y
})
})
}
const handleObjectDelete = () => {
if (confirm('삭제하실거?')) {
canvasProps.remove(activeObject)
}
}
const handleObjectCopy = () => {
activeObject.clone((cloned) => {
cloned.set({
left: activeObject.left + activeObject.width + 20,
initOptions: { ...activeObject.initOptions },
lockMovementX: true, // X
lockMovementY: true, // Y
lockRotation: true, //
lockScalingX: true, // X
lockScalingY: true, // Y
})
canvasProps?.add(cloned)
})
}
return ( return (
<> <>

View File

@ -26,6 +26,7 @@ export default function QSelectBox({
targetKey = '', targetKey = '',
showKey = '', showKey = '',
params = {}, params = {},
tagTitle = '',
}) { }) {
const { getMessage } = useMessage() const { getMessage } = useMessage()
@ -39,7 +40,7 @@ export default function QSelectBox({
if (showKey !== '' && !value) { if (showKey !== '' && !value) {
//value showKey //value showKey
// return options[0][showKey] // return options[0][showKey]
return title return title !== '' ? title : getMessage('selectbox.title')
} else if (showKey !== '' && value) { } else if (showKey !== '' && value) {
//value sourceKey targetKey //value sourceKey targetKey
@ -82,12 +83,13 @@ export default function QSelectBox({
className={`sort-select ${openSelect ? 'active' : ''} ${disabled ? 'disabled' : ''}`} className={`sort-select ${openSelect ? 'active' : ''} ${disabled ? 'disabled' : ''}`}
ref={ref} ref={ref}
onClick={disabled ? () => {} : () => setOpenSelect(!openSelect)} onClick={disabled ? () => {} : () => setOpenSelect(!openSelect)}
title={tagTitle}
> >
<p>{selected}</p> <p>{selected}</p>
<ul className="select-item-wrap"> <ul className="select-item-wrap">
{options?.length > 0 && {options?.length > 0 &&
options?.map((option, index) => ( options?.map((option, index) => (
<li key={option.id || index} className="select-item" onClick={() => handleClickSelectOption(option)}> <li key={option.id + '_' + index} className="select-item" onClick={() => handleClickSelectOption(option)}>
<button key={option.id + 'btn'}>{showKey !== '' ? option[showKey] : option.name}</button> <button key={option.id + 'btn'}>{showKey !== '' ? option[showKey] : option.name}</button>
</li> </li>
))} ))}

View File

@ -13,7 +13,7 @@ import dayjs from 'dayjs'
import { useCommonCode } from '@/hooks/common/useCommonCode' import { useCommonCode } from '@/hooks/common/useCommonCode'
import { useEstimateController } from '@/hooks/floorPlan/estimate/useEstimateController' import { useEstimateController } from '@/hooks/floorPlan/estimate/useEstimateController'
import { SessionContext } from '@/app/SessionProvider' import { SessionContext } from '@/app/SessionProvider'
import Select from 'react-select' import Select, { components } from 'react-select'
import { convertNumberToPriceDecimal, convertNumberToPriceDecimalToFixed } from '@/util/common-utils' import { convertNumberToPriceDecimal, convertNumberToPriceDecimalToFixed } from '@/util/common-utils'
import ProductFeaturesPop from './popup/ProductFeaturesPop' import ProductFeaturesPop from './popup/ProductFeaturesPop'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
@ -175,7 +175,10 @@ export default function Estimate({}) {
row.check = false row.check = false
estimateOption.map((row2) => { estimateOption.map((row2) => {
if (row.pkgYn === '0') { if (row.pkgYn === '0') {
if (row2 === row.code) { // if (row2 === row.code) {
// row.check = true
// }
if (row.code.split('、').includes(row2)) {
row.check = true row.check = true
} }
} else { } else {
@ -217,7 +220,10 @@ export default function Estimate({}) {
row.check = false row.check = false
estimateOption.map((row2) => { estimateOption.map((row2) => {
if (row.pkgYn === '0') { if (row.pkgYn === '0') {
if (row2 === row.code) { // if (row2 === row.code) {
// row.check = true
// }
if (row.code.split('、').includes(row2)) {
row.check = true row.check = true
} }
} else { } else {
@ -240,7 +246,6 @@ export default function Estimate({}) {
} }
} }
}) })
setSpecialNoteList(res) setSpecialNoteList(res)
setSpecialNoteFirstFlg(true) setSpecialNoteFirstFlg(true)
@ -377,8 +382,8 @@ export default function Estimate({}) {
useEffect(() => { useEffect(() => {
if (estimateContextState.estimateType !== '') { if (estimateContextState.estimateType !== '') {
const param = { const param = {
saleStoreId: session.storeId, saleStoreId: estimateContextState.sapSaleStoreId,
sapSalesStoreCd: session.custCd, sapSalesStoreCd: estimateContextState.sapSalesStoreCd,
docTpCd: estimateContextState?.estimateType, docTpCd: estimateContextState?.estimateType,
} }
@ -387,6 +392,8 @@ export default function Estimate({}) {
if (isNotEmptyArray(res?.data)) { if (isNotEmptyArray(res?.data)) {
setStorePriceList(res.data) setStorePriceList(res.data)
} }
setItemChangeYn(true)
}) })
if (estimateContextState.estimateType === 'YJSS') { if (estimateContextState.estimateType === 'YJSS') {
@ -416,8 +423,6 @@ export default function Estimate({}) {
handlePricing('UNIT_PRICE') handlePricing('UNIT_PRICE')
} }
} }
setItemChangeYn(true)
} }
}, [estimateContextState?.estimateType]) }, [estimateContextState?.estimateType])
@ -469,6 +474,21 @@ export default function Estimate({}) {
} else { } else {
item.check = false item.check = false
} }
} else {
let codes = item.code.split('、')
let flg = '0'
if (codes.length > 1) {
for (let i = 0; i < pushData.length; i++) {
if (codes.indexOf(pushData[i]) > -1) {
flg = '1'
}
}
if (flg === '1') {
item.check = true
} else {
item.check = false
}
}
} }
}) })
@ -478,12 +498,26 @@ export default function Estimate({}) {
}) })
} }
//Pricing confirm
const handlePricingBtn = (showPriceCd) => {
swalFire({
text: getMessage('estimate.detail.showPrice.pricingBtn.confirm'),
type: 'confirm',
icon: 'warning',
confirmFn: () => {
handlePricing(showPriceCd)
},
})
}
//Pricing //Pricing
const handlePricing = async (showPriceCd) => { const handlePricing = async (showPriceCd) => {
const param = { const param = {
saleStoreId: session.storeId, saleStoreId: estimateContextState.sapSaleStoreId,
sapSalesStoreCd: session.custCd, sapSalesStoreCd: estimateContextState.sapSalesStoreCd,
docTpCd: estimateContextState.estimateType, docTpCd: estimateContextState.estimateType,
secSapSalesStoreCd:
estimateContextState.secSapSalesStoreCd?.length > 0 && showPriceCd === 'QSP_PRICE' ? estimateContextState.secSapSalesStoreCd : '',
priceCd: showPriceCd, priceCd: showPriceCd,
itemIdList: estimateContextState.itemList.filter((item) => item.delFlg === '0' && item.paDispOrder === null), itemIdList: estimateContextState.itemList.filter((item) => item.delFlg === '0' && item.paDispOrder === null),
} }
@ -506,7 +540,6 @@ export default function Estimate({}) {
}) })
} }
} }
setIsGlobalLoading(true) setIsGlobalLoading(true)
await promisePost({ url: '/api/estimate/price/item-price-list', data: param }).then((res) => { await promisePost({ url: '/api/estimate/price/item-price-list', data: param }).then((res) => {
let updateList = [] let updateList = []
@ -531,6 +564,7 @@ export default function Estimate({}) {
updateList.push({ updateList.push({
...item, ...item,
openFlg: data.data2[i].unitPrice === '0.0' ? '1' : '0', openFlg: data.data2[i].unitPrice === '0.0' ? '1' : '0',
unitOpenFlg: (showPriceCd === 'QSP_PRICE' && item.openFlg === '1') ? '1' : '0',
salePrice: data.data2[i].unitPrice === null ? '0' : data.data2[i].unitPrice, salePrice: data.data2[i].unitPrice === null ? '0' : data.data2[i].unitPrice,
saleTotPrice: (item.amount * data.data2[i].unitPrice).toString(), saleTotPrice: (item.amount * data.data2[i].unitPrice).toString(),
}) })
@ -696,7 +730,7 @@ export default function Estimate({}) {
/* 케이블 select 변경시 */ /* 케이블 select 변경시 */
const onChangeDisplayCableItem = (value, itemList) => { const onChangeDisplayCableItem = (value, itemList) => {
itemList.map((item, index) => { itemList.map((item, index) => {
if (item.dispCableFlg === '1') { if (item.dispCableFlg === '1' && item.itemTpCd !== 'M12') {
if (value !== '') { if (value !== '') {
onChangeDisplayItem(value, item.dispOrder, index, true) onChangeDisplayItem(value, item.dispOrder, index, true)
} }
@ -1054,7 +1088,7 @@ export default function Estimate({}) {
item.showSaleTotPrice = '0' item.showSaleTotPrice = '0'
} }
if (item.dispCableFlg === '1') { if (item.dispCableFlg === '1' && item.itemTpCd !== 'M12') {
dispCableFlgCnt++ dispCableFlgCnt++
setCableItem(item.itemId) setCableItem(item.itemId)
} }
@ -1125,7 +1159,7 @@ export default function Estimate({}) {
dispCableFlgCnt++ dispCableFlgCnt++
} }
if (item.dispCableFlg === '1') { if (item.dispCableFlg === '1' && item.itemTpCd !== 'M12') {
setCableItem(item.itemId) setCableItem(item.itemId)
} }
} }
@ -1197,6 +1231,21 @@ export default function Estimate({}) {
} }
}, [estimateContextState?.itemList, cableItemList]) }, [estimateContextState?.itemList, cableItemList])
const [agencyCustList, setAgencyCustList] = useState([])
useEffect(() => {
// 952 - 2 sapSalesStoreCd
if (estimateContextState?.sapSalesStoreCd && session?.storeLvl === '1') {
const param = {
sapSalesStoreCd: estimateContextState.sapSalesStoreCd,
}
const apiUrl = `api/estimate/agency-cust-list?${queryStringFormatter(param)}`
get({ url: apiUrl }).then((res) => {
if (isNotEmptyArray(res?.data)) {
setAgencyCustList(res?.data)
}
})
}
}, [estimateContextState?.sapSalesStoreCd])
return ( return (
<div className="sub-content estimate"> <div className="sub-content estimate">
<div className="sub-content-inner"> <div className="sub-content-inner">
@ -1341,36 +1390,79 @@ export default function Estimate({}) {
{getMessage('estimate.detail.estimateType')} <span className="important">*</span> {getMessage('estimate.detail.estimateType')} <span className="important">*</span>
</th> </th>
<td colSpan={3}> <td colSpan={3}>
<div className="radio-wrap"> <div className="form-flex-wrap">
<div className="d-check-radio light mr10"> <div className="radio-wrap">
<input {/*pkgRank is null, empty 인 경우 : 사용불가, 이전에 등록된 경우 사용가능, style로 제어*/}
type="radio" <div
name="estimateType" className="d-check-radio light mr10"
id="YJSS" style={{
value={'YJSS'} display:
checked={estimateContextState?.estimateType === 'YJSS' ? true : false} (isNotEmptyArray(storePriceList) > 0 && storePriceList[0].pkgRank !== null && storePriceList[0].pkgRank !== '') ||
onChange={(e) => { estimateContextState?.estimateType === 'YJSS'
// ? ''
setHandlePricingFlag(true) : 'none',
setEstimateContextState({ estimateType: e.target.value })
}} }}
/> >
<label htmlFor="YJSS">{getMessage('estimate.detail.estimateType.yjss')}</label> <input
</div> type="radio"
<div className="d-check-radio light"> name="estimateType"
<input id="YJSS"
type="radio" value={'YJSS'}
name="estimateType" checked={estimateContextState?.estimateType === 'YJSS' ? true : false}
id="YJOD" onChange={(e) => {
value={'YJOD'} //
checked={estimateContextState?.estimateType === 'YJOD' ? true : false} setHandlePricingFlag(true)
onChange={(e) => { setEstimateContextState({ estimateType: e.target.value })
setHandlePricingFlag(true) }}
setEstimateContextState({ estimateType: e.target.value }) />
}} <label htmlFor="YJSS">{getMessage('estimate.detail.estimateType.yjss')}</label>
/> </div>
<label htmlFor="YJOD">{getMessage('estimate.detail.estimateType.yjod')}</label> <div className="d-check-radio light">
<input
type="radio"
name="estimateType"
id="YJOD"
value={'YJOD'}
checked={estimateContextState?.estimateType === 'YJOD' ? true : false}
onChange={(e) => {
setHandlePricingFlag(true)
setEstimateContextState({ estimateType: e.target.value })
}}
/>
<label htmlFor="YJOD">{getMessage('estimate.detail.estimateType.yjod')}</label>
</div>
</div> </div>
{session?.storeLvl === '1' && agencyCustList.length > 0 ? (
<div className="form-flex-select ml10">
<label htmlFor="">{getMessage('estimate.detail.agency')}</label>
<div className="select-wrap" style={{ width: '400px' }}>
<Select
id="agencyName"
instanceId="agencyName"
className="react-select-custom"
classNamePrefix="custom"
placeholder="Select"
options={agencyCustList}
onChange={(e) => {
if (isObjectNotEmpty(e)) {
setEstimateContextState({ secSapSalesStoreCd: e.sapSalesStoreCd })
} else {
setEstimateContextState({ secSapSalesStoreCd: '' })
}
}}
getOptionLabel={(x) => x.sapSalesStoreNm}
getOptionValue={(x) => x.sapSalesStoreCd}
isClearable={true}
isSearchable={true}
value={agencyCustList.filter(function (option) {
return option.sapSalesStoreCd === estimateContextState.secSapSalesStoreCd
})}
/>
</div>
</div>
) : (
''
)}
</div> </div>
</td> </td>
</tr> </tr>
@ -1729,9 +1821,10 @@ export default function Estimate({}) {
<button <button
type="button" type="button"
className="btn-origin grey ml5" className="btn-origin grey ml5"
onClick={() => { onClick={(event) => {
setHandlePricingFlag(true) setHandlePricingFlag(true)
handlePricing(showPriceCd) handlePricingBtn(showPriceCd)
}} }}
> >
{getMessage('estimate.detail.showPrice.pricingBtn')} {getMessage('estimate.detail.showPrice.pricingBtn')}
@ -1859,8 +1952,13 @@ export default function Estimate({}) {
} }
}} }}
menuPlacement={'auto'} menuPlacement={'auto'}
getOptionLabel={(x) => x.itemName} getOptionLabel={(x) => x.itemName + ' (' + x.itemNo + ')'}
getOptionValue={(x) => x.itemNo} getOptionValue={(x) => x.itemNo}
components={{
SingleValue: ({ children, ...props }) => {
return <components.SingleValue {...props}>{props.data.itemName}</components.SingleValue>
},
}}
isClearable={false} isClearable={false}
isDisabled={!!item?.paDispOrder} isDisabled={!!item?.paDispOrder}
value={displayItemList.filter(function (option) { value={displayItemList.filter(function (option) {
@ -1880,12 +1978,17 @@ export default function Estimate({}) {
placeholder="Select" placeholder="Select"
options={cableItemList} options={cableItemList}
menuPlacement={'auto'} menuPlacement={'auto'}
getOptionLabel={(x) => x.clRefChr3} getOptionLabel={(x) => x.clRefChr3 + ' (' + x.clRefChr1 + ')'}
getOptionValue={(x) => x.clRefChr1} getOptionValue={(x) => x.clRefChr1}
components={{
SingleValue: ({ children, ...props }) => {
return <components.SingleValue {...props}>{(item.itemTpCd === 'M12')? item.itemName : props.data.clRefChr3}</components.SingleValue>
},
}}
isClearable={false} isClearable={false}
isDisabled={true} isDisabled={true}
value={cableItemList.filter(function (option) { value={cableItemList.filter(function (option) {
return option.clRefChr1 === item.itemId return (item.itemTpCd === 'M12')? item.itemId : option.clRefChr1 === item.itemId
})} })}
/> />
)} )}
@ -1936,7 +2039,11 @@ export default function Estimate({}) {
<input <input
type="text" type="text"
className="input-light al-r" className="input-light al-r"
value={convertNumberToPriceDecimal(item?.showSalePrice === '0' ? null : item?.salePrice?.replaceAll(',', ''))} value={
item.openFlg === '1'
? 'OPEN'
: convertNumberToPriceDecimal(item?.showSalePrice === '0' ? null : item?.salePrice?.replaceAll(',', ''))
}
disabled={ disabled={
item.openFlg === '1' item.openFlg === '1'
? true ? true
@ -1964,15 +2071,17 @@ export default function Estimate({}) {
</div> </div>
</td> </td>
<td className="al-r"> <td className="al-r">
{convertNumberToPriceDecimal( {item?.openFlg === '1'
item?.showSaleTotPrice === '0' ? 'OPEN'
? null : convertNumberToPriceDecimal(
: item?.amount === '' item?.showSaleTotPrice === '0'
? null
: item?.saleTotPrice === '0'
? null ? null
: item?.saleTotPrice?.replaceAll(',', ''), : item?.amount === ''
)} ? null
: item?.saleTotPrice === '0'
? null
: item?.saleTotPrice?.replaceAll(',', ''),
)}
</td> </td>
</tr> </tr>
) )

View File

@ -45,8 +45,11 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
options.sort = options.sort ?? true options.sort = options.sort ?? true
options.parentId = options.parentId ?? null options.parentId = options.parentId ?? null
this.isSortedPoints = false
if (!options.sort && points.length <= 8) { if (!options.sort && points.length <= 8) {
points = sortedPointLessEightPoint(points) points = sortedPointLessEightPoint(points)
this.isSortedPoints = true
} else { } else {
let isDiagonal = false let isDiagonal = false
points.forEach((point, i) => { points.forEach((point, i) => {
@ -62,6 +65,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
if (!isDiagonal) { if (!isDiagonal) {
points = sortedPoints(points) points = sortedPoints(points)
this.isSortedPoints = true
} }
} }
@ -119,10 +123,12 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
this.addLengthText() this.addLengthText()
this.on('moving', () => { this.on('moving', () => {
this.initLines()
this.addLengthText() this.addLengthText()
}) })
this.on('modified', (e) => { this.on('modified', (e) => {
this.initLines()
this.addLengthText() this.addLengthText()
}) })
@ -183,8 +189,8 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
this.lines = [] this.lines = []
this.points.forEach((point, i) => { this.getCurrentPoints().forEach((point, i) => {
const nextPoint = this.points[(i + 1) % this.points.length] const nextPoint = this.getCurrentPoints()[(i + 1) % this.points.length]
const line = new QLine([point.x, point.y, nextPoint.x, nextPoint.y], { const line = new QLine([point.x, point.y, nextPoint.x, nextPoint.y], {
stroke: this.stroke, stroke: this.stroke,
strokeWidth: this.strokeWidth, strokeWidth: this.strokeWidth,

View File

@ -30,11 +30,14 @@ import { useCanvasSetting } from '@/hooks/option/useCanvasSetting'
import { useCanvasMenu } from '@/hooks/common/useCanvasMenu' import { useCanvasMenu } from '@/hooks/common/useCanvasMenu'
import { useEvent } from '@/hooks/useEvent' import { useEvent } from '@/hooks/useEvent'
import { compasDegAtom } from '@/store/orientationAtom' import { compasDegAtom } from '@/store/orientationAtom'
import { hotkeyStore } from '@/store/hotkeyAtom'
import { usePopup } from '@/hooks/usePopup'
export default function CanvasFrame() { export default function CanvasFrame() {
const canvasRef = useRef(null) const canvasRef = useRef(null)
const { canvas } = useCanvas('canvas') const { canvas } = useCanvas('canvas')
const { canvasLoadInit, gridInit } = useCanvasConfigInitialize() const { canvasLoadInit, gridInit } = useCanvasConfigInitialize()
const { closeAll } = usePopup()
const currentMenu = useRecoilValue(currentMenuState) const currentMenu = useRecoilValue(currentMenuState)
const { floorPlanState } = useContext(FloorPlanContext) const { floorPlanState } = useContext(FloorPlanContext)
const { contextMenu, handleClick } = useContextMenu() const { contextMenu, handleClick } = useContextMenu()
@ -92,6 +95,8 @@ export default function CanvasFrame() {
useEffect(() => { useEffect(() => {
setIsGlobalLoading(false) setIsGlobalLoading(false)
// .
closeAll()
return () => { return () => {
canvas?.clear() canvas?.clear()
@ -110,6 +115,38 @@ export default function CanvasFrame() {
resetPcsCheckState() resetPcsCheckState()
} }
/**
* 캔버스가 있을 경우 핫키 이벤트 처리
* hotkeyStore에 핫키 이벤트 리스너 추가
*
* const hotkeys = [
{ key: 'c', fn: () => asdf() },
{ key: 'v', fn: () => qwer() },
]
setHotkeyStore(hotkeys)
*/
const hotkeyState = useRecoilValue(hotkeyStore)
const hotkeyHandlerRef = useRef(null)
useEffect(() => {
hotkeyHandlerRef.current = (e) => {
hotkeyState.forEach((hotkey) => {
if (e.key === hotkey.key) {
hotkey.fn()
}
})
}
document.addEventListener('keyup', hotkeyHandlerRef.current)
return () => {
if (hotkeyHandlerRef.current) {
document.removeEventListener('keyup', hotkeyHandlerRef.current)
}
}
}, [hotkeyState])
/** 핫키 이벤트 처리 끝 */
return ( return (
<div className="canvas-frame"> <div className="canvas-frame">
<canvas ref={canvasRef} id="canvas" style={{ position: 'relative' }}></canvas> <canvas ref={canvasRef} id="canvas" style={{ position: 'relative' }}></canvas>

View File

@ -31,12 +31,13 @@ export default function CanvasLayout({ children }) {
return ( return (
<div className="canvas-layout"> <div className="canvas-layout">
<div className={`canvas-page-list ${['outline', 'surface', 'module'].includes(selectedMenu) ? 'active' : ''}`}> <div className={`canvas-page-list ${['outline', 'surface', 'module'].includes(selectedMenu) ? 'active' : ''}`}>
<div className="canvas-id">{objectNo}</div>
<div className="canvas-plane-wrap"> <div className="canvas-plane-wrap">
{plans.map((plan, index) => ( {plans.map((plan, index) => (
<button <button
key={`plan-${plan.id}`} key={`plan-${plan.id}`}
className={`canvas-page-box ${plan.isCurrent === true ? 'on' : ''}`} className={`canvas-page-box ${plan.isCurrent === true ? 'on' : ''}`}
onClick={() => handleCurrentPlan(plan.id)} onClick={() => (plan.isCurrent ? '' : handleCurrentPlan(plan.id))}
> >
<span>{`Plan ${plan.planNo}`}</span> <span>{`Plan ${plan.planNo}`}</span>
{plans.length > 1 && !pathname.includes('/estimate') && !pathname.includes('/simulator') && ( {plans.length > 1 && !pathname.includes('/estimate') && !pathname.includes('/simulator') && (

View File

@ -548,13 +548,26 @@ export default function CanvasMenu(props) {
{ {
<div className={`vertical-horizontal ${verticalHorizontalMode ? 'on' : ''}`}> <div className={`vertical-horizontal ${verticalHorizontalMode ? 'on' : ''}`}>
<span>{getMessage('plan.mode.vertical.horizontal')}</span> <span>{getMessage('plan.mode.vertical.horizontal')}</span>
<button onClick={() => setVerticalHorizontalMode(!verticalHorizontalMode)}>{verticalHorizontalMode ? 'ON' : 'OFF'}</button> <button
title={`${getMessage('plan.mode.vertical.horizontal')} ${verticalHorizontalMode ? 'ON' : 'OFF'}`}
onClick={() => setVerticalHorizontalMode(!verticalHorizontalMode)}
>
{verticalHorizontalMode ? 'ON' : 'OFF'}
</button>
</div> </div>
} }
<div className="btn-from"> <div className="btn-from">
<button className={`btn01 ${commonUtils.text ? 'active' : ''}`} onClick={() => commonFunctions('text')}></button> <button className={`btn01 ${commonUtils.text ? 'active' : ''}`} onClick={() => commonFunctions('text')} title="文字作成"></button>
<button className={`btn02 ${commonUtils.dimension ? 'active' : ''} `} onClick={() => commonFunctions('dimension')}></button> <button
<button className={`btn03 ${commonUtils.distance ? 'active' : ''} `} onClick={() => commonFunctions('distance')}></button> className={`btn02 ${commonUtils.dimension ? 'active' : ''} `}
onClick={() => commonFunctions('dimension')}
title="寸法作成"
></button>
<button
className={`btn03 ${commonUtils.distance ? 'active' : ''} `}
onClick={() => commonFunctions('distance')}
title="定規"
></button>
</div> </div>
{isObjectNotEmpty(selectedRoofMaterial) && addedRoofs.length > 0 && ( {isObjectNotEmpty(selectedRoofMaterial) && addedRoofs.length > 0 && (
<div className="select-box"> <div className="select-box">
@ -580,6 +593,7 @@ export default function CanvasMenu(props) {
sourceKey={'index'} sourceKey={'index'}
targetKey={'index'} targetKey={'index'}
disabled={+basicSetting.roofSizeSet === 3} disabled={+basicSetting.roofSizeSet === 3}
tagTitle={'屋根材変更'}
/> />
} }
</div> </div>
@ -588,9 +602,10 @@ export default function CanvasMenu(props) {
<button <button
className={`btn10 ${floorPlanState.refFileModalOpen && 'active'}`} className={`btn10 ${floorPlanState.refFileModalOpen && 'active'}`}
onClick={() => setFloorPlanState({ ...floorPlanState, refFileModalOpen: true })} onClick={() => setFloorPlanState({ ...floorPlanState, refFileModalOpen: true })}
title="読込"
></button> ></button>
{/*<button className="btn04" onClick={() => setShowCanvasSettingModal(true)}></button>*/} {/*<button className="btn04" onClick={() => setShowCanvasSettingModal(true)}></button>*/}
<button className="btn04" onClick={handlePopup}></button> <button className="btn04" onClick={handlePopup} title="設定"></button>
</div> </div>
<div className="size-control"> <div className="size-control">
<button <button
@ -599,7 +614,9 @@ export default function CanvasMenu(props) {
handleZoom(false) handleZoom(false)
}} }}
></button> ></button>
<span onClick={handleZoomClear}>{canvasZoom}%</span> <span onClick={handleZoomClear} title="拡大・縮小">
{canvasZoom}%
</span>
<button <button
className="control-btn plus" className="control-btn plus"
onClick={() => { onClick={() => {
@ -608,8 +625,8 @@ export default function CanvasMenu(props) {
></button> ></button>
</div> </div>
<div className="btn-from"> <div className="btn-from">
<button className="btn08" onClick={handleSaveCanvas}></button> <button className="btn08" onClick={handleSaveCanvas} title="保存"></button>
<button className="btn09" onClick={handleLeaveCanvas}></button> <button className="btn09" onClick={handleLeaveCanvas} title="物件検索画面へ移動"></button>
</div> </div>
</> </>
)} )}
@ -634,7 +651,7 @@ export default function CanvasMenu(props) {
onClick={() => setEstimatePopupOpen(true)} onClick={() => setEstimatePopupOpen(true)}
> >
<span className="ico ico01"></span> <span className="ico ico01"></span>
<span className="name">{getMessage('plan.menu.estimate.docDown')}</span> <span className="name">{getMessage('plan.menu.estimate.docDownload')}</span>
</button> </button>
<button type="button" style={{ display: saveButtonStyle }} className="btn-frame gray ico-flx" onClick={handleEstimateSubmit}> <button type="button" style={{ display: saveButtonStyle }} className="btn-frame gray ico-flx" onClick={handleEstimateSubmit}>
<span className="ico ico02"></span> <span className="ico ico02"></span>

View File

@ -1,15 +1,18 @@
'use client' 'use client'
import { useEffect } from 'react' import { useContext, useEffect } from 'react'
import CanvasMenu from '@/components/floor-plan/CanvasMenu' import CanvasMenu from '@/components/floor-plan/CanvasMenu'
import { useCanvasMenu } from '@/hooks/common/useCanvasMenu' import { useCanvasMenu } from '@/hooks/common/useCanvasMenu'
import { useCanvasSetting } from '@/hooks/option/useCanvasSetting' import { useCanvasSetting } from '@/hooks/option/useCanvasSetting'
import { usePopup } from '@/hooks/usePopup' import { usePopup } from '@/hooks/usePopup'
import '@/styles/contents.scss' import '@/styles/contents.scss'
import { notFound, useSearchParams } from 'next/navigation' import { notFound, useSearchParams } from 'next/navigation'
import { useRecoilState, useResetRecoilState } from 'recoil' import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil'
import { correntObjectNoState } from '@/store/settingAtom' import { correntObjectNoState } from '@/store/settingAtom'
import { currentMenuState } from '@/store/canvasAtom' import { currentMenuState } from '@/store/canvasAtom'
import { globalLocaleStore } from '@/store/localeAtom'
import { useAxios } from '@/hooks/useAxios'
import { GlobalDataContext } from '@/app/GlobalDataProvider'
export default function FloorPlan({ children }) { export default function FloorPlan({ children }) {
const [correntObjectNo, setCurrentObjectNo] = useRecoilState(correntObjectNoState) const [correntObjectNo, setCurrentObjectNo] = useRecoilState(correntObjectNoState)
@ -20,12 +23,39 @@ export default function FloorPlan({ children }) {
const { selectedMenu, setSelectedMenu } = useCanvasMenu() const { selectedMenu, setSelectedMenu } = useCanvasMenu()
const { fetchSettings } = useCanvasSetting() const { fetchSettings } = useCanvasSetting()
const resetCurrentMenu = useResetRecoilState(currentMenuState) const resetCurrentMenu = useResetRecoilState(currentMenuState)
const globalLocaleState = useRecoilValue(globalLocaleStore)
const { promiseGet } = useAxios(globalLocaleState)
const { setManagementState } = useContext(GlobalDataContext)
useEffect(() => { useEffect(() => {
getStuffDetailInfo()
return () => { return () => {
resetCurrentMenu() resetCurrentMenu()
} }
}, []) }, [])
const getStuffDetailInfo = () => {
promiseGet({ url: `/api/object/${objectNo}/detail` }).then((res) => {
if (res.status === 200) {
const { data } = res
console.log(data)
let surfaceTypeValue
if (res.data.surfaceType === 'Ⅲ・Ⅳ') {
surfaceTypeValue = '3'
} else if (res.data.surfaceType === 'Ⅱ') {
surfaceTypeValue = '2'
}
// 0
if (res.data.installHeight === '0') {
res.data.installHeight = ''
}
setManagementState({ ...res.data, surfaceTypeValue: surfaceTypeValue })
}
})
}
/** /**
* URL 파라미터에서 objectNo 설정 * URL 파라미터에서 objectNo 설정
*/ */

View File

@ -40,15 +40,15 @@ export default function AuxiliaryEdit(props) {
if (currentObject) { if (currentObject) {
copy( copy(
currentObject, currentObject,
arrow2 ? (arrow2 === '←' ? Number(horizonSize) * -1 : Number(horizonSize)) : 0, arrow2 ? (arrow2 === '←' ? Number(+horizonSize / 10) * -1 : Number(+horizonSize / 10)) : 0,
arrow1 ? (arrow1 === '↑' ? Number(verticalSize) * -1 : Number(verticalSize)) : 0, arrow1 ? (arrow1 === '↑' ? Number(+verticalSize / 10) * -1 : Number(+verticalSize / 10)) : 0,
) )
} }
} else { } else {
move( move(
currentObject, currentObject,
arrow2 ? (arrow2 === '←' ? Number(horizonSize) * -1 : Number(horizonSize)) : 0, arrow2 ? (arrow2 === '←' ? Number(+horizonSize / 10) * -1 : Number(+horizonSize / 10)) : 0,
arrow1 ? (arrow1 === '↑' ? Number(verticalSize) * -1 : Number(verticalSize)) : 0, arrow1 ? (arrow1 === '↑' ? Number(+verticalSize / 10) * -1 : Number(+verticalSize / 10)) : 0,
) )
} }

View File

@ -1,109 +1,88 @@
import { useMessage } from '@/hooks/useMessage' import { POLYGON_TYPE, MODULE_SETUP_TYPE } from '@/common/common'
import WithDraggable from '@/components/common/draggable/WithDraggable' import WithDraggable from '@/components/common/draggable/WithDraggable'
import { useContext, useEffect, useRef, useState } from 'react' import { Orientation } from '@/components/floor-plan/modal/basic/step/Orientation'
import Module from '@/components/floor-plan/modal/basic/step/Module'
import PitchModule from '@/components/floor-plan/modal/basic/step/pitch/PitchModule'
import PitchPlacement from '@/components/floor-plan/modal/basic/step/pitch/PitchPlacement' import PitchPlacement from '@/components/floor-plan/modal/basic/step/pitch/PitchPlacement'
import Placement from '@/components/floor-plan/modal/basic/step/Placement' import Placement from '@/components/floor-plan/modal/basic/step/Placement'
import { useRecoilValue, useRecoilState } from 'recoil'
import { canvasSettingState, canvasState, checkedModuleState, isManualModuleSetupState } from '@/store/canvasAtom'
import { usePopup } from '@/hooks/usePopup'
import { Orientation } from '@/components/floor-plan/modal/basic/step/Orientation'
import { useModuleBasicSetting } from '@/hooks/module/useModuleBasicSetting'
import { useEvent } from '@/hooks/useEvent'
import { moduleSelectionDataState, selectedModuleState } from '@/store/selectedModuleOptions'
import { addedRoofsState, corridorDimensionSelector, basicSettingState } from '@/store/settingAtom'
import { isObjectNotEmpty } from '@/util/common-utils'
import Swal from 'sweetalert2'
import { useCanvasPopupStatusController } from '@/hooks/common/useCanvasPopupStatusController' import { useCanvasPopupStatusController } from '@/hooks/common/useCanvasPopupStatusController'
import { useMasterController } from '@/hooks/common/useMasterController' import { useMasterController } from '@/hooks/common/useMasterController'
import { useModuleBasicSetting } from '@/hooks/module/useModuleBasicSetting'
import { useModuleSelection } from '@/hooks/module/useModuleSelection'
import { useOrientation } from '@/hooks/module/useOrientation'
import { useMessage } from '@/hooks/useMessage'
import { usePopup } from '@/hooks/usePopup'
import {
canvasState,
checkedModuleState,
currentCanvasPlanState,
isManualModuleLayoutSetupState,
isManualModuleSetupState,
toggleManualSetupModeState,
} from '@/store/canvasAtom'
import { loginUserStore } from '@/store/commonAtom' import { loginUserStore } from '@/store/commonAtom'
import { currentCanvasPlanState } from '@/store/canvasAtom' import { roofsState } from '@/store/roofAtom'
import { POLYGON_TYPE } from '@/common/common' import { moduleSelectionDataState } from '@/store/selectedModuleOptions'
import { addedRoofsState, basicSettingState } from '@/store/settingAtom'
import { isObjectNotEmpty } from '@/util/common-utils'
import { useEffect, useRef, useState } from 'react'
import { useRecoilState, useRecoilValue } from 'recoil'
import Swal from 'sweetalert2'
import Trestle from './step/Trestle'
export default function BasicSetting({ id, pos = { x: 50, y: 230 } }) { export default function BasicSetting({ id, pos = { x: 50, y: 230 } }) {
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { closePopup } = usePopup() const { closePopup } = usePopup()
const [tabNum, setTabNum] = useState(1) const [tabNum, setTabNum] = useState(1)
const canvasSetting = useRecoilValue(canvasSettingState)
const orientationRef = useRef(null) const orientationRef = useRef(null)
const { initEvent } = useEvent()
const [isManualModuleSetup, setIsManualModuleSetup] = useRecoilState(isManualModuleSetupState) const [isManualModuleSetup, setIsManualModuleSetup] = useRecoilState(isManualModuleSetupState)
const moduleSelectionData = useRecoilValue(moduleSelectionDataState) const [isManualModuleLayoutSetup, setIsManualModuleLayoutSetup] = useRecoilState(isManualModuleLayoutSetupState)
const addedRoofs = useRecoilValue(addedRoofsState) const trestleRef = useRef(null)
const [moduleSelectionData, setModuleSelectionData] = useRecoilState(moduleSelectionDataState)
const [addedRoofs, setAddedRoofs] = useRecoilState(addedRoofsState)
const loginUserState = useRecoilValue(loginUserStore) const loginUserState = useRecoilValue(loginUserStore)
const currentCanvasPlan = useRecoilValue(currentCanvasPlanState) const currentCanvasPlan = useRecoilValue(currentCanvasPlanState)
const canvas = useRecoilValue(canvasState) const canvas = useRecoilValue(canvasState)
const [basicSetting, setBasicSettings] = useRecoilState(basicSettingState) const [basicSetting, setBasicSettings] = useRecoilState(basicSettingState)
const [isClosePopup, setIsClosePopup] = useState({ close: false, id: 0 }) const [isClosePopup, setIsClosePopup] = useState({ close: false, id: 0 })
const [checkedModules, setCheckedModules] = useRecoilState(checkedModuleState) const [checkedModules, setCheckedModules] = useRecoilState(checkedModuleState)
const [roofs, setRoofs] = useState(addedRoofs)
const [manualSetupMode, setManualSetupMode] = useRecoilState(toggleManualSetupModeState)
const [layoutSetup, setLayoutSetup] = useState([{}])
const {
selectedModules,
roughnessCodes,
windSpeedCodes,
managementState,
setManagementState,
moduleList,
setSelectedModules,
selectedSurfaceType,
setSelectedSurfaceType,
installHeight,
setInstallHeight,
standardWindSpeed,
setStandardWindSpeed,
verticalSnowCover,
setVerticalSnowCover,
handleChangeModule,
handleChangeSurfaceType,
handleChangeWindSpeed,
handleChangeInstallHeight,
handleChangeVerticalSnowCover,
} = useModuleSelection({ addedRoofs })
const { nextStep, compasDeg, setCompasDeg } = useOrientation()
const { trigger: orientationTrigger } = useCanvasPopupStatusController(1)
const { trigger: trestleTrigger } = useCanvasPopupStatusController(2)
const { trigger: placementTrigger } = useCanvasPopupStatusController(3)
const [roofsStore, setRoofsStore] = useRecoilState(roofsState)
// const { initEvent } = useContext(EventContext) // const { initEvent } = useContext(EventContext)
const { manualModuleSetup, autoModuleSetup, manualFlatroofModuleSetup, autoFlatroofModuleSetup } = useModuleBasicSetting(tabNum) const { manualModuleSetup, autoModuleSetup, manualFlatroofModuleSetup, autoFlatroofModuleSetup, manualModuleLayoutSetup, restoreModuleInstArea } =
useModuleBasicSetting(tabNum)
const { updateObjectDate } = useMasterController() const { updateObjectDate } = useMasterController()
const handleBtnNextStep = () => {
if (tabNum === 1) {
orientationRef.current.handleNextStep()
} else if (tabNum === 2) {
if (basicSetting.roofSizeSet !== '3') {
if (!isObjectNotEmpty(moduleSelectionData.module)) {
Swal.fire({
title: getMessage('module.not.found'),
icon: 'warning',
})
return
}
if (addedRoofs.length !== moduleSelectionData.roofConstructions.length) {
Swal.fire({
title: getMessage('construction.length.difference'),
icon: 'warning',
})
return
}
//
updateObjectDataApi({
objectNo: currentCanvasPlan.objectNo, //_no
standardWindSpeedId: moduleSelectionData.common.stdWindSpeed, //
verticalSnowCover: moduleSelectionData.common.stdSnowLd, //
surfaceType: moduleSelectionData.common.illuminationTpNm, //
installHeight: moduleSelectionData.common.instHt, //
userId: loginUserState.userId, //
})
} else {
if (!isObjectNotEmpty(moduleSelectionData.module)) {
Swal.fire({
title: getMessage('module.not.found'),
icon: 'warning',
})
return
}
}
}
setTabNum(tabNum + 1)
}
const placementRef = {
isChidori: useRef('false'),
setupLocation: useRef('eaves'),
isMaxSetup: useRef('false'),
}
const placementFlatRef = {
setupLocation: useRef('south'),
}
const handleManualModuleSetup = () => {
setIsManualModuleSetup(!isManualModuleSetup)
}
const updateObjectDataApi = async (params) => {
const res = await updateObjectDate(params)
}
useEffect(() => { useEffect(() => {
const moduleTabNum = basicSetting.roofSizeSet != 3 ? 3 : 2
let hasModules = canvas let hasModules = canvas
.getObjects() .getObjects()
.filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE) .filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE)
@ -111,23 +90,42 @@ export default function BasicSetting({ id, pos = { x: 50, y: 230 } }) {
if (hasModules) { if (hasModules) {
orientationRef.current.handleNextStep() orientationRef.current.handleNextStep()
setTabNum(3) setTabNum(moduleTabNum)
} }
}, []) }, [])
// useEffect(() => {
const handleClosePopup = (id) => { if (roofsStore && addedRoofs) {
if (tabNum == 3) { setRoofs(
if (isManualModuleSetup) { addedRoofs.map((roof, index) => {
setIsManualModuleSetup(false) return {
} ...roof,
...roofsStore[index]?.addRoof,
construction: roofsStore[index]?.construction,
trestle: roofsStore[index]?.trestle,
trestleDetail: roofsStore[index]?.trestleDetail,
}
}),
)
setModuleSelectionData({
...moduleSelectionData,
roofConstructions: roofsStore.map((roof) => {
return {
roofIndex: roof.roofIndex,
addRoof: roof.addRoof,
construction: roof.construction,
trestle: roof.trestle,
trestleDetail: roof.trestleDetail,
}
}),
})
} }
setIsClosePopup({ close: true, id: id }) }, [roofsStore, addedRoofs])
}
useEffect(() => { useEffect(() => {
if (basicSetting.roofSizeSet !== '3') { if (basicSetting.roofSizeSet !== '3') {
manualModuleSetup(placementRef) manualModuleSetup()
} else { } else {
manualFlatroofModuleSetup(placementFlatRef) manualFlatroofModuleSetup(placementFlatRef)
} }
@ -140,55 +138,224 @@ export default function BasicSetting({ id, pos = { x: 50, y: 230 } }) {
setIsManualModuleSetup(false) setIsManualModuleSetup(false)
}, [checkedModules]) }, [checkedModules])
useEffect(() => {
if (basicSetting.roofSizeSet !== '3') {
if (manualSetupMode.indexOf('manualSetup') > -1) {
manualModuleSetup()
} else if (manualSetupMode.indexOf('manualLayoutSetup') > -1) {
manualModuleLayoutSetup(layoutSetup)
} else if (manualSetupMode.indexOf('off') > -1) {
manualModuleSetup()
manualModuleLayoutSetup(layoutSetup)
}
} else {
manualFlatroofModuleSetup(placementFlatRef)
}
if (isClosePopup.close) {
closePopup(isClosePopup.id)
}
}, [manualSetupMode, isClosePopup])
useEffect(() => {
if (isManualModuleLayoutSetup) {
manualModuleLayoutSetup(layoutSetup)
}
}, [layoutSetup])
useEffect(() => {
setIsManualModuleSetup(false)
setIsManualModuleLayoutSetup(false)
setManualSetupMode(`off`)
}, [checkedModules])
const handleBtnNextStep = () => {
if (tabNum === 1) {
orientationRef.current.handleNextStep()
setAddedRoofs(roofs)
// setTabNum(tabNum + 1)
return
} else if (tabNum === 2) {
if (basicSetting.roofSizeSet !== '3') {
// if (addedRoofs.length !== moduleSelectionData.roofConstructions.length) {
// Swal.fire({
// title: getMessage('construction.length.difference'),
// icon: 'warning',
// })
// return
// }
trestleRef.current.isComplete().then((res) => {
if (!res) return
})
//
} else {
if (!isObjectNotEmpty(moduleSelectionData.module)) {
Swal.fire({
title: getMessage('module.not.found'),
icon: 'warning',
})
return
}
setTabNum(tabNum + 1)
}
}
}
const placementFlatRef = {
setupLocation: useRef('south'),
}
const handleManualModuleSetup = () => {
setManualSetupMode(`manualSetup_${!isManualModuleSetup}`)
setIsManualModuleSetup(!isManualModuleSetup)
}
const handleManualModuleLayoutSetup = () => {
setManualSetupMode(`manualLayoutSetup_${!isManualModuleLayoutSetup}`)
setIsManualModuleLayoutSetup(!isManualModuleLayoutSetup)
}
const updateObjectDataApi = async (params) => {
const res = await updateObjectDate(params)
}
//
const handleClosePopup = (id) => {
if (tabNum == 3) {
if (isManualModuleSetup) {
setIsManualModuleSetup(false)
}
if (isManualModuleLayoutSetup) {
setIsManualModuleLayoutSetup(false)
}
}
setIsClosePopup({ close: true, id: id })
}
const orientationProps = {
roofs,
setRoofs,
tabNum,
setTabNum,
compasDeg, //
setCompasDeg,
selectedModules,
moduleSelectionData,
setModuleSelectionData,
roughnessCodes, //
windSpeedCodes, //
managementState,
setManagementState,
moduleList, //
setSelectedModules,
selectedSurfaceType,
setSelectedSurfaceType,
installHeight, //
setInstallHeight,
standardWindSpeed, //
setStandardWindSpeed,
verticalSnowCover, //
setVerticalSnowCover,
currentCanvasPlan,
loginUserState,
handleChangeModule,
handleChangeSurfaceType,
handleChangeWindSpeed,
handleChangeInstallHeight,
handleChangeVerticalSnowCover,
orientationTrigger,
nextStep,
updateObjectDataApi,
}
const trestleProps = {
roofs,
setRoofs,
setRoofsStore,
tabNum,
setTabNum,
moduleSelectionData,
setModuleSelectionData,
trestleTrigger,
}
const placementProps = {}
return ( return (
<WithDraggable isShow={true} pos={pos} className="lx-2"> <WithDraggable isShow={true} pos={pos} className={basicSetting.roofSizeSet && basicSetting.roofSizeSet != '3' ? 'll' : 'lx-2'}>
<WithDraggable.Header title={getMessage('plan.menu.module.circuit.setting.default')} onClose={() => handleClosePopup(id)} /> <WithDraggable.Header title={getMessage('plan.menu.module.circuit.setting.default')} onClose={() => handleClosePopup(id)} />
<WithDraggable.Body> <WithDraggable.Body>
<div className="roof-module-tab"> <div className="roof-module-tab">
<div className={`module-tab-bx act`}>{getMessage('modal.module.basic.setting.orientation.setting')}</div> <div className={`module-tab-bx act`}>{getMessage('modal.module.basic.setting.orientation.setting')}</div>
<span className={`tab-arr ${tabNum !== 1 ? 'act' : ''}`}></span> <span className={`tab-arr ${tabNum !== 1 ? 'act' : ''}`}></span>
<div className={`module-tab-bx ${tabNum !== 1 ? 'act' : ''}`}>{getMessage('modal.module.basic.setting.module.setting')}</div> {basicSetting.roofSizeSet && basicSetting.roofSizeSet != '3' && (
<span className={`tab-arr ${tabNum === 3 ? 'act' : ''}`}></span> <>
<div className={`module-tab-bx ${tabNum === 3 ? 'act' : ''}`}>{getMessage('modal.module.basic.setting.module.placement')}</div> <div className={`module-tab-bx ${tabNum !== 1 ? 'act' : ''}`}>{getMessage('modal.module.basic.setting.module.setting')}</div>
<span className={`tab-arr ${tabNum === 3 ? 'act' : ''}`}></span>
<div className={`module-tab-bx ${tabNum === 3 ? 'act' : ''}`}>{getMessage('modal.module.basic.setting.module.placement')}</div>
</>
)}
{basicSetting.roofSizeSet && basicSetting.roofSizeSet == '3' && (
<>
<div className={`module-tab-bx ${tabNum === 2 ? 'act' : ''}`}>{getMessage('modal.module.basic.setting.module.placement')}</div>
</>
)}
</div> </div>
{tabNum === 1 && <Orientation ref={orientationRef} tabNum={tabNum} setTabNum={setTabNum} />} {tabNum === 1 && <Orientation ref={orientationRef} {...orientationProps} />}
{/*배치면 초기설정 - 입력방법: 복시도 입력 || 실측값 입력*/} {/*배치면 초기설정 - 입력방법: 복시도 입력 || 실측값 입력*/}
{basicSetting.roofSizeSet && basicSetting.roofSizeSet != '3' && tabNum === 2 && <Module setTabNum={setTabNum} />} {basicSetting.roofSizeSet && basicSetting.roofSizeSet != '3' && tabNum === 2 && <Trestle ref={trestleRef} {...trestleProps} />}
{basicSetting.roofSizeSet && basicSetting.roofSizeSet != '3' && tabNum === 3 && <Placement setTabNum={setTabNum} ref={placementRef} />} {basicSetting.roofSizeSet && basicSetting.roofSizeSet != '3' && tabNum === 3 && (
<Placement setTabNum={setTabNum} layoutSetup={layoutSetup} setLayoutSetup={setLayoutSetup} />
)}
{/*배치면 초기설정 - 입력방법: 육지붕*/} {/*배치면 초기설정 - 입력방법: 육지붕*/}
{basicSetting.roofSizeSet && basicSetting.roofSizeSet == '3' && tabNum === 2 && <PitchModule setTabNum={setTabNum} />} {/* {basicSetting.roofSizeSet && basicSetting.roofSizeSet == '3' && tabNum === 3 && <PitchModule setTabNum={setTabNum} />} */}
{basicSetting.roofSizeSet && basicSetting.roofSizeSet == '3' && tabNum === 3 && ( {basicSetting.roofSizeSet && basicSetting.roofSizeSet == '3' && tabNum === 2 && (
<PitchPlacement setTabNum={setTabNum} ref={placementFlatRef} /> <PitchPlacement setTabNum={setTabNum} ref={placementFlatRef} />
)} )}
<div className="grid-btn-wrap"> <div className="grid-btn-wrap">
{tabNum !== 1 && ( {/* {tabNum === 1 && <button className="btn-frame modal mr5">{getMessage('modal.common.save')}</button>} */}
<button className="btn-frame modal mr5" onClick={() => setTabNum(tabNum - 1)}> {basicSetting.roofSizeSet && basicSetting.roofSizeSet != '3' && (
{getMessage('modal.module.basic.setting.prev')}
</button>
)}
{/*{tabNum !== 3 && <button className="btn-frame modal act mr5">{getMessage('modal.common.save')}</button>}*/}
{tabNum !== 3 && (
<button className="btn-frame modal" onClick={handleBtnNextStep}>
Next
</button>
)}
{tabNum === 3 && (
<> <>
{basicSetting.roofSizeSet && basicSetting.roofSizeSet != '3' && ( {tabNum !== 1 && (
<button className="btn-frame modal mr5" onClick={() => setTabNum(tabNum - 1)}>
{getMessage('modal.module.basic.setting.prev')}
</button>
)}
{tabNum !== 3 && (
<button className="btn-frame modal" onClick={handleBtnNextStep}>
Next
</button>
)}
{tabNum === 3 && (
<> <>
<button className={`btn-frame modal mr5 ${isManualModuleLayoutSetup ? 'act' : ''}`} onClick={handleManualModuleLayoutSetup}>
{getMessage('modal.module.basic.setting.row.batch')}
</button>
<button className="btn-frame modal mr5" onClick={() => autoModuleSetup(MODULE_SETUP_TYPE.LAYOUT, layoutSetup)}>
{getMessage('modal.module.basic.setting.auto.row.batch')}
</button>
<button className={`btn-frame modal mr5 ${isManualModuleSetup ? 'act' : ''}`} onClick={handleManualModuleSetup}> <button className={`btn-frame modal mr5 ${isManualModuleSetup ? 'act' : ''}`} onClick={handleManualModuleSetup}>
{getMessage('modal.module.basic.setting.passivity.placement')} {getMessage('modal.module.basic.setting.passivity.placement')}
</button> </button>
<button className="btn-frame modal act" onClick={() => autoModuleSetup(placementRef)}> <button className="btn-frame modal act mr5" onClick={() => autoModuleSetup(MODULE_SETUP_TYPE.AUTO)}>
{getMessage('modal.module.basic.setting.auto.placement')} {getMessage('modal.module.basic.setting.auto.placement')}
</button> </button>
</> </>
)} )}
{basicSetting.roofSizeSet && basicSetting.roofSizeSet == '3' && ( </>
)}
{basicSetting.roofSizeSet && basicSetting.roofSizeSet == '3' && (
<>
{tabNum === 1 && (
<button className="btn-frame modal" onClick={handleBtnNextStep}>
Next
</button>
)}
{tabNum === 2 && (
<> <>
<button className="btn-frame modal mr5" onClick={() => setTabNum(tabNum - 1)}>
{getMessage('modal.module.basic.setting.prev')}
</button>
<button className={`btn-frame modal mr5 ${isManualModuleSetup ? 'act' : ''}`} onClick={handleManualModuleSetup}> <button className={`btn-frame modal mr5 ${isManualModuleSetup ? 'act' : ''}`} onClick={handleManualModuleSetup}>
{getMessage('modal.module.basic.setting.passivity.placement')} {getMessage('modal.module.basic.setting.passivity.placement')}
</button> </button>

View File

@ -1,115 +1,465 @@
import { forwardRef, useContext, useEffect, useImperativeHandle, useState } from 'react' import { forwardRef, use, useContext, useEffect, useImperativeHandle, useState } from 'react'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { useOrientation } from '@/hooks/module/useOrientation'
import { getDegreeInOrientation } from '@/util/canvas-util' import { getDegreeInOrientation } from '@/util/canvas-util'
import { numberCheck } from '@/util/common-utils' import { numberCheck } from '@/util/common-utils'
import { useCanvasPopupStatusController } from '@/hooks/common/useCanvasPopupStatusController' import { addedRoofsState, basicSettingState } from '@/store/settingAtom'
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
import QSelectBox from '@/components/common/select/QSelectBox'
import { roofsState } from '@/store/roofAtom'
import { useModuleBasicSetting } from '@/hooks/module/useModuleBasicSetting'
import Swal from 'sweetalert2'
export const Orientation = forwardRef(({ tabNum }, ref) => { export const Orientation = forwardRef((props, ref) => {
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { trigger: canvasPopupStatusTrigger } = useCanvasPopupStatusController(1)
const { nextStep, compasDeg, setCompasDeg } = useOrientation()
const [hasAnglePassivity, setHasAnglePassivity] = useState(false) const [hasAnglePassivity, setHasAnglePassivity] = useState(false)
const basicSetting = useRecoilValue(basicSettingState)
const [addedRoofs, setAddedRoofs] = useRecoilState(addedRoofsState) //
const [roofsStore, setRoofsStore] = useRecoilState(roofsState)
const [roofTab, setRoofTab] = useState(0) //
const {
roofs,
setRoofs,
tabNum,
setTabNum,
compasDeg,
setCompasDeg,
selectedModules,
roughnessCodes,
windSpeedCodes,
managementState,
setManagementState,
moduleList,
moduleSelectionData,
setModuleSelectionData,
setSelectedModules,
selectedSurfaceType,
setSelectedSurfaceType,
installHeight,
setInstallHeight,
standardWindSpeed,
setStandardWindSpeed,
verticalSnowCover,
setVerticalSnowCover,
orientationTrigger,
nextStep,
currentCanvasPlan,
loginUserState,
updateObjectDataApi,
} = props
const [inputCompasDeg, setInputCompasDeg] = useState(compasDeg ?? 0)
const [inputInstallHeight, setInputInstallHeight] = useState('0')
const [inputMargin, setInputMargin] = useState('0')
const [inputVerticalSnowCover, setInputVerticalSnowCover] = useState('0')
const [inputRoughness, setInputRoughness] = useState(selectedSurfaceType)
const [inputStandardWindSpeed, setInputStandardWindSpeed] = useState(standardWindSpeed)
const { restoreModuleInstArea } = useModuleBasicSetting()
const moduleData = {
header: [
{ name: getMessage('module'), width: 150, prop: 'module', type: 'color-box' },
{
name: `${getMessage('height')} (mm)`,
prop: 'height',
},
{ name: `${getMessage('width')} (mm)`, prop: 'width' },
{ name: `${getMessage('output')} (W)`, prop: 'output' },
],
}
useEffect(() => {
if (basicSetting.roofSizeSet == '3') {
restoreModuleInstArea()
}
}, [])
useEffect(() => {
if (moduleSelectionData?.common) {
setInputMargin(moduleSelectionData?.common?.margin)
}
}, [moduleSelectionData])
useEffect(() => {
if (selectedModules) {
setSelectedModules(moduleList.find((module) => module.itemId === selectedModules.itemId))
}
}, [selectedModules])
useEffect(() => {
if (selectedSurfaceType) {
setInputRoughness(roughnessCodes.find((code) => code.clCode === managementState?.surfaceTypeValue))
}
}, [selectedSurfaceType])
useEffect(() => {
if (standardWindSpeed) setInputStandardWindSpeed(windSpeedCodes.find((code) => code.clCode === managementState?.standardWindSpeedId))
}, [standardWindSpeed])
useEffect(() => {
if (managementState?.installHeight && managementState?.installHeight) {
setSelectedSurfaceType(roughnessCodes.find((code) => code.clCode === managementState?.surfaceTypeValue))
setInputInstallHeight(managementState?.installHeight)
setStandardWindSpeed(windSpeedCodes.find((code) => code.clCode === managementState?.standardWindSpeedId))
setInputVerticalSnowCover(managementState?.verticalSnowCover)
}
}, [managementState])
useImperativeHandle(ref, () => ({ useImperativeHandle(ref, () => ({
handleNextStep, handleNextStep,
})) }))
const handleNextStep = () => { const handleNextStep = () => {
nextStep() if (isComplete()) {
canvasPopupStatusTrigger(compasDeg) const common = {
illuminationTp: inputRoughness.clCode,
illuminationTpNm: inputRoughness.clCodeNm,
instHt: inputInstallHeight,
stdWindSpeed: inputStandardWindSpeed?.clCode,
stdSnowLd: inputVerticalSnowCover,
saleStoreNorthFlg: managementState?.saleStoreNorthFlg,
moduleTpCd: selectedModules.itemTp,
moduleItemId: selectedModules.itemId,
margin: inputMargin,
}
setCompasDeg(inputCompasDeg)
setInstallHeight(inputInstallHeight)
setVerticalSnowCover(inputVerticalSnowCover)
setSelectedSurfaceType(inputRoughness)
setStandardWindSpeed(inputStandardWindSpeed)
nextStep(inputCompasDeg)
setManagementState({
...managementState,
installHeight: inputInstallHeight,
verticalSnowCover: inputVerticalSnowCover,
standardWindSpeedId: inputStandardWindSpeed?.clCode,
surfaceType: inputRoughness.clCodeNm,
surfaceTypeValue: inputRoughness.clCode,
})
setModuleSelectionData({
...moduleSelectionData,
module: {
...selectedModules,
},
common,
})
orientationTrigger({
compasDeg: inputCompasDeg,
common: common,
module: {
...selectedModules,
},
margin: inputMargin,
})
updateObjectDataApi({
objectNo: currentCanvasPlan.objectNo, //_no
standardWindSpeedId: inputStandardWindSpeed?.clCode, //
verticalSnowCover: inputVerticalSnowCover, //
surfaceType: inputRoughness.clCodeNm, //
installHeight: inputInstallHeight, //
userId: loginUserState.userId, //
})
setTabNum(2)
} else {
if (!selectedModules || !selectedModules.itemId) {
Swal.fire({
title: getMessage('module.not.found'),
icon: 'warning',
})
return
}
}
} }
useEffect(() => {
checkDegree(compasDeg)
}, [compasDeg])
const checkDegree = (e) => { const checkDegree = (e) => {
if (e === '-0' || e === '-') { if (e === '-0' || e === '-') {
setCompasDeg('-') setInputCompasDeg('-')
return return
} }
if (e === '0-') { if (e === '0-') {
setCompasDeg('-0') setInputCompasDeg('-0')
return return
} }
if (Number(e) >= -180 && Number(e) <= 180) { if (Number(e) >= -180 && Number(e) <= 180) {
if (numberCheck(Number(e))) { if (numberCheck(Number(e))) {
setCompasDeg(Number(e)) setInputCompasDeg(Number(e))
} }
} else { } else {
setCompasDeg(compasDeg) setInputCompasDeg(compasDeg)
} }
} }
const isComplete = () => {
if (!selectedModules || !selectedModules.itemId) return false
if (basicSetting && basicSetting.roofSizeSet !== '3') {
if (inputInstallHeight <= 0) {
return false
}
if (+inputVerticalSnowCover <= 0) {
return false
}
if (!inputStandardWindSpeed) return false
if (!inputRoughness) return false
}
return true
}
const handleChangeModule = (e) => {
resetRoofs()
setSelectedModules(e)
}
const handleChangeRoughness = (e) => {
resetRoofs()
setInputRoughness(e)
}
const handleChangeInstallHeight = (e) => {
resetRoofs()
setInputInstallHeight(e)
}
const handleChangeStandardWindSpeed = (e) => {
resetRoofs()
setInputStandardWindSpeed(e)
}
const handleChangeVerticalSnowCover = (e) => {
resetRoofs()
setInputVerticalSnowCover(e)
}
const resetRoofs = () => {
const newRoofs = addedRoofs.map((roof) => {
return {
...roof,
trestle: {
lengthBase: null,
trestleMkrCd: null,
constMthdCd: null,
constTp: null,
roofBaseCd: null,
roofPchBase: null,
},
addRoof: {
...roof.addRoof,
lengthBase: null,
eavesMargin: null,
kerabaMargin: null,
ridgeMargin: null,
},
construction: {
constTp: null,
cvrYn: 'N',
snowGdPossYn: 'N',
cvrChecked: false,
snowGdChecked: false,
},
}
})
// setRoofs(newRoofs)
// setAddedRoofs(newRoofs)
setRoofsStore(newRoofs)
}
return ( return (
<> <>
<div className="properties-setting-wrap"> <div className="properties-setting-wrap">
<div className="outline-wrap"> <div className="outline-wrap">
<div className="guide">{getMessage('modal.module.basic.setting.orientation.setting.info')}</div> <div className="roof-module-inner">
<div className="roof-module-compas"> <div className="compas-wrapper">
<div className="compas-box"> <div className="guide">{getMessage('modal.module.basic.setting.orientation.setting.info')}</div>
<div className="compas-box-inner"> <div className="roof-module-compas">
{Array.from({ length: 180 / 15 }).map((dot, index) => ( <div className="compas-box">
<div <div className="compas-box-inner">
key={index} {Array.from({ length: 180 / 15 }).map((dot, index) => (
className={`circle ${getDegreeInOrientation(compasDeg) === -1 * (-15 * index + 180) || (index === 0 && compasDeg >= 172 && index === 0 && compasDeg <= 180) || (compasDeg === -180 && index === 0) ? 'act' : ''}`} <div
onClick={() => { key={index}
if (index === 0) { className={`circle ${getDegreeInOrientation(inputCompasDeg) === -1 * (-15 * index + 180) || (index === 0 && inputCompasDeg >= 172 && index === 0 && inputCompasDeg <= 180) || (inputCompasDeg === -180 && index === 0) ? 'act' : ''}`}
setCompasDeg(180) onClick={() => {
return if (index === 0) {
} setInputCompasDeg(180)
setCompasDeg(-1 * (-15 * index + 180)) return
}} }
> setInputCompasDeg(-1 * (-15 * index + 180))
{index === 0 && <i>180°</i>} }}
{index === 6 && <i>-90°</i>} >
{index === 0 && <i>180°</i>}
{index === 6 && <i>-90°</i>}
</div>
))}
{Array.from({ length: 180 / 15 }).map((dot, index) => (
<div
key={index}
className={`circle ${inputCompasDeg !== 180 && getDegreeInOrientation(inputCompasDeg) === 15 * index ? 'act' : ''}`}
onClick={() => setInputCompasDeg(15 * index)}
>
{index === 0 && <i>0°</i>}
{index === 6 && <i>90°</i>}
</div>
))}
<div className="compas">
<div className="compas-arr" style={{ transform: `rotate(${getDegreeInOrientation(inputCompasDeg)}deg)` }}></div>
</div>
</div> </div>
))} </div>
{Array.from({ length: 180 / 15 }).map((dot, index) => ( </div>
<div <div className="center-wrap">
key={index} <div className="outline-form">
className={`circle ${compasDeg !== 180 && getDegreeInOrientation(compasDeg) === 15 * index ? 'act' : ''}`} <div className="d-check-box pop mr10">
onClick={() => setCompasDeg(15 * index)} <input type="checkbox" id="ch99" checked={hasAnglePassivity} onChange={() => setHasAnglePassivity(!hasAnglePassivity)} />
> <label htmlFor="ch99">{getMessage('modal.module.basic.setting.orientation.setting.angle.passivity')}</label>
{index === 0 && <i>0°</i>}
{index === 6 && <i>90°</i>}
</div> </div>
))} <div className="input-grid mr10" style={{ width: '60px' }}>
<div className="compas"> <input
<div className="compas-arr" style={{ transform: `rotate(${getDegreeInOrientation(compasDeg)}deg)` }}></div> type="text"
className="input-origin block"
value={inputCompasDeg}
readOnly={!hasAnglePassivity}
placeholder={0}
onChange={(e) => checkDegree(e.target.value)}
/>
</div>
<span className="thin">°</span>
<span className="thin"> -180 180 </span>
</div> </div>
</div> </div>
</div> </div>
</div> <div className="compas-table-wrap">
<div className="center-wrap"> <div className="compas-table-box mb10">
<div className="d-check-box pop"> <div className="outline-form mb10">
<input type="checkbox" id="ch99" checked={hasAnglePassivity} onChange={() => setHasAnglePassivity(!hasAnglePassivity)} /> <span>{getMessage('modal.module.basic.setting.module.setting')}</span>
<label htmlFor="ch99">{getMessage('modal.module.basic.setting.orientation.setting.angle.passivity')}-180 180</label> <div className="grid-select">
</div> {moduleList && (
<div className="outline-form"> <QSelectBox
<div className="input-grid mr10" style={{ width: '160px' }}> options={moduleList}
<input value={selectedModules}
type="text" targetKey={'itemId'}
className="input-origin block" sourceKey={'itemId'}
value={compasDeg} showKey={'itemNm'}
readOnly={!hasAnglePassivity} onChange={(e) => handleChangeModule(e)}
placeholder={0} />
onChange={ )}
(e) => checkDegree(e.target.value) </div>
// setCompasDeg( </div>
<div className="roof-module-table">
// e.target.value === '-' || (e.target.value !== '' && parseInt(e.target.value) <= 180 && parseInt(e.target.value) >= -180) <table>
// ? e.target.value <thead>
// : 0, <tr>
// ) {moduleData.header.map((header) => {
} return (
/> <th key={header.prop} style={{ width: header.width ? header.width + 'px' : '' }}>
{header.name}
</th>
)
})}
</tr>
</thead>
<tbody>
{Array.from({ length: 3 }).map((_, index) => {
return selectedModules && selectedModules?.itemList && selectedModules?.itemList?.length >= index + 1 ? (
<tr key={index}>
<td>
<div className="color-wrap">
<span
className="color-box"
style={{
backgroundColor: selectedModules.itemList[index].color,
}}
></span>
<span className="name">{selectedModules.itemList[index].itemNm}</span>
</div>
</td>
<td className="al-r">{Number(selectedModules.itemList[index].shortAxis).toFixed(0)}</td>
<td className="al-r">{Number(selectedModules.itemList[index].longAxis).toFixed(0)}</td>
<td className="al-r">{Number(selectedModules.itemList[index].wpOut).toFixed(0)}</td>
</tr>
) : (
<tr key={index}>
<td>
<div className="color-wrap"></div>
</td>
<td className="al-r"></td>
<td className="al-r"></td>
<td className="al-r"></td>
</tr>
)
})}
</tbody>
</table>
</div>
{basicSetting && basicSetting.roofSizeSet == '3' && (
<div className="outline-form mt15">
<span>{getMessage('modal.module.basic.setting.module.placement.area')}</span>
<div className="input-grid mr10" style={{ width: '60px' }}>
<input type="number" className="input-origin block" value={inputMargin} onChange={(e) => setInputMargin(e.target.value)} />
</div>
<span className="thin">m</span>
</div>
)}
</div> </div>
<span className="thin">°</span>
{basicSetting && basicSetting.roofSizeSet != '3' && (
<div className="compas-table-box">
<div className="compas-grid-table">
<div className="outline-form">
<span>{getMessage('modal.module.basic.setting.module.surface.type')}</span>
<div className="grid-select">
{roughnessCodes.length > 0 && managementState && (
<QSelectBox
options={roughnessCodes}
value={inputRoughness}
targetKey={'clCode'}
sourceKey={'clCode'}
showKey={'clCodeNm'}
onChange={(e) => handleChangeRoughness(e)}
/>
)}
</div>
</div>
<div className="outline-form">
<span>{getMessage('modal.module.basic.setting.module.fitting.height')}</span>
<div className="input-grid mr10">
<input
type="number"
className="input-origin block"
value={inputInstallHeight}
onChange={(e) => handleChangeInstallHeight(e.target.value)}
/>
</div>
<span className="thin">m</span>
</div>
<div className="outline-form">
<span>{getMessage('modal.module.basic.setting.module.standard.wind.speed')}</span>
<div className="grid-select">
{windSpeedCodes.length > 0 && managementState && (
<QSelectBox
title={''}
options={windSpeedCodes}
value={inputStandardWindSpeed}
targetKey={'clCode'}
sourceKey={'clCode'}
showKey={'clCodeNm'}
onChange={(e) => handleChangeStandardWindSpeed(e)}
/>
)}
</div>
</div>
<div className="outline-form">
<span>{getMessage('modal.module.basic.setting.module.standard.snowfall.amount')}</span>
<div className="input-grid mr10">
<input
type="number"
className="input-origin block"
value={inputVerticalSnowCover}
onChange={(e) => handleChangeVerticalSnowCover(e.target.value)}
/>
</div>
<span className="thin">cm</span>
</div>
</div>
</div>
)}
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,29 +1,43 @@
import { forwardRef, useEffect, useState } from 'react' import { forwardRef, useEffect, useState } from 'react'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { useModuleBasicSetting } from '@/hooks/module/useModuleBasicSetting' import { useModuleBasicSetting } from '@/hooks/module/useModuleBasicSetting'
import { checkedModuleState, currentCanvasPlanState, isManualModuleSetupState } from '@/store/canvasAtom' import {
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil' checkedModuleState,
isManualModuleLayoutSetupState,
isManualModuleSetupState,
moduleRowColArrayState,
moduleSetupOptionState,
toggleManualSetupModeState,
} from '@/store/canvasAtom'
import { useRecoilState, useRecoilValue, useResetRecoilState, useSetRecoilState } from 'recoil'
import { moduleSelectionDataState, selectedModuleState } from '@/store/selectedModuleOptions' import { moduleSelectionDataState, selectedModuleState } from '@/store/selectedModuleOptions'
import { isObjectNotEmpty } from '@/util/common-utils' import { isObjectNotEmpty } from '@/util/common-utils'
const Placement = forwardRef((props, refs) => { const Placement = forwardRef((props, refs) => {
const { getMessage } = useMessage() const { getMessage } = useMessage()
const [isChidori, setIsChidori] = useState(false) const [useTab, setUseTab] = useState(true)
const [isChidoriNotAble, setIsChidoriNotAble] = useState(false) const [isChidoriNotAble, setIsChidoriNotAble] = useState(false)
const [setupLocation, setSetupLocation] = useState('eaves')
const [isMaxSetup, setIsMaxSetup] = useState('false')
const [selectedItems, setSelectedItems] = useState({}) const [selectedItems, setSelectedItems] = useState({})
const [selectedModules, setSelectedModules] = useRecoilState(selectedModuleState) const [selectedModules, setSelectedModules] = useRecoilState(selectedModuleState)
const setCheckedModules = useSetRecoilState(checkedModuleState) const setCheckedModules = useSetRecoilState(checkedModuleState)
const moduleSelectionData = useRecoilValue(moduleSelectionDataState) const moduleSelectionData = useRecoilValue(moduleSelectionDataState)
const { makeModuleInitArea } = useModuleBasicSetting(3) const { makeModuleInitArea, roofOutlineColor } = useModuleBasicSetting(3)
const [isMultiModule, setIsMultiModule] = useState(false) const [isMultiModule, setIsMultiModule] = useState(false)
const [isManualModuleSetup, setIsManualModuleSetup] = useRecoilState(isManualModuleSetupState) //
const setIsManualModuleSetup = useSetRecoilState(isManualModuleSetupState)
const setIsManualModuleLayoutSetup = useSetRecoilState(isManualModuleLayoutSetupState)
const setManualSetupMode = useSetRecoilState(toggleManualSetupModeState)
const [moduleSetupOption, setModuleSetupOption] = useRecoilState(moduleSetupOptionState) //
const resetModuleSetupOption = useResetRecoilState(moduleSetupOptionState)
const [colspan, setColspan] = useState(1)
const moduleRowColArray = useRecoilValue(moduleRowColArrayState)
// //
useEffect(() => { useEffect(() => {
@ -36,11 +50,24 @@ const Placement = forwardRef((props, refs) => {
makeModuleInitArea(moduleSelectionData) makeModuleInitArea(moduleSelectionData)
} }
if (moduleSelectionData.module.itemList.length > 1) {
setColspan(2)
}
return () => { return () => {
// refs.isChidori.current = 'false'
// refs.setupLocation.current = 'eaves'
setIsManualModuleSetup(false) setIsManualModuleSetup(false)
setIsManualModuleLayoutSetup(false)
setManualSetupMode('off')
resetModuleSetupOption()
} }
}, []) }, [])
// useEffect(() => {
// console.log('moduleRowColArray', moduleRowColArray)
// }, [moduleRowColArray])
// //
useEffect(() => { useEffect(() => {
if (isObjectNotEmpty(moduleSelectionData)) { if (isObjectNotEmpty(moduleSelectionData)) {
@ -54,8 +81,10 @@ const Placement = forwardRef((props, refs) => {
initCheckedModule = { ...initCheckedModule, [obj.itemId]: true } initCheckedModule = { ...initCheckedModule, [obj.itemId]: true }
} }
}) })
setSelectedItems(initCheckedModule) setSelectedItems(initCheckedModule)
setSelectedModules(moduleSelectionData.module) setSelectedModules(moduleSelectionData.module)
props.setLayoutSetup(moduleSelectionData.module.itemList.map((item) => ({ moduleId: item.itemId, col: 0, row: 0, checked: true })))
} }
// //
@ -80,63 +109,79 @@ const Placement = forwardRef((props, refs) => {
header: [ header: [
{ type: 'check', name: '', prop: 'check', width: 70 }, { type: 'check', name: '', prop: 'check', width: 70 },
{ type: 'color-box', name: getMessage('module'), prop: 'module' }, { type: 'color-box', name: getMessage('module'), prop: 'module' },
{ type: 'text', name: `${getMessage('output')} (W)`, prop: 'output', width: 70 }, { type: 'text', name: getMessage('modal.module.basic.setting.module.placement.mix.asg.yn'), prop: 'mixAsgYn', width: 50 },
{ type: 'text', name: `段数`, prop: 'rows', width: 60 },
{ type: 'text', name: `列数`, prop: 'cols', width: 60 },
], ],
rows: [], rows: [],
} }
const handleChangeChidori = (e) => { const handleChangeChidori = (e) => {
const bool = e.target.value === 'true' ? true : false const bool = e.target.value === 'true' ? true : false
setIsChidori(bool) setModuleSetupOption({ ...moduleSetupOption, isChidori: bool })
refs.isChidori.current = e.target.value
//
setIsManualModuleSetup(false)
setIsManualModuleLayoutSetup(false)
setManualSetupMode('off')
} }
const handleSetupLocation = (e) => { const handleSetupLocation = (e) => {
setSetupLocation(e.target.value) setModuleSetupOption({ ...moduleSetupOption, setupLocation: e.target.value })
refs.setupLocation.current = e.target.value
}
const handleMaxSetup = (e) => { //
if (e.target.checked) { setIsManualModuleSetup(false)
setIsMaxSetup('true') setIsManualModuleLayoutSetup(false)
refs.isMaxSetup.current = 'true' setManualSetupMode('off')
} else {
setIsMaxSetup('false')
refs.isMaxSetup.current = 'false'
}
} }
// //
const handleSelectedItem = (e) => { const handleSelectedItem = (e, itemId) => {
setSelectedItems({ ...selectedItems, [e.target.name]: e.target.checked }) setSelectedItems({ ...selectedItems, [e.target.name]: e.target.checked })
const newLayoutSetup = [...props.layoutSetup]
props.layoutSetup.forEach((item, index) => {
if (item.moduleId === itemId) {
newLayoutSetup[index] = { ...props.layoutSetup[index], checked: e.target.checked }
}
})
props.setLayoutSetup(newLayoutSetup)
}
const handleLayoutSetup = (e, itemId, index) => {
const newLayoutSetup = [...props.layoutSetup]
newLayoutSetup[index] = {
...newLayoutSetup[index],
moduleId: itemId,
[e.target.name]: Number(e.target.value),
}
props.setLayoutSetup(newLayoutSetup)
} }
return ( return (
<> <>
<div className="module-table-flex-wrap mb10"> <div className="module-table-flex-wrap">
<div className="module-table-box"> <div className="module-table-box">
<div className="module-table-inner"> <div className="module-table-inner">
<div className="roof-module-table"> <div className="roof-module-table">
<table> <table>
<thead> <thead>
<tr> {moduleData.header.map((data) => (
{moduleData.header.map((data) => ( <th key={data.prop} style={{ width: data.width ? data.width : '' }}>
<th key={data.prop} style={{ width: data.width ? data.width : '' }}> {data.type === 'check' ? (
{data.type === 'check' ? ( <div className="d-check-box no-text pop">
<div className="d-check-box no-text pop"> <input type="checkbox" id="ch01" disabled />
<input type="checkbox" id="ch01" disabled /> <label htmlFor="ch01"></label>
<label htmlFor="ch01"></label> </div>
</div> ) : (
) : ( data.name
data.name )}
)} </th>
</th> ))}
))}
</tr>
</thead> </thead>
<tbody> <tbody>
{selectedModules.itemList && {selectedModules?.itemList &&
selectedModules.itemList.map((item, index) => ( selectedModules?.itemList?.map((item, index) => (
<tr key={index}> <tr key={index}>
<td className="al-c"> <td className="al-c">
<div className="d-check-box no-text pop"> <div className="d-check-box no-text pop">
@ -145,7 +190,7 @@ const Placement = forwardRef((props, refs) => {
id={item.itemId} id={item.itemId}
name={item.itemId} name={item.itemId}
checked={selectedItems[item.itemId]} checked={selectedItems[item.itemId]}
onChange={handleSelectedItem} onChange={(e) => handleSelectedItem(e, item.itemId)}
/> />
<label htmlFor={item.itemId}></label> <label htmlFor={item.itemId}></label>
</div> </div>
@ -156,93 +201,178 @@ const Placement = forwardRef((props, refs) => {
<span className="name">{item.itemNm}</span> <span className="name">{item.itemNm}</span>
</div> </div>
</td> </td>
<td className="al-r">{item.wpOut}</td> <td className="al-c">
<div className="color-wrap">
<span className="name">{item.mixAsgYn}</span>
</div>
</td>
<td className="al-r">
<div className="input-grid">
<input
type="text"
className="input-origin block"
name="row"
value={props.layoutSetup[index]?.row ?? 1}
defaultValue={0}
onChange={(e) => handleLayoutSetup(e, item.itemId, index)}
/>
</div>
</td>
<td className="al-r">
<div className="input-grid">
<input
type="text"
className="input-origin block"
name="col"
value={props.layoutSetup[index]?.col ?? 1}
defaultValue={0}
onChange={(e) => handleLayoutSetup(e, item.itemId, index)}
/>
</div>
</td>
</tr> </tr>
))} ))}
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody> </tbody>
</table> </table>
</div> </div>
</div> </div>
</div> </div>
<div className="module-table-box"> <div className="module-table-box non-flex">
<div className="module-table-inner"> <div className="module-table-inner">
<div className="self-table-tit">{getMessage('modal.module.basic.setting.module.placement.select.fitting.type')}</div> <div className="roof-module-table">
<div className="module-self-table"> <table>
<div className="self-table-item"> <thead>
<div className="self-item-th">{getMessage('modal.module.basic.setting.module.placement.waterfowl.arrangement')}</div> <tr>
<div className="self-item-td"> <th>{getMessage('modal.module.basic.setting.module.placement.waterfowl.arrangement')}</th>
<div className="pop-form-radio"> <th>{getMessage('modal.module.basic.setting.module.placement.arrangement.standard')}</th>
<div className="d-check-radio pop"> </tr>
<input </thead>
type="radio" <tbody>
name="radio01" <tr>
id="ra01" <td>
checked={isChidori} <div className="hexagonal-radio-wrap">
disabled={isChidoriNotAble} <div className="d-check-radio pop mb10">
value={'true'} <input
onChange={(e) => handleChangeChidori(e)} type="radio"
/> name="radio02"
<label htmlFor="ra01">{getMessage('modal.module.basic.setting.module.placement.do')}</label> id="ra03"
</div> checked={moduleSetupOption.isChidori}
<div className="d-check-radio pop"> disabled={isChidoriNotAble}
<input type="radio" name="radio02" id="ra02" checked={!isChidori} value={'false'} onChange={(e) => handleChangeChidori(e)} /> value={'true'}
<label htmlFor="ra02">{getMessage('modal.module.basic.setting.module.placement.do.not')}</label> onChange={(e) => handleChangeChidori(e)}
</div> />
</div> <label htmlFor="ra03">{getMessage('modal.module.basic.setting.module.placement.do')}</label>
</div> </div>
</div> <div className="d-check-radio pop">
<div className="self-table-item"> <input
<div className="self-item-th">{getMessage('modal.module.basic.setting.module.placement.arrangement.standard')}</div> type="radio"
<div className="self-item-td"> name="radio02"
<div className="pop-form-radio"> id="ra04"
<div className="d-check-radio pop"> checked={!moduleSetupOption.isChidori}
<input value={'false'}
type="radio" onChange={(e) => handleChangeChidori(e)}
name="radio03" />
id="ra03" <label htmlFor="ra04">{getMessage('modal.module.basic.setting.module.placement.do.not')}</label>
checked={setupLocation === 'center'} </div>
value={'center'} </div>
onChange={handleSetupLocation} </td>
disabled={isMultiModule} <td>
/> <div className="hexagonal-radio-wrap">
<label htmlFor="ra03">{getMessage('modal.module.basic.setting.module.placement.arrangement.standard.center')}</label> <div className="d-check-radio pop mb10">
</div> <input
<div className="d-check-radio pop"> type="radio"
<input name="radio03"
type="radio" id="ra05"
name="radio04" checked={moduleSetupOption.setupLocation === 'eaves'}
id="ra04" value={'eaves'}
checked={setupLocation === 'eaves'} onChange={handleSetupLocation}
value={'eaves'} />
onChange={handleSetupLocation} <label htmlFor="ra05">{getMessage('modal.module.basic.setting.module.placement.arrangement.standard.eaves')}</label>
/> </div>
<label htmlFor="ra04">{getMessage('modal.module.basic.setting.module.placement.arrangement.standard.eaves')}</label> <div className="d-check-radio pop">
</div> <input
<div className="d-check-radio pop"> type="radio"
<input name="radio03"
type="radio" id="ra06"
name="radio05" checked={moduleSetupOption.setupLocation === 'ridge'}
id="ra05" value={'ridge'}
checked={setupLocation === 'ridge'} onChange={handleSetupLocation}
value={'ridge'} disabled={isMultiModule}
onChange={handleSetupLocation} />
disabled={isMultiModule} <label htmlFor="ra06">{getMessage('modal.module.basic.setting.module.placement.arrangement.standard.ridge')}</label>
/> </div>
<label htmlFor="ra05">{getMessage('modal.module.basic.setting.module.placement.arrangement.standard.ridge')}</label> </div>
</div> </td>
</div> </tr>
</div> </tbody>
</div> </table>
</div>
<div className="self-table-flx">
{/* <div className="d-check-box pop">
<input type="checkbox" id="ch04" checked={isMaxSetup === 'true'} value={'true'} onChange={handleMaxSetup} />
<label htmlFor="ch04">{getMessage('modal.module.basic.setting.module.placement.maximum')}</label>
</div> */}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div className="hide-check-guide">
{getMessage('modal.module.basic.setting.module.placement.max.size.check')}
<button className={`arr ${!useTab ? 'act' : ''}`} onClick={() => setUseTab(!useTab)}></button>
</div>
<div className={`module-table-box mt10 ${useTab ? 'hide' : ''}`}>
<div className="module-table-inner">
<div className="roof-module-table">
<table className="">
<thead>
<tr>
<th rowSpan={2} style={{ width: '22%' }}></th>
{selectedModules &&
selectedModules.itemList?.map((item) => (
// <th colSpan={colspan}>
<th>
<div className="color-wrap">
<span className="color-box" style={{ backgroundColor: item.color }}></span>
<span className="name">{item.itemNm}</span>
</div>
</th>
))}
{colspan > 1 && <th rowSpan={2}>{getMessage('modal.module.basic.setting.module.placement.max.rows.multiple')}</th>}
</tr>
<tr>
{selectedModules &&
selectedModules.itemList?.map((item) => (
<>
<th>{getMessage('modal.module.basic.setting.module.placement.max.row')}</th>
{/* {colspan > 1 && <th>{getMessage('modal.module.basic.setting.module.placement.max.rows.multiple')}</th>} */}
</>
))}
</tr>
</thead>
<tbody>
{moduleSelectionData.roofConstructions.map((item, index) => (
<tr>
<td>
<div className="color-wrap">
<span className="color-box" style={{ backgroundColor: roofOutlineColor(item.addRoof?.index) }}></span>
<span className="name">{item.addRoof?.roofMatlNmJp}</span>
</div>
</td>
{moduleRowColArray[index]?.map((item, index2) => (
<>
<td className="al-c">{item.moduleMaxRows}</td>
{/* {colspan > 1 && <td className="al-c">{item.mixModuleMaxRows}</td>} */}
{colspan > 1 && index2 === moduleRowColArray[index].length - 1 && <td className="al-c">{item.maxRow}</td>}
</>
))}
</tr>
))}
</tbody>
</table>
</div>
</div>
</div>
</> </>
) )
}) })

View File

@ -0,0 +1,769 @@
import { GlobalDataContext } from '@/app/GlobalDataProvider'
import QSelectBox from '@/components/common/select/QSelectBox'
import { useModuleBasicSetting } from '@/hooks/module/useModuleBasicSetting'
import { useModuleTrestle } from '@/hooks/module/useModuleTrestle'
import { useMessage } from '@/hooks/useMessage'
import { currentAngleTypeSelector, pitchTextSelector } from '@/store/canvasAtom'
import { roofsState } from '@/store/roofAtom'
import { moduleSelectionDataState, selectedModuleState } from '@/store/selectedModuleOptions'
import { forwardRef, useContext, useEffect, useImperativeHandle, useRef, useState } from 'react'
import { useRecoilState, useRecoilValue } from 'recoil'
import Swal from 'sweetalert2'
const Trestle = forwardRef((props, ref) => {
const { tabNum, setTabNum, trestleTrigger, roofs, setRoofs, moduleSelectionData, setModuleSelectionData, setRoofsStore } = props
const { getMessage } = useMessage()
// const [selectedTrestle, setSelectedTrestle] = useState()
const currentAngleType = useRecoilValue(currentAngleTypeSelector)
const pitchText = useRecoilValue(pitchTextSelector)
const [selectedRoof, setSelectedRoof] = useState(null)
const {
trestleState,
trestleDetail,
dispatch,
raftBaseList,
trestleList,
constMthdList,
roofBaseList,
constructionList,
eavesMargin,
ridgeMargin,
kerabaMargin,
setEavesMargin,
setRidgeMargin,
setKerabaMargin,
lengthBase,
setLengthBase,
hajebichi,
setHajebichi,
cvrYn,
cvrChecked,
snowGdPossYn,
snowGdChecked,
setCvrYn,
setCvrChecked,
setSnowGdPossYn,
setSnowGdChecked,
} = useModuleTrestle({
selectedRoof,
})
const selectedModules = useRecoilValue(selectedModuleState) //
// const [moduleSelectionData, setModuleSelectionData] = useRecoilState(moduleSelectionDataState)
const [selectedRaftBase, setSelectedRaftBase] = useState(null)
const [selectedTrestle, setSelectedTrestle] = useState(null)
const [selectedConstMthd, setSelectedConstMthd] = useState(null)
const [selectedConstruction, setSelectedConstruction] = useState(null)
const [selectedRoofBase, setSelectedRoofBase] = useState(null)
const { managementState } = useContext(GlobalDataContext)
const { restoreModuleInstArea } = useModuleBasicSetting()
const [flag, setFlag] = useState(false)
const tempModuleSelectionData = useRef(null)
useEffect(() => {
if (roofs && !selectedRoof) {
setSelectedRoof(roofs[0])
}
//
restoreModuleInstArea()
}, [roofs])
useEffect(() => {
if (flag && moduleSelectionData) {
if (JSON.stringify(tempModuleSelectionData.current) === JSON.stringify(moduleSelectionData)) {
setTabNum(tabNum + 1)
}
}
}, [flag, moduleSelectionData])
useEffect(() => {
if (selectedRoof) {
if (moduleSelectionData?.roofConstructions?.length >= selectedRoof.index + 1) {
const { construction, trestle, trestleDetail } = moduleSelectionData?.roofConstructions[selectedRoof.index]
dispatch({
type: 'SET_INITIALIZE',
roof: { common: moduleSelectionData.common, module: moduleSelectionData.module, construction, trestle, trestleDetail, ...selectedRoof },
})
} else {
dispatch({ type: 'SET_INITIALIZE', roof: { ...selectedRoof, common: moduleSelectionData.common, module: moduleSelectionData.module } })
}
}
}, [selectedRoof])
useEffect(() => {
if (raftBaseList.length > 0) {
setSelectedRaftBase(raftBaseList.find((raft) => raft.clCode === selectedRoof?.raft) ?? null)
} else {
setSelectedRaftBase(null)
}
}, [raftBaseList])
useEffect(() => {
if (trestleList.length > 0) {
setSelectedTrestle(trestleList.find((trestle) => trestle.trestleMkrCd === trestleState?.trestleMkrCd) ?? null)
} else {
setSelectedTrestle(null)
}
}, [trestleList])
useEffect(() => {
if (roofBaseList.length > 0) {
setSelectedRoofBase(roofBaseList.find((roofBase) => roofBase.roofBaseCd === trestleState?.roofBaseCd) ?? null)
} else {
setSelectedRoofBase(null)
}
}, [roofBaseList])
useEffect(() => {
if (constMthdList.length > 0) {
setSelectedConstMthd(constMthdList.find((constMthd) => constMthd.constMthdCd === trestleState?.constMthdCd) ?? null)
} else {
setSelectedConstMthd(null)
}
}, [constMthdList])
useEffect(() => {
if (constructionList.length > 0) {
setSelectedConstruction(constructionList.find((construction) => construction.constTp === trestleState?.construction?.constTp) ?? null)
if (constructionList.filter((construction) => construction.constPossYn === 'Y').length === 0) {
Swal.fire({
title: getMessage('modal.module.basic.settting.module.error4', [selectedRoof?.nameJp]), // .
icon: 'warning',
})
}
} else {
setSelectedConstruction(null)
}
}, [constructionList])
const getConstructionState = (index) => {
if (constructionList && constructionList.length > 0) {
if (constructionList[index].constPossYn === 'Y') {
if (trestleState && trestleState.constTp === constructionList[index].constTp) {
return 'blue'
}
return 'white'
}
return 'no-click'
}
return 'no-click'
}
const onChangeLength = (e) => {
setLengthBase(e)
dispatch({
type: 'SET_LENGTH',
roof: {
length: e,
moduleTpCd: selectedModules.itemTp ?? '',
roofMatlCd: selectedRoof?.roofMatlCd ?? '',
raft: selectedRaftBase?.clCode,
},
})
}
const onChangeRaftBase = (e) => {
setSelectedRaftBase(e)
dispatch({
type: 'SET_RAFT_BASE',
roof: {
moduleTpCd: selectedModules.itemTp ?? '',
roofMatlCd: selectedRoof?.roofMatlCd ?? '',
raft: e.clCode,
},
})
}
const onChangeTrestleMaker = (e) => {
setSelectedTrestle(e)
dispatch({
type: 'SET_TRESTLE_MAKER',
roof: {
moduleTpCd: selectedModules.itemTp ?? '',
roofMatlCd: selectedRoof?.roofMatlCd ?? '',
raft: selectedRaftBase?.clCode,
trestleMkrCd: e.trestleMkrCd,
},
})
}
const onChangeConstMthd = (e) => {
setSelectedConstMthd(e)
dispatch({
type: 'SET_CONST_MTHD',
roof: {
moduleTpCd: selectedModules.itemTp ?? '',
roofMatlCd: selectedRoof?.roofMatlCd ?? '',
raft: selectedRaftBase?.clCode,
trestleMkrCd: selectedTrestle.trestleMkrCd,
constMthdCd: e.constMthdCd,
},
})
}
const onChangeRoofBase = (e) => {
setSelectedRoofBase(e)
dispatch({
type: 'SET_ROOF_BASE',
roof: {
moduleTpCd: selectedModules.itemTp ?? '',
roofMatlCd: selectedRoof?.roofMatlCd ?? '',
raft: selectedRaftBase?.clCode,
trestleMkrCd: selectedTrestle.trestleMkrCd,
constMthdCd: selectedConstMthd.constMthdCd,
roofBaseCd: e.roofBaseCd,
illuminationTp: managementState?.surfaceTypeValue ?? '',
instHt: managementState?.installHeight ?? '',
stdWindSpeed: managementState?.standardWindSpeedId ?? '',
stdSnowLd: managementState?.verticalSnowCover ?? '',
inclCd: selectedRoof?.pitch ?? 0,
roofPitch: Math.round(selectedRoof?.roofPchBase ?? 0),
},
})
}
const handleConstruction = (index) => {
if (constructionList[index]?.constPossYn === 'Y') {
dispatch({
type: 'SET_CONSTRUCTION',
roof: {
moduleTpCd: selectedModules.itemTp ?? '',
roofMatlCd: selectedRoof?.roofMatlCd ?? '',
raft: selectedRaftBase?.clCode,
trestleMkrCd: selectedTrestle.trestleMkrCd,
constMthdCd: selectedConstMthd.constMthdCd,
roofBaseCd: selectedRoofBase.roofBaseCd,
illuminationTp: managementState?.surfaceTypeValue ?? '',
instHt: managementState?.installHeight ?? '',
stdWindSpeed: managementState?.standardWindSpeedId ?? '',
stdSnowLd: +managementState?.verticalSnowCover ?? '',
inclCd: selectedRoof?.pitch ?? 0,
roofPitch: Math.round(selectedRoof?.roofPchBase ?? 0),
constTp: constructionList[index].constTp,
snowGdPossYn: constructionList[index].snowGdPossYn,
cvrYn: constructionList[index].cvrYn,
mixMatlNo: selectedModules.mixMatlNo,
// workingWidth: selectedRoof?.length?.toString() ?? '',
workingWidth: lengthBase,
},
})
setCvrYn(constructionList[index].cvrYn)
setSnowGdPossYn(constructionList[index].snowGdPossYn)
setCvrChecked(false)
setSnowGdChecked(false)
}
}
const handleChangeRoofMaterial = (index) => {
const newAddedRoofs = roofs.map((roof, i) => {
if (i === selectedRoof.index) {
return {
...selectedRoof,
hajebichi,
length: lengthBase,
eavesMargin,
ridgeMargin,
kerabaMargin,
roofIndex: selectedRoof.index,
raft: selectedRaftBase?.clCode,
trestle: {
hajebichi: hajebichi,
length: lengthBase,
...selectedRaftBase,
...selectedTrestle,
...selectedConstMthd,
...selectedRoofBase,
},
construction: {
...constructionList.find((data) => data.constTp === trestleState.constTp),
cvrYn: cvrYn,
snowGdPossYn: snowGdPossYn,
cvrChecked: cvrChecked,
snowGdChecked: snowGdChecked,
setupCover: cvrChecked ?? false,
setupSnowCover: snowGdChecked ?? false,
},
trestleDetail: trestleDetail,
}
}
return roof
})
setRoofs(newAddedRoofs)
setSelectedRoof(newAddedRoofs[index])
}
const isComplete = async () => {
const newAddedRoofs = roofs.map((roof, i) => {
if (i === selectedRoof?.index) {
return {
...selectedRoof,
length: lengthBase,
eavesMargin,
ridgeMargin,
kerabaMargin,
roofIndex: roof.index,
raft: selectedRaftBase?.clCode,
trestle: {
length: lengthBase,
hajebichi: hajebichi,
...selectedRaftBase,
...selectedTrestle,
...selectedConstMthd,
...selectedRoofBase,
},
construction: {
...constructionList.find((data) => data.constTp === trestleState.constTp),
cvrYn,
snowGdPossYn,
cvrChecked,
snowGdChecked,
setupCover: cvrChecked ?? false,
setupSnowCover: snowGdChecked ?? false,
},
trestleDetail: trestleDetail,
}
}
return roof
})
let result = true
for (let i = 0; i < newAddedRoofs.length; i++) {
const roof = newAddedRoofs[i]
if (!roof.trestle?.trestleMkrCd) {
Swal.fire({
title: getMessage('modal.module.basic.settting.module.error1', [roof.nameJp]), // .
icon: 'warning',
})
result = false
return false
}
if (!roof.trestle?.constMthdCd) {
Swal.fire({
title: getMessage('modal.module.basic.settting.module.error2', [roof.nameJp]), // .
icon: 'warning',
})
result = false
return false
}
if (!roof.trestle?.roofBaseCd) {
Swal.fire({
title: getMessage('modal.module.basic.settting.module.error3', [roof.nameJp]), // .
icon: 'warning',
})
result = false
return false
}
if (!roof.construction?.constTp) {
Swal.fire({
title: getMessage('modal.module.basic.settting.module.error12', [roof.nameJp]), // .
icon: 'warning',
})
result = false
return false
}
if (roof.lenAuth === 'C') {
if (!roof.trestle?.length) {
Swal.fire({
title: getMessage('modal.module.basic.settting.module.error5', [roof.nameJp]), // L .
icon: 'warning',
})
result = false
return false
}
}
if (['C', 'R'].includes(roof.raftAuth)) {
if (!roof?.raft) {
Swal.fire({
title: getMessage('modal.module.basic.settting.module.error6', [roof.nameJp]), // .
icon: 'warning',
})
result = false
return false
}
}
if (['C', 'R'].includes(roof.roofPchAuth)) {
if (!roof?.roofPchBase) {
Swal.fire({
title: getMessage('modal.module.basic.settting.module.error7', [roof.nameJp]), // .
icon: 'warning',
})
result = false
return false
}
}
if (!roof?.eavesMargin || !roof?.ridgeMargin || !roof?.kerabaMargin) {
Swal.fire({
title: getMessage('modal.module.basic.settting.module.error8', [roof.nameJp]), // .
icon: 'warning',
})
result = false
return false
}
if (roof.trestle.trestleMkrCd !== 'NO_DATA') {
//
if (roof.trestleDetail?.eaveIntvl > roof.eavesMargin) {
Swal.fire({
title: getMessage('modal.module.basic.settting.module.error9', [roof.trestleDetail?.eaveIntvl, roof.nameJp]), // {0}mm .
icon: 'warning',
})
result = false
return false
}
if (roof.trestleDetail?.ridgeIntvl > roof.ridgeMargin) {
Swal.fire({
title: getMessage('modal.module.basic.settting.module.error10', [roof.trestleDetail?.ridgeIntvl, roof.nameJp]), // {0}mm .
icon: 'warning',
})
result = false
return false
}
if (roof.trestleDetail?.kerabaIntvl > roof.kerabaMargin) {
Swal.fire({
title: getMessage('modal.module.basic.settting.module.error11', [roof.trestleDetail?.kerabaIntvl, roof.nameJp]), // {0}mm .
icon: 'warning',
})
result = false
return false
}
}
}
if (result) {
const newRoofs = newAddedRoofs.map((roof) => {
const { addRoof, construction, trestle, trestleDetail, roofConstructions, ...rest } = roof
return rest
})
setModuleSelectionData({
...moduleSelectionData,
roofConstructions: newAddedRoofs.map((roof, index) => ({
roofIndex: newRoofs[index].index,
trestle: roof.trestle,
addRoof: newRoofs[index],
construction: roof.construction,
trestleDetail: roof.trestleDetail,
})),
})
setFlag(true)
tempModuleSelectionData.current = {
...moduleSelectionData,
roofConstructions: newAddedRoofs.map((roof, index) => ({
roofIndex: newRoofs[index].index,
trestle: roof.trestle,
addRoof: newRoofs[index],
construction: roof.construction,
trestleDetail: roof.trestleDetail,
})),
}
const updatePromises = [
// new Promise((resolve) => {
// resolve()
// }),
new Promise((resolve) => {
setRoofs(newRoofs)
resolve()
}),
new Promise((resolve) => {
const roofConstructions = newAddedRoofs.map((roof, index) => ({
roofIndex: newRoofs[index].index,
addRoof: newRoofs[index],
trestle: {
...roof.trestle,
raft: roof.raftBaseCd,
},
construction: {
// ...constructionList.find((construction) => newAddedRoofs[index].construction.constTp === construction.constTp),
...roof.construction,
roofIndex: roof.index,
selectedIndex: roof.index,
},
trestleDetail: roof.trestleDetail,
}))
trestleTrigger({
roofConstructions,
})
setRoofsStore(roofConstructions)
resolve()
}),
]
await Promise.all(updatePromises)
return true
}
return false
}
useImperativeHandle(ref, () => ({
isComplete,
}))
return (
<div className="roof-module-tab2-overflow">
<div className="module-table-box mb10">
<div className="module-box-tab">
{roofs &&
roofs.map((roof, index) => (
<button
key={index}
className={`module-btn ${selectedRoof?.index === index ? 'act' : ''}`}
onClick={() => (roof ? handleChangeRoofMaterial(index) : null)}
>
{roof !== undefined ? `${roof.nameJp} (${currentAngleType === 'slope' ? roof.pitch : roof.angle}${pitchText})` : '-'}
</button>
))}
</div>
<div className="module-table-inner">
<div className="module-table-flex-wrap tab2">
<div className="module-flex-item">
<div className="eaves-keraba-table">
{selectedRoof && selectedRoof.lenAuth === 'C' && (
<>
<div className="eaves-keraba-item">
<div className="eaves-keraba-th">L</div>
<div className="eaves-keraba-td">
<div className="grid-select">
<input
type="text"
className="input-origin block"
value={lengthBase}
onChange={(e) => onChangeLength(e.target.value)}
disabled={selectedRoof.lenAuth === 'R'}
/>
</div>
</div>
</div>
</>
)}
{selectedRoof && ['C', 'R'].includes(selectedRoof.raftAuth) && (
<>
<div className="eaves-keraba-item">
<div className="eaves-keraba-th">{getMessage('modal.module.basic.setting.module.rafter.margin')}</div>
<div className="eaves-keraba-td">
<div className="grid-select">
{raftBaseList.length > 0 && (
<QSelectBox
options={raftBaseList}
value={selectedRaftBase}
sourceKey={'clCode'}
targetKey={'clCode'}
showKey={'clCodeNm'}
disabled={selectedRoof.raftAuth === 'R'}
onChange={(e) => onChangeRaftBase(e)}
/>
)}
</div>
</div>
</div>
</>
)}
{selectedRoof && ['C', 'R'].includes(selectedRoof.roofPchAuth) && (
<>
<div className="eaves-keraba-item">
<div className="eaves-keraba-th">{getMessage('modal.module.basic.setting.module.hajebichi')}</div>
<div className="eaves-keraba-td">
<div className="grid-select">
<input
type="text"
className="input-origin block"
disabled={selectedRoof.roofPchAuth === 'R'}
onChange={(e) => setHajebichi(e.target.value)}
value={hajebichi}
/>
</div>
</div>
</div>
</>
)}
<div className="eaves-keraba-item">
<div className="eaves-keraba-th">{getMessage('modal.module.basic.setting.module.trestle.maker')}</div>
<div className="eaves-keraba-td">
<div className="grid-select">
{trestleList && (
<QSelectBox
title={getMessage('selectbox.title')}
options={trestleList}
value={selectedTrestle}
sourceKey={'trestleMkrCd'}
targetKey={'trestleMkrCd'}
showKey={'trestleMkrCdJp'}
onChange={(e) => onChangeTrestleMaker(e)}
/>
)}
</div>
</div>
</div>
<div className="eaves-keraba-item">
<div className="eaves-keraba-th">{getMessage('modal.module.basic.setting.module.construction.method')}</div>
<div className="eaves-keraba-td">
<div className="grid-select">
{constMthdList && (
<QSelectBox
title={getMessage('selectbox.title')}
options={constMthdList}
value={selectedConstMthd}
sourceKey={'constMthdCd'}
targetKey={'constMthdCd'}
showKey={'constMthdCdJp'}
onChange={(e) => onChangeConstMthd(e)}
/>
)}
</div>
</div>
</div>
<div className="eaves-keraba-item">
<div className="eaves-keraba-th">{getMessage('modal.module.basic.setting.module.under.roof')}</div>
<div className="eaves-keraba-td">
<div className="grid-select">
{roofBaseList && (
<QSelectBox
title={getMessage('selectbox.title')}
options={roofBaseList}
sourceKey={'roofBaseCd'}
targetKey={'roofBaseCd'}
showKey={'roofBaseCdJp'}
value={selectedRoofBase}
onChange={(e) => onChangeRoofBase(e)}
/>
)}
</div>
</div>
</div>
</div>
</div>
<div className="module-flex-item non-flex">
<div className="flex-item-btn-wrap">
<button className={`btn-frame roof ${getConstructionState(0)}`} onClick={() => handleConstruction(0)}>
{getMessage('modal.module.basic.setting.module.standard.construction')}(I)
</button>
<button className={`btn-frame roof ${getConstructionState(3)}`} onClick={() => handleConstruction(3)}>
{getMessage('modal.module.basic.setting.module.multiple.construction')}
</button>
<button className={`btn-frame roof ${getConstructionState(1)}`} onClick={() => handleConstruction(1)}>
{getMessage('modal.module.basic.setting.module.standard.construction')}
</button>
<button className={`btn-frame roof ${getConstructionState(4)}`} onClick={() => handleConstruction(4)}>
{getMessage('modal.module.basic.setting.module.multiple.construction')}(II)
</button>
<button className={`btn-frame roof ${getConstructionState(2)}`} onClick={() => handleConstruction(2)}>
{getMessage('modal.module.basic.setting.module.enforce.construction')}
</button>
</div>
<div className="grid-check-form-flex">
<div className="d-check-box pop">
<input
type="checkbox"
id={`ch01`}
disabled={!cvrYn || cvrYn === 'N'}
checked={cvrChecked || false}
// onChange={() => dispatch({ type: 'SET_TRESTLE_DETAIL', roof: { ...trestleState, cvrChecked: !trestleState.cvrChecked } })}
onChange={() => setCvrChecked(!cvrChecked)}
/>
<label htmlFor={`ch01`}>{getMessage('modal.module.basic.setting.module.eaves.bar.fitting')}</label>
</div>
<div className="d-check-box pop">
<input
type="checkbox"
id={`ch02`}
disabled={!snowGdPossYn || snowGdPossYn === 'N'}
checked={snowGdChecked || false}
// onChange={() => dispatch({ type: 'SET_TRESTLE_DETAIL', roof: { ...trestleState, snowGdChecked: !trestleState.snowGdChecked } })}
onChange={() => setSnowGdChecked(!snowGdChecked)}
/>
<label htmlFor={`ch02`}>{getMessage('modal.module.basic.setting.module.blind.metal.fitting')}</label>
</div>
</div>
</div>
</div>
<div className="module-input-area">
<div className="module-area-title">{getMessage('modal.module.basic.setting.module.placement.area')}</div>
<div className="module-input-wrap">
<div className="outline-form mr15">
<span>{getMessage('modal.module.basic.setting.module.placement.area.eaves')}</span>
<div className="input-grid mr10">
<input
type="number"
className="input-origin block"
value={eavesMargin ?? 0}
// onChange={(e) => dispatch({ type: 'SET_TRESTLE_DETAIL', roof: { ...trestleState, eavesMargin: e.target.value } })}
onChange={(e) => setEavesMargin(+e.target.value)}
/>
</div>
<span className="thin">mm</span>
</div>
<div className="outline-form mr15">
<span>{getMessage('modal.module.basic.setting.module.placement.area.ridge')}</span>
<div className="input-grid mr10">
<input
type="number"
className="input-origin block"
value={ridgeMargin ?? 0}
// onChange={(e) => dispatch({ type: 'SET_TRESTLE_DETAIL', roof: { ...trestleState, ridgeMargin: e.target.value } })}
onChange={(e) => setRidgeMargin(+e.target.value)}
/>
</div>
<span className="thin">mm</span>
</div>
<div className="outline-form ">
<span>{getMessage('modal.module.basic.setting.module.placement.area.keraba')}</span>
<div className="input-grid mr10">
<input
type="number"
className="input-origin block"
value={kerabaMargin ?? 0}
// onChange={(e) => dispatch({ type: 'SET_TRESTLE_DETAIL', roof: { ...trestleState, kerabaMargin: e.target.value } })}
onChange={(e) => setKerabaMargin(+e.target.value)}
/>
</div>
<span className="thin">mm</span>
</div>
</div>
</div>
<div className="module-input-area">
<div className="module-area-title">{getMessage('modal.module.basic.setting.module.placement.margin')}</div>
<div className="module-input-wrap">
<div className="outline-form">
<span>{getMessage('modal.module.basic.setting.module.placement.margin.horizontal')}</span>
<div className="input-grid mr10">
<input type="text" className="input-origin block" defaultValue={trestleDetail?.moduleIntvlHor} readOnly />
</div>
<span className="thin">mm</span>
</div>
<div className="outline-form">
<span>{getMessage('modal.module.basic.setting.module.placement.margin.vertical')}</span>
<div className="input-grid mr10">
<input type="text" className="input-origin block" defaultValue={trestleDetail?.moduleIntvlVer} readOnly />
</div>
<span className="thin">mm</span>
</div>
</div>
</div>
</div>
</div>
<div className="module-bottom">
<div className="module-table-box ">
<div className="warning-guide">
<div className="warning">
{getMessage('modal.module.basic.setting.module.setting.info1')}
<br />
{getMessage('modal.module.basic.setting.module.setting.info2')}
</div>
</div>
</div>
</div>
</div>
)
})
export default Trestle

View File

@ -33,14 +33,6 @@ const PitchPlacement = forwardRef((props, refs) => {
setSelectedItems({ ...selectedItems, [e.target.name]: e.target.checked }) setSelectedItems({ ...selectedItems, [e.target.name]: e.target.checked })
} }
const moduleData = {
header: [
{ type: 'check', name: '', prop: 'check', width: 70 },
{ type: 'color-box', name: getMessage('module'), prop: 'module' },
{ type: 'text', name: `${getMessage('output')} (W)`, prop: 'output', width: 70 },
],
}
// //
useEffect(() => { useEffect(() => {
const checkedModuleIds = Object.keys(selectedItems).filter((key) => selectedItems[key]) const checkedModuleIds = Object.keys(selectedItems).filter((key) => selectedItems[key])
@ -105,87 +97,91 @@ const PitchPlacement = forwardRef((props, refs) => {
return ( return (
<> <>
<div className="module-table-box mb10"> <div className="hexagonal-flex-wrap">
<div className="module-table-inner"> <div className="module-table-box ">
<div className="roof-module-table"> <div className="module-table-inner">
<table> <div className="roof-module-table">
<thead> <table>
<tr> <thead>
{moduleData.header.map((data) => ( <tr>
<th key={data.prop} style={{ width: data.width ? data.width : '' }}> <th style={{ width: '70px' }}>
{data.type === 'check' ? ( <div className="d-check-box no-text pop">
<div className="d-check-box no-text pop"> <input type="checkbox" id="ch01" disabled />
<input type="checkbox" id="ch01" disabled /> <label htmlFor="ch01"></label>
<label htmlFor="ch01"></label> </div>
</div>
) : (
data.name
)}
</th> </th>
))} <th>{getMessage('module')}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{selectedModules.itemList && {selectedModules.itemList &&
selectedModules.itemList.map((item, index) => ( selectedModules.itemList.map((item, index) => (
<tr key={index}> <tr key={index}>
<td className="al-c"> <td className="al-c">
<div className="d-check-box no-text pop"> <div className="d-check-box no-text pop">
<input <input
type="checkbox" type="checkbox"
id={item.itemId} id={item.itemId}
name={item.itemId} name={item.itemId}
checked={selectedItems[item.itemId]} checked={selectedItems[item.itemId]}
onChange={handleSelectedItem} onChange={handleSelectedItem}
/> />
<label htmlFor={item.itemId}></label> <label htmlFor={item.itemId}></label>
</div> </div>
</td> </td>
<td> <td>
<div className="color-wrap"> <div className="color-wrap">
<span className="color-box" style={{ backgroundColor: item.color }}></span> <span className="color-box" style={{ backgroundColor: item.color }}></span>
<span className="name">{item.itemNm}</span> <span className="name">{item.itemNm}</span>
</div> </div>
</td> </td>
<td className="al-r">{item.wpOut}</td> </tr>
</tr> ))}
))} </tbody>
</tbody> </table>
</table> </div>
</div> </div>
</div> </div>
</div> <div className="module-table-box non-flex">
<div className="module-table-box mb10"> <div className="module-table-inner">
<div className="module-table-inner"> <div className="roof-module-table">
<div className="hexagonal-wrap"> <table>
<div className="hexagonal-item"> <thead>
<div className="bold-font">{getMessage('modal.module.basic.setting.pitch.module.placement.standard.setting')}</div> <tr>
</div> <th>{getMessage('modal.module.basic.setting.pitch.module.placement.standard.setting')}</th>
<div className="hexagonal-item"> </tr>
<div className="pop-form-radio"> </thead>
<div className="d-check-radio pop"> <tbody>
<input <tr>
type="radio" <td>
name="radio01" <div className="hexagonal-radio-wrap">
id="ra01" <div className="d-check-radio pop mb10">
value={'south'} <input
defaultChecked={setupLocation === 'south'} type="radio"
onClick={handleSetupLocation} name="radio01"
/> id="ra01"
<label htmlFor="ra01">{getMessage('modal.module.basic.setting.pitch.module.placement.standard.setting.south')}</label> value={'south'}
</div> defaultChecked={setupLocation === 'south'}
<div className="d-check-radio pop"> onClick={handleSetupLocation}
<input />
type="radio" <label htmlFor="ra01">{getMessage('modal.module.basic.setting.pitch.module.placement.standard.setting.south')}</label>
name="radio01" </div>
id="ra02" <div className="d-check-radio pop">
value={'excreta'} <input
defaultChecked={setupLocation === 'excreta'} type="radio"
onClick={handleSetupLocation} name="radio01"
/> id="ra02"
<label htmlFor="ra02">{getMessage('modal.module.basic.setting.pitch.module.placement.standard.setting.select')}</label> value={'excreta'}
</div> defaultChecked={setupLocation === 'excreta'}
</div> onClick={handleSetupLocation}
/>
<label htmlFor="ra02">{getMessage('modal.module.basic.setting.pitch.module.placement.standard.setting.select')}</label>
</div>
</div>
</td>
</tr>
</tbody>
</table>
</div> </div>
</div> </div>
</div> </div>

View File

@ -41,7 +41,7 @@ export default function CircuitTrestleSetting({ id }) {
const [tabNum, setTabNum] = useState(1) const [tabNum, setTabNum] = useState(1)
const [allocationType, setAllocationType] = useState(ALLOCATION_TYPE.AUTO) const [allocationType, setAllocationType] = useState(ALLOCATION_TYPE.AUTO)
const [circuitAllocationType, setCircuitAllocationType] = useState(1) const [circuitAllocationType, setCircuitAllocationType] = useState(1)
const { managementState, setManagementState, managementStateLoaded } = useContext(GlobalDataContext) const { managementState, setManagementState } = useContext(GlobalDataContext)
const selectedModules = useRecoilValue(selectedModuleState) const selectedModules = useRecoilValue(selectedModuleState)
const { getPcsAutoRecommendList, getPcsVoltageChk, getPcsVoltageStepUpList, getPcsManualConfChk } = useMasterController() const { getPcsAutoRecommendList, getPcsVoltageChk, getPcsVoltageStepUpList, getPcsManualConfChk } = useMasterController()
@ -84,7 +84,6 @@ export default function CircuitTrestleSetting({ id }) {
// const { trigger: moduleSelectedDataTrigger } = useCanvasPopupStatusController(2) // const { trigger: moduleSelectedDataTrigger } = useCanvasPopupStatusController(2)
useEffect(() => { useEffect(() => {
if (!managementState) { if (!managementState) {
setManagementState(managementStateLoaded)
} }
// setCircuitData({ // setCircuitData({
// makers, // makers,
@ -479,7 +478,7 @@ export default function CircuitTrestleSetting({ id }) {
console.log(stepUpListData) console.log(stepUpListData)
stepUpListData[0].pcsItemList.map((item, index) => { stepUpListData[0].pcsItemList.map((item, index) => {
return item.serQtyList return item.serQtyList
.filter((serQty) => serQty.selected) .filter((serQty) => serQty.selected && serQty.paralQty > 0)
.forEach((serQty) => { .forEach((serQty) => {
pcs.push({ pcs.push({
pcsMkrCd: item.pcsMkrCd, pcsMkrCd: item.pcsMkrCd,

View File

@ -44,7 +44,7 @@ export default function PowerConditionalSelect(props) {
const { getPcsMakerList, getPcsModelList } = useMasterController() const { getPcsMakerList, getPcsModelList } = useMasterController()
const selectedModules = useRecoilValue(selectedModuleState) const selectedModules = useRecoilValue(selectedModuleState)
const { swalFire } = useSwal() const { swalFire } = useSwal()
const { trigger: moduleSelectedDataTrigger } = useCanvasPopupStatusController(2) // const { trigger: moduleSelectedDataTrigger } = useCanvasPopupStatusController(2)
const [moduleSelectionData, setModuleSelectionData] = useRecoilState(moduleSelectionDataState) const [moduleSelectionData, setModuleSelectionData] = useRecoilState(moduleSelectionDataState)
const modelHeader = [ const modelHeader = [
{ name: getMessage('modal.circuit.trestle.setting.circuit.allocation.passivity.series'), width: '15%', prop: 'pcsSerNm', type: 'color-box' }, { name: getMessage('modal.circuit.trestle.setting.circuit.allocation.passivity.series'), width: '15%', prop: 'pcsSerNm', type: 'color-box' },
@ -110,6 +110,7 @@ export default function PowerConditionalSelect(props) {
selected: s.pcsSerCd === data.pcsSerCd ? !s.selected : false, selected: s.pcsSerCd === data.pcsSerCd ? !s.selected : false,
} }
}) })
setSelectedModels([])
} }
setSeries(copySeries) setSeries(copySeries)
handleSetmodels(copySeries.filter((s) => s.selected)) handleSetmodels(copySeries.filter((s) => s.selected))

View File

@ -42,7 +42,6 @@ export default function StepUp(props) {
const [arrayLength, setArrayLength] = useState(3) //module-table-inner const [arrayLength, setArrayLength] = useState(3) //module-table-inner
const [pcsCheck, setPcsCheck] = useRecoilState(pcsCheckState) const [pcsCheck, setPcsCheck] = useRecoilState(pcsCheckState)
const { getPcsVoltageStepUpList, getPcsAutoRecommendList, getPcsVoltageChk, getPcsConnOptionItemList } = useMasterController() const { getPcsVoltageStepUpList, getPcsAutoRecommendList, getPcsVoltageChk, getPcsConnOptionItemList } = useMasterController()
const { managementState, setManagementState, managementStateLoaded } = useContext(GlobalDataContext)
const canvas = useRecoilValue(canvasState) const canvas = useRecoilValue(canvasState)
const selectedModules = useRecoilValue(selectedModuleState) const selectedModules = useRecoilValue(selectedModuleState)
const [optCodes, setOptCodes] = useState([]) const [optCodes, setOptCodes] = useState([])
@ -573,7 +572,7 @@ export default function StepUp(props) {
value={seletedMainOption} value={seletedMainOption}
sourceKey="code" sourceKey="code"
targetKey="code" targetKey="code"
showKey="name" showKey={`${globalLocale === 'ja' ? 'nameJp' : 'name'}`}
onChange={(e) => setSeletedMainOption(e)} onChange={(e) => setSeletedMainOption(e)}
/> />
)} )}
@ -586,7 +585,7 @@ export default function StepUp(props) {
value={seletedSubOption} value={seletedSubOption}
sourceKey="code" sourceKey="code"
targetKey="code" targetKey="code"
showKey="name" showKey={`${globalLocale === 'ja' ? 'nameJp' : 'name'}`}
onChange={(e) => setSeletedSubOption(e)} onChange={(e) => setSeletedSubOption(e)}
/> />
)} )}

View File

@ -25,7 +25,7 @@ export default function PassivityCircuitAllocation(props) {
const { swalFire } = useSwal() const { swalFire } = useSwal()
const { getMessage } = useMessage() const { getMessage } = useMessage()
const canvas = useRecoilValue(canvasState) const canvas = useRecoilValue(canvasState)
const { managementState, setManagementState, managementStateLoaded } = useContext(GlobalDataContext) const { managementState } = useContext(GlobalDataContext)
const selectedModules = useRecoilValue(selectedModuleState) const selectedModules = useRecoilValue(selectedModuleState)
const [selectedPcs, setSelectedPcs] = useState(selectedModels[0]) const [selectedPcs, setSelectedPcs] = useState(selectedModels[0])
const { header, rows, footer } = useRecoilValue(moduleStatisticsState) const { header, rows, footer } = useRecoilValue(moduleStatisticsState)
@ -38,7 +38,6 @@ export default function PassivityCircuitAllocation(props) {
useEffect(() => { useEffect(() => {
setModuleStatisticsData() setModuleStatisticsData()
if (!managementState) { if (!managementState) {
setManagementState(managementStateLoaded)
} }
canvas canvas
.getObjects() .getObjects()

View File

@ -7,6 +7,7 @@ import { useMessage } from '@/hooks/useMessage'
import { usePopup } from '@/hooks/usePopup' import { usePopup } from '@/hooks/usePopup'
import { canvasState } from '@/store/canvasAtom' import { canvasState } from '@/store/canvasAtom'
import { usePolygon } from '@/hooks/usePolygon' import { usePolygon } from '@/hooks/usePolygon'
import { useSurfaceShapeBatch } from '@/hooks/surface/useSurfaceShapeBatch'
const FLOW_DIRECTION_TYPE = { const FLOW_DIRECTION_TYPE = {
EIGHT_AZIMUTH: 'eightAzimuth', EIGHT_AZIMUTH: 'eightAzimuth',
@ -19,6 +20,8 @@ export default function FlowDirectionSetting(props) {
const canvas = useRecoilValue(canvasState) const canvas = useRecoilValue(canvasState)
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { changeSurfaceLineType } = useSurfaceShapeBatch({})
useEffect(() => { useEffect(() => {
return () => { return () => {
canvas?.discardActiveObject() canvas?.discardActiveObject()
@ -53,6 +56,7 @@ export default function FlowDirectionSetting(props) {
}) })
drawDirectionArrow(roof) drawDirectionArrow(roof)
canvas?.renderAll() canvas?.renderAll()
changeSurfaceLineType(roof)
closePopup(id) closePopup(id)
} }

View File

@ -38,6 +38,11 @@ export default function PanelEdit(props) {
const isSetupModules = canvas.getObjects().filter((obj) => obj.name === 'module') // selectedObj const isSetupModules = canvas.getObjects().filter((obj) => obj.name === 'module') // selectedObj
isSetupModules.forEach((obj) => obj.set({ lockMovementX: false, lockMovementY: false })) isSetupModules.forEach((obj) => obj.set({ lockMovementX: false, lockMovementY: false }))
} }
//
return () => {
canvas?.discardActiveObject() //
}
}, []) }, [])
// //
@ -87,7 +92,7 @@ export default function PanelEdit(props) {
moduleMultiCopy('row', length, direction) moduleMultiCopy('row', length, direction)
break break
} }
closePopup(id) // closePopup(id)
} }
return ( return (

View File

@ -100,7 +100,7 @@ export default function ObjectSetting({ id, pos = { x: 50, y: 230 } }) {
] ]
return ( return (
<WithDraggable isShow={true} pos={pos} className="lrr" style={{ visibility: isHidden ? 'hidden' : 'visible' }}> <WithDraggable isShow={true} pos={pos} className="lrr" isHidden={isHidden}>
<WithDraggable.Header title={getMessage('plan.menu.placement.surface.object')} onClose={() => closePopup(id)} /> <WithDraggable.Header title={getMessage('plan.menu.placement.surface.object')} onClose={() => closePopup(id)} />
<WithDraggable.Body> <WithDraggable.Body>
<div className="modal-btn-wrap"> <div className="modal-btn-wrap">

View File

@ -19,6 +19,7 @@ import { getChonByDegree, getDegreeByChon } from '@/util/canvas-util'
import { usePolygon } from '@/hooks/usePolygon' import { usePolygon } from '@/hooks/usePolygon'
import { canvasState } from '@/store/canvasAtom' import { canvasState } from '@/store/canvasAtom'
import { useRoofFn } from '@/hooks/common/useRoofFn' import { useRoofFn } from '@/hooks/common/useRoofFn'
import { usePlan } from '@/hooks/usePlan'
/** /**
* 지붕 레이아웃 * 지붕 레이아웃
@ -53,7 +54,7 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla
rafter: useRef(null), rafter: useRef(null),
hajebichi: useRef(null), hajebichi: useRef(null),
} }
const { saveCanvas } = usePlan()
/** /**
* 치수 입력방법(복시도입력/실측값입력/육지붕) * 치수 입력방법(복시도입력/실측값입력/육지붕)
*/ */
@ -205,7 +206,7 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla
/** /**
* 배치면초기설정 저장 버튼 클릭 * 배치면초기설정 저장 버튼 클릭
*/ */
const handleSaveBtn = () => { const handleSaveBtn = async () => {
const roofInfo = { const roofInfo = {
...currentRoof, ...currentRoof,
planNo: basicSetting.planNo, planNo: basicSetting.planNo,
@ -254,14 +255,13 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla
/* 저장 후 화면 닫기 */ /* 저장 후 화면 닫기 */
closePopup(id) closePopup(id)
await saveCanvas(false)
} }
return ( return (
<WithDraggable isShow={true} pos={pos} className="ll"> <WithDraggable isShow={true} pos={pos} className="ll">
<WithDraggable.Header title={getMessage('plan.menu.placement.surface.initial.setting')} /> <WithDraggable.Header title={getMessage('plan.menu.placement.surface.initial.setting')} />
<WithDraggable.Body> <WithDraggable.Body>
<div className="left-bar modal-handle"></div>
<div className="right-bar modal-handle"></div>
<div className="placement-table"> <div className="placement-table">
<table> <table>
<colgroup> <colgroup>
@ -271,7 +271,11 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla
<tbody> <tbody>
<tr> <tr>
<th>{getMessage('modal.placement.initial.setting.plan.drawing')}</th> <th>{getMessage('modal.placement.initial.setting.plan.drawing')}</th>
<td>{getMessage('modal.placement.initial.setting.plan.drawing.size.stuff')}</td> <td>
{getMessage('modal.placement.initial.setting.plan.drawing.size.stuff')}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
{getMessage('modal.placement.initial.setting.plan.drawing.only.number')}
</td>
</tr> </tr>
<tr> <tr>
<th> <th>

View File

@ -75,19 +75,19 @@ export default function Header(props) {
userSession.storeId === 'T01' userSession.storeId === 'T01'
? [ ? [
{ id: 0, name: getMessage('site.header.link1'), target: '_blank' }, { id: 0, name: getMessage('site.header.link1'), target: '_blank' },
{ id: 1, name: 'Q.ORDER', link: `${qOrderUrl}?autoLoginParam1=${encodeURIComponent(res.data)}`, target: '_blank' }, { id: 1, name: 'HANASYS ORDER', link: `${qOrderUrl}?autoLoginParam1=${encodeURIComponent(res.data)}`, target: '_blank' },
{ id: 2, name: 'Q.Musubi', link: `${qMusubiUrl}?autoLoginParam1=${encodeURIComponent(res.data)}`, target: '_blank' }, { id: 2, name: 'HANASYS Musubi', link: `${qMusubiUrl}?autoLoginParam1=${encodeURIComponent(res.data)}`, target: '_blank' },
{ id: 3, name: getMessage('site.header.link2'), link: `https://q-warranty.q-cells.jp/seller_login`, target: '_blank' }, { id: 3, name: getMessage('site.header.link2'), link: `https://q-warranty.q-cells.jp/seller_login`, target: '_blank' },
] ]
: userSession.groupId === '60000' : userSession.groupId === '60000'
? [ ? [
{ id: 0, name: getMessage('site.header.link1'), target: '_blank' }, { id: 0, name: getMessage('site.header.link1'), target: '_blank' },
{ id: 1, name: 'Q.ORDER', link: `${qOrderUrl}?autoLoginParam1=${encodeURIComponent(res.data)}`, target: '_blank' }, { id: 1, name: 'HANASYS ORDER', link: `${qOrderUrl}?autoLoginParam1=${encodeURIComponent(res.data)}`, target: '_blank' },
{ id: 2, name: getMessage('site.header.link2'), link: `https://q-warranty.q-cells.jp/seller_login`, target: '_blank' }, { id: 2, name: getMessage('site.header.link2'), link: `https://q-warranty.q-cells.jp/seller_login`, target: '_blank' },
] ]
: [ : [
{ id: 0, name: getMessage('site.header.link1'), target: '_blank' }, { id: 0, name: getMessage('site.header.link1'), target: '_blank' },
{ id: 1, name: 'Q.Musubi', link: `${qMusubiUrl}?autoLoginParam1=${encodeURIComponent(res.data)}`, target: '_blank' }, { id: 1, name: 'HANASYS Musubi', link: `${qMusubiUrl}?autoLoginParam1=${encodeURIComponent(res.data)}`, target: '_blank' },
{ id: 2, name: getMessage('site.header.link2'), link: `https://q-warranty.q-cells.jp/seller_login`, target: '_blank' }, { id: 2, name: getMessage('site.header.link2'), link: `https://q-warranty.q-cells.jp/seller_login`, target: '_blank' },
], ],
) )

View File

@ -203,7 +203,7 @@ export default function Stuff() {
if (event.column.colId === 'objectNo') { if (event.column.colId === 'objectNo') {
return return
} else { } else {
//T R //T S
if (event.data.objectNo) { if (event.data.objectNo) {
setIsGlobalLoading(true) setIsGlobalLoading(true)
if (event.data.tempFlg === '0') { if (event.data.tempFlg === '0') {
@ -238,6 +238,8 @@ export default function Stuff() {
schSortType: stuffSearchParams.schSortType, schSortType: stuffSearchParams.schSortType,
pageNo: stuffSearchParams?.pageNo ? stuffSearchParams.pageNo : 1, pageNo: stuffSearchParams?.pageNo ? stuffSearchParams.pageNo : 1,
pageSize: stuffSearchParams?.pageSize ? stuffSearchParams.pageSize : 100, pageSize: stuffSearchParams?.pageSize ? stuffSearchParams.pageSize : 100,
builderNo: session.builderNo,
userId: session.userId
} }
if (!params.saleStoreId) { if (!params.saleStoreId) {

View File

@ -54,7 +54,7 @@ export default function StuffDetail() {
const { get, promiseGet, del, promisePost, promisePut } = useAxios(globalLocaleState) const { get, promiseGet, del, promisePost, promisePut } = useAxios(globalLocaleState)
//form //form
const formInitValue = { const formInitValue = {
// T...() R...() // T...() S...()
planReqNo: '', //No planReqNo: '', //No
receiveUser: session?.userNm, // receiveUser: session?.userNm, //
objectStatusId: '0', //(:0 : 1) objectStatusId: '0', //(:0 : 1)
@ -1033,8 +1033,7 @@ export default function StuffDetail() {
const _saleStoreId = watch('saleStoreId') const _saleStoreId = watch('saleStoreId')
// 2 // 2
const _otherSaleStoreId = watch('otherSaleStoreId') const _otherSaleStoreId = watch('otherSaleStoreId')
// zipNo: '', // // zipNo: '', // #947
const _zipNo = watch('zipNo')
// prefId: '', // // prefId: '', //
const _prefId = watch('prefId') const _prefId = watch('prefId')
// address: '', // // address: '', //
@ -1071,10 +1070,6 @@ export default function StuffDetail() {
} }
} }
if (!formData.zipNo) {
errors.zipNo = true
}
if (!formData.prefId) { if (!formData.prefId) {
errors.prefId = true errors.prefId = true
} }
@ -1115,10 +1110,6 @@ export default function StuffDetail() {
} }
} }
if (!formData.zipNo) {
errors.zipNo = true
}
if (!formData.prefId || formData.prefId === '0') { if (!formData.prefId || formData.prefId === '0') {
errors.prefId = true errors.prefId = true
} }
@ -1144,7 +1135,6 @@ export default function StuffDetail() {
_objectName, _objectName,
_saleStoreId, _saleStoreId,
_otherSaleStoreId, _otherSaleStoreId,
_zipNo,
_prefId, _prefId,
_address, _address,
_areaId, _areaId,
@ -1189,6 +1179,14 @@ export default function StuffDetail() {
} }
}, [prefValue]) }, [prefValue])
// / disabled
const onChangePrefCode = (e) => {
setPrefValue(e.prefId)
form.setValue('prefId', e.prefId)
form.setValue('prefName', e.prefName)
}
// //
const handleAreaIdOnChange = (e) => { const handleAreaIdOnChange = (e) => {
form.setValue('areaId', e.areaId) form.setValue('areaId', e.areaId)
@ -1243,12 +1241,6 @@ export default function StuffDetail() {
errors = fieldNm errors = fieldNm
} }
//
if (!formData.zipNo) {
fieldNm = getMessage('stuff.detail.zipNo')
errors = fieldNm
}
//1 //1
if (!formData.saleStoreId) { if (!formData.saleStoreId) {
fieldNm = getMessage('stuff.detail.saleStoreId') fieldNm = getMessage('stuff.detail.saleStoreId')
@ -1558,7 +1550,7 @@ export default function StuffDetail() {
type: 'alert', type: 'alert',
icon: 'error', icon: 'error',
}) })
console.log('error::::::', error) console.error('error::::::', error)
}) })
} }
} }
@ -2004,9 +1996,7 @@ export default function StuffDetail() {
</td> </td>
</tr> </tr>
<tr> <tr>
<th> <th>{getMessage('stuff.detail.zipNo')}</th>
{getMessage('stuff.detail.zipNo')} <span className="important">*</span>
</th>
<td> <td>
<div className="flx-box"> <div className="flx-box">
<div className="input-wrap mr5" style={{ width: '200px' }}> <div className="input-wrap mr5" style={{ width: '200px' }}>
@ -2038,10 +2028,10 @@ export default function StuffDetail() {
getOptionLabel={(x) => x.prefName} getOptionLabel={(x) => x.prefName}
getOptionValue={(x) => x.prefId} getOptionValue={(x) => x.prefId}
isSearchable={false} isSearchable={false}
onChange={onChangePrefCode}
value={prefCodeList.filter(function (option) { value={prefCodeList.filter(function (option) {
return option.prefId === prefValue return option.prefId === prefValue
})} })}
isDisabled={true}
/> />
)} )}
</div> </div>
@ -2571,9 +2561,7 @@ export default function StuffDetail() {
</td> </td>
</tr> </tr>
<tr> <tr>
<th> <th>{getMessage('stuff.detail.zipNo')}</th>
{getMessage('stuff.detail.zipNo')} <span className="important">*</span>
</th>
<td> <td>
<div className="flx-box"> <div className="flx-box">
<div className="input-wrap mr5" style={{ width: '200px' }}> <div className="input-wrap mr5" style={{ width: '200px' }}>
@ -2606,10 +2594,10 @@ export default function StuffDetail() {
getOptionLabel={(x) => x.prefName} getOptionLabel={(x) => x.prefName}
getOptionValue={(x) => x.prefId} getOptionValue={(x) => x.prefId}
isSearchable={false} isSearchable={false}
onChange={onChangePrefCode}
value={prefCodeList.filter(function (option) { value={prefCodeList.filter(function (option) {
return option.prefId === prefValue return option.prefId === prefValue
})} })}
isDisabled={true}
/> />
)} )}
</div> </div>

View File

@ -7,8 +7,6 @@ import { POLYGON_TYPE } from '@/common/common'
export const useCanvasMenu = () => { export const useCanvasMenu = () => {
const [selectedMenu, setSelectedMenu] = useRecoilState(selectedMenuState) const [selectedMenu, setSelectedMenu] = useRecoilState(selectedMenuState)
const canvas = useRecoilValue(canvasState)
const { drawDirectionArrow } = usePolygon()
return { return {
selectedMenu, selectedMenu,

View File

@ -1,6 +1,6 @@
'use client' 'use client'
import { useRecoilState, useRecoilValue } from 'recoil' import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
import useSWRMutation from 'swr/mutation' import useSWRMutation from 'swr/mutation'
import { useAxios } from '../useAxios' import { useAxios } from '../useAxios'
import { unescapeString } from '@/util/common-utils' import { unescapeString } from '@/util/common-utils'
@ -9,7 +9,10 @@ import { compasDegAtom } from '@/store/orientationAtom'
import { canvasState, currentCanvasPlanState } from '@/store/canvasAtom' import { canvasState, currentCanvasPlanState } from '@/store/canvasAtom'
import { POLYGON_TYPE } from '@/common/common' import { POLYGON_TYPE } from '@/common/common'
import { useCircuitTrestle } from '../useCirCuitTrestle' import { useCircuitTrestle } from '../useCirCuitTrestle'
import { useEffect } from 'react' import { useContext } from 'react'
import { addedRoofsState } from '@/store/settingAtom'
import { roofsState } from '@/store/roofAtom'
import { GlobalDataContext } from '@/app/GlobalDataProvider'
/** /**
* 캔버스 팝업 상태 관리 * 캔버스 팝업 상태 관리
@ -19,13 +22,15 @@ import { useEffect } from 'react'
export function useCanvasPopupStatusController(param = 1) { export function useCanvasPopupStatusController(param = 1) {
const popupType = parseInt(param) const popupType = parseInt(param)
const [compasDeg, setCompasDeg] = useRecoilState(compasDegAtom) const setCompasDeg = useSetRecoilState(compasDegAtom)
const [moduleSelectionDataStore, setModuleSelectionDataStore] = useRecoilState(moduleSelectionDataState) const [moduleSelectionDataStore, setModuleSelectionDataStore] = useRecoilState(moduleSelectionDataState)
const [selectedModules, setSelectedModules] = useRecoilState(selectedModuleState) const setSelectedModules = useSetRecoilState(selectedModuleState)
const { get, promiseGet, getFetcher, postFetcher } = useAxios() const { get, promiseGet, getFetcher, postFetcher } = useAxios()
const canvas = useRecoilValue(canvasState) const canvas = useRecoilValue(canvasState)
const currentCanvasPlan = useRecoilValue(currentCanvasPlanState) const currentCanvasPlan = useRecoilValue(currentCanvasPlanState)
const [addedRoofs, setAddedRoofs] = useRecoilState(addedRoofsState)
const [roofs, setRoofs] = useRecoilState(roofsState)
const { managementState, setManagementState } = useContext(GlobalDataContext)
/** /**
* 팝업 상태 조회 * 팝업 상태 조회
* @param {number} popupTypeParam * @param {number} popupTypeParam
@ -51,23 +56,36 @@ export function useCanvasPopupStatusController(param = 1) {
* 조회 전체 데이터 recoil에 저장 * 조회 전체 데이터 recoil에 저장
*/ */
const handleModuleSelectionTotal = async () => { const handleModuleSelectionTotal = async () => {
let resultData = {}
for (let i = 1; i < 3; i++) { for (let i = 1; i < 3; i++) {
const result = await getModuleSelection(i) const result = await getModuleSelection(i)
console.log('🚀 ~ handleModuleSelectionTotal ~ result:', result)
if (!result.objectNo) return if (!result.objectNo) return
if (i === 1) { if (i === 1) {
setCompasDeg(result.popupStatus) if (result.popupStatus && unescapeString(result.popupStatus)) {
const data = JSON.parse(unescapeString(result.popupStatus))
if (data?.compasDeg) setCompasDeg(data.compasDeg)
if (data?.module) setSelectedModules(data.module)
// setModuleSelectionDataStore(data)
resultData = { ...data }
}
} else if (i === 2) { } else if (i === 2) {
const data = JSON.parse(unescapeString(result.popupStatus)) const data = JSON.parse(unescapeString(result.popupStatus))
setModuleSelectionDataStore(data)
const roofSurfaceList = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE) const roofSurfaceList = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE)
const modules = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE) const modules = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE)
roofSurfaceList.forEach((surface) => { roofSurfaceList.forEach((surface) => {
surface.modules = modules.filter((module) => module.surfaceId === surface.id) surface.modules = modules.filter((module) => module.surfaceId === surface.id)
}) })
if (data.module) setSelectedModules(data.module) if (data.roofConstructions) {
setRoofs(data.roofConstructions)
// setManagementState({ ...managementState, roofs: data.roofConstructions.map((roof) => roof.construction.managementState) })
// setModuleSelectionDataStore({ ...moduleSelectionDataStore, roofConstructions: data.roofConstruction })
resultData = { ...resultData, roofConstructions: data.roofConstructions }
}
// if (data?.module) setManagementState(data.common.managementState)
} }
} }
setModuleSelectionDataStore(resultData)
} }
/** /**
@ -80,7 +98,8 @@ export function useCanvasPopupStatusController(param = 1) {
objectNo: currentCanvasPlan.objectNo, objectNo: currentCanvasPlan.objectNo,
planNo: parseInt(currentCanvasPlan.planNo), planNo: parseInt(currentCanvasPlan.planNo),
popupType: popupType.toString(), popupType: popupType.toString(),
popupStatus: popupType === 1 ? arg : JSON.stringify(arg).replace(/"/g, '\"'), // popupStatus: popupType === 1 ? arg : JSON.stringify(arg).replace(/"/g, '\"'),
popupStatus: JSON.stringify(arg).replace(/"/g, '\"'),
} }
postFetcher(`/api/v1/canvas-popup-status`, params) postFetcher(`/api/v1/canvas-popup-status`, params)
}, },

View File

@ -18,7 +18,7 @@ export function useMasterController() {
*/ */
const getRoofMaterialList = async () => { const getRoofMaterialList = async () => {
return await get({ url: '/api/v1/master/getRoofMaterialList' }).then((res) => { return await get({ url: '/api/v1/master/getRoofMaterialList' }).then((res) => {
console.log('🚀🚀 ~ getRoofMaterialList ~ res:', res) // console.log('🚀🚀 ~ getRoofMaterialList ~ res:', res)
return res return res
}) })
} }

View File

@ -16,12 +16,13 @@ import { usePopup } from '@/hooks/usePopup'
import { useState } from 'react' import { useState } from 'react'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
import { useSurfaceShapeBatch } from '@/hooks/surface/useSurfaceShapeBatch' import { useSurfaceShapeBatch } from '@/hooks/surface/useSurfaceShapeBatch'
import { useRecoilValue } from 'recoil' import { useRecoilState, useRecoilValue } from 'recoil'
import { canvasState, currentMenuState } from '@/store/canvasAtom' import { canvasState, currentMenuState } from '@/store/canvasAtom'
import { MENU } from '@/common/common' import { MENU } from '@/common/common'
import { useTrestle } from '@/hooks/module/useTrestle' import { useTrestle } from '@/hooks/module/useTrestle'
import { usePolygon } from '@/hooks/usePolygon' import { usePolygon } from '@/hooks/usePolygon'
import { useOrientation } from '@/hooks/module/useOrientation' import { useOrientation } from '@/hooks/module/useOrientation'
import { corridorDimensionSelector, settingModalFirstOptionsState } from '@/store/settingAtom'
/** /**
* 메뉴 처리 * 메뉴 처리
@ -36,8 +37,11 @@ export default function useMenu() {
const { deleteAllSurfacesAndObjects } = useSurfaceShapeBatch({}) const { deleteAllSurfacesAndObjects } = useSurfaceShapeBatch({})
const { clear: trestleClear, setAllModuleSurfaceIsComplete } = useTrestle() const { clear: trestleClear, setAllModuleSurfaceIsComplete } = useTrestle()
const { nextStep } = useOrientation() const { nextStep } = useOrientation()
const [corridorDimension, setCorridorDimension] = useRecoilState(corridorDimensionSelector)
const handleMenu = (type) => { const handleMenu = (type) => {
if (type === 'outline') { if (type === 'outline') {
// 지붕 덮개 메뉴의 경우는 복도치수로 적용한다.
setCorridorDimension(0)
switch (currentMenu) { switch (currentMenu) {
case MENU.ROOF_COVERING.EXTERIOR_WALL_LINE: case MENU.ROOF_COVERING.EXTERIOR_WALL_LINE:
addPopup(popupId, 1, <WallLineSetting id={popupId} />) addPopup(popupId, 1, <WallLineSetting id={popupId} />)
@ -67,6 +71,8 @@ export default function useMenu() {
} }
if (type === 'surface') { if (type === 'surface') {
// 배치면 메뉴의 경우는 실치수로 적용한다.
setCorridorDimension(1)
switch (currentMenu) { switch (currentMenu) {
// case MENU.BATCH_CANVAS.SLOPE_SETTING: // case MENU.BATCH_CANVAS.SLOPE_SETTING:
// addPopup(popupId, 1, <Slope id={popupId} />) // addPopup(popupId, 1, <Slope id={popupId} />)
@ -87,6 +93,8 @@ export default function useMenu() {
} }
if (type === 'module') { if (type === 'module') {
// 모듈,회로 구성 메뉴의 경우는 실치수로 적용한다.
setCorridorDimension(1)
switch (currentMenu) { switch (currentMenu) {
case MENU.MODULE_CIRCUIT_SETTING.BASIC_SETTING: case MENU.MODULE_CIRCUIT_SETTING.BASIC_SETTING:
trestleClear() trestleClear()

View File

@ -130,13 +130,14 @@ export const useEstimateController = (planNo, flag) => {
addFlg: true, addFlg: true,
paDispOrder: null, paDispOrder: null,
dispCableFlg: '0', dispCableFlg: '0',
itemTpCd: '',
}, },
], ],
}) })
} }
useEffect(() => { useEffect(() => {
setEstimateData({ ...estimateContextState, userId: session.userId, sapSalesStoreCd: session.custCd }) setEstimateData({ ...estimateContextState, userId: session.userId })
}, [estimateContextState]) }, [estimateContextState])
// 첨부파일 다운로드 // 첨부파일 다운로드
@ -309,6 +310,7 @@ export const useEstimateController = (planNo, flag) => {
//봄 컴포넌트 제품은 0으로 //봄 컴포넌트 제품은 0으로
item.openFlg = '0' item.openFlg = '0'
item.unitOpenFlg = '0'
} }
} }
}) })
@ -321,6 +323,7 @@ export const useEstimateController = (planNo, flag) => {
} }
}) })
if (delCnt === estimateData.itemList.length) { if (delCnt === estimateData.itemList.length) {
itemFlg = false
setIsGlobalLoading(false) setIsGlobalLoading(false)
return swalFire({ text: getMessage('estimate.detail.save.requiredItem'), type: 'alert', icon: 'warning' }) return swalFire({ text: getMessage('estimate.detail.save.requiredItem'), type: 'alert', icon: 'warning' })
} }
@ -452,8 +455,6 @@ export const useEstimateController = (planNo, flag) => {
} }
const params = { const params = {
saleStoreId: session.storeId,
sapSalesStoreCd: session.custCd,
objectNo: estimateData.objectNo, objectNo: estimateData.objectNo,
planNo: sendPlanNo, planNo: sendPlanNo,
copySaleStoreId: otherSaleStoreId ? otherSaleStoreId : saleStoreId, copySaleStoreId: otherSaleStoreId ? otherSaleStoreId : saleStoreId,
@ -462,24 +463,30 @@ export const useEstimateController = (planNo, flag) => {
} }
setIsGlobalLoading(true) setIsGlobalLoading(true)
await promisePost({ url: '/api/estimate/save-estimate-copy', data: params }).then((res) => { await promisePost({ url: '/api/estimate/save-estimate-copy', data: params })
setIsGlobalLoading(false) .then((res) => {
if (res.status === 201) {
if (isObjectNotEmpty(res.data)) {
let newObjectNo = res.data.objectNo
swalFire({
text: getMessage('estimate.detail.estimateCopyPopup.copy.alertMessage'),
type: 'alert',
confirmFn: () => {
setEstimateCopyPopupOpen(false) //팝업닫고
router.push(`/management/stuff/detail?objectNo=${newObjectNo.toString()}`, { scroll: false })
},
})
}
} else {
setIsGlobalLoading(false) setIsGlobalLoading(false)
} if (res.status === 201) {
}) if (isObjectNotEmpty(res.data)) {
let newObjectNo = res.data.objectNo
swalFire({
text: getMessage('estimate.detail.estimateCopyPopup.copy.alertMessage'),
type: 'alert',
confirmFn: () => {
setEstimateCopyPopupOpen(false) //팝업닫고
router.push(`/management/stuff/detail?objectNo=${newObjectNo.toString()}`, { scroll: false })
},
})
}
} else {
setIsGlobalLoading(false)
}
})
.catch((e) => {
console.error('캔버스 복사 중 오류 발생')
swalFire({ text: getMessage('estimate.detail.estimateCopyPopup.copy.alertMessageError'), type: 'alert', icon: 'error' })
setIsGlobalLoading(false)
})
} }
const checkLength = (value) => { const checkLength = (value) => {

View File

@ -27,7 +27,7 @@ export const useMainContentsController = () => {
*/ */
const fetchObjectList = async () => { const fetchObjectList = async () => {
try { try {
const apiUrl = `/api/main-page/object/${session?.storeId}/list` const apiUrl = `/api/main-page/object/${session?.storeId}/${session?.userId}/${session?.builderNo}/list`
await promiseGet({ await promiseGet({
url: apiUrl, url: apiUrl,
}).then((res) => { }).then((res) => {

View File

@ -54,7 +54,7 @@ export function useModule() {
}) })
return return
} }
canvas.discardActiveObject() //선택해제 // canvas.discardActiveObject() //선택해제
const isSetupModules = getOtherModules(selectedObj) const isSetupModules = getOtherModules(selectedObj)
const selectedModules = canvas.getObjects().filter((obj) => selectedIds.includes(obj.id) && obj.name === 'module') //선택했던 객체들만 가져옴 const selectedModules = canvas.getObjects().filter((obj) => selectedIds.includes(obj.id) && obj.name === 'module') //선택했던 객체들만 가져옴
@ -991,14 +991,14 @@ export function useModule() {
const getRowModules = (target) => { const getRowModules = (target) => {
return canvas return canvas
.getObjects() .getObjects()
.filter((obj) => target.surfaceId === obj.surfaceId && obj.name === POLYGON_TYPE.MODULE && obj.top === target.top) .filter((obj) => target.surfaceId === obj.surfaceId && obj.name === POLYGON_TYPE.MODULE && Math.abs(obj.top - target.top) < 1)
.sort((a, b) => a.left - b.left) .sort((a, b) => a.left - b.left)
} }
const getColumnModules = (target) => { const getColumnModules = (target) => {
return canvas return canvas
.getObjects() .getObjects()
.filter((obj) => target.surfaceId === obj.surfaceId && obj.name === POLYGON_TYPE.MODULE && obj.left === target.left) .filter((obj) => target.surfaceId === obj.surfaceId && obj.name === POLYGON_TYPE.MODULE && Math.abs(obj.left - target.left) < 1)
.sort((a, b) => a.top - b.top) .sort((a, b) => a.top - b.top)
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,35 +0,0 @@
import { useEffect, useState } from 'react'
import { useRecoilValue, useSetRecoilState } from 'recoil'
import { moduleSelectionDataState, selectedModuleState } from '@/store/selectedModuleOptions'
import { useMasterController } from '@/hooks/common/useMasterController'
import { canvasSettingState, canvasState, currentCanvasPlanState, moduleSetupSurfaceState } from '@/store/canvasAtom'
import { POLYGON_TYPE, BATCH_TYPE } from '@/common/common'
import { useRoofFn } from '@/hooks/common/useRoofFn'
import { roofDisplaySelector } from '@/store/settingAtom'
import offsetPolygon from '@/util/qpolygon-utils'
import { v4 as uuidv4 } from 'uuid'
import { QPolygon } from '@/components/fabric/QPolygon'
import { useEvent } from '@/hooks/useEvent'
import { useSwal } from '@/hooks/useSwal'
import { useMessage } from '@/hooks/useMessage'
export function useModulePlace() {
const canvas = useRecoilValue(canvasState)
const moduleSelectionData = useRecoilValue(moduleSelectionDataState)
const [trestleDetailParams, setTrestleDetailParams] = useState([])
const [trestleDetailList, setTrestleDetailList] = useState([])
const selectedModules = useRecoilValue(selectedModuleState)
const { getTrestleDetailList } = useMasterController()
const canvasSetting = useRecoilValue(canvasSettingState)
const { setSurfaceShapePattern } = useRoofFn()
const roofDisplay = useRecoilValue(roofDisplaySelector)
const { addTargetMouseEventListener } = useEvent()
const setModuleSetupSurface = useSetRecoilState(moduleSetupSurfaceState)
const [saleStoreNorthFlg, setSaleStoreNorthFlg] = useState(false)
const { swalFire } = useSwal()
const { getMessage } = useMessage()
return {
selectedModules,
}
}

View File

@ -3,7 +3,7 @@ import { useContext, useEffect, useState } from 'react'
import { GlobalDataContext } from '@/app/GlobalDataProvider' import { GlobalDataContext } from '@/app/GlobalDataProvider'
import { useMasterController } from '@/hooks/common/useMasterController' import { useMasterController } from '@/hooks/common/useMasterController'
import { useCommonCode } from '@/hooks/common/useCommonCode' import { useCommonCode } from '@/hooks/common/useCommonCode'
import { selectedModuleState, moduleSelectionInitParamsState, moduleSelectionDataState } from '@/store/selectedModuleOptions' import { selectedModuleState, moduleSelectionDataState } from '@/store/selectedModuleOptions'
import { isObjectNotEmpty } from '@/util/common-utils' import { isObjectNotEmpty } from '@/util/common-utils'
import { canvasState } from '@/store/canvasAtom' import { canvasState } from '@/store/canvasAtom'
import { POLYGON_TYPE } from '@/common/common' import { POLYGON_TYPE } from '@/common/common'
@ -12,23 +12,20 @@ import { useModuleBasicSetting } from '@/hooks/module/useModuleBasicSetting'
export function useModuleSelection(props) { export function useModuleSelection(props) {
const canvas = useRecoilValue(canvasState) const canvas = useRecoilValue(canvasState)
const { managementState, setManagementState, managementStateLoaded } = useContext(GlobalDataContext) const { managementState, setManagementState } = useContext(GlobalDataContext)
const [roughnessCodes, setRoughnessCodes] = useState([]) //면조도 목록 const [roughnessCodes, setRoughnessCodes] = useState([]) //면조도 목록
const [windSpeedCodes, setWindSpeedCodes] = useState([]) //기준풍속 목록 const [windSpeedCodes, setWindSpeedCodes] = useState([]) //기준풍속 목록
const [moduleList, setModuleList] = useState([{}]) //모듈 목록 const [moduleList, setModuleList] = useState([{}]) //모듈 목록
const [selectedSurfaceType, setSelectedSurfaceType] = useState({}) //선택된 면조도 const [selectedSurfaceType, setSelectedSurfaceType] = useState({}) //선택된 면조도
const [installHeight, setInstallHeight] = useState() //설치 높이 const [installHeight, setInstallHeight] = useState(managementState?.installHeight) //설치 높이
const [standardWindSpeed, setStandardWindSpeed] = useState({}) //기준풍속 const [standardWindSpeed, setStandardWindSpeed] = useState() //기준풍속
const [verticalSnowCover, setVerticalSnowCover] = useState() //수직적설량 const [verticalSnowCover, setVerticalSnowCover] = useState(managementState?.verticalSnowCover) //수직적설량
const [selectedModules, setSelectedModules] = useRecoilState(selectedModuleState) //선택된 모듈 const [selectedModules, setSelectedModules] = useRecoilState(selectedModuleState) //선택된 모듈
const [moduleSelectionInitParams, setModuleSelectionInitParams] = useRecoilState(moduleSelectionInitParamsState) //모듈 기본 데이터 ex) 면조도, 높이등등
// const [moduleSelectionInitParams, setModuleSelectionInitParams] = useRecoilState(moduleSelectionInitParamsState) //모듈 기본 데이터 ex) 면조도, 높이등등
const { getModuleTypeItemList } = useMasterController() const { getModuleTypeItemList } = useMasterController()
const { findCommonCode } = useCommonCode() const { findCommonCode } = useCommonCode()
const resetStatisticsData = useResetRecoilState(moduleStatisticsState) const resetStatisticsData = useResetRecoilState(moduleStatisticsState)
const { restoreModuleInstArea } = useModuleBasicSetting() const { restoreModuleInstArea } = useModuleBasicSetting()
const bindInitData = () => { const bindInitData = () => {
@ -43,21 +40,21 @@ export function useModuleSelection(props) {
//탭별 파라메터 초기화 //탭별 파라메터 초기화
useEffect(() => { useEffect(() => {
bindInitData() bindInitData()
const initParams = { // const initParams = {
illuminationTp: managementState?.surfaceTypeValue, //면조도 // illuminationTp: managementState?.surfaceTypeValue, //면조도
illuminationTpNm: managementState?.surfaceType, //면조도명 // illuminationTpNm: managementState?.surfaceType, //면조도명
instHt: managementState?.installHeight, //설치높이 // instHt: managementState?.installHeight, //설치높이
stdWindSpeed: managementState?.standardWindSpeedId, //기준풍속 // stdWindSpeed: managementState?.standardWindSpeedId, //기준풍속
stdSnowLd: managementState?.verticalSnowCover, //기준적설량 // stdSnowLd: managementState?.verticalSnowCover, //기준적설량
saleStoreNorthFlg: managementState?.saleStoreNorthFlg, //북쪽 설치 여부 // saleStoreNorthFlg: managementState?.saleStoreNorthFlg, //북쪽 설치 여부
} // }
if (selectedModules) { // if (selectedModules) {
initParams.moduleTpCd = selectedModules.itemTp // initParams.moduleTpCd = selectedModules.itemTp
initParams.moduleItemId = selectedModules.itemId // initParams.moduleItemId = selectedModules.itemId
} // }
setModuleSelectionInitParams(initParams) // setModuleSelectionInitParams(initParams)
}, [managementState]) }, [managementState])
useEffect(() => { useEffect(() => {
@ -87,7 +84,7 @@ export function useModuleSelection(props) {
//새로고침시 데이터 날아가는거 방지 //새로고침시 데이터 날아가는거 방지
if (managementState === null) { if (managementState === null) {
setManagementState(managementStateLoaded) setManagementState(managementState)
} else { } else {
bindInitData() bindInitData()
} }
@ -95,7 +92,7 @@ export function useModuleSelection(props) {
getModuleData(roofsIds) getModuleData(roofsIds)
//모듈설치면 초기화 //모듈설치면 초기화
restoreModuleInstArea() // restoreModuleInstArea()
resetStatisticsData() resetStatisticsData()
}, []) }, [])
@ -125,19 +122,19 @@ export function useModuleSelection(props) {
setSelectedModules(option) //선택값 저장 setSelectedModules(option) //선택값 저장
//init 데이터에 선택된 모듈 추가 //init 데이터에 선택된 모듈 추가
setModuleSelectionInitParams({ // setModuleSelectionInitParams({
...moduleSelectionInitParams, // ...moduleSelectionInitParams,
moduleTpCd: option.itemTp, // moduleTpCd: option.itemTp,
moduleItemId: option.itemId, // moduleItemId: option.itemId,
}) // })
} }
const handleChangeSurfaceType = (option) => { const handleChangeSurfaceType = (option) => {
setModuleSelectionInitParams({ // setModuleSelectionInitParams({
...moduleSelectionInitParams, // ...moduleSelectionInitParams,
illuminationTp: option.clCode, // illuminationTp: option.clCode,
illuminationTpNm: option.clCodeNm, // illuminationTpNm: option.clCodeNm,
}) // })
setManagementState({ setManagementState({
...managementState, ...managementState,
@ -147,10 +144,10 @@ export function useModuleSelection(props) {
} }
const handleChangeWindSpeed = (option) => { const handleChangeWindSpeed = (option) => {
setModuleSelectionInitParams({ // setModuleSelectionInitParams({
...moduleSelectionInitParams, // ...moduleSelectionInitParams,
stdWindSpeed: option.clCode, // stdWindSpeed: option.clCode,
}) // })
setManagementState({ setManagementState({
...managementState, ...managementState,
@ -160,10 +157,10 @@ export function useModuleSelection(props) {
const handleChangeInstallHeight = (option) => { const handleChangeInstallHeight = (option) => {
setInstallHeight(option) setInstallHeight(option)
setModuleSelectionInitParams({ // setModuleSelectionInitParams({
...moduleSelectionInitParams, // ...moduleSelectionInitParams,
instHt: option, // instHt: option,
}) // })
setManagementState({ setManagementState({
...managementState, ...managementState,
@ -173,10 +170,10 @@ export function useModuleSelection(props) {
const handleChangeVerticalSnowCover = (option) => { const handleChangeVerticalSnowCover = (option) => {
setVerticalSnowCover(option) setVerticalSnowCover(option)
setModuleSelectionInitParams({ // setModuleSelectionInitParams({
...moduleSelectionInitParams, // ...moduleSelectionInitParams,
stdSnowLd: option, // stdSnowLd: option,
}) // })
setManagementState({ setManagementState({
...managementState, ...managementState,
@ -184,53 +181,23 @@ export function useModuleSelection(props) {
}) })
} }
useEffect(() => {
// console.log('installHeight', installHeight)
}, [installHeight])
useEffect(() => {
// console.log('verticalSnowCover', verticalSnowCover)
}, [verticalSnowCover])
//TODO: 설치높이, 기준적설량 debounce 적용해서 추가해야됨
// useEffect(() => {
// getConstructionListData(constructionListParams)
// }, [constructionListParams])
// const getConstructionListData = async (params) => {
// if (params.trestleMkrCd && params.constMthdCd && params.roofBaseCd) {
// const optionsList = await getConstructionList(params)
// console.log('optionsList', optionsList)
// setConstructionList(optionsList.data)
// }
// }
//state 배열에 데이터 추가 함수
// const addRoofTabParams = (key, value, excludeArray = []) => {
// const index = roofTabParams.findIndex((obj) => obj.roofTab === roofTab)
// if (index !== -1) {
// roofTabParams[index][key] = value
// if (excludeArray.length > 0) {
// excludeArray.forEach((exclude) => {
// roofTabParams[index][exclude] = ''
// })
// }
// setRoofTabParams((prev) => [...prev.slice(0, index), roofTabParams[index], ...prev.slice(index + 1)])
// }
// }
return { return {
moduleSelectionInitParams, // moduleSelectionInitParams,
selectedModules, selectedModules,
roughnessCodes, roughnessCodes,
windSpeedCodes, windSpeedCodes,
managementState, managementState,
setManagementState,
moduleList, moduleList,
setSelectedModules,
selectedSurfaceType, selectedSurfaceType,
setSelectedSurfaceType,
installHeight, installHeight,
setInstallHeight,
standardWindSpeed, standardWindSpeed,
setStandardWindSpeed,
verticalSnowCover, verticalSnowCover,
setVerticalSnowCover,
handleChangeModule, handleChangeModule,
handleChangeSurfaceType, handleChangeSurfaceType,
handleChangeWindSpeed, handleChangeWindSpeed,

View File

@ -0,0 +1,302 @@
import { use, useContext, useEffect, useReducer, useState } from 'react'
import { useCommonCode } from '../common/useCommonCode'
import { useMasterController } from '../common/useMasterController'
import { selectedModuleState } from '@/store/selectedModuleOptions'
import { useRecoilState, useRecoilValue } from 'recoil'
import { GlobalDataContext } from '@/app/GlobalDataProvider'
import { popSpinnerState } from '@/store/popupAtom'
const RAFT_BASE_CODE = '203800'
const trestleReducer = (state, action) => {
switch (action.type) {
case 'SET_LENGTH':
case 'SET_RAFT_BASE':
case 'SET_TRESTLE_MAKER':
case 'SET_CONST_MTHD':
case 'SET_ROOF_BASE':
case 'SET_CONSTRUCTION':
case 'SET_TRESTLE_DETAIL':
return {
...action.roof,
}
case 'SET_INITIALIZE':
return {
moduleTpCd: action.roof.module?.itemTp ?? '',
roofMatlCd: action.roof?.roofMatlCd ?? '',
hajebichi: action.roof?.hajebichi ?? 0,
raft: action.roof?.raft ?? null,
trestleMkrCd: action.roof.trestle?.trestleMkrCd ?? null,
constMthdCd: action.roof.trestle?.constMthdCd ?? null,
constTp: action.roof.construction?.constTp ?? null,
roofBaseCd: action.roof.trestle?.roofBaseCd ?? null,
workingWidth: action.roof.workingWidth ?? 0,
lengthBase: action.roof?.length ?? 0,
illuminationTp: action.roof.common?.illuminationTp ?? null,
instHt: action.roof.common?.instHt ?? null,
stdWindSpeed: action.roof.common?.stdWindSpeed ?? null,
stdSnowLd: action.roof.common?.stdSnowLd ?? null,
inclCd: action.roof?.pitch ?? null,
roofPitch: action.roof?.roofPchBase ?? 0,
eavesMargin: action.roof?.eavesMargin ?? null,
ridgeMargin: action.roof?.ridgeMargin ?? null,
kerabaMargin: action.roof?.kerabaMargin ?? null,
}
default:
return state
}
}
export function useModuleTrestle(props) {
const { selectedRoof } = props
const { findCommonCode } = useCommonCode()
const [raftBaseList, setRaftBaseList] = useState([])
const [trestleList, setTrestleList] = useState([])
const [constMthdList, setConstMthdList] = useState([])
const [roofBaseList, setRoofBaseList] = useState([])
const [constructionList, setConstructionList] = useState([])
const { getTrestleList, getConstructionList, getTrestleDetailList } = useMasterController()
const [lengthBase, setLengthBase] = useState(0)
const [hajebichi, setHajebichi] = useState(0)
const [cvrYn, setCvrYn] = useState('N')
const [cvrChecked, setCvrChecked] = useState(false)
const [snowGdPossYn, setSnowGdPossYn] = useState('N')
const [snowGdChecked, setSnowGdChecked] = useState(false)
const [eavesMargin, setEavesMargin] = useState(0)
const [ridgeMargin, setRidgeMargin] = useState(0)
const [kerabaMargin, setKerabaMargin] = useState(0)
const [trestleState, dispatch] = useReducer(trestleReducer, null)
const [trestleDetail, setTrestleDetail] = useState(null)
const [popSpinnerStore, setPopSpinnerStore] = useRecoilState(popSpinnerState)
useEffect(() => {
const raftCodeList = findCommonCode(RAFT_BASE_CODE)
setRaftBaseList(raftCodeList)
setTrestleList([])
setConstMthdList([])
setRoofBaseList([])
setConstructionList([])
// setEavesMargin(selectedRoof?.addRoof?.eavesMargin ?? 0)
// setRidgeMargin(selectedRoof?.addRoof?.ridgeMargin ?? 0)
// setKerabaMargin(selectedRoof?.addRoof?.kerabaMargin ?? 0)
setHajebichi(selectedRoof?.hajebichi ?? 0)
setEavesMargin(selectedRoof?.eavesMargin ?? 0)
setRidgeMargin(selectedRoof?.ridgeMargin ?? 0)
setKerabaMargin(selectedRoof?.kerabaMargin ?? 0)
setLengthBase(Math.round(selectedRoof?.length ?? 0))
setCvrYn(selectedRoof?.construction?.cvrYn ?? 'N')
setCvrChecked(selectedRoof?.construction?.cvrChecked ?? false)
setSnowGdPossYn(selectedRoof?.construction?.snowGdPossYn ?? 'N')
setSnowGdChecked(selectedRoof?.construction?.snowGdChecked ?? false)
setTrestleDetail(selectedRoof?.trestleDetail)
}, [selectedRoof])
useEffect(() => {
if (trestleState) {
handleSetTrestleList()
if (!trestleState?.trestleMkrCd) {
setConstMthdList([])
setRoofBaseList([])
setConstructionList([])
setTrestleDetail(null)
setEavesMargin(0)
setRidgeMargin(0)
setKerabaMargin(0)
return
}
handleSetConstMthdList()
if (!trestleState?.constMthdCd) {
setRoofBaseList([])
setConstructionList([])
setTrestleDetail(null)
setEavesMargin(0)
setRidgeMargin(0)
setKerabaMargin(0)
return
}
handleSetRoofBaseList()
if (!trestleState?.roofBaseCd) {
setConstructionList([])
setTrestleDetail(null)
setEavesMargin(0)
setRidgeMargin(0)
setKerabaMargin(0)
return
}
handleSetConstructionList()
if (!trestleState?.constTp) {
setTrestleDetail(null)
setEavesMargin(0)
setRidgeMargin(0)
setKerabaMargin(0)
return
}
if (!trestleState?.eavesMargin) {
handleSetTrestleDetailData()
}
}
}, [trestleState])
const handleSetTrestleList = () => {
setPopSpinnerStore(true)
getTrestleList({
moduleTpCd: trestleState?.moduleTpCd ?? '',
roofMatlCd: trestleState?.roofMatlCd ?? '',
raftBaseCd: trestleState?.raft ?? '',
})
.then((res) => {
if (res?.data) setTrestleList(res.data)
setPopSpinnerStore(false)
})
.catch((e) => {
setPopSpinnerStore(false)
})
}
const handleSetConstMthdList = () => {
setPopSpinnerStore(true)
getTrestleList({
moduleTpCd: trestleState?.moduleTpCd ?? '',
roofMatlCd: trestleState?.roofMatlCd ?? '',
raftBaseCd: trestleState?.raft ?? '',
trestleMkrCd: trestleState?.trestleMkrCd ?? '',
})
.then((res) => {
if (res?.data) setConstMthdList(res.data)
setPopSpinnerStore(false)
})
.catch((e) => {
setPopSpinnerStore(false)
})
}
const handleSetRoofBaseList = () => {
setPopSpinnerStore(true)
getTrestleList({
moduleTpCd: trestleState?.moduleTpCd ?? '',
roofMatlCd: trestleState?.roofMatlCd ?? '',
raftBaseCd: trestleState?.raft ?? '',
trestleMkrCd: trestleState?.trestleMkrCd ?? '',
constMthdCd: trestleState?.constMthdCd ?? '',
})
.then((res) => {
if (res?.data) setRoofBaseList(res.data)
setPopSpinnerStore(false)
})
.catch((e) => {
setPopSpinnerStore(false)
})
}
const handleSetConstructionList = () => {
setPopSpinnerStore(true)
getConstructionList({
moduleTpCd: trestleState?.moduleTpCd ?? '',
roofMatlCd: trestleState?.roofMatlCd ?? '',
trestleMkrCd: trestleState?.trestleMkrCd ?? '',
constMthdCd: trestleState?.constMthdCd ?? '',
roofBaseCd: trestleState?.roofBaseCd ?? '',
illuminationTp: trestleState.illuminationTp ?? '',
instHt: trestleState.instHt ?? '',
stdWindSpeed: trestleState.stdWindSpeed ?? '',
stdSnowLd: trestleState.stdSnowLd ?? '',
inclCd: trestleState.inclCd ?? '',
raftBaseCd: trestleState.raft ?? '',
roofPitch: Math.round(trestleState.roofPitch) ?? '',
})
.then((res) => {
if (res?.data) setConstructionList(res.data)
setPopSpinnerStore(false)
})
.catch((e) => {
setPopSpinnerStore(false)
})
}
const handleSetTrestleDetailData = () => {
setPopSpinnerStore(true)
getTrestleDetailList([
{
moduleTpCd: trestleState.moduleTpCd ?? '',
roofMatlCd: trestleState.roofMatlCd ?? '',
trestleMkrCd: trestleState.trestleMkrCd ?? '',
constMthdCd: trestleState.constMthdCd ?? '',
roofBaseCd: trestleState.roofBaseCd ?? '',
illuminationTp: trestleState.illuminationTp ?? '',
instHt: trestleState.instHt ?? '',
stdWindSpeed: trestleState.stdWindSpeed ?? '',
stdSnowLd: trestleState.stdSnowLd ?? '',
inclCd: trestleState.inclCd ?? '',
constTp: trestleState.constTp ?? '',
mixMatlNo: trestleState.mixMatlNo ?? '',
roofPitch: trestleState.roofPitch ?? '',
// workingWidth: trestleState.length ?? '',
workingWidth: lengthBase ?? '',
},
])
.then((res) => {
if (res.length > 0) {
if (!res[0].data) return
setEavesMargin(res[0].data.eaveIntvl)
setRidgeMargin(res[0].data.ridgeIntvl)
setKerabaMargin(res[0].data.kerabaIntvl)
setTrestleDetail(res[0].data)
// dispatch({
// type: 'SET_TRESTLE_DETAIL',
// roof: {
// ...trestleState,
// eavesMargin: res[0].data.eaveIntvl,
// ridgeMargin: res[0].data.ridgeIntvl,
// kerabaMargin: res[0].data.kerabaIntvl,
// },
// })
}
setPopSpinnerStore(false)
})
.catch((e) => {
setPopSpinnerStore(false)
})
}
return {
trestleState,
trestleDetail,
dispatch,
raftBaseList,
trestleList,
constMthdList,
roofBaseList,
constructionList,
handleSetTrestleList,
handleSetConstMthdList,
handleSetRoofBaseList,
handleSetConstructionList,
handleSetTrestleDetailData,
lengthBase,
setLengthBase,
hajebichi,
setHajebichi,
cvrYn,
cvrChecked,
snowGdPossYn,
snowGdChecked,
eavesMargin,
ridgeMargin,
kerabaMargin,
setEavesMargin,
setRidgeMargin,
setKerabaMargin,
setCvrYn,
setCvrChecked,
setSnowGdPossYn,
setSnowGdChecked,
}
}

View File

@ -22,8 +22,8 @@ export function useOrientation() {
}) })
}, [])*/ }, [])*/
const nextStep = () => { const nextStep = (compas = compasDeg) => {
if (isNaN(compasDeg)) { if (isNaN(compas)) {
setCompasDeg(0) setCompasDeg(0)
} }
const roofs = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF) const roofs = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
@ -36,7 +36,7 @@ export function useOrientation() {
roofs.forEach((roof) => { roofs.forEach((roof) => {
roof.set({ roof.set({
moduleCompass: isNaN(compasDeg) ? 0 : compasDeg, moduleCompass: isNaN(compas) ? 0 : compas,
}) })
drawDirectionArrow(roof) drawDirectionArrow(roof)
}) })

View File

@ -10,6 +10,7 @@ import { useSwal } from '@/hooks/useSwal'
import { useContext } from 'react' import { useContext } from 'react'
import { QcastContext } from '@/app/QcastProvider' import { QcastContext } from '@/app/QcastProvider'
import { useCircuitTrestle } from '@/hooks/useCirCuitTrestle' import { useCircuitTrestle } from '@/hooks/useCirCuitTrestle'
import { useMessage } from '@/hooks/useMessage'
// 모듈간 같은 행, 열의 마진이 10 이하인 경우는 같은 행, 열로 간주 // 모듈간 같은 행, 열의 마진이 10 이하인 경우는 같은 행, 열로 간주
const MODULE_MARGIN = 10 const MODULE_MARGIN = 10
@ -26,6 +27,7 @@ export const useTrestle = () => {
const { getSelectedPcsItemList } = useCircuitTrestle() const { getSelectedPcsItemList } = useCircuitTrestle()
const { resetCircuits } = useCircuitTrestle() const { resetCircuits } = useCircuitTrestle()
const { getMessage } = useMessage()
const apply = () => { const apply = () => {
const notAllocationModules = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE && !obj.circuit) const notAllocationModules = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE && !obj.circuit)
@ -58,7 +60,6 @@ export const useTrestle = () => {
} }
const construction = moduleSelectionData?.roofConstructions?.find((construction) => construction.roofIndex === roofMaterialIndex).construction const construction = moduleSelectionData?.roofConstructions?.find((construction) => construction.roofIndex === roofMaterialIndex).construction
if (!construction) { if (!construction) {
swalFire({ text: 'construction 존재안함', icon: 'error' })
return return
} }
@ -131,9 +132,9 @@ export const useTrestle = () => {
surface.isChidory = isChidory surface.isChidory = isChidory
if (plvrYn === 'N' && isChidory) { if (plvrYn === 'N' && isChidory) {
swalFire({ text: '치조불가공법입니다.', icon: 'error' }) swalFire({ text: getMessage('chidory.can.not.install'), icon: 'error' })
clear() clear()
throw new Error('치조불가공법입니다.') throw new Error(getMessage('chidory.can.not.install'))
} }
surface.set({ isChidory: isChidory }) surface.set({ isChidory: isChidory })
@ -370,11 +371,16 @@ export const useTrestle = () => {
rack.value.moduleRows === leftRowsInfo.rowsInfo.reduce((acc, row) => acc + row.count, 0) rack.value.moduleRows === leftRowsInfo.rowsInfo.reduce((acc, row) => acc + row.count, 0)
) )
} else { } else {
//C1C2C3인 경우
let newLeftRowsInfo = normalizeModules(rack.value.moduleTpCd, leftRowsInfo)
return ( return (
rack.value.moduleTpCd === leftRowsInfo.moduleTotalTp && rack.value.moduleTpCd === newLeftRowsInfo.moduleTotalTp &&
rack.value.moduleRows === leftRowsInfo.rowsInfo.reduce((acc, row) => acc + row.count, 0) && rack.value.moduleRows === newLeftRowsInfo.rowsInfo.reduce((acc, row) => acc + row.count, 0) &&
Number(rack.value.moduleTpRows1) === leftRowsInfo.rowsInfo[0].count && newLeftRowsInfo.rowsInfo.every((row, index) => {
Number(rack.value.moduleTpRows2) === leftRowsInfo.rowsInfo[1].count const rackRowCount = Number(rack.value[`moduleTpRows${index + 1}`]) // 동적으로 접근
return rackRowCount === row.count
})
) )
} }
})?.value.racks })?.value.racks
@ -387,11 +393,15 @@ export const useTrestle = () => {
rack.value.moduleRows === rightRowsInfo.rowsInfo.reduce((acc, row) => acc + row.count, 0) rack.value.moduleRows === rightRowsInfo.rowsInfo.reduce((acc, row) => acc + row.count, 0)
) )
} else { } else {
let newRightRowsInfo = normalizeModules(rack.value.moduleTpCd, rightRowsInfo)
return ( return (
rack.value.moduleTpCd === rightRowsInfo.moduleTotalTp && rack.value.moduleTpCd === newRightRowsInfo.moduleTotalTp &&
rack.value.moduleRows === rightRowsInfo.rowsInfo.reduce((acc, row) => acc + row.count, 0) && rack.value.moduleRows === newRightRowsInfo.rowsInfo.reduce((acc, row) => acc + row.count, 0) &&
Number(rack.value.moduleTpRows1) === rightRowsInfo.rowsInfo[0].count && newRightRowsInfo.rowsInfo.every((row, index) => {
Number(rack.value.moduleTpRows2) === rightRowsInfo.rowsInfo[1].count const rackRowCount = Number(rack.value[`moduleTpRows${index + 1}`]) // 동적으로 접근
return rackRowCount === row.count
})
) )
} }
})?.value.racks })?.value.racks
@ -404,11 +414,15 @@ export const useTrestle = () => {
rack.value.moduleRows === centerRowsInfo.rowsInfo.reduce((acc, row) => acc + row.count, 0) rack.value.moduleRows === centerRowsInfo.rowsInfo.reduce((acc, row) => acc + row.count, 0)
) )
} else { } else {
let newCenterRowsInfo = normalizeModules(rack.value.moduleTpCd, centerRowsInfo)
return ( return (
rack.value.moduleTpCd === centerRowsInfo.moduleTotalTp && rack.value.moduleTpCd === newCenterRowsInfo.moduleTotalTp &&
rack.value.moduleRows === centerRowsInfo.rowsInfo.reduce((acc, row) => acc + row.count, 0) && rack.value.moduleRows === newCenterRowsInfo.rowsInfo.reduce((acc, row) => acc + row.count, 0) &&
Number(rack.value.moduleTpRows1) === centerRowsInfo.rowsInfo[0].count && newCenterRowsInfo.rowsInfo.every((row, index) => {
Number(rack.value.moduleTpRows2) === centerRowsInfo.rowsInfo[1].count const rackRowCount = Number(rack.value[`moduleTpRows${index + 1}`]) // 동적으로 접근
return rackRowCount === row.count
})
) )
} }
})?.value.racks })?.value.racks
@ -499,11 +513,15 @@ export const useTrestle = () => {
rack.value.moduleRows === leftRowsInfo.rowsInfo.reduce((acc, row) => acc + row.count, 0) rack.value.moduleRows === leftRowsInfo.rowsInfo.reduce((acc, row) => acc + row.count, 0)
) )
} else { } else {
let newLeftRowsInfo = normalizeModules(rack.value.moduleTpCd, leftRowsInfo)
return ( return (
rack.value.moduleTpCd === leftRowsInfo.moduleTotalTp && rack.value.moduleTpCd === newLeftRowsInfo.moduleTotalTp &&
rack.value.moduleRows === leftRowsInfo.rowsInfo.reduce((acc, row) => acc + row.count, 0) && rack.value.moduleRows === newLeftRowsInfo.rowsInfo.reduce((acc, row) => acc + row.count, 0) &&
Number(rack.value.moduleTpRows1) === leftRowsInfo.rowsInfo[0].count && newLeftRowsInfo.rowsInfo.every((row, index) => {
Number(rack.value.moduleTpRows2) === leftRowsInfo.rowsInfo[1].count const rackRowCount = Number(rack.value[`moduleTpRows${index + 1}`]) // 동적으로 접근
return rackRowCount === row.count
})
) )
} }
})?.value.racks })?.value.racks
@ -575,11 +593,16 @@ export const useTrestle = () => {
rack.value.moduleRows === rightRowsInfo.rowsInfo.reduce((acc, row) => acc + row.count, 0) rack.value.moduleRows === rightRowsInfo.rowsInfo.reduce((acc, row) => acc + row.count, 0)
) )
} else { } else {
// 변환 C1C2만 있는경우 C3 0개로 추가해준다.
let newRightRowsInfo = normalizeModules(rack.value.moduleTpCd, rightRowsInfo)
return ( return (
rack.value.moduleTpCd === rightRowsInfo.moduleTotalTp && rack.value.moduleTpCd === newRightRowsInfo.moduleTotalTp &&
rack.value.moduleRows === rightRowsInfo.rowsInfo.reduce((acc, row) => acc + row.count, 0) && rack.value.moduleRows === newRightRowsInfo.rowsInfo.reduce((acc, row) => acc + row.count, 0) &&
Number(rack.value.moduleTpRows1) === rightRowsInfo.rowsInfo[0].count && newRightRowsInfo.rowsInfo.every((row, index) => {
Number(rack.value.moduleTpRows2) === rightRowsInfo.rowsInfo[1].count const rackRowCount = Number(rack.value[`moduleTpRows${index + 1}`]) // 동적으로 접근
return rackRowCount === row.count
})
) )
} }
})?.value.racks })?.value.racks
@ -634,6 +657,31 @@ export const useTrestle = () => {
return { moduleTotalTp, rowsInfo } return { moduleTotalTp, rowsInfo }
} }
function normalizeModules(rackTpCd, data) {
// rackTpCd를 숫자를 기준으로 자른다.
const allModules = rackTpCd.match(/[A-Za-z]+\d+/g) || [] // 모든 모듈 유형
// 현재 존재하는 모듈 유형을 추출
const existingModules = data.rowsInfo.map((row) => row.moduleTpCd)
const result = { ...data, rowsInfo: [...data.rowsInfo] }
// 없는 모듈을 추가 (count: 0)
allModules.forEach((module) => {
if (!existingModules.includes(module)) {
result.rowsInfo.push({ moduleTpCd: module, count: 0 })
}
})
// rowsInfo를 C1, C2, C3 순서로 정렬
result.rowsInfo.sort((a, b) => allModules.indexOf(a.moduleTpCd) - allModules.indexOf(b.moduleTpCd))
// moduleTotalTp를 C1C2C3로 설정
result.moduleTotalTp = allModules.join('')
return result
}
// itemList 조회 후 estimateParam에 저장 // itemList 조회 후 estimateParam에 저장
const getEstimateData = async () => { const getEstimateData = async () => {
const surfaces = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE) const surfaces = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE)
@ -660,13 +708,18 @@ export const useTrestle = () => {
}) })
// trestles 배열에서 null인 경우 제거 // trestles 배열에서 null인 경우 제거
const params = { trestles, pcses, modules }
const dblCblTotCnt = getTotalConnectCableCnt()
const params = { trestles, pcses, modules, dblCblTotCnt }
//견적서 itemList 조회 //견적서 itemList 조회
const { data, data2, result } = await getQuotationItem(params) const { data, data2, result } = await getQuotationItem(params)
if (result.resultCode === 'E') { if (result.resultCode === 'E') {
swalFire({ text: result.resultMsg, icon: 'error' }) swalFire({ text: result.resultMsg, icon: 'error' })
clear()
setViewCircuitNumberTexts(true)
setIsGlobalLoading(false)
return return
} }
@ -999,9 +1052,7 @@ export const useTrestle = () => {
if (!rackInfos) { if (!rackInfos) {
const maxRows = surface.trestleDetail.moduleMaxRows const maxRows = surface.trestleDetail.moduleMaxRows
const maxCols = surface.trestleDetail.moduleMaxCols const maxCols = surface.trestleDetail.moduleMaxCols
const msg = `選択した家で設置可能 const msg = `段数の上限は${maxRows}段です。 上限より上の段には設置できません`
モジュールの最大段数は${maxRows}最大列数は${maxCols}です
上限より上部に取り付けたモジュールを削除してください`
swalFire({ title: msg, type: 'alert' }) swalFire({ title: msg, type: 'alert' })
throw new Error('rackInfos is null') throw new Error('rackInfos is null')
} }
@ -2130,12 +2181,13 @@ export const useTrestle = () => {
const visited = new Set() const visited = new Set()
const width = Math.floor(moduleExample.width) const width = Math.floor(moduleExample.width)
const height = Math.floor(moduleExample.height) const height = Math.floor(moduleExample.height)
const horizonPadding = 0 // 가로 패딩 const horizonPadding = 3 // 가로 패딩
const verticalPadding = 0 // 세로 패딩 const verticalPadding = 7 // 세로 패딩
function isAdjacent(p1, p2) { function isAdjacent(p1, p2) {
const dx = Math.abs(p1.x - p2.x) const dx = Math.abs(p1.x - p2.x)
const dy = Math.abs(p1.y - p2.y) const dy = Math.abs(p1.y - p2.y)
return ( return (
(Math.abs(width + horizonPadding - dx) < 2 && dy < 2) || (Math.abs(width + horizonPadding - dx) < 2 && dy < 2) ||
(dx < 2 && Math.abs(dy - height + verticalPadding)) < 2 || (dx < 2 && Math.abs(dy - height + verticalPadding)) < 2 ||
@ -2167,6 +2219,136 @@ export const useTrestle = () => {
return groups return groups
} }
function areConnected(m1, m2, surface) {
/*const m1Fill = m1.fill
const m2Fill = m2.fill
m1.set({ fill: 'red' })
m2.set({ fill: 'blue' })
canvas.renderAll()*/
let sizes = []
const { width: currentWidth, height: currentHeight, moduleInfo: currentModuleInfo } = m1
const { width: neighborWidth, height: neighborHeight, moduleInfo: neighborModuleInfo } = m2
const { moduleTpCd: currentModuleTpCd } = currentModuleInfo
const { moduleTpCd: neighborModuleTpCd } = neighborModuleInfo
const { x: m1X, y: m1Y } = m1.getCenterPoint()
const { x: m2X, y: m2Y } = m2.getCenterPoint()
sizes.push({ width: currentWidth, height: currentHeight })
if (currentModuleTpCd !== neighborModuleTpCd) {
sizes.push({ width: neighborWidth, height: neighborHeight })
}
/*m1.set({ fill: m1Fill })
m2.set({ fill: m2Fill })
canvas.renderAll()*/
return sizes.some(({ width, height }) => {
let maxX
let maxY
let halfMaxX
let halfMaxY
const { direction, trestleDetail } = surface
let moduleIntvlHor, moduleIntvlVer
if (+roofSizeSet === 3) {
//육지붕의 경우 값 고정
moduleIntvlHor = 300
moduleIntvlVer = 100
} else {
moduleIntvlHor = trestleDetail.moduleIntvlHor
moduleIntvlVer = trestleDetail.moduleIntvlVer
}
if (direction === 'south' || direction === 'north') {
maxX = width + moduleIntvlHor / 10
maxY = height + moduleIntvlVer / 10
halfMaxX = moduleIntvlHor / 10
halfMaxY = moduleIntvlVer / 10
if (currentModuleTpCd !== neighborModuleTpCd) {
maxX = currentWidth / 2 + neighborWidth / 2 + moduleIntvlHor / 10
maxY = currentHeight / 2 + neighborHeight / 2 + moduleIntvlVer / 10
}
// console.log(maxX, maxY, halfMaxX, halfMaxY)
if (Math.abs(m1X - m2X) < 1) {
return Math.abs(Math.abs(m1Y - m2Y) - maxY) < 1
} else if (Math.abs(m1Y - m2Y) < 1) {
return Math.abs(Math.abs(m1X - m2X) - maxX) < 1
}
return (
(Math.abs(m1X - m2X) < maxX - moduleIntvlHor / 10 && Math.abs(m1Y - m2Y) < maxY - moduleIntvlVer / 10) ||
(Math.abs(Math.abs(m1X - m2X) - maxX / 2) < halfMaxX + 1 && Math.abs(Math.abs(m1Y - m2Y) - maxY) < halfMaxY + 1) ||
(Math.abs(Math.abs(m1X - m2X) - maxX) < halfMaxX + 1 && Math.abs(Math.abs(m1Y - m2Y) - maxY / 2) < halfMaxY + 1)
)
} else if (direction === 'east' || direction === 'west') {
maxX = height + moduleIntvlHor / 10
maxY = width + moduleIntvlVer / 10
halfMaxX = moduleIntvlVer / 10
halfMaxY = moduleIntvlHor / 10
if (currentModuleTpCd !== neighborModuleTpCd) {
maxX = currentHeight / 2 + neighborHeight / 2 + moduleIntvlVer / 10
maxY = currentWidth / 2 + neighborWidth / 2 + moduleIntvlHor / 10
}
if (Math.abs(m1X - m2X) < 1) {
return Math.abs(Math.abs(m1Y - m2Y) - maxX) < 1
} else if (Math.abs(m1Y - m2Y) < 1) {
return Math.abs(Math.abs(m1X - m2X) - maxY) < 1
}
return (
(Math.abs(m1X - m2X) <= maxY - moduleIntvlVer / 10 && Math.abs(m1Y - m2Y) <= maxX - moduleIntvlHor / 10) ||
(Math.abs(Math.abs(m1X - m2X) - maxY / 2) < halfMaxY + 1 && Math.abs(Math.abs(m1Y - m2Y) - maxX) < halfMaxX + 1) ||
(Math.abs(Math.abs(m1X - m2X) - maxY) < halfMaxY + 1 && Math.abs(Math.abs(m1Y - m2Y) - maxX / 2) < halfMaxX + 1)
)
}
})
}
// 25-04-02 추가
// 그룹화
function groupPoints(modules, surface) {
const groups = []
const visited = new Set()
for (const point of modules) {
const { x: pointX, y: pointY } = point.getCenterPoint()
const key = `${pointX},${pointY}`
if (visited.has(key)) continue
const queue = [point]
const group = []
while (queue.length > 0) {
const current = queue.shift()
const { x: currentX, y: currentY } = current.getCenterPoint()
const currentKey = `${currentX},${currentY}`
if (visited.has(currentKey)) continue
visited.add(currentKey)
group.push(current)
for (const neighbor of modules) {
const { x: neighborX, y: neighborY } = neighbor.getCenterPoint()
const neighborKey = `${neighborX},${neighborY}`
if (!visited.has(neighborKey) && areConnected(current, neighbor, surface)) {
queue.push(neighbor)
}
}
}
groups.push(group)
}
return groups
}
// 각도에 따른 길이 반환 // 각도에 따른 길이 반환
function getTrestleLength(length, degree) { function getTrestleLength(length, degree) {
if (roofSizeSet !== 1) { if (roofSizeSet !== 1) {
@ -2864,5 +3046,82 @@ export const useTrestle = () => {
return surfaces.every((surface) => surface.isComplete) return surfaces.every((surface) => surface.isComplete)
} }
return { apply, getTrestleParams, clear, setViewCircuitNumberTexts, getEstimateData, setAllModuleSurfaceIsComplete, isAllComplete } const groupByType = (originArr = []) => {
const grouped = {}
const newArr = [...originArr]
// 타입별로 객체들을 분류
for (const item of newArr) {
if (!grouped[item.circuitNumber]) {
grouped[item.circuitNumber] = []
}
grouped[item.circuitNumber].push(item)
}
// 객체를 배열로 변환
return Object.values(grouped)
}
function groupByCircuitAndSurface(arr) {
const circuitGroups = {}
for (const item of arr) {
const { circuitNumber, surfaceId } = item
if (!circuitGroups[circuitNumber]) {
circuitGroups[circuitNumber] = new Map()
}
const surfaceMap = circuitGroups[circuitNumber]
const key = surfaceId
if (!surfaceMap.has(key)) {
surfaceMap.set(key, [])
}
surfaceMap.get(key).push(item)
}
// 결과: circuitNumber별 surface 그룹 수
const result = {}
for (const [circuit, surfaceMap] of Object.entries(circuitGroups)) {
result[circuit] = surfaceMap.size
}
return result
}
// 양단 케이블 구하는 공식
const getTotalConnectCableCnt = () => {
let cnt = 0
const surfaces = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE)
const modules = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE)
surfaces.forEach((surface) => {
const modules = surface.modules
const groups = groupByType(modules)
groups.forEach((group) => {
const result = groupPoints(group, surface)
cnt += result.length - 1
})
})
const groupByCircuitAndSurfaceCnt = groupByCircuitAndSurface(modules)
Object.keys(groupByCircuitAndSurfaceCnt).forEach((key) => {
cnt += groupByCircuitAndSurfaceCnt[key] - 1
})
return cnt
}
return {
apply,
getTrestleParams,
clear,
setViewCircuitNumberTexts,
getEstimateData,
setAllModuleSurfaceIsComplete,
isAllComplete,
groupCoordinates,
groupPoints,
}
} }

View File

@ -123,6 +123,7 @@ export function useCanvasSetting(executeEffect = true) {
const resetModuleSelectionData = useResetRecoilState(moduleSelectionDataState) /* 다음으로 넘어가는 최종 데이터 */ const resetModuleSelectionData = useResetRecoilState(moduleSelectionDataState) /* 다음으로 넘어가는 최종 데이터 */
const resetSelectedModules = useResetRecoilState(selectedModuleState) /* 선택된 모듈 */ const resetSelectedModules = useResetRecoilState(selectedModuleState) /* 선택된 모듈 */
const { trigger: orientationTrigger } = useCanvasPopupStatusController(1)
const { trigger: moduleSelectedDataTrigger } = useCanvasPopupStatusController(2) const { trigger: moduleSelectedDataTrigger } = useCanvasPopupStatusController(2)
const [raftCodes, setRaftCodes] = useState([]) /* 서까래 정보 */ const [raftCodes, setRaftCodes] = useState([]) /* 서까래 정보 */
@ -526,7 +527,10 @@ export function useCanvasSetting(executeEffect = true) {
/** 모듈 선택 데이터 초기화 */ /** 모듈 선택 데이터 초기화 */
resetModuleSelectionData() resetModuleSelectionData()
moduleSelectedDataTrigger({ common: {}, module: {}, roofConstructions: [] }) //1번 초기화
orientationTrigger({ compasDeg: 0, common: {}, module: {} })
//2번 초기화
moduleSelectedDataTrigger({ roofConstructions: [] })
const isModuleExist = canvas.getObjects().some((obj) => obj.name === POLYGON_TYPE.MODULE) const isModuleExist = canvas.getObjects().some((obj) => obj.name === POLYGON_TYPE.MODULE)
if (!isModuleExist) { if (!isModuleExist) {
resetSelectedModules() resetSelectedModules()

View File

@ -5,16 +5,6 @@ import { useEvent } from '@/hooks/useEvent'
import { useMouse } from '@/hooks/useMouse' import { useMouse } from '@/hooks/useMouse'
import { useLine } from '@/hooks/useLine' import { useLine } from '@/hooks/useLine'
import { useTempGrid } from '@/hooks/useTempGrid' import { useTempGrid } from '@/hooks/useTempGrid'
import {
outerLineAngle1State,
outerLineAngle2State,
outerLineArrow1State,
outerLineArrow2State,
outerLineDiagonalState,
outerLineLength1State,
outerLineLength2State,
outerLineTypeState,
} from '@/store/outerLineAtom'
import { calculateIntersection, distanceBetweenPoints, findClosestPoint, isPointOnLine } from '@/util/canvas-util' import { calculateIntersection, distanceBetweenPoints, findClosestPoint, isPointOnLine } from '@/util/canvas-util'
import { fabric } from 'fabric' import { fabric } from 'fabric'
import { useAdsorptionPoint } from '@/hooks/useAdsorptionPoint' import { useAdsorptionPoint } from '@/hooks/useAdsorptionPoint'
@ -23,6 +13,16 @@ import { usePopup } from '@/hooks/usePopup'
import { calculateAngle, isSamePoint } from '@/util/qpolygon-utils' import { calculateAngle, isSamePoint } from '@/util/qpolygon-utils'
import { POLYGON_TYPE } from '@/common/common' import { POLYGON_TYPE } from '@/common/common'
import { useMessage } from '../useMessage' import { useMessage } from '../useMessage'
import {
auxiliaryLineAngle1State,
auxiliaryLineAngle2State,
auxiliaryLineArrow1State,
auxiliaryLineArrow2State,
auxiliaryLineDiagonalState,
auxiliaryLineLength1State,
auxiliaryLineLength2State,
auxiliaryLineTypeState,
} from '@/store/auxiliaryLineAtom'
// 보조선 작성 // 보조선 작성
export function useAuxiliaryDrawing(id, isUseEffect = true) { export function useAuxiliaryDrawing(id, isUseEffect = true) {
@ -49,20 +49,20 @@ export function useAuxiliaryDrawing(id, isUseEffect = true) {
const length2Ref = useRef(0) const length2Ref = useRef(0)
const angle1Ref = useRef('') const angle1Ref = useRef('')
const angle2Ref = useRef('') const angle2Ref = useRef('')
const [length1, setLength1] = useRecoilState(outerLineLength1State) const [length1, setLength1] = useRecoilState(auxiliaryLineLength1State)
const [length2, setLength2] = useRecoilState(outerLineLength2State) const [length2, setLength2] = useRecoilState(auxiliaryLineLength2State)
const [arrow1, setArrow1] = useRecoilState(outerLineArrow1State) const [arrow1, setArrow1] = useRecoilState(auxiliaryLineArrow1State)
const [arrow2, setArrow2] = useRecoilState(outerLineArrow2State) const [arrow2, setArrow2] = useRecoilState(auxiliaryLineArrow2State)
const [type, setType] = useRecoilState(outerLineTypeState) const [type, setType] = useRecoilState(auxiliaryLineTypeState)
const [angle1, setAngle1] = useRecoilState(outerLineAngle1State) const [angle1, setAngle1] = useRecoilState(auxiliaryLineAngle1State)
const [angle2, setAngle2] = useRecoilState(outerLineAngle2State) const [angle2, setAngle2] = useRecoilState(auxiliaryLineAngle2State)
const [outerLineDiagonalLength, setOuterLineDiagonalLength] = useRecoilState(outerLineDiagonalState) const [auxiliaryLineDiagonalLength, setAuxiliaryLineDiagonalLength] = useRecoilState(auxiliaryLineDiagonalState)
const arrow1Ref = useRef(arrow1) const arrow1Ref = useRef(arrow1)
const arrow2Ref = useRef(arrow2) const arrow2Ref = useRef(arrow2)
const typeRef = useRef(type) const typeRef = useRef(type)
const outerLineDiagonalLengthRef = useRef(0) const auxiliaryLineDiagonalLengthRef = useRef(0)
const intersectionPoints = useRef([]) const intersectionPoints = useRef([])
const verticalHorizontalMode = useRecoilValue(verticalHorizontalModeState) const verticalHorizontalMode = useRecoilValue(verticalHorizontalModeState)
@ -84,7 +84,7 @@ export function useAuxiliaryDrawing(id, isUseEffect = true) {
// innerLines가 있을경우 삭제 // innerLines가 있을경우 삭제
const roofs = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF) const roofs = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
if (roofs.length === 0) { if (roofs.length === 0) {
swalFire({ text: '지붕형상이 없습니다.' }) swalFire({ text: getMessage('roof.line.not.found') })
closePopup(id) closePopup(id)
return return
} }
@ -124,7 +124,7 @@ export function useAuxiliaryDrawing(id, isUseEffect = true) {
setAngle1(0) setAngle1(0)
setAngle2(0) setAngle2(0)
setOuterLineDiagonalLength(0) setAuxiliaryLineDiagonalLength(0)
} }
const move = (object, x, y) => { const move = (object, x, y) => {
@ -408,7 +408,7 @@ export function useAuxiliaryDrawing(id, isUseEffect = true) {
//대각선 완료될 경우 확인 //대각선 완료될 경우 확인
const checkDiagonal = (direction) => { const checkDiagonal = (direction) => {
const activeElem = document.activeElement const activeElem = document.activeElement
const diagonalLength = outerLineDiagonalLengthRef.current.value // 대각선 길이 const diagonalLength = auxiliaryLineDiagonalLengthRef.current.value // 대각선 길이
const length1Value = length1Ref.current.value const length1Value = length1Ref.current.value
@ -913,9 +913,9 @@ export function useAuxiliaryDrawing(id, isUseEffect = true) {
angle2, angle2,
setAngle2, setAngle2,
angle2Ref, angle2Ref,
outerLineDiagonalLength, auxiliaryLineDiagonalLength,
setOuterLineDiagonalLength, setAuxiliaryLineDiagonalLength,
outerLineDiagonalLengthRef, auxiliaryLineDiagonalLengthRef,
type, type,
setType, setType,
handleFix, handleFix,

View File

@ -52,7 +52,7 @@ export function useEavesGableEdit(id) {
useEffect(() => { useEffect(() => {
const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
if (!outerLineFix || outerLines.length === 0) { if (!outerLineFix || outerLines.length === 0) {
swalFire({ text: '외벽선이 없습니다.' }) swalFire({ text: getMessage('wall.line.not.found') })
closePopup(id) closePopup(id)
} }
}, []) }, [])

View File

@ -32,6 +32,7 @@ import { outlineDisplaySelector } from '@/store/settingAtom'
import { usePopup } from '@/hooks/usePopup' import { usePopup } from '@/hooks/usePopup'
import PropertiesSetting from '@/components/floor-plan/modal/outerlinesetting/PropertiesSetting' import PropertiesSetting from '@/components/floor-plan/modal/outerlinesetting/PropertiesSetting'
import Big from 'big.js' import Big from 'big.js'
import RoofShapeSetting from '@/components/floor-plan/modal/roofShape/RoofShapeSetting'
//외벽선 그리기 //외벽선 그리기
export function useOuterLineWall(id, propertiesId) { export function useOuterLineWall(id, propertiesId) {
@ -256,7 +257,7 @@ export function useOuterLineWall(id, propertiesId) {
canvas?.renderAll() canvas?.renderAll()
setOuterLineFix(true) setOuterLineFix(true)
closePopup(id) closePopup(id)
addPopup(propertiesId, 1, <PropertiesSetting id={propertiesId} pos={{ x: 50, y: 230 }} />) addPopup(propertiesId, 1, <RoofShapeSetting id={propertiesId} pos={{ x: 50, y: 230 }} />)
} }
if (points.length < 3) { if (points.length < 3) {

View File

@ -1,6 +1,6 @@
import { useRecoilState, useRecoilValue, useResetRecoilState, useSetRecoilState } from 'recoil' import { useRecoilState, useRecoilValue, useResetRecoilState, useSetRecoilState } from 'recoil'
import { canvasState, currentAngleTypeSelector, currentMenuState, currentObjectState } from '@/store/canvasAtom' import { canvasState, currentAngleTypeSelector, currentMenuState, currentObjectState } from '@/store/canvasAtom'
import { useEffect, useRef, useState } from 'react' import { useContext, useEffect, useRef, useState } from 'react'
import { useAxios } from '@/hooks/useAxios' import { useAxios } from '@/hooks/useAxios'
import { useSwal } from '@/hooks/useSwal' import { useSwal } from '@/hooks/useSwal'
import { usePolygon } from '@/hooks/usePolygon' import { usePolygon } from '@/hooks/usePolygon'
@ -11,6 +11,8 @@ import {
roofDisplaySelector, roofDisplaySelector,
roofMaterialsSelector, roofMaterialsSelector,
selectedRoofMaterialSelector, selectedRoofMaterialSelector,
settingModalFirstOptionsState,
corridorDimensionSelector,
} from '@/store/settingAtom' } from '@/store/settingAtom'
import { usePopup } from '@/hooks/usePopup' import { usePopup } from '@/hooks/usePopup'
import { POLYGON_TYPE } from '@/common/common' import { POLYGON_TYPE } from '@/common/common'
@ -26,6 +28,9 @@ import { getChonByDegree, getDegreeByChon } from '@/util/canvas-util'
import { moduleSelectionDataState } from '@/store/selectedModuleOptions' import { moduleSelectionDataState } from '@/store/selectedModuleOptions'
import { useCanvasPopupStatusController } from '@/hooks/common/useCanvasPopupStatusController' import { useCanvasPopupStatusController } from '@/hooks/common/useCanvasPopupStatusController'
import { outerLinePointsState } from '@/store/outerLineAtom' import { outerLinePointsState } from '@/store/outerLineAtom'
import { QcastContext } from '@/app/QcastProvider'
import { usePlan } from '@/hooks/usePlan'
import { roofsState } from '@/store/roofAtom'
export function useRoofAllocationSetting(id) { export function useRoofAllocationSetting(id) {
const canvas = useRecoilValue(canvasState) const canvas = useRecoilValue(canvasState)
@ -49,11 +54,13 @@ export function useRoofAllocationSetting(id) {
const { get, post } = useAxios(globalLocaleState) const { get, post } = useAxios(globalLocaleState)
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { swalFire } = useSwal() const { swalFire } = useSwal()
const { setIsGlobalLoading } = useContext(QcastContext)
const { setSurfaceShapePattern } = useRoofFn() const { setSurfaceShapePattern } = useRoofFn()
const { saveCanvas } = usePlan()
const [roofsStore, setRoofsStore] = useRecoilState(roofsState)
const [moduleSelectionData, setModuleSelectionData] = useRecoilState(moduleSelectionDataState) const [moduleSelectionData, setModuleSelectionData] = useRecoilState(moduleSelectionDataState)
const resetPoints = useResetRecoilState(outerLinePointsState) const resetPoints = useResetRecoilState(outerLinePointsState)
const [corridorDimension, setCorridorDimension] = useRecoilState(corridorDimensionSelector)
useEffect(() => { useEffect(() => {
/** 배치면 초기설정에서 선택한 지붕재 배열 설정 */ /** 배치면 초기설정에서 선택한 지붕재 배열 설정 */
@ -68,30 +75,19 @@ export function useRoofAllocationSetting(id) {
roof.innerLines.forEach((line) => { roof.innerLines.forEach((line) => {
/** 실측값이 없는 경우 라인 두께 4로 설정 */ /** 실측값이 없는 경우 라인 두께 4로 설정 */
if (!line.attributes.actualSize || line.attributes?.actualSize === 0) { if (!line.attributes.actualSize || line.attributes?.actualSize === 0) {
line.set({ line.set({ strokeWidth: 4, stroke: 'black', selectable: true })
strokeWidth: 4,
stroke: 'black',
selectable: true,
})
} }
/** 현재 선택된 라인인 경우 라인 두께 2로 설정 */ /** 현재 선택된 라인인 경우 라인 두께 2로 설정 */
if (editingLines.includes(line)) { if (editingLines.includes(line)) {
line.set({ line.set({ strokeWidth: 2, stroke: 'black', selectable: true })
strokeWidth: 2,
stroke: 'black',
selectable: true,
})
} }
}) })
}) })
/** 현재 선택된 객체가 보조라인, 피라미드, 힙인 경우 두께 4로 설정 */ /** 현재 선택된 객체가 보조라인, 피라미드, 힙인 경우 두께 4로 설정 */
if (currentObject && currentObject.name && ['auxiliaryLine', 'ridge', 'hip'].includes(currentObject.name)) { if (currentObject && currentObject.name && ['auxiliaryLine', 'ridge', 'hip'].includes(currentObject.name)) {
currentObject.set({ currentObject.set({ strokeWidth: 4, stroke: '#EA10AC' })
strokeWidth: 4,
stroke: '#EA10AC',
})
} }
}, [currentObject]) }, [currentObject])
@ -99,7 +95,7 @@ export function useRoofAllocationSetting(id) {
/** 현재 선택된 객체가 보조라인, 피라미드, 힙인 경우 두께 4로 설정 */ /** 현재 선택된 객체가 보조라인, 피라미드, 힙인 경우 두께 4로 설정 */
const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF) const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
if (roofBases.length === 0) { if (roofBases.length === 0) {
swalFire({ text: '할당할 지붕이 없습니다.' }) swalFire({ text: getMessage('roofAllocation.not.found'), icon: 'warning' })
closePopup(id) closePopup(id)
} }
@ -112,9 +108,7 @@ export function useRoofAllocationSetting(id) {
*/ */
const fetchBasicSettings = async (planNo) => { const fetchBasicSettings = async (planNo) => {
try { try {
await get({ await get({ url: `/api/canvas-management/canvas-basic-settings/by-object/${correntObjectNo}/${planNo}` }).then((res) => {
url: `/api/canvas-management/canvas-basic-settings/by-object/${correntObjectNo}/${planNo}`,
}).then((res) => {
let roofsArray = {} let roofsArray = {}
if (res.length > 0) { if (res.length > 0) {
@ -184,11 +178,7 @@ export function useRoofAllocationSetting(id) {
selectedRoofMaterial: selectRoofs.find((roof) => roof.selected), selectedRoofMaterial: selectRoofs.find((roof) => roof.selected),
}) })
setBasicInfo({ setBasicInfo({ planNo: '' + res[0].planNo, roofSizeSet: '' + res[0].roofSizeSet, roofAngleSet: '' + res[0].roofAngleSet })
planNo: '' + res[0].planNo,
roofSizeSet: '' + res[0].roofSizeSet,
roofAngleSet: '' + res[0].roofAngleSet,
})
}) })
} catch (error) { } catch (error) {
console.error('Data fetching error:', error) console.error('Data fetching error:', error)
@ -200,6 +190,7 @@ export function useRoofAllocationSetting(id) {
*/ */
const basicSettingSave = async () => { const basicSettingSave = async () => {
try { try {
setIsGlobalLoading(true)
const patternData = { const patternData = {
objectNo: correntObjectNo, objectNo: correntObjectNo,
planNo: Number(basicSetting.planNo), planNo: Number(basicSetting.planNo),
@ -222,6 +213,8 @@ export function useRoofAllocationSetting(id) {
await post({ url: `/api/canvas-management/roof-allocation-settings`, data: patternData }).then((res) => { await post({ url: `/api/canvas-management/roof-allocation-settings`, data: patternData }).then((res) => {
swalFire({ text: getMessage(res.returnMessage) }) swalFire({ text: getMessage(res.returnMessage) })
setIsGlobalLoading(false)
saveCanvas(false)
}) })
//Recoil 설정 //Recoil 설정
@ -242,36 +235,48 @@ export function useRoofAllocationSetting(id) {
swalFire({ type: 'alert', icon: 'error', text: getMessage('roof.exceed.count') }) swalFire({ type: 'alert', icon: 'error', text: getMessage('roof.exceed.count') })
return return
} }
setCurrentRoofList([
...currentRoofList, const originCurrentRoofList = currentRoofList.map((roof) => {
{ return { ...roof, selected: false }
...currentRoofMaterial, })
selected: false, originCurrentRoofList.push({
id: currentRoofMaterial.roofMatlCd, ...currentRoofMaterial,
name: currentRoofMaterial.roofMatlNm, selected: true,
index: currentRoofList.length, id: currentRoofMaterial.roofMatlCd,
}, name: currentRoofMaterial.roofMatlNm,
]) index: currentRoofList.length,
})
setCurrentRoofList(originCurrentRoofList)
} }
/** /**
* 지붕재 삭제 * 지붕재 삭제
*/ */
const onDeleteRoofMaterial = (idx) => { const onDeleteRoofMaterial = (idx) => {
const roofs = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
for (let i = 0; i < roofs.length; i++) {
if (roofs[i].roofMaterial?.index === idx) {
swalFire({ type: 'alert', icon: 'error', text: getMessage('roof.material.can.not.delete') })
return
}
}
const isSelected = currentRoofList[idx].selected const isSelected = currentRoofList[idx].selected
const newRoofList = JSON.parse(JSON.stringify(currentRoofList)).filter((_, index) => index !== idx) const newRoofList = JSON.parse(JSON.stringify(currentRoofList)).filter((_, index) => index !== idx)
if (isSelected) { if (isSelected) {
newRoofList[0].selected = true newRoofList[0].selected = true
} }
setCurrentRoofList(newRoofList) setCurrentRoofList(newRoofList)
setRoofsStore(newRoofList)
setModuleSelectionData({ ...moduleSelectionData, roofConstructions: newRoofList })
} }
/** /**
* 선택한 지붕재로 할당 * 선택한 지붕재로 할당
*/ */
const handleSave = () => { const handleSave = () => {
basicSettingSave()
/** /**
* 모두 actualSize 있으면 바로 적용 없으면 actualSize 설정 * 모두 actualSize 있으면 바로 적용 없으면 actualSize 설정
*/ */
@ -280,6 +285,7 @@ export function useRoofAllocationSetting(id) {
} else { } else {
apply() apply()
resetPoints() resetPoints()
basicSettingSave()
} }
} }
@ -287,23 +293,47 @@ export function useRoofAllocationSetting(id) {
* 지붕재 오른쪽 마우스 클릭 단일로 지붕재 변경 필요한 경우 * 지붕재 오른쪽 마우스 클릭 단일로 지붕재 변경 필요한 경우
*/ */
const handleSaveContext = () => { const handleSaveContext = () => {
basicSettingSave()
const newRoofList = currentRoofList.map((roof, idx) => { const newRoofList = currentRoofList.map((roof, idx) => {
if (roof.index !== idx) {
// 기존 저장된 지붕재의 index 수정
const roofs = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF && obj.roofMaterial?.index === roof.index)
roofs.forEach((roof) => {
setSurfaceShapePattern(roof, roofDisplay.column, false, { ...roof, index: idx }, true)
})
}
return { ...roof, index: idx, raft: roof.raft ? roof.raft : roof.raftBaseCd } return { ...roof, index: idx, raft: roof.raft ? roof.raft : roof.raftBaseCd }
}) })
setBasicSetting((prev) => { setBasicSetting((prev) => {
return { return { ...prev, selectedRoofMaterial: newRoofList.find((roof) => roof.selected) }
...prev,
selectedRoofMaterial: newRoofList.find((roof) => roof.selected),
}
}) })
setRoofList(newRoofList) setRoofList(newRoofList)
setRoofMaterials(newRoofList)
setRoofsStore(newRoofList)
const selectedRoofMaterial = newRoofList.find((roof) => roof.selected) const selectedRoofMaterial = newRoofList.find((roof) => roof.selected)
setSurfaceShapePattern(currentObject, roofDisplay.column, false, selectedRoofMaterial, true) setSurfaceShapePattern(currentObject, roofDisplay.column, false, selectedRoofMaterial, true)
drawDirectionArrow(currentObject) drawDirectionArrow(currentObject)
modifyModuleSelectionData() modifyModuleSelectionData()
closeAll() closeAll()
basicSettingSave()
setModuleSelectionData({ ...moduleSelectionData, roofConstructions: newRoofList })
}
/**
* 기존 세팅된 지붕에 지붕재 내용을 바뀐 내용으로 수정
* @param newRoofMaterials
*/
const setRoofMaterials = (newRoofMaterials) => {
const roofs = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
newRoofMaterials.forEach((roofMaterial) => {
const index = roofMaterial.index
const tempRoofs = roofs.filter((roof) => roof.roofMaterial?.index === index)
tempRoofs.forEach((roof) => {
setSurfaceShapePattern(roof, roofDisplay.column, false, roofMaterial)
})
})
} }
/** /**
@ -313,11 +343,7 @@ export function useRoofAllocationSetting(id) {
if (!checkInnerLines()) { if (!checkInnerLines()) {
apply() apply()
} else { } else {
swalFire({ swalFire({ type: 'alert', icon: 'error', text: getMessage('실제치수를 입력해 주세요.') })
type: 'alert',
icon: 'error',
text: getMessage('실제치수를 입력해 주세요.'),
})
} }
} }
@ -332,11 +358,7 @@ export function useRoofAllocationSetting(id) {
if (roof.separatePolygon.length === 0) { if (roof.separatePolygon.length === 0) {
roof.innerLines.forEach((line) => { roof.innerLines.forEach((line) => {
if (!line.attributes.actualSize || line.attributes?.actualSize === 0) { if (!line.attributes.actualSize || line.attributes?.actualSize === 0) {
line.set({ line.set({ strokeWidth: 4, stroke: 'black', selectable: true })
strokeWidth: 4,
stroke: 'black',
selectable: true,
})
result = true result = true
} }
}) })
@ -361,6 +383,7 @@ export function useRoofAllocationSetting(id) {
splitPolygonWithLines(roofBase) splitPolygonWithLines(roofBase)
} }
} catch (e) { } catch (e) {
console.log(e)
return return
} }
@ -383,10 +406,7 @@ export function useRoofAllocationSetting(id) {
}) })
setBasicSetting((prev) => { setBasicSetting((prev) => {
return { return { ...prev, selectedRoofMaterial: newRoofList.find((roof) => roof.selected) }
...prev,
selectedRoofMaterial: newRoofList.find((roof) => roof.selected),
}
}) })
setRoofList(newRoofList) setRoofList(newRoofList)
@ -394,9 +414,7 @@ export function useRoofAllocationSetting(id) {
roofs.forEach((roof) => { roofs.forEach((roof) => {
if (roof.isFixed) return if (roof.isFixed) return
roof.set({ roof.set({ isFixed: true })
isFixed: true,
})
/** 모양 패턴 설정 */ /** 모양 패턴 설정 */
setSurfaceShapePattern( setSurfaceShapePattern(
@ -408,6 +426,8 @@ export function useRoofAllocationSetting(id) {
drawDirectionArrow(roof) drawDirectionArrow(roof)
}) })
setRoofMaterials(newRoofList)
setRoofsStore(newRoofList)
/** 외곽선 삭제 */ /** 외곽선 삭제 */
const removeTargets = canvas.getObjects().filter((obj) => obj.name === 'outerLinePoint' || obj.name === 'outerLine') const removeTargets = canvas.getObjects().filter((obj) => obj.name === 'outerLinePoint' || obj.name === 'outerLine')
removeTargets.forEach((obj) => { removeTargets.forEach((obj) => {
@ -416,9 +436,12 @@ export function useRoofAllocationSetting(id) {
setEditingLines([]) setEditingLines([])
closeAll() closeAll()
setSelectedMenu('surface') setSelectedMenu('surface')
//지붕면 완성 후 실측치 로 보이도록 수정
setCorridorDimension(1)
/** 모듈 선택 데이터 초기화 */ /** 모듈 선택 데이터 초기화 */
modifyModuleSelectionData() // modifyModuleSelectionData()
setModuleSelectionData({ ...moduleSelectionData, roofConstructions: newRoofList })
} }
/** /**
@ -431,10 +454,7 @@ export function useRoofAllocationSetting(id) {
if (id === line.id) { if (id === line.id) {
setEditingLines([...editingLines.filter((editLine) => editLine.id !== line.id), line]) setEditingLines([...editingLines.filter((editLine) => editLine.id !== line.id), line])
line.attributes.actualSize = size line.attributes.actualSize = size
line.set({ line.set({ strokeWidth: 2, stroke: 'black' })
strokeWidth: 2,
stroke: 'black',
})
} }
}) })
}) })
@ -449,7 +469,7 @@ export function useRoofAllocationSetting(id) {
const selectedRoofMaterial = roofMaterials.find((roof) => roof.roofMatlCd === value.id) const selectedRoofMaterial = roofMaterials.find((roof) => roof.roofMatlCd === value.id)
const newRoofList = currentRoofList.map((roof, idx) => { const newRoofList = currentRoofList.map((roof, idx) => {
if (idx === index) { if (idx === index) {
return { ...selectedRoofMaterial, selected: roof.selected } return { ...selectedRoofMaterial, selected: roof.selected, index }
} }
return roof return roof
}) })
@ -539,7 +559,7 @@ export function useRoofAllocationSetting(id) {
* 모듈 선택에서 선택한 데이터 초기화 * 모듈 선택에서 선택한 데이터 초기화
*/ */
const modifyModuleSelectionData = () => { const modifyModuleSelectionData = () => {
if (moduleSelectionData.roofConstructions.length > 0) { if (moduleSelectionData.roofConstructions?.length > 0) {
setModuleSelectionData({ ...moduleSelectionData, roofConstructions: [] }) setModuleSelectionData({ ...moduleSelectionData, roofConstructions: [] })
moduleSelectedDataTrigger({ ...moduleSelectionData, roofConstructions: [] }) moduleSelectedDataTrigger({ ...moduleSelectionData, roofConstructions: [] })
} }

View File

@ -28,7 +28,7 @@ export function useRoofShapePassivitySetting(id) {
const { addCanvasMouseEventListener, initEvent } = useEvent() const { addCanvasMouseEventListener, initEvent } = useEvent()
// const { addCanvasMouseEventListener, initEvent } = useContext(EventContext) // const { addCanvasMouseEventListener, initEvent } = useContext(EventContext)
const { drawRoofPolygon } = useMode() const { drawRoofPolygon } = useMode()
const { addPolygonByLines } = usePolygon() const { addPolygonByLines, addLengthText } = usePolygon()
const currentObject = useRecoilValue(currentObjectState) const currentObject = useRecoilValue(currentObjectState)
const offsetRef = useRef(null) const offsetRef = useRef(null)
const pitchRef = useRef(null) const pitchRef = useRef(null)
@ -51,7 +51,7 @@ export function useRoofShapePassivitySetting(id) {
useEffect(() => { useEffect(() => {
const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
if (!outerLineFix || outerLines.length === 0) { if (!outerLineFix || outerLines.length === 0) {
swalFire({ text: '외벽선이 없습니다.' }) swalFire({ text: getMessage('wall.line.not.found') })
closePopup(id) closePopup(id)
return return
} }
@ -248,6 +248,7 @@ export function useRoofShapePassivitySetting(id) {
// 완료 한 경우에는 지붕까지 그려줌 // 완료 한 경우에는 지붕까지 그려줌
addPitchTextsByOuterLines() addPitchTextsByOuterLines()
const roof = drawRoofPolygon(wall) const roof = drawRoofPolygon(wall)
addLengthText(roof)
} }
canvas.renderAll() canvas.renderAll()

View File

@ -191,7 +191,7 @@ export function useRoofShapeSetting(id) {
let direction let direction
if (outerLines.length < 2) { if (outerLines.length < 2) {
swalFire({ text: '외벽선이 없습니다.', icon: 'error' }) swalFire({ text: getMessage('wall.line.not.found') })
return return
} }
@ -231,6 +231,21 @@ export function useRoofShapeSetting(id) {
pitch: pitchRef.current, pitch: pitchRef.current,
onlyOffset: true, onlyOffset: true,
} }
switch (line.direction) {
case 'bottom':
direction = 'east'
break
case 'top':
direction = 'west'
break
case 'left':
direction = 'south'
break
case 'right':
direction = 'north'
break
}
} }
}) })
} }
@ -716,6 +731,7 @@ export function useRoofShapeSetting(id) {
type: LINE_TYPE.WALLLINE.SHED, type: LINE_TYPE.WALLLINE.SHED,
pitch: shedPitchRef.current, pitch: shedPitchRef.current,
width: shedWidth / 10, width: shedWidth / 10,
offset: shedWidth / 10,
} }
selectedLine.attributes = { ...attributes, isFixed: true } selectedLine.attributes = { ...attributes, isFixed: true }
addPitchText(currentObject) addPitchText(currentObject)

View File

@ -60,7 +60,7 @@ export function useWallLineOffsetSetting(id) {
useEffect(() => { useEffect(() => {
const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
if (outerLines.length === 0) { if (outerLines.length === 0) {
swalFire({ text: '외벽선이 없습니다.' }) swalFire({ text: getMessage('wall.line.not.found') })
closePopup(id) closePopup(id)
return return
} }

View File

@ -14,7 +14,7 @@ import { useMouse } from '@/hooks/useMouse'
import { useLine } from '@/hooks/useLine' import { useLine } from '@/hooks/useLine'
import { useTempGrid } from '@/hooks/useTempGrid' import { useTempGrid } from '@/hooks/useTempGrid'
import { useEffect, useRef } from 'react' import { useEffect, useRef } from 'react'
import { distanceBetweenPoints } from '@/util/canvas-util' import { calculateIntersection, distanceBetweenPoints, findClosestPoint } from '@/util/canvas-util'
import { fabric } from 'fabric' import { fabric } from 'fabric'
import { calculateAngle } from '@/util/qpolygon-utils' import { calculateAngle } from '@/util/qpolygon-utils'
import { import {
@ -32,16 +32,18 @@ import {
import { usePolygon } from '@/hooks/usePolygon' import { usePolygon } from '@/hooks/usePolygon'
import { POLYGON_TYPE } from '@/common/common' import { POLYGON_TYPE } from '@/common/common'
import { usePopup } from '@/hooks/usePopup' import { usePopup } from '@/hooks/usePopup'
import { useSurfaceShapeBatch } from './useSurfaceShapeBatch'
import { roofDisplaySelector } from '@/store/settingAtom' import { roofDisplaySelector } from '@/store/settingAtom'
import { useRoofFn } from '@/hooks/common/useRoofFn' import { useRoofFn } from '@/hooks/common/useRoofFn'
import PlacementSurfaceLineProperty from '@/components/floor-plan/modal/placementShape/PlacementSurfaceLineProperty' import PlacementSurfaceLineProperty from '@/components/floor-plan/modal/placementShape/PlacementSurfaceLineProperty'
import { useAdsorptionPoint } from '@/hooks/useAdsorptionPoint'
// 배치면 그리기 // 배치면 그리기
export function usePlacementShapeDrawing(id) { export function usePlacementShapeDrawing(id) {
const canvas = useRecoilValue(canvasState) const canvas = useRecoilValue(canvasState)
const roofDisplay = useRecoilValue(roofDisplaySelector) const roofDisplay = useRecoilValue(roofDisplaySelector)
const { addCanvasMouseEventListener, addDocumentEventListener, removeAllMouseEventListeners, removeAllDocumentEventListeners, removeMouseEvent } = const { addCanvasMouseEventListener, addDocumentEventListener, removeAllMouseEventListeners, removeAllDocumentEventListeners, removeMouseLine } =
useEvent() useEvent()
// const { addCanvasMouseEventListener, addDocumentEventListener, removeAllMouseEventListeners, removeAllDocumentEventListeners, removeMouseEvent } = // const { addCanvasMouseEventListener, addDocumentEventListener, removeAllMouseEventListeners, removeAllDocumentEventListeners, removeMouseEvent } =
// useContext(EventContext) // useContext(EventContext)
@ -50,6 +52,8 @@ export function usePlacementShapeDrawing(id) {
const { addPolygonByLines, drawDirectionArrow } = usePolygon() const { addPolygonByLines, drawDirectionArrow } = usePolygon()
const { tempGridMode } = useTempGrid() const { tempGridMode } = useTempGrid()
const { setSurfaceShapePattern } = useRoofFn() const { setSurfaceShapePattern } = useRoofFn()
const { changeSurfaceLineType } = useSurfaceShapeBatch({})
const canvasSetting = useRecoilValue(canvasSettingState) const canvasSetting = useRecoilValue(canvasSettingState)
const verticalHorizontalMode = useRecoilValue(verticalHorizontalModeState) const verticalHorizontalMode = useRecoilValue(verticalHorizontalModeState)
const adsorptionPointAddMode = useRecoilValue(adsorptionPointAddModeState) const adsorptionPointAddMode = useRecoilValue(adsorptionPointAddModeState)
@ -115,6 +119,7 @@ export function usePlacementShapeDrawing(id) {
}, [type]) }, [type])
const clear = () => { const clear = () => {
addCanvasMouseEventListener('mouse:move', mouseMove)
setLength1(0) setLength1(0)
setLength2(0) setLength2(0)
@ -170,6 +175,80 @@ export function usePlacementShapeDrawing(id) {
} }
} }
/*
mouseMove
*/
const roofs = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
const roofAdsorptionPoints = useRef([])
const intersectionPoints = useRef([])
const { getAdsorptionPoints } = useAdsorptionPoint()
const mouseMove = (e) => {
removeMouseLine();
const pointer = canvas.getPointer(e.e)
const roofsPoints = roofs.map((roof) => roof.points).flat()
roofAdsorptionPoints.current = [...roofsPoints]
const auxiliaryLines = canvas.getObjects().filter((obj) => obj.name === 'auxiliaryLine' && !obj.isFixed)
const otherAdsorptionPoints = []
auxiliaryLines.forEach((line1) => {
auxiliaryLines.forEach((line2) => {
if (line1 === line2) {
return
}
const intersectionPoint = calculateIntersection(line1, line2)
if (!intersectionPoint || intersectionPoints.current.some((point) => point.x === intersectionPoint.x && point.y === intersectionPoint.y)) {
return
}
otherAdsorptionPoints.push(intersectionPoint)
})
})
let innerLinePoints = []
canvas
.getObjects()
.filter((obj) => obj.innerLines)
.forEach((polygon) => {
polygon.innerLines.forEach((line) => {
innerLinePoints.push({ x: line.x1, y: line.y1 })
innerLinePoints.push({ x: line.x2, y: line.y2 })
})
})
const adsorptionPoints = [
...getAdsorptionPoints(),
...roofAdsorptionPoints.current,
...otherAdsorptionPoints,
...intersectionPoints.current,
...innerLinePoints,
]
let arrivalPoint = { x: pointer.x, y: pointer.y }
let adsorptionPoint = findClosestPoint(pointer, adsorptionPoints)
if (adsorptionPoint && distanceBetweenPoints(pointer, adsorptionPoint) <= adsorptionRange) {
arrivalPoint = { ...adsorptionPoint }
}
const horizontalLine = new fabric.Line([-1 * canvas.width, arrivalPoint.y, 2 * canvas.width, arrivalPoint.y], {
stroke: 'red',
strokeWidth: 1,
selectable: false,
name: 'mouseLine',
})
const verticalLine = new fabric.Line([arrivalPoint.x, -1 * canvas.height, arrivalPoint.x, 2 * canvas.height], {
stroke: 'red',
strokeWidth: 1,
selectable: false,
name: 'mouseLine',
})
canvas?.add(horizontalLine, verticalLine)
canvas?.renderAll()
}
useEffect(() => { useEffect(() => {
canvas canvas
?.getObjects() ?.getObjects()
@ -253,11 +332,14 @@ export function usePlacementShapeDrawing(id) {
setPoints([]) setPoints([])
canvas?.renderAll() canvas?.renderAll()
if (+canvasSetting?.roofSizeSet === 3) { // if (+canvasSetting?.roofSizeSet === 3) {
closePopup(id) // closePopup(id)
return // return
} // }
addPopup(id, 1, <PlacementSurfaceLineProperty id={id} roof={roof} />, false) // addPopup(id, 1, <PlacementSurfaceLineProperty id={id} roof={roof} />, false)
changeSurfaceLineType(roof)
closePopup(id)
} }
if (points.length < 3) { if (points.length < 3) {

View File

@ -3,7 +3,7 @@
import { useEffect } from 'react' import { useEffect } from 'react'
import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil' import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil'
import { canvasSettingState, canvasState, currentCanvasPlanState, globalPitchState } from '@/store/canvasAtom' import { canvasSettingState, canvasState, currentCanvasPlanState, globalPitchState } from '@/store/canvasAtom'
import { MENU, POLYGON_TYPE } from '@/common/common' import { MENU, POLYGON_TYPE, LINE_TYPE } from '@/common/common'
import { getIntersectionPoint, toFixedWithoutRounding } from '@/util/canvas-util' import { getIntersectionPoint, toFixedWithoutRounding } from '@/util/canvas-util'
import { degreesToRadians } from '@turf/turf' import { degreesToRadians } from '@turf/turf'
import { QPolygon } from '@/components/fabric/QPolygon' import { QPolygon } from '@/components/fabric/QPolygon'
@ -111,7 +111,7 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) {
lockScalingX: true, // X 축 크기 조정 잠금 lockScalingX: true, // X 축 크기 조정 잠금
lockScalingY: true, // Y 축 크기 조정 잠금 lockScalingY: true, // Y 축 크기 조정 잠금
name: MENU.BATCH_CANVAS.SURFACE_SHAPE_BATCH_TEMP, name: MENU.BATCH_CANVAS.SURFACE_SHAPE_BATCH_TEMP,
flipX: xInversion !== yInversion, // flipX: xInversion !== yInversion,
// angle: xInversion && yInversion ? Math.abs((rotate + 180) % 360) : Math.abs(rotate), // angle: xInversion && yInversion ? Math.abs((rotate + 180) % 360) : Math.abs(rotate),
// angle: rotate, // angle: rotate,
originX: 'center', originX: 'center',
@ -120,6 +120,7 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) {
} }
obj = new QPolygon(points, options) obj = new QPolygon(points, options)
let imageRotate = 0 let imageRotate = 0
if (xInversion && !yInversion) { if (xInversion && !yInversion) {
if (rotate % 180 === 0 || rotate < 0) { if (rotate % 180 === 0 || rotate < 0) {
@ -148,7 +149,7 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) {
} else { } else {
imageRotate = (rotate + 360) % 360 imageRotate = (rotate + 360) % 360
} }
obj.set({ angle: imageRotate }) obj.set({ angle: imageRotate, flipX: xInversion !== yInversion })
obj.setCoords() //좌표 변경 적용 obj.setCoords() //좌표 변경 적용
canvas?.add(obj) canvas?.add(obj)
@ -158,6 +159,7 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) {
addCanvasMouseEventListener('mouse:down', (e) => { addCanvasMouseEventListener('mouse:down', (e) => {
isDrawing = false isDrawing = false
const { xInversion, yInversion } = surfaceRefs
canvas?.remove(obj) canvas?.remove(obj)
//각도 추가 //각도 추가
@ -178,6 +180,7 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) {
} }
//회전, flip등이 먹은 기준으로 새로생성 //회전, flip등이 먹은 기준으로 새로생성
// const batchSurface = addPolygon(reorderedPoints, {
const batchSurface = addPolygon(obj.getCurrentPoints(), { const batchSurface = addPolygon(obj.getCurrentPoints(), {
fill: 'transparent', fill: 'transparent',
stroke: 'red', stroke: 'red',
@ -196,18 +199,25 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) {
pitch: globalPitch, pitch: globalPitch,
surfaceId: surfaceId, surfaceId: surfaceId,
direction: direction, direction: direction,
isXInversion: xInversion,
isYInversion: yInversion,
}) })
canvas.setActiveObject(batchSurface) canvas.setActiveObject(batchSurface)
setSurfaceShapePattern(batchSurface, roofDisplay.column) setSurfaceShapePattern(batchSurface, roofDisplay.column)
drawDirectionArrow(batchSurface) drawDirectionArrow(batchSurface)
// if (setIsHidden) setIsHidden(false)
// closePopup(id) // closePopup(id)
initEvent() initEvent()
if (+canvasSetting?.roofSizeSet === 3) return // if (+canvasSetting?.roofSizeSet === 3) return
const popupId = uuidv4() // const popupId = uuidv4()
addPopup(popupId, 2, <PlacementSurfaceLineProperty roof={batchSurface} id={popupId} setIsHidden={setIsHidden} />) // addPopup(popupId, 2, <PlacementSurfaceLineProperty roof={batchSurface} id={popupId} setIsHidden={setIsHidden} />)
// console.log('xInversion', xInversion) //상하반전
// console.log('yInversion', yInversion) //좌우반전
changeSurfaceLineType(batchSurface)
if (setIsHidden) setIsHidden(false)
}) })
} else { } else {
if (setIsHidden) setIsHidden(false) if (setIsHidden) setIsHidden(false)
@ -488,18 +498,18 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) {
} }
case 10: { case 10: {
points = [ points = [
{ x: pointer.x + length1 / 2, y: pointer.y + length4 / 2 },
{ x: pointer.x + length1 / 2 - length1, y: pointer.y + length4 / 2 },
{ x: pointer.x + length1 / 2 - length1, y: pointer.y + length4 / 2 - length5 }, { x: pointer.x + length1 / 2 - length1, y: pointer.y + length4 / 2 - length5 },
{ x: pointer.x + length1 / 2 - length1 + length2, y: pointer.y + length4 / 2 - length5 }, { x: pointer.x + length1 / 2 - length1, y: pointer.y + length4 / 2 },
{ { x: pointer.x + length1 / 2, y: pointer.y + length4 / 2 },
x: pointer.x + length1 / 2 - length1 + length2,
y: pointer.y + length4 / 2 - length5 - (length4 - length5),
},
{ {
x: pointer.x + length1 / 2 - length1 + length2 + length3, x: pointer.x + length1 / 2 - length1 + length2 + length3,
y: pointer.y + length4 / 2 - length5 - (length4 - length5), y: pointer.y + length4 / 2 - length5 - (length4 - length5),
}, },
{
x: pointer.x + length1 / 2 - length1 + length2,
y: pointer.y + length4 / 2 - length5 - (length4 - length5),
},
{ x: pointer.x + length1 / 2 - length1 + length2, y: pointer.y + length4 / 2 - length5 },
] ]
break break
} }
@ -613,27 +623,27 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) {
} }
case 14: { case 14: {
points = [ points = [
{ x: pointer.x - length1 / 2 + length2, y: pointer.y + length4 / 2 },
{ x: pointer.x - length1 / 2, y: pointer.y + length4 / 2 },
{ x: pointer.x - length1 / 2, y: pointer.y + length4 / 2 - length4 }, { x: pointer.x - length1 / 2, y: pointer.y + length4 / 2 - length4 },
{ x: pointer.x - length1 / 2 + length1, y: pointer.y + length4 / 2 - length4 }, { x: pointer.x - length1 / 2, y: pointer.y + length4 / 2 },
{ x: pointer.x - length1 / 2 + length1, y: pointer.y + length4 / 2 - length4 + length4 }, { x: pointer.x - length1 / 2 + length2, y: pointer.y + length4 / 2 },
{ x: pointer.x - length1 / 2 + length1 - length3, y: pointer.y + length4 / 2 - length4 + length4 },
{ {
x: pointer.x - length1 / 2 + length2 + (length1 - length2 - length3) / 2, x: pointer.x - length1 / 2 + length2 + (length1 - length2 - length3) / 2,
y: pointer.y + length4 / 2 - length4 + length5, y: pointer.y + length4 / 2 - length4 + length5,
}, },
{ x: pointer.x - length1 / 2 + length1 - length3, y: pointer.y + length4 / 2 - length4 + length4 },
{ x: pointer.x - length1 / 2 + length1, y: pointer.y + length4 / 2 - length4 + length4 },
{ x: pointer.x - length1 / 2 + length1, y: pointer.y + length4 / 2 - length4 },
] ]
break break
} }
case 15: { case 15: {
points = [ points = [
{ x: pointer.x - length1 / 2, y: pointer.y + length2 - length2 / 2 },
{ x: pointer.x - length1 / 2, y: pointer.y + length2 - length2 / 2 - length3 }, { x: pointer.x - length1 / 2, y: pointer.y + length2 - length2 / 2 - length3 },
{ x: pointer.x, y: pointer.y + length2 - length2 / 2 - length3 - (length2 - length3) }, { x: pointer.x - length1 / 2, y: pointer.y + length2 - length2 / 2 },
{ x: pointer.x + length1 / 2, y: pointer.y + length2 - length2 / 2 - length3 },
{ x: pointer.x + length1 / 2, y: pointer.y + length2 - length2 / 2 - length3 + length3 }, { x: pointer.x + length1 / 2, y: pointer.y + length2 - length2 / 2 - length3 + length3 },
{ x: pointer.x + length1 / 2, y: pointer.y + length2 - length2 / 2 - length3 },
{ x: pointer.x, y: pointer.y + length2 - length2 / 2 - length3 - (length2 - length3) },
] ]
break break
} }
@ -641,28 +651,28 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) {
case 16: { case 16: {
points = [ points = [
{ {
x: pointer.x - length1 / 2, x: pointer.x - length1 / 2 + (length1 - length2) / 2,
y: pointer.y + length3 / 2, y: pointer.y + length3 / 2 - (length3 - length4) - length4,
}, },
{ {
x: pointer.x - length1 / 2 + (length1 - length2) / 2, x: pointer.x - length1 / 2 + (length1 - length2) / 2,
y: pointer.y + length3 / 2 - (length3 - length4), y: pointer.y + length3 / 2 - (length3 - length4),
}, },
{ {
x: pointer.x - length1 / 2 + (length1 - length2) / 2, x: pointer.x - length1 / 2,
y: pointer.y + length3 / 2 - (length3 - length4) - length4, y: pointer.y + length3 / 2,
}, },
{ {
x: pointer.x - length1 / 2 + (length1 - length2) / 2 + length2, x: pointer.x - length1 / 2 + length1,
y: pointer.y + length3 / 2 - (length3 - length4) - length4, y: pointer.y + length3 / 2,
}, },
{ {
x: pointer.x - length1 / 2 + (length1 - length2) / 2 + length2, x: pointer.x - length1 / 2 + (length1 - length2) / 2 + length2,
y: pointer.y + length3 / 2 - (length3 - length4) - length4 + length4, y: pointer.y + length3 / 2 - (length3 - length4) - length4 + length4,
}, },
{ {
x: pointer.x - length1 / 2 + length1, x: pointer.x - length1 / 2 + (length1 - length2) / 2 + length2,
y: pointer.y + length3 / 2, y: pointer.y + length3 / 2 - (length3 - length4) - length4,
}, },
] ]
break break
@ -673,25 +683,25 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) {
const topL = (length1 - length2) / 2 / Math.cos((angle * Math.PI) / 180) // 꺽이는부분 윗쪽 길이 const topL = (length1 - length2) / 2 / Math.cos((angle * Math.PI) / 180) // 꺽이는부분 윗쪽 길이
points = [ points = [
{
x: pointer.x - length1 / 2 + length1,
y: pointer.y + length3 / 2,
},
{ {
x: pointer.x - length1 / 2, x: pointer.x - length1 / 2,
y: pointer.y + length3 / 2, y: pointer.y + length3 / 2,
}, },
{ {
x: pointer.x - length1 / 2 + length4 * Math.cos(degreesToRadians(angle)), x: pointer.x - length1 / 2 + length1,
y: pointer.y + length3 / 2 - length4 * Math.sin(degreesToRadians(angle)), y: pointer.y + length3 / 2,
},
{
x: pointer.x - length1 / 2 + length4 * Math.cos(degreesToRadians(angle)) + length2 + topL * Math.cos(degreesToRadians(angle)),
y: pointer.y + length3 / 2 - length4 * Math.sin(degreesToRadians(angle)) + topL * Math.sin(degreesToRadians(angle)),
}, },
{ {
x: pointer.x - length1 / 2 + length4 * Math.cos(degreesToRadians(angle)) + length2, x: pointer.x - length1 / 2 + length4 * Math.cos(degreesToRadians(angle)) + length2,
y: pointer.y + length3 / 2 - length4 * Math.sin(degreesToRadians(angle)), y: pointer.y + length3 / 2 - length4 * Math.sin(degreesToRadians(angle)),
}, },
{ {
x: pointer.x - length1 / 2 + length4 * Math.cos(degreesToRadians(angle)) + length2 + topL * Math.cos(degreesToRadians(angle)), x: pointer.x - length1 / 2 + length4 * Math.cos(degreesToRadians(angle)),
y: pointer.y + length3 / 2 - length4 * Math.sin(degreesToRadians(angle)) + topL * Math.sin(degreesToRadians(angle)), y: pointer.y + length3 / 2 - length4 * Math.sin(degreesToRadians(angle)),
}, },
] ]
break break
@ -1066,45 +1076,294 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) {
canvas?.renderAll() canvas?.renderAll()
} }
const updateFlippedPoints = (polygon) => { /**
if (!(polygon instanceof fabric.Polygon)) { * 면형상 작도시 라인 속성 넣는 로직
console.error('The object is not a Polygon.') * 폴리곤으로 보면 직선방향에 따라 아래쪽인지 윗쪽인지 판단이 가능하다고 생각하여
return * south -> 밑면은 무조건 right direction이라 가정하고 작업함 좌우반전시 반대로 그려지는 경우도 생기지만 그럴땐 흐름방향에 따라 최대값(최소값) 찾아
} * 해당 하는 흐름에 맞게 변경함
* @param { } polygon
*/
const { flipX, flipY, width, height, points, left, top, scaleX, scaleY } = polygon //폴리곤, 상하반전, 좌우반전
const changeSurfaceLineType = (polygon) => {
const { isXInversion, isYInversion } = polygon //상하반전, 좌우반전
// 현재 points의 사본 가져오기 polygon.lines.forEach((line) => {
const newPoints = points.map((point) => { line.attributes.type = LINE_TYPE.WALLLINE.GABLE
let x = point.x
let y = point.y
// flipX 적용
if (flipX) {
x = width - x
}
// flipY 적용
if (flipY) {
y = height - y
}
// 스케일 및 전역 좌표 고려
x = (x - width / 2) * scaleX + width / 2
y = (y - height / 2) * scaleY + height / 2
return { x, y }
}) })
// flipX, flipY를 초기화 const directionConfig = {
polygon.flipX = false south: { evaesDirection: 'right', ridgeDirection: 'left', coord1: 'y1', coord2: 'y2' },
polygon.flipY = false north: { evaesDirection: 'left', ridgeDirection: 'right', coord1: 'y1', coord2: 'y2' },
east: { evaesDirection: 'top', ridgeDirection: 'bottom', coord1: 'x1', coord2: 'x2' },
west: { evaesDirection: 'bottom', ridgeDirection: 'top', coord1: 'x1', coord2: 'x2' },
}
// points 업데이트 const { evaesDirection, ridgeDirection, coord1, coord2 } = directionConfig[polygon.direction] || directionConfig.west
polygon.set({ points: newPoints })
polygon.setCoords()
return polygon polygon.lines.forEach((line) => {
if (line[coord1] === line[coord2]) {
if (line.direction === evaesDirection) {
line.attributes.type = LINE_TYPE.WALLLINE.EAVES
} else if (line.direction === ridgeDirection) {
line.attributes.type = LINE_TYPE.SUBLINE.RIDGE
}
}
})
/**
* 진짜 처마 라인인지 확인하는 로직 -> 특정 모양에 따라 처마가 없는 경우가 있는데 위에 로직으로는
* 용마루도 처마로 만들어서 재보정
*/
//직선 찾는 로직
const maxLine = polygon.lines.filter((line) => line[coord1] === line[coord2])
if (maxLine.length > 0) {
const maxLineSorted = maxLine.reduce((a, b) => {
return (polygon.direction === 'south' || polygon.direction === 'east' ? b : a)[coord1] >
(polygon.direction === 'south' || polygon.direction === 'east' ? a : b)[coord1]
? b
: a
})
//정렬된 폴리곤이 아니면(대각선이 존재하는 폴리곤일때)
if (!polygon.isSortedPoints) {
//좌우 반전을 했으면 반대로 정의함
if (isYInversion || isXInversion) {
polygon.lines.forEach((line) => {
if (line.attributes.type === LINE_TYPE.WALLLINE.EAVES) {
line.attributes.type = LINE_TYPE.SUBLINE.RIDGE
} else if (line.attributes.type === LINE_TYPE.SUBLINE.RIDGE) {
line.attributes.type = LINE_TYPE.WALLLINE.EAVES
}
})
}
}
if (maxLine.length === 1) {
const maxLineCoord = polygon.lines.reduce((a, b) => {
return (polygon.direction === 'south' || polygon.direction === 'east' ? b : a)[coord1] >
(polygon.direction === 'south' || polygon.direction === 'east' ? a : b)[coord1]
? b
: a
})
const isRealEavesLine = polygon.lines.filter((line) => line.attributes.type === LINE_TYPE.WALLLINE.EAVES)
if (isRealEavesLine.length > 0) {
isRealEavesLine.forEach((line) => {
if (polygon.direction === 'south' || polygon.direction === 'north') {
const targetCoord =
polygon.direction === 'south' ? Math.max(maxLineCoord.y1, maxLineCoord.y2) : Math.min(maxLineCoord.y1, maxLineCoord.y2)
const realLineCoord = polygon.direction === 'south' ? Math.max(line.y1, line.y2) : Math.min(line.y1, line.y2)
if (targetCoord !== realLineCoord) {
line.attributes.type = LINE_TYPE.SUBLINE.RIDGE
}
} else if (polygon.direction === 'east' || polygon.direction === 'west') {
const targetCoord =
polygon.direction === 'east' ? Math.max(maxLineCoord.x1, maxLineCoord.x2) : Math.min(maxLineCoord.x1, maxLineCoord.x2)
const realLineCoord = polygon.direction === 'east' ? Math.max(line.x1, line.x2) : Math.min(line.x1, line.x2)
if (targetCoord !== realLineCoord) {
line.attributes.type = LINE_TYPE.SUBLINE.RIDGE
}
}
})
}
}
}
}
function findCentroid(points) {
let sumX = 0,
sumY = 0
for (let i = 0; i < points.length; i++) {
sumX += points[i].x
sumY += points[i].y
}
return { x: sumX / points.length, y: sumY / points.length }
}
// 도형의 포인트를 왼쪽부터 반시계 방향으로 정렬하는 함수
/**
* 다각형의 점들을 시계 반대 방향으로 정렬하는 함수
* @param {Array} points - {x, y} 좌표 객체 배열
* @param {Object} startPoint - 시작점 (제공되지 않으면 가장 왼쪽 아래 점을 사용)
* @returns {Array} 시계 반대 방향으로 정렬된 점들의 배열
*/
function orderPointsCounterClockwise(points, startPoint = null) {
if (points.length <= 3) {
return points // 점이 3개 이하면 이미 다각형의 모든 점이므로 그대로 반환
}
// 시작점이 제공되지 않았다면 가장 왼쪽 아래 점을 찾음
let start = startPoint
if (!start) {
start = points[0]
for (let i = 1; i < points.length; i++) {
if (points[i].x < start.x || (points[i].x === start.x && points[i].y < start.y)) {
start = points[i]
}
}
}
// 다각형의 중심점 계산
let centerX = 0,
centerY = 0
for (let i = 0; i < points.length; i++) {
centerX += points[i].x
centerY += points[i].y
}
centerX /= points.length
centerY /= points.length
// 시작점에서 시계 반대 방향으로 각도 계산
let angles = []
for (let i = 0; i < points.length; i++) {
// 시작점은 제외
if (points[i] === start) continue
// 시작점을 기준으로 각 점의 각도 계산
let angle = Math.atan2(points[i].y - start.y, points[i].x - start.x)
// 각도가 음수면 2π를 더해 0~2π 범위로 변환
if (angle < 0) angle += 2 * Math.PI
angles.push({
point: points[i],
angle: angle,
})
}
// 각도에 따라 정렬 (시계 반대 방향)
angles.sort((a, b) => a.angle - b.angle)
// 정렬된 배열 생성 (시작점을 첫 번째로)
let orderedPoints = [start]
for (let i = 0; i < angles.length; i++) {
orderedPoints.push(angles[i].point)
}
return orderedPoints
}
/**
* 특정 점에서 시작하여 시계 반대 방향으로 다음 점을 찾는 함수
* @param {Object} currentPoint - 현재 {x, y}
* @param {Array} points - 모든 점들의 배열
* @param {Array} visited - 방문한 점들의 인덱스 배열
* @param {Object} prevVector - 이전 벡터 방향 ( 호출에서는 null)
* @returns {Object} 다음 점의 인덱스와 객체
*/
function findNextCounterClockwisePoint(currentPoint, points, visited, prevVector = null) {
let minAngle = Infinity
let nextIndex = -1
// 이전 벡터가 없으면 (첫 점인 경우) 아래쪽을 향하는 벡터 사용
if (!prevVector) {
prevVector = { x: 0, y: -1 }
}
for (let i = 0; i < points.length; i++) {
// 이미 방문했거나 현재 점이면 건너뜀
if (visited.includes(i) || (points[i].x === currentPoint.x && points[i].y === currentPoint.y)) {
continue
}
// 현재 점에서 다음 후보 점으로의 벡터
let vector = {
x: points[i].x - currentPoint.x,
y: points[i].y - currentPoint.y,
}
// 벡터의 크기
let magnitude = Math.sqrt(vector.x * vector.x + vector.y * vector.y)
// 단위 벡터로 정규화
vector.x /= magnitude
vector.y /= magnitude
// 이전 벡터와 현재 벡터 사이의 각도 계산 (내적 사용)
let dotProduct = prevVector.x * vector.x + prevVector.y * vector.y
let crossProduct = prevVector.x * vector.y - prevVector.y * vector.x
// 각도 계산 (atan2 사용)
let angle = Math.atan2(crossProduct, dotProduct)
// 시계 반대 방향으로 가장 작은 각도를 가진 점 찾기
// 각도가 음수면 2π를 더해 0~2π 범위로 변환
if (angle < 0) angle += 2 * Math.PI
if (angle < minAngle) {
minAngle = angle
nextIndex = i
}
}
return nextIndex !== -1 ? { index: nextIndex, point: points[nextIndex] } : null
}
/**
* 다각형의 점들을 시계 반대 방향으로 추적하는 함수
* @param {Array} points - {x, y} 좌표 객체 배열
* @param {Object} startPoint - 시작점 (제공되지 않으면 가장 왼쪽 아래 점을 사용)
* @returns {Array} 시계 반대 방향으로 정렬된 점들의 배열
*/
function tracePolygonCounterClockwise(points, startPoint = null) {
if (points.length <= 3) {
return orderPointsCounterClockwise(points, startPoint)
}
// 시작점이 제공되지 않았다면 가장 왼쪽 아래 점을 찾음
let startIndex = 0
if (!startPoint) {
for (let i = 1; i < points.length; i++) {
if (points[i].x < points[startIndex].x || (points[i].x === points[startIndex].x && points[i].y < points[startIndex].y)) {
startIndex = i
}
}
startPoint = points[startIndex]
} else {
// 시작점이 제공된 경우 해당 점의 인덱스 찾기
for (let i = 0; i < points.length; i++) {
if (points[i].x === startPoint.x && points[i].y === startPoint.y) {
startIndex = i
break
}
}
}
// 결과 배열 초기화
let orderedPoints = [startPoint]
let visited = [startIndex]
let currentPoint = startPoint
let prevVector = null
// 모든 점을 방문할 때까지 반복
while (visited.length < points.length) {
let next = findNextCounterClockwisePoint(currentPoint, points, visited, prevVector)
if (!next) break // 더 이상 찾을 점이 없으면 종료
orderedPoints.push(next.point)
visited.push(next.index)
// 이전 벡터 업데이트 (현재 점에서 다음 점으로의 벡터)
prevVector = {
x: next.point.x - currentPoint.x,
y: next.point.y - currentPoint.y,
}
// 벡터 정규화
let magnitude = Math.sqrt(prevVector.x * prevVector.x + prevVector.y * prevVector.y)
prevVector.x /= magnitude
prevVector.y /= magnitude
currentPoint = next.point
}
return orderedPoints
} }
return { return {
@ -1115,5 +1374,6 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) {
changeSurfaceLinePropertyEvent, changeSurfaceLinePropertyEvent,
changeSurfaceLineProperty, changeSurfaceLineProperty,
changeSurfaceLinePropertyReset, changeSurfaceLinePropertyReset,
changeSurfaceLineType,
} }
} }

View File

@ -1,9 +1,10 @@
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { useRecoilState, useRecoilValue } from 'recoil' import { useRecoilState, useRecoilValue } from 'recoil'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
import { canvasSizeState, canvasState, canvasZoomState, currentObjectState } from '@/store/canvasAtom' import { canvasSizeState, canvasState, canvasZoomState, currentMenuState, currentObjectState } from '@/store/canvasAtom'
import { QPolygon } from '@/components/fabric/QPolygon' import { QPolygon } from '@/components/fabric/QPolygon'
import { fontSelector } from '@/store/fontAtom' import { fontSelector } from '@/store/fontAtom'
import { MENU, POLYGON_TYPE } from '@/common/common'
// 캔버스에 필요한 이벤트 // 캔버스에 필요한 이벤트
export function useCanvasEvent() { export function useCanvasEvent() {
@ -13,11 +14,16 @@ export function useCanvasEvent() {
const canvasSize = useRecoilValue(canvasSizeState) const canvasSize = useRecoilValue(canvasSizeState)
const [canvasZoom, setCanvasZoom] = useRecoilState(canvasZoomState) const [canvasZoom, setCanvasZoom] = useRecoilState(canvasZoomState)
const lengthTextOption = useRecoilValue(fontSelector('lengthText')) const lengthTextOption = useRecoilValue(fontSelector('lengthText'))
const currentMenu = useRecoilValue(currentMenuState)
useEffect(() => { useEffect(() => {
canvas?.setZoom(canvasZoom / 100) canvas?.setZoom(canvasZoom / 100)
}, [canvasZoom]) }, [canvasZoom])
useEffect(() => {
attachDefaultEventOnCanvas()
}, [currentMenu])
// 기본적인 이벤트 필요시 추가 // 기본적인 이벤트 필요시 추가
const attachDefaultEventOnCanvas = () => { const attachDefaultEventOnCanvas = () => {
removeEventOnCanvas() removeEventOnCanvas()
@ -198,8 +204,20 @@ export function useCanvasEvent() {
if (selected?.length > 0) { if (selected?.length > 0) {
selected.forEach((obj) => { selected.forEach((obj) => {
if (obj.type === 'QPolygon' && obj.name !== 'module') { // if (obj.type === 'QPolygon' && currentMenu !== MENU.MODULE_CIRCUIT_SETTING.BASIC_SETTING) {
if (obj.type === 'QPolygon') {
const originStroke = obj.stroke
obj.set({ stroke: 'red' }) obj.set({ stroke: 'red' })
if (currentMenu === MENU.MODULE_CIRCUIT_SETTING.BASIC_SETTING) {
if (obj.name === POLYGON_TYPE.MODULE) {
obj.set({ strokeWidth: 3 })
}
if (obj.name === POLYGON_TYPE.ROOF) {
canvas.discardActiveObject()
obj.set({ stroke: originStroke })
}
}
} }
}) })
canvas.renderAll() canvas.renderAll()
@ -215,6 +233,9 @@ export function useCanvasEvent() {
if (obj.name !== 'moduleSetupSurface') { if (obj.name !== 'moduleSetupSurface') {
obj.set({ stroke: 'black' }) obj.set({ stroke: 'black' })
} }
if (obj.name === POLYGON_TYPE.MODULE && currentMenu === MENU.MODULE_CIRCUIT_SETTING.BASIC_SETTING) {
obj.set({ strokeWidth: 0.3 })
}
} }
}) })
} }
@ -229,14 +250,22 @@ export function useCanvasEvent() {
deselected.forEach((obj) => { deselected.forEach((obj) => {
if (obj.type === 'QPolygon') { if (obj.type === 'QPolygon') {
obj.set({ stroke: 'black' }) obj.set({ stroke: 'black' })
if (obj.name === POLYGON_TYPE.MODULE && currentMenu === MENU.MODULE_CIRCUIT_SETTING.BASIC_SETTING) {
//모듈 미선택시 라인 두께 변경
obj.set({ strokeWidth: 0.3 })
}
} }
}) })
} }
if (selected?.length > 0) { if (selected?.length > 0) {
selected.forEach((obj) => { selected.forEach((obj) => {
if (obj.type === 'QPolygon' && obj.name !== 'module') { if (obj.type === 'QPolygon') {
obj.set({ stroke: 'red' }) obj.set({ stroke: 'red' })
if (obj.name === POLYGON_TYPE.MODULE && currentMenu === MENU.MODULE_CIRCUIT_SETTING.BASIC_SETTING) {
//모듈 선택시 라인 두께 변경
obj.set({ strokeWidth: 3 })
}
} }
}) })
} }

View File

@ -24,14 +24,14 @@ export function useCircuitTrestle(executeEffect = false) {
const [selectedModels, setSelectedModels] = useRecoilState(selectedModelsState) const [selectedModels, setSelectedModels] = useRecoilState(selectedModelsState)
const [pcsCheck, setPcsCheck] = useRecoilState(pcsCheckState) const [pcsCheck, setPcsCheck] = useRecoilState(pcsCheckState)
const selectedModules = useRecoilValue(selectedModuleState) const selectedModules = useRecoilValue(selectedModuleState)
const { managementState, setManagementState, managementStateLoaded } = useContext(GlobalDataContext) const { managementState } = useContext(GlobalDataContext)
const canvas = useRecoilValue(canvasState) const canvas = useRecoilValue(canvasState)
const setModuleStatistics = useSetRecoilState(moduleStatisticsState) const setModuleStatistics = useSetRecoilState(moduleStatisticsState)
const resetModuleStatistics = useResetRecoilState(moduleStatisticsState) const resetModuleStatistics = useResetRecoilState(moduleStatisticsState)
const { getMessage } = useMessage() const { getMessage } = useMessage()
useEffect(() => { useEffect(() => {
if (Object.keys(selectedModules).length > 0 && executeEffect) setModuleStatisticsData() if (selectedModules && Object.keys(selectedModules).length > 0 && executeEffect) setModuleStatisticsData()
}, [selectedModules]) }, [selectedModules])
const getOptYn = () => { const getOptYn = () => {

View File

@ -81,9 +81,9 @@ export function useContextMenu() {
switch (selectedMenu) { switch (selectedMenu) {
case 'outline': case 'outline':
break break
default: // default:
setContextMenu([]) // setContextMenu([])
break // break
} }
} }

View File

@ -13,7 +13,7 @@ import { useTrestle } from '@/hooks/module/useTrestle'
import { usePlan } from '@/hooks/usePlan' import { usePlan } from '@/hooks/usePlan'
export function useEstimate() { export function useEstimate() {
const { managementStateLoaded } = useContext(GlobalDataContext) const { managementState } = useContext(GlobalDataContext)
const { setIsGlobalLoading } = useContext(QcastContext) const { setIsGlobalLoading } = useContext(QcastContext)
const router = useRouter() const router = useRouter()
const loginUserState = useRecoilValue(loginUserStore) const loginUserState = useRecoilValue(loginUserStore)
@ -31,16 +31,18 @@ export function useEstimate() {
* @param {Object} estimateParam - 견적서 저장 데이터 * @param {Object} estimateParam - 견적서 저장 데이터
*/ */
const saveEstimate = async (estimateParam) => { const saveEstimate = async (estimateParam) => {
console.log('managementState', managementState)
const userId = loginUserState.userId const userId = loginUserState.userId
const saleStoreId = managementStateLoaded.saleStoreId const saleStoreId = managementState.saleStoreId
const objectNo = currentCanvasPlan.objectNo const objectNo = currentCanvasPlan.objectNo
const planNo = currentCanvasPlan.planNo const planNo = currentCanvasPlan.planNo
const slope = estimateParam.roofSurfaceList[0].slope const slope = estimateParam.roofSurfaceList[0].slope
const angle = estimateParam.roofSurfaceList[0].angle const angle = estimateParam.roofSurfaceList[0].angle
const surfaceType = managementStateLoaded.surfaceType const surfaceType = managementState.surfaceType
const setupHeight = managementStateLoaded.installHeight const setupHeight = managementState.installHeight
const standardWindSpeedId = managementStateLoaded.standardWindSpeedId const standardWindSpeedId = managementState.standardWindSpeedId
const snowfall = managementStateLoaded.verticalSnowCover const snowfall = managementState.verticalSnowCover
const drawingFlg = '1' const drawingFlg = '1'
const saveEstimateData = { const saveEstimateData = {

View File

@ -77,7 +77,10 @@ export function useEvent() {
setCanvasZoom(Number((zoom * 100).toFixed(0))) setCanvasZoom(Number((zoom * 100).toFixed(0)))
// 마우스 위치 기준으로 확대/축소 // 마우스 위치 기준으로 확대/축소
canvas.zoomToPoint({ x: opt.e.offsetX, y: opt.e.offsetY }, zoom) canvas.zoomToPoint(new fabric.Point(opt.e.offsetX, opt.e.offsetY), zoom)
canvas.calcOffset()
canvas.setViewportTransform(canvas.viewportTransform)
canvas.requestRenderAll()
// 이벤트의 기본 동작 방지 (스크롤 방지) // 이벤트의 기본 동작 방지 (스크롤 방지)
opt.e.preventDefault() opt.e.preventDefault()
@ -104,6 +107,10 @@ export function useEvent() {
const horizonLines = canvas.getObjects().filter((obj) => obj.name === 'lineGrid' && obj.direction === 'horizontal') const horizonLines = canvas.getObjects().filter((obj) => obj.name === 'lineGrid' && obj.direction === 'horizontal')
const verticalLines = canvas.getObjects().filter((obj) => obj.name === 'lineGrid' && obj.direction === 'vertical') const verticalLines = canvas.getObjects().filter((obj) => obj.name === 'lineGrid' && obj.direction === 'vertical')
if (!horizonLines || !verticalLines) {
return
}
const closestHorizontalLine = horizonLines.reduce((prev, curr) => { const closestHorizontalLine = horizonLines.reduce((prev, curr) => {
const prevDistance = calculateDistance(pointer, prev) const prevDistance = calculateDistance(pointer, prev)
const currDistance = calculateDistance(pointer, curr) const currDistance = calculateDistance(pointer, curr)

View File

@ -53,8 +53,8 @@ export const useLine = () => {
}) })
canvas canvas
?.getObjects() ?.getObjects()
.find((obj) => obj.parentId === line.id) ?.find((obj) => obj.parentId === line.id)
.set({ ?.set({
visible: true, visible: true,
}) })
canvas?.renderAll() canvas?.renderAll()

View File

@ -20,6 +20,8 @@ import { compasDegAtom } from '@/store/orientationAtom'
import { moduleSelectionDataState, selectedModuleState } from '@/store/selectedModuleOptions' import { moduleSelectionDataState, selectedModuleState } from '@/store/selectedModuleOptions'
import { useCanvasPopupStatusController } from './common/useCanvasPopupStatusController' import { useCanvasPopupStatusController } from './common/useCanvasPopupStatusController'
import { useCanvasMenu } from './common/useCanvasMenu' import { useCanvasMenu } from './common/useCanvasMenu'
import { QcastContext } from '@/app/QcastProvider'
import { unescapeString } from '@/util/common-utils'
/** /**
* 플랜 처리 * 플랜 처리
@ -52,6 +54,9 @@ export function usePlan(params = {}) {
const { fetchBasicSettings, basicSettingCopySave } = useCanvasSetting() const { fetchBasicSettings, basicSettingCopySave } = useCanvasSetting()
const [canvasSetting, setCanvasSetting] = useRecoilState(canvasSettingState) const [canvasSetting, setCanvasSetting] = useRecoilState(canvasSettingState)
/** 전역 로딩바 컨텍스트 */
const { setIsGlobalLoading } = useContext(QcastContext)
/** /**
* 플랜 복사 모듈이 있을경우 모듈 데이터 복사하기 위한 처리 * 플랜 복사 모듈이 있을경우 모듈 데이터 복사하기 위한 처리
*/ */
@ -260,7 +265,7 @@ export function usePlan(params = {}) {
objectNo, objectNo,
planNo: parseInt(newPlan.planNo), planNo: parseInt(newPlan.planNo),
popupType: 1, popupType: 1,
popupStatus: sourceDegree.popupStatus, popupStatus: unescapeString(sourceDegree.popupStatus),
} }
console.log('🚀 ~ postObjectPlan ~ degreeData:', degreeData) console.log('🚀 ~ postObjectPlan ~ degreeData:', degreeData)
await post({ url: `/api/v1/canvas-popup-status`, data: degreeData }) await post({ url: `/api/v1/canvas-popup-status`, data: degreeData })
@ -402,19 +407,28 @@ export function usePlan(params = {}) {
} }
}) })
} else { } else {
if (!currentCanvasPlan || currentCanvasPlan.id !== newCurrentId) { swalFire({
await saveCanvas(true) text: getMessage('plan.message.confirm.save'),
clearRecoilState() type: 'confirm',
} confirmFn: async () => {
setCurrentCanvasPlan(plans.find((plan) => plan.id === newCurrentId)) //저장 전에 플랜이 이동되어 state가 변경되는 이슈가 있음
setPlans((plans) => plans.map((plan) => ({ ...plan, isCurrent: plan.id === newCurrentId }))) await saveCanvas(true)
clearRecoilState()
setCurrentCanvasPlan(plans.find((plan) => plan.id === newCurrentId))
setPlans((plans) => plans.map((plan) => ({ ...plan, isCurrent: plan.id === newCurrentId })))
},
denyFn: async () => {
setCurrentCanvasPlan(plans.find((plan) => plan.id === newCurrentId))
setPlans((plans) => plans.map((plan) => ({ ...plan, isCurrent: plan.id === newCurrentId })))
},
})
} }
} }
useEffect(() => { useEffect(() => {
setSelectedPlan(currentCanvasPlan) setSelectedPlan(currentCanvasPlan)
handleCurrentPlanUrl() handleCurrentPlanUrl()
resetCurrentObject() // resetCurrentObject()
resetModuleSetupSurface() resetModuleSetupSurface()
}, [currentCanvasPlan]) }, [currentCanvasPlan])
@ -442,23 +456,46 @@ export function usePlan(params = {}) {
* @param {string} objectNo - 물건번호 * @param {string} objectNo - 물건번호
*/ */
const handleAddPlan = async (userId, objectNo) => { const handleAddPlan = async (userId, objectNo) => {
let isSelected = false
if (currentCanvasPlan?.id) { if (currentCanvasPlan?.id) {
await saveCanvas(false) swalFire({
text: getMessage('plan.message.confirm.save'),
type: 'confirm',
confirmFn: async () => {
//저장 전에 플랜이 이동되어 state가 변경되는 이슈가 있음
await saveCanvas(true)
handleAddPlanCopyConfirm(userId, objectNo)
},
denyFn: async () => {
handleAddPlanCopyConfirm(userId, objectNo)
},
})
} }
JSON.parse(currentCanvasData()).objects.length > 0
? swalFire({
text: `Plan ${currentCanvasPlan.planNo} ` + getMessage('plan.message.confirm.copy'),
type: 'confirm',
confirmFn: async () => {
await postObjectPlan(userId, objectNo, true, false)
},
denyFn: async () => {
await postObjectPlan(userId, objectNo, false, false)
},
})
: await postObjectPlan(userId, objectNo, false, false)
} }
const handleAddPlanCopyConfirm = async (userId, objectNo) => {
if (JSON.parse(currentCanvasData()).objects.length > 0) {
swalFire({
text: `Plan ${currentCanvasPlan.planNo} ` + getMessage('plan.message.confirm.copy'),
type: 'confirm',
confirmFn: async () => {
setIsGlobalLoading(true)
await postObjectPlan(userId, objectNo, true, false)
setIsGlobalLoading(false)
},
denyFn: async () => {
setIsGlobalLoading(true)
await postObjectPlan(userId, objectNo, false, false)
setIsGlobalLoading(false)
},
})
} else {
setIsGlobalLoading(true)
await postObjectPlan(userId, objectNo, false, false)
setIsGlobalLoading(false)
}
}
/** /**
* 물건번호(object) plan 삭제 (canvas 삭제 planNo 삭제) * 물건번호(object) plan 삭제 (canvas 삭제 planNo 삭제)
* *

View File

@ -176,6 +176,10 @@ export const usePolygon = () => {
* @param showDirectionText * @param showDirectionText
*/ */
const drawDirectionArrow = (polygon, showDirectionText = true) => { const drawDirectionArrow = (polygon, showDirectionText = true) => {
if (!polygon) {
return
}
if (polygon.points.length < 3) { if (polygon.points.length < 3) {
return return
} }
@ -767,7 +771,7 @@ export const usePolygon = () => {
obj.type === 'QLine' && obj.type === 'QLine' &&
obj.attributes?.type !== 'pitchSizeLine' && obj.attributes?.type !== 'pitchSizeLine' &&
obj.attributes?.roofId === polygon.id && obj.attributes?.roofId === polygon.id &&
(innerLineTypes.includes(obj.name) || !obj.name), innerLineTypes.includes(obj.name),
) )
innerLines = [...polygon.innerLines] innerLines = [...polygon.innerLines]

View File

@ -48,6 +48,7 @@ export async function setSession(data) {
session.pwdInitYn = data.pwdInitYn session.pwdInitYn = data.pwdInitYn
session.custCd = data.custCd session.custCd = data.custCd
session.isLoggedIn = true session.isLoggedIn = true
session.builderNo = data.builderNo
await session.save() await session.save()
} }

View File

@ -3,7 +3,7 @@
"welcome": "ようこそ。 {0}さん", "welcome": "ようこそ。 {0}さん",
"header.menus.home": "ホーム", "header.menus.home": "ホーム",
"header.menus.management": "見積書管理画面", "header.menus.management": "見積書管理画面",
"header.menus.management.newStuff": "新規見積登録", "header.menus.management.newStuff": "新規物件登録",
"header.menus.management.detail": "物件詳細", "header.menus.management.detail": "物件詳細",
"header.menus.management.stuffList": "物件検索", "header.menus.management.stuffList": "物件検索",
"header.menus.community": "コミュニティ", "header.menus.community": "コミュニティ",
@ -18,6 +18,7 @@
"plan.menu.placement.surface.initial.setting": "配置面初期設定", "plan.menu.placement.surface.initial.setting": "配置面初期設定",
"modal.placement.initial.setting.plan.drawing": "図面の作成方法", "modal.placement.initial.setting.plan.drawing": "図面の作成方法",
"modal.placement.initial.setting.plan.drawing.size.stuff": "寸法入力による物件作成", "modal.placement.initial.setting.plan.drawing.size.stuff": "寸法入力による物件作成",
"modal.placement.initial.setting.plan.drawing.size.info": "※数字は[半角]入力のみ可能です。",
"modal.placement.initial.setting.size": "寸法入力方法", "modal.placement.initial.setting.size": "寸法入力方法",
"modal.placement.initial.setting.size.info": "寸法入力方法案内", "modal.placement.initial.setting.size.info": "寸法入力方法案内",
"modal.placement.initial.setting.size.roof": "伏図入力", "modal.placement.initial.setting.size.roof": "伏図入力",
@ -36,8 +37,8 @@
"modal.roof.shape.setting.ridge": "棟", "modal.roof.shape.setting.ridge": "棟",
"modal.roof.shape.setting.patten.a": "Aパターン", "modal.roof.shape.setting.patten.a": "Aパターン",
"modal.roof.shape.setting.patten.b": "Bパターン", "modal.roof.shape.setting.patten.b": "Bパターン",
"modal.roof.shape.setting.side": "別に設定", "modal.roof.shape.setting.side": "別に設定",
"plan.menu.roof.cover": "屋根作図", "plan.menu.roof.cover": "伏せ図入力",
"plan.menu.roof.cover.outline.drawing": "外壁線を描く", "plan.menu.roof.cover.outline.drawing": "外壁線を描く",
"plan.menu.roof.cover.roof.shape.setting": "屋根形状の設定", "plan.menu.roof.cover.roof.shape.setting": "屋根形状の設定",
"plan.menu.roof.cover.roof.shape.passivity.setting": "屋根形状の手動設定", "plan.menu.roof.cover.roof.shape.passivity.setting": "屋根形状の手動設定",
@ -72,7 +73,7 @@
"common.setting.rollback": "前に戻る", "common.setting.rollback": "前に戻る",
"modal.cover.outline.remove": "外壁の取り外し", "modal.cover.outline.remove": "外壁の取り外し",
"modal.cover.outline.select.move": "外壁選択の移動", "modal.cover.outline.select.move": "外壁選択の移動",
"plan.menu.placement.surface": "配置面", "plan.menu.placement.surface": "実測値入力",
"plan.menu.placement.surface.slope.setting": "傾斜設定", "plan.menu.placement.surface.slope.setting": "傾斜設定",
"plan.menu.placement.surface.drawing": "配置面の描画", "plan.menu.placement.surface.drawing": "配置面の描画",
"modal.placement.surface.drawing.straight.line": "直線", "modal.placement.surface.drawing.straight.line": "直線",
@ -88,13 +89,20 @@
"plan.menu.module.circuit.setting.default": "モジュール/架台設定", "plan.menu.module.circuit.setting.default": "モジュール/架台設定",
"modal.module.basic.setting.orientation.setting": "方位設定", "modal.module.basic.setting.orientation.setting": "方位設定",
"modal.module.basic.setting.orientation.setting.info": "※シミュレーション計算用方位を指定します。南の方位を設定してください。", "modal.module.basic.setting.orientation.setting.info": "※シミュレーション計算用方位を指定します。南の方位を設定してください。",
"modal.module.basic.setting.orientation.setting.angle.passivity": "勾配を直接入力", "modal.module.basic.setting.orientation.setting.angle.passivity": "角度変更",
"modal.module.basic.setting.module.roof.material": "屋根材", "modal.module.basic.setting.module.roof.material": "屋根材",
"modal.module.basic.setting.module.trestle.maker": "架台メーカー", "modal.module.basic.setting.module.trestle.maker": "架台メーカー",
"modal.module.basic.setting.module.rafter.margin": "垂木の間隔", "modal.module.basic.setting.module.rafter.margin": "垂木の間隔",
"modal.module.basic.setting.module.construction.method": "工法", "modal.module.basic.setting.module.construction.method": "工法",
"modal.module.basic.setting.module.under.roof": "屋根下", "modal.module.basic.setting.module.under.roof": "屋根",
"modal.module.basic.setting.module.setting": "モジュールの選択", "modal.module.basic.setting.module.setting": "モジュールの選択",
"modal.module.basic.setting.module.placement.area": "モジュール配置領域",
"modal.module.basic.setting.module.placement.margin": "モジュール間の間隙",
"modal.module.basic.setting.module.placement.area.eaves": "軒側",
"modal.module.basic.setting.module.placement.area.ridge": "棟側",
"modal.module.basic.setting.module.placement.area.keraba": "けらぱ",
"modal.module.basic.setting.module.placement.margin.horizontal": "左右",
"modal.module.basic.setting.module.placement.margin.vertical": "上下",
"modal.module.basic.setting.module.hajebichi": "ハゼピッチ", "modal.module.basic.setting.module.hajebichi": "ハゼピッチ",
"modal.module.basic.setting.module.setting.info1": "※勾配の範囲には制限があります。屋根傾斜が2.5値未満10値を超える場合は、施工が可能かどうか施工マニュアルを確認してください。", "modal.module.basic.setting.module.setting.info1": "※勾配の範囲には制限があります。屋根傾斜が2.5値未満10値を超える場合は、施工が可能かどうか施工マニュアルを確認してください。",
"modal.module.basic.setting.module.setting.info2": "※モジュール配置時は、施工マニュアルに記載されている<モジュール配置条件>を必ずご確認ください。", "modal.module.basic.setting.module.setting.info2": "※モジュール配置時は、施工マニュアルに記載されている<モジュール配置条件>を必ずご確認ください。",
@ -105,32 +113,50 @@
"modal.module.basic.setting.module.standard.snowfall.amount": "積雪量", "modal.module.basic.setting.module.standard.snowfall.amount": "積雪量",
"modal.module.basic.setting.module.standard.construction": "標準施工", "modal.module.basic.setting.module.standard.construction": "標準施工",
"modal.module.basic.setting.module.enforce.construction": "強化施工", "modal.module.basic.setting.module.enforce.construction": "強化施工",
"modal.module.basic.setting.module.multiple.construction": "多施工", "modal.module.basic.setting.module.multiple.construction": "多施工",
"modal.module.basic.setting.module.eaves.bar.fitting": "軒カバーの設置", "modal.module.basic.setting.module.eaves.bar.fitting": "軒カバーの設置",
"modal.module.basic.setting.module.blind.metal.fitting": "落雪防止金具設置", "modal.module.basic.setting.module.blind.metal.fitting": "落雪防止金具設置",
"modal.module.basic.setting.module.select": "モジュール/架台選択", "modal.module.basic.setting.module.select": "モジュール/架台選択",
"modal.module.basic.settting.module.error1": "架台メーカーを選択してください。\n(屋根材: {0})",
"modal.module.basic.settting.module.error2": "工法を選択してください。\n(屋根材: {0})",
"modal.module.basic.settting.module.error3": "屋根の下を選択してください。\n(屋根材: {0})",
"modal.module.basic.settting.module.error4": "設置可能な施工条件がないので設置条件を変更してください。\n(屋根材: {0})",
"modal.module.basic.settting.module.error5": "L を選択してください。\n(屋根材: {0})",
"modal.module.basic.settting.module.error6": "垂木の間隔を入力してください。\n(屋根材: {0})",
"modal.module.basic.settting.module.error7": "下在ビーチを入力してください。\n(屋根材: {0})",
"modal.module.basic.settting.module.error8": "モジュール配置領域の値を入力してください。\n(屋根材: {0})",
"modal.module.basic.settting.module.error9": "軒側の値は{0} mm以上でなければなりません。\n(屋根材: {1})",
"modal.module.basic.settting.module.error10": "吊下側の値は{0} mm以上でなければなりません。\n(屋根材: {1})",
"modal.module.basic.settting.module.error11": "ケラバ側の値は{0} mm以上でなければなりません。\n(屋根材: {1})",
"modal.module.basic.settting.module.error12": "施工方法を選択してください。\n(屋根材: {0})",
"modal.module.basic.setting.module.placement": "モジュールの配置", "modal.module.basic.setting.module.placement": "モジュールの配置",
"modal.module.basic.setting.module.placement.select.fitting.type": "設置形態を選択してください。", "modal.module.basic.setting.module.placement.select.fitting.type": "設置形態を選択してください。",
"modal.module.basic.setting.module.placement.waterfowl.arrangement": "千鳥配置", "modal.module.basic.setting.module.placement.waterfowl.arrangement": "千鳥配置",
"modal.module.basic.setting.module.placement.max.row.amount": "Max段数",
"modal.module.basic.setting.module.placement.mix.max.row.amount": "混合Max段数",
"modal.module.basic.setting.module.placement.row.amount": "段数",
"modal.module.basic.setting.module.placement.column.amount": "列数",
"modal.module.basic.setting.module.placement.do": "する", "modal.module.basic.setting.module.placement.do": "する",
"modal.module.basic.setting.module.placement.do.not": "しない", "modal.module.basic.setting.module.placement.do.not": "しない",
"modal.module.basic.setting.module.placement.arrangement.standard": "配置基準", "modal.module.basic.setting.module.placement.arrangement.standard": "配置基準",
"modal.module.basic.setting.module.placement.arrangement.standard.center": "中央", "modal.module.basic.setting.module.placement.arrangement.standard.center": "中央",
"modal.module.basic.setting.module.placement.arrangement.standard.eaves": "軒", "modal.module.basic.setting.module.placement.arrangement.standard.eaves": "軒",
"modal.module.basic.setting.module.placement.arrangement.standard.ridge": "棟", "modal.module.basic.setting.module.placement.arrangement.standard.ridge": "棟",
"modal.module.basic.setting.module.placement.maximum": "最大配置", "modal.module.basic.setting.module.placement.maximum": "最大配置",
"modal.module.basic.setting.pitch.module.placement.standard.setting": "配置基準設定", "modal.module.basic.setting.pitch.module.placement.standard.setting": "配置基準設定",
"modal.module.basic.setting.pitch.module.placement.standard.setting.south": "南向き設置", "modal.module.basic.setting.pitch.module.placement.standard.setting.south": "南向き設置",
"modal.module.basic.setting.pitch.module.placement.standard.setting.select": "指定した辺を基準に設置", "modal.module.basic.setting.pitch.module.placement.standard.setting.select": "指定した辺を基準に設置する",
"modal.module.basic.setting.pitch.module.allocation.setting": "割り当て設定", "modal.module.basic.setting.pitch.module.allocation.setting": "割り当て設定",
"modal.module.basic.setting.pitch.module.allocation.setting.info": "※配置パネルの種類が1種類の場合のみ使用できます。", "modal.module.basic.setting.pitch.module.allocation.setting.info": "※配置パネルの種類が1種類の場合のみ使用できます。",
"modal.module.basic.setting.pitch.module.row.amount": "単数", "modal.module.basic.setting.pitch.module.row.amount": "単数",
"modal.module.basic.setting.pitch.module.row.margin": "上下間隔", "modal.module.basic.setting.pitch.module.row.margin": "上下間隔",
"modal.module.basic.setting.pitch.module.column.amount": "列数", "modal.module.basic.setting.pitch.module.column.amount": "列数",
"modal.module.basic.setting.pitch.module.column.margin": "左右間隔", "modal.module.basic.setting.pitch.module.column.margin": "左右間隔",
"modal.module.basic.setting.prev": "移転", "modal.module.basic.setting.prev": "前に戻る",
"modal.module.basic.setting.row.batch": "レイアウト指定",
"modal.module.basic.setting.passivity.placement": "手動配置", "modal.module.basic.setting.passivity.placement": "手動配置",
"modal.module.basic.setting.auto.placement": "設定値に自動配置", "modal.module.basic.setting.auto.placement": "自動配置",
"modal.module.basic.setting.auto.row.batch": "自動レイアウト指定",
"plan.menu.module.circuit.setting.circuit.trestle.setting": "回路設定", "plan.menu.module.circuit.setting.circuit.trestle.setting": "回路設定",
"modal.circuit.trestle.setting": "回路設定", "modal.circuit.trestle.setting": "回路設定",
"modal.circuit.trestle.setting.alloc.trestle": "架台配置", "modal.circuit.trestle.setting.alloc.trestle": "架台配置",
@ -142,7 +168,7 @@
"modal.circuit.trestle.setting.power.conditional.select.max.connection": "標準枚数", "modal.circuit.trestle.setting.power.conditional.select.max.connection": "標準枚数",
"modal.circuit.trestle.setting.power.conditional.select.max.overload": "最大枚数", "modal.circuit.trestle.setting.power.conditional.select.max.overload": "最大枚数",
"modal.circuit.trestle.setting.power.conditional.select.output.current": "出力電流", "modal.circuit.trestle.setting.power.conditional.select.output.current": "出力電流",
"modal.circuit.trestle.setting.power.conditional.select.check1": "同じ傾斜同じ方向の面積ケース同じ面として回路分ける。", "modal.circuit.trestle.setting.power.conditional.select.check1": "同一勾配・同一方面の面である場合、同じ面として回路分けを行う",
"modal.circuit.trestle.setting.power.conditional.select.check2": "MAX接続過積で回路を分ける。", "modal.circuit.trestle.setting.power.conditional.select.check2": "MAX接続過積で回路を分ける。",
"modal.circuit.trestle.setting.circuit.allocation": "回路割り当て", "modal.circuit.trestle.setting.circuit.allocation": "回路割り当て",
"modal.circuit.trestle.setting.circuit.allocation.auto": "自動回路割り当て", "modal.circuit.trestle.setting.circuit.allocation.auto": "自動回路割り当て",
@ -153,7 +179,7 @@
"modal.circuit.trestle.setting.circuit.allocation.passivity.info": "同じ回路のモジュールを選択状態にした後、[番号確定]ボタンを押すと番号が割り当てられます。", "modal.circuit.trestle.setting.circuit.allocation.passivity.info": "同じ回路のモジュールを選択状態にした後、[番号確定]ボタンを押すと番号が割り当てられます。",
"modal.circuit.trestle.setting.circuit.allocation.passivity.selected.power.conditional": "選択したパワーコンディショナー", "modal.circuit.trestle.setting.circuit.allocation.passivity.selected.power.conditional": "選択したパワーコンディショナー",
"modal.circuit.trestle.setting.circuit.allocation.passivity.circuit.num": "設定する回路番号1", "modal.circuit.trestle.setting.circuit.allocation.passivity.circuit.num": "設定する回路番号1",
"modal.circuit.trestle.setting.circuit.allocation.passivity.circuit.info": "標準回路{0}章~{1}章", "modal.circuit.trestle.setting.circuit.allocation.passivity.circuit.info": "標準回路{0}{1}直",
"modal.circuit.trestle.setting.circuit.allocation.passivity.selected.power.conditional.reset": "選択したパワーコンディショナーの回路番号の初期化", "modal.circuit.trestle.setting.circuit.allocation.passivity.selected.power.conditional.reset": "選択したパワーコンディショナーの回路番号の初期化",
"modal.circuit.trestle.setting.circuit.allocation.passivity.selected.power.conditional.reset.info": "選択したパワーコンディショナーの回路割り当てを初期化します。", "modal.circuit.trestle.setting.circuit.allocation.passivity.selected.power.conditional.reset.info": "選択したパワーコンディショナーの回路割り当てを初期化します。",
"modal.circuit.trestle.setting.circuit.allocation.passivity.all.power.conditional.reset": "すべての回路番号の初期化", "modal.circuit.trestle.setting.circuit.allocation.passivity.all.power.conditional.reset": "すべての回路番号の初期化",
@ -162,7 +188,7 @@
"modal.circuit.trestle.setting.circuit.allocation.passivity.all.power.conditional.validation.error02": "シリーズを選択してください。", "modal.circuit.trestle.setting.circuit.allocation.passivity.all.power.conditional.validation.error02": "シリーズを選択してください。",
"modal.circuit.trestle.setting.circuit.allocation.passivity.circuit.num.fix": "番号確定", "modal.circuit.trestle.setting.circuit.allocation.passivity.circuit.num.fix": "番号確定",
"modal.circuit.trestle.setting.step.up.allocation": "昇圧設定", "modal.circuit.trestle.setting.step.up.allocation": "昇圧設定",
"modal.circuit.trestle.setting.step.up.allocation.serial.amount": "シリアル枚数", "modal.circuit.trestle.setting.step.up.allocation.serial.amount": "直列枚数",
"modal.circuit.trestle.setting.step.up.allocation.total.amount": "総回路数", "modal.circuit.trestle.setting.step.up.allocation.total.amount": "総回路数",
"modal.circuit.trestle.setting.step.up.allocation.connected": "接続する", "modal.circuit.trestle.setting.step.up.allocation.connected": "接続する",
"modal.circuit.trestle.setting.step.up.allocation.circuit.amount": "昇圧回路数", "modal.circuit.trestle.setting.step.up.allocation.circuit.amount": "昇圧回路数",
@ -178,7 +204,7 @@
"modal.roof.alloc.select.parallel": "筋配置", "modal.roof.alloc.select.parallel": "筋配置",
"modal.roof.alloc.select.stairs": "千鳥配置", "modal.roof.alloc.select.stairs": "千鳥配置",
"modal.roof.alloc.apply": "選択した屋根材として割り当て", "modal.roof.alloc.apply": "選択した屋根材として割り当て",
"plan.menu.estimate.docDown": "各種資料ダウンロード", "plan.menu.estimate.docDownload": "見積書出力",
"plan.menu.estimate.save": "保存", "plan.menu.estimate.save": "保存",
"plan.menu.estimate.reset": "初期化", "plan.menu.estimate.reset": "初期化",
"plan.menu.estimate.copy": "見積書のコピー", "plan.menu.estimate.copy": "見積書のコピー",
@ -299,7 +325,7 @@
"modal.object.setting.offset.width": "幅の出幅", "modal.object.setting.offset.width": "幅の出幅",
"modal.object.setting.offset.slope": "勾配", "modal.object.setting.offset.slope": "勾配",
"modal.object.setting.direction.select": "方向の選択", "modal.object.setting.direction.select": "方向の選択",
"modal.placement.surface.setting.info": "ⓘ①の長さ入力後、対角線長を入力すると②の長さを自動計算します。", "modal.placement.surface.setting.info": "①の長さを入力後、③の長さを入力すると②の長さを自動計算します。",
"modal.placement.surface.setting.diagonal.length": "斜めの長さ", "modal.placement.surface.setting.diagonal.length": "斜めの長さ",
"modal.color.picker.title": "色設定", "modal.color.picker.title": "色設定",
"modal.color.picker.default.color": "基本色", "modal.color.picker.default.color": "基本色",
@ -549,7 +575,7 @@
"color.pink": "ピンク", "color.pink": "ピンク",
"color.gold": "黄金色", "color.gold": "黄金色",
"color.darkblue": "藍色", "color.darkblue": "藍色",
"site.name": "HANASYS設計", "site.name": "HANASYS DESIGN",
"site.sub_name": "太陽光発電システム図面管理サイト", "site.sub_name": "太陽光発電システム図面管理サイト",
"site.header.link1": "選択してください。", "site.header.link1": "選択してください。",
"site.header.link2": "オンライン保証システム", "site.header.link2": "オンライン保証システム",
@ -558,7 +584,7 @@
"board.faq.title": "FAQ", "board.faq.title": "FAQ",
"board.faq.sub.title": "FAQリスト", "board.faq.sub.title": "FAQリスト",
"board.archive.title": "各種資料ダウンロード", "board.archive.title": "各種資料ダウンロード",
"board.archive.sub.title": "見積書一覧", "board.archive.sub.title": "掲載資料一覧",
"board.list.header.rownum": "番号", "board.list.header.rownum": "番号",
"board.list.header.title": "タイトル", "board.list.header.title": "タイトル",
"board.list.header.regDt": "登録日", "board.list.header.regDt": "登録日",
@ -594,6 +620,7 @@
"myinfo.message.password.error": "パスワードが間違っています。", "myinfo.message.password.error": "パスワードが間違っています。",
"login": "ログイン", "login": "ログイン",
"login.auto.page.text": "自動ログイン中です。", "login.auto.page.text": "自動ログイン中です。",
"login.fail": "계정이 없거나 비밀번호가 잘못되었습니다.",
"login.id.save": "ID保存", "login.id.save": "ID保存",
"login.id.placeholder": "IDを入力してください。", "login.id.placeholder": "IDを入力してください。",
"login.password.placeholder": "パスワードを入力してください。", "login.password.placeholder": "パスワードを入力してください。",
@ -841,7 +868,7 @@
"has.not.sleeve": "袖なし", "has.not.sleeve": "袖なし",
"jerkinhead.width": "半切妻幅", "jerkinhead.width": "半切妻幅",
"jerkinhead.slope": "半切妻傾斜", "jerkinhead.slope": "半切妻傾斜",
"shed.width": "片流幅", "shed.width": "片流れの出幅",
"windage": "片流れ", "windage": "片流れ",
"windage.width": "片流れの出幅", "windage.width": "片流れの出幅",
"write": "作成", "write": "作成",
@ -886,7 +913,7 @@
"estimate.detail.drawingEstimateCreateDate": "登録日", "estimate.detail.drawingEstimateCreateDate": "登録日",
"estimate.detail.lastEditDatetime": "変更日時", "estimate.detail.lastEditDatetime": "変更日時",
"estimate.detail.saleStoreId": "一次販売店名", "estimate.detail.saleStoreId": "一次販売店名",
"estimate.detail.estimateDate": "見積日", "estimate.detail.estimateDate": "見積作成日",
"estimate.detail.otherSaleStoreId": "二次販売店名", "estimate.detail.otherSaleStoreId": "二次販売店名",
"estimate.detail.noOtherSaleStoreId": "二次店なし", "estimate.detail.noOtherSaleStoreId": "二次店なし",
"estimate.detail.receiveUser": "担当者", "estimate.detail.receiveUser": "担当者",
@ -895,6 +922,7 @@
"estimate.detail.estimateType": "注文分類", "estimate.detail.estimateType": "注文分類",
"estimate.detail.estimateType.yjss": "住宅PKG", "estimate.detail.estimateType.yjss": "住宅PKG",
"estimate.detail.estimateType.yjod": "積上げYJOD", "estimate.detail.estimateType.yjod": "積上げYJOD",
"estimate.detail.agency": "2次店名",
"estimate.detail.roofCns": "屋根材・施工区分", "estimate.detail.roofCns": "屋根材・施工区分",
"estimate.detail.remarks": "備考", "estimate.detail.remarks": "備考",
"estimate.detail.fileFlg": "後日資料提出", "estimate.detail.fileFlg": "後日資料提出",
@ -918,6 +946,7 @@
"estimate.detail.header.unitPrice": "定価", "estimate.detail.header.unitPrice": "定価",
"estimate.detail.showPrice.pricingBtn": "Pricing", "estimate.detail.showPrice.pricingBtn": "Pricing",
"estimate.detail.showPrice.pricingBtn.noItemId": "Pricingが欠落しているアイテムがあります。 Pricingを進めてください。", "estimate.detail.showPrice.pricingBtn.noItemId": "Pricingが欠落しているアイテムがあります。 Pricingを進めてください。",
"estimate.detail.showPrice.pricingBtn.confirm": "価格登録初期化されますがよろしいですか?",
"estimate.detail.showPrice.description1": "製品価格OPEN", "estimate.detail.showPrice.description1": "製品価格OPEN",
"estimate.detail.showPrice.description2": "追加の変更品目", "estimate.detail.showPrice.description2": "追加の変更品目",
"estimate.detail.showPrice.description3": "添付必須", "estimate.detail.showPrice.description3": "添付必須",
@ -957,6 +986,7 @@
"estimate.detail.estimateCopyPopup.close": "閉じる", "estimate.detail.estimateCopyPopup.close": "閉じる",
"estimate.detail.estimateCopyPopup.copyBtn": "見積コピー", "estimate.detail.estimateCopyPopup.copyBtn": "見積コピー",
"estimate.detail.estimateCopyPopup.copy.alertMessage": "見積書がコピーされました。コピーした見積情報に移動します。", "estimate.detail.estimateCopyPopup.copy.alertMessage": "見積書がコピーされました。コピーした見積情報に移動します。",
"estimate.detail.estimateCopyPopup.copy.alertMessageError": "キャンバスのコピー中にエラーが発生しました.",
"estimate.detail.productFeaturesPopup.title": "製品特異事項", "estimate.detail.productFeaturesPopup.title": "製品特異事項",
"estimate.detail.productFeaturesPopup.close": "閉じる", "estimate.detail.productFeaturesPopup.close": "閉じる",
"estimate.detail.productFeaturesPopup.requiredStoreId": "一次販売店は必須です。", "estimate.detail.productFeaturesPopup.requiredStoreId": "一次販売店は必須です。",
@ -997,7 +1027,7 @@
"simulator.table.sub5": "枚数", "simulator.table.sub5": "枚数",
"simulator.table.sub6": "合計", "simulator.table.sub6": "合計",
"simulator.table.sub7": "パワーコンディショナー", "simulator.table.sub7": "パワーコンディショナー",
"simulator.table.sub8": "ティーン", "simulator.table.sub8": "",
"simulator.table.sub9": "予測発電量kWh", "simulator.table.sub9": "予測発電量kWh",
"simulator.notice.sub1": "ハンファジャパン年間発電量", "simulator.notice.sub1": "ハンファジャパン年間発電量",
"simulator.notice.sub2": "シミュレーションガイド", "simulator.notice.sub2": "シミュレーションガイド",
@ -1034,5 +1064,20 @@
"roof.exceed.count": "屋根材は4つまで選択可能です。", "roof.exceed.count": "屋根材は4つまで選択可能です。",
"outerLine.property.fix": "外壁線の属性設定 を完了しますか?", "outerLine.property.fix": "外壁線の属性設定 を完了しますか?",
"outerLine.property.close": "外壁線の属性設定 を終了しますか?", "outerLine.property.close": "外壁線の属性設定 を終了しますか?",
"want.to.complete.auxiliary.creation": "보補助線の作成を完了しますか?" "want.to.complete.auxiliary.creation": "補助線の作成を完了しますか?",
"module.layout.setup.has.zero.value": "モジュールの列数、段数を入力して下さい。",
"modal.placement.initial.setting.plan.drawing.only.number": "(※数字は[半角]入力のみ可能です。)",
"wall.line.not.found": "外壁がありません",
"roof.line.not.found": "屋根形状がありません",
"roof.material.can.not.delete": "割り当てられた配置面があります。",
"chidory.can.not.install": "千鳥配置できない工法です。",
"module.layout.setup.max.count": "モジュールの単体での最大段数は{0}、最大列数は{1}です。 (JA)",
"module.layout.setup.max.count.multiple": "モジュール{0}の単体での最大段数は{1}、最大列数は{2}です。 (JA)",
"roofAllocation.not.found": "割り当てる屋根がありません。 (JA)",
"modal.module.basic.setting.module.placement.max.size.check": "屋根材別モジュールの単体の単体での最大段数、2種混合の段数を確認して下さい",
"modal.module.basic.setting.module.placement.max.row": "単体で\rの最大段数",
"modal.module.basic.setting.module.placement.max.rows.multiple": "2種混合時\rの最大段数",
"modal.module.basic.setting.module.placement.mix.asg.yn.error": "混合インストール不可能なモジュールです。 (JA)",
"modal.module.basic.setting.module.placement.mix.asg.yn": "混合",
"modal.module.basic.setting.module.placement.over.max.row": "{0} 最大段数超過しました。最大段数表を参考にしてください。"
} }

View File

@ -18,6 +18,7 @@
"plan.menu.placement.surface.initial.setting": "배치면 초기설정", "plan.menu.placement.surface.initial.setting": "배치면 초기설정",
"modal.placement.initial.setting.plan.drawing": "도면 작성방법", "modal.placement.initial.setting.plan.drawing": "도면 작성방법",
"modal.placement.initial.setting.plan.drawing.size.stuff": "치수 입력에 의한 물건 작성", "modal.placement.initial.setting.plan.drawing.size.stuff": "치수 입력에 의한 물건 작성",
"modal.placement.initial.setting.plan.drawing.size.info": "※숫자는 [반각] 입력만 가능합니다.",
"modal.placement.initial.setting.size": "치수 입력방법", "modal.placement.initial.setting.size": "치수 입력방법",
"modal.placement.initial.setting.size.info": "치수 입력방법 안내", "modal.placement.initial.setting.size.info": "치수 입력방법 안내",
"modal.placement.initial.setting.size.roof": "복시도 입력", "modal.placement.initial.setting.size.roof": "복시도 입력",
@ -88,13 +89,20 @@
"plan.menu.module.circuit.setting.default": "모듈/가대설정", "plan.menu.module.circuit.setting.default": "모듈/가대설정",
"modal.module.basic.setting.orientation.setting": "방위 설정", "modal.module.basic.setting.orientation.setting": "방위 설정",
"modal.module.basic.setting.orientation.setting.info": "※시뮬레이션 계산용 방위를 지정합니다. 남쪽의 방위를 설정해주세요.", "modal.module.basic.setting.orientation.setting.info": "※시뮬레이션 계산용 방위를 지정합니다. 남쪽의 방위를 설정해주세요.",
"modal.module.basic.setting.orientation.setting.angle.passivity": "각도를 직접 입력", "modal.module.basic.setting.orientation.setting.angle.passivity": "각도 입력",
"modal.module.basic.setting.module.roof.material": "지붕재", "modal.module.basic.setting.module.roof.material": "지붕재",
"modal.module.basic.setting.module.trestle.maker": "가대메이커", "modal.module.basic.setting.module.trestle.maker": "가대메이커",
"modal.module.basic.setting.module.rafter.margin": "서까래 간격", "modal.module.basic.setting.module.rafter.margin": "서까래 간격",
"modal.module.basic.setting.module.construction.method": "공법", "modal.module.basic.setting.module.construction.method": "공법",
"modal.module.basic.setting.module.under.roof": "지붕밑바탕", "modal.module.basic.setting.module.under.roof": "지붕밑바탕",
"modal.module.basic.setting.module.setting": "모듈 선택", "modal.module.basic.setting.module.setting": "모듈 선택",
"modal.module.basic.setting.module.placement.area": "모듈 배치 영역",
"modal.module.basic.setting.module.placement.margin": "모듈 배치 간격",
"modal.module.basic.setting.module.placement.area.eaves": "처마쪽",
"modal.module.basic.setting.module.placement.area.ridge": "용마루쪽",
"modal.module.basic.setting.module.placement.area.keraba": "케라바쪽",
"modal.module.basic.setting.module.placement.margin.horizontal": "좌우",
"modal.module.basic.setting.module.placement.margin.vertical": "상하",
"modal.module.basic.setting.module.hajebichi": "망둥어 피치", "modal.module.basic.setting.module.hajebichi": "망둥어 피치",
"modal.module.basic.setting.module.setting.info1": "※ 구배의 범위에는 제한이 있습니다. 지붕경사가 2.5치 미만 10치를 초과하는 경우에는 시공이 가능한지 시공 매뉴얼을 확인해주십시오.", "modal.module.basic.setting.module.setting.info1": "※ 구배의 범위에는 제한이 있습니다. 지붕경사가 2.5치 미만 10치를 초과하는 경우에는 시공이 가능한지 시공 매뉴얼을 확인해주십시오.",
"modal.module.basic.setting.module.setting.info2": "※ 모듈 배치 시에는 시공 매뉴얼에 기재된 <모듈 배치 조건>을 반드시 확인해주십시오.", "modal.module.basic.setting.module.setting.info2": "※ 모듈 배치 시에는 시공 매뉴얼에 기재된 <모듈 배치 조건>을 반드시 확인해주십시오.",
@ -109,16 +117,33 @@
"modal.module.basic.setting.module.eaves.bar.fitting": "처마커버설치", "modal.module.basic.setting.module.eaves.bar.fitting": "처마커버설치",
"modal.module.basic.setting.module.blind.metal.fitting": "적설방지금구설치", "modal.module.basic.setting.module.blind.metal.fitting": "적설방지금구설치",
"modal.module.basic.setting.module.select": "모듈/가대 선택", "modal.module.basic.setting.module.select": "모듈/가대 선택",
"modal.module.basic.settting.module.error1": "가대메이커를 선택해주세요.\n(지붕재: {0})",
"modal.module.basic.settting.module.error2": "공법를 선택해주세요.\n(지붕재: {0})",
"modal.module.basic.settting.module.error3": "지붕밑바탕을 선택해주세요.\n(지붕재: {0})",
"modal.module.basic.settting.module.error4": "설치 조건이 없으므로 설치 조건을 변경하십시오.\n(지붕재: {0})",
"modal.module.basic.settting.module.error5": "L 을 입력해주세요.\n(지붕재: {0})",
"modal.module.basic.settting.module.error6": "서까래 간격을 입력해주세요.\n(지붕재: {0})",
"modal.module.basic.settting.module.error7": "하제비치를 입력해주세요.\n(지붕재: {0})",
"modal.module.basic.settting.module.error8": "모듈 배치 영역 값을 입력해주세요.\n(지붕재: {0})",
"modal.module.basic.settting.module.error9": "처마쪽 값은 {0}mm 이상이어야 합니다.\n(지붕재: {1})",
"modal.module.basic.settting.module.error10": "용마루쪽 값은 {0}mm 이상이어야 합니다.\n(지붕재: {1})",
"modal.module.basic.settting.module.error11": "케라바쪽 값은 {0}mm 이상이어야 합니다.\n(지붕재: {1})",
"modal.module.basic.settting.module.error12": "시공법을 선택해주세요.\n(지붕재: {0})",
"modal.module.basic.setting.module.placement": "모듈 배치", "modal.module.basic.setting.module.placement": "모듈 배치",
"modal.module.basic.setting.module.placement.select.fitting.type": "설치형태를 선택합니다.", "modal.module.basic.setting.module.placement.select.fitting.type": "설치형태를 선택합니다.",
"modal.module.basic.setting.module.placement.waterfowl.arrangement": "물떼새 배치", "modal.module.basic.setting.module.placement.waterfowl.arrangement": "물떼새 배치",
"modal.module.basic.setting.module.placement.max.row.amount": "Max 단수",
"modal.module.basic.setting.module.placement.mix.max.row.amount": "혼합Max 단수",
"modal.module.basic.setting.module.placement.row.amount": "단수",
"modal.module.basic.setting.module.placement.column.amount": "열수",
"modal.module.basic.setting.module.placement.do": "한다", "modal.module.basic.setting.module.placement.do": "한다",
"modal.module.basic.setting.module.placement.do.not": "하지 않는다", "modal.module.basic.setting.module.placement.do.not": "하지 않는다",
"modal.module.basic.setting.module.placement.arrangement.standard": "배치 기준", "modal.module.basic.setting.module.placement.arrangement.standard": "배치 기준",
"modal.module.basic.setting.module.placement.arrangement.standard.center": "중앙", "modal.module.basic.setting.module.placement.arrangement.standard.center": "중앙",
"modal.module.basic.setting.module.placement.arrangement.standard.eaves": "처마", "modal.module.basic.setting.module.placement.arrangement.standard.eaves": "처마",
"modal.module.basic.setting.module.placement.arrangement.standard.ridge": "용마루", "modal.module.basic.setting.module.placement.arrangement.standard.ridge": "용마루",
"modal.module.basic.setting.module.placement.maximum": "최대배치", "modal.module.basic.setting.module.placement.maximum": "최대배치",
"modal.module.basic.setting.module.placement.margin.check1": "가대메이커를 선택해주세요.",
"modal.module.basic.setting.pitch.module.placement.standard.setting": "배치기준 설정", "modal.module.basic.setting.pitch.module.placement.standard.setting": "배치기준 설정",
"modal.module.basic.setting.pitch.module.placement.standard.setting.south": "남향설치", "modal.module.basic.setting.pitch.module.placement.standard.setting.south": "남향설치",
"modal.module.basic.setting.pitch.module.placement.standard.setting.select": "지정한 변을 기준으로 설치", "modal.module.basic.setting.pitch.module.placement.standard.setting.select": "지정한 변을 기준으로 설치",
@ -129,8 +154,10 @@
"modal.module.basic.setting.pitch.module.column.amount": "열수", "modal.module.basic.setting.pitch.module.column.amount": "열수",
"modal.module.basic.setting.pitch.module.column.margin": "좌우간격", "modal.module.basic.setting.pitch.module.column.margin": "좌우간격",
"modal.module.basic.setting.prev": "이전", "modal.module.basic.setting.prev": "이전",
"modal.module.basic.setting.row.batch": "단·열수 지정 배치",
"modal.module.basic.setting.passivity.placement": "수동 배치", "modal.module.basic.setting.passivity.placement": "수동 배치",
"modal.module.basic.setting.auto.placement": "설정값으로 자동 배치", "modal.module.basic.setting.auto.placement": "자동 배치",
"modal.module.basic.setting.auto.row.batch": "자동 단·열수 지정 배치 ",
"plan.menu.module.circuit.setting.circuit.trestle.setting": "회로설정", "plan.menu.module.circuit.setting.circuit.trestle.setting": "회로설정",
"modal.circuit.trestle.setting": "회로설정", "modal.circuit.trestle.setting": "회로설정",
"modal.circuit.trestle.setting.alloc.trestle": "가대할당", "modal.circuit.trestle.setting.alloc.trestle": "가대할당",
@ -142,7 +169,7 @@
"modal.circuit.trestle.setting.power.conditional.select.max.connection": "표준매수", "modal.circuit.trestle.setting.power.conditional.select.max.connection": "표준매수",
"modal.circuit.trestle.setting.power.conditional.select.max.overload": "최대매수", "modal.circuit.trestle.setting.power.conditional.select.max.overload": "최대매수",
"modal.circuit.trestle.setting.power.conditional.select.output.current": "출력전류", "modal.circuit.trestle.setting.power.conditional.select.output.current": "출력전류",
"modal.circuit.trestle.setting.power.conditional.select.check1": "동일경사 동일방면의 면인 경우 같은 면으로서 회로를 나눈다.", "modal.circuit.trestle.setting.power.conditional.select.check1": "동일 구배·동일 방면의 면인 경우, 같은 면으로서 회로를 나눈다",
"modal.circuit.trestle.setting.power.conditional.select.check2": "MAX 접속(과적)으로 회로를 나눈다.", "modal.circuit.trestle.setting.power.conditional.select.check2": "MAX 접속(과적)으로 회로를 나눈다.",
"modal.circuit.trestle.setting.circuit.allocation": "회로 할당", "modal.circuit.trestle.setting.circuit.allocation": "회로 할당",
"modal.circuit.trestle.setting.circuit.allocation.auto": "자동회로 할당", "modal.circuit.trestle.setting.circuit.allocation.auto": "자동회로 할당",
@ -153,7 +180,7 @@
"modal.circuit.trestle.setting.circuit.allocation.passivity.info": "동일한 회로의 모듈을 선택 상태로 만든 후 [번호 확정] 버튼을 누르면 번호가 할당됩니다.", "modal.circuit.trestle.setting.circuit.allocation.passivity.info": "동일한 회로의 모듈을 선택 상태로 만든 후 [번호 확정] 버튼을 누르면 번호가 할당됩니다.",
"modal.circuit.trestle.setting.circuit.allocation.passivity.selected.power.conditional": "선택된 파워컨디셔너", "modal.circuit.trestle.setting.circuit.allocation.passivity.selected.power.conditional": "선택된 파워컨디셔너",
"modal.circuit.trestle.setting.circuit.allocation.passivity.circuit.num": "설정할 회로번호(1~)", "modal.circuit.trestle.setting.circuit.allocation.passivity.circuit.num": "설정할 회로번호(1~)",
"modal.circuit.trestle.setting.circuit.allocation.passivity.circuit.info": "표준회로{0}장~{1}장", "modal.circuit.trestle.setting.circuit.allocation.passivity.circuit.info": "표준회로{0}{1}직",
"modal.circuit.trestle.setting.circuit.allocation.passivity.selected.power.conditional.reset": "선택된 파워컨디셔너의 회로번호 초기화", "modal.circuit.trestle.setting.circuit.allocation.passivity.selected.power.conditional.reset": "선택된 파워컨디셔너의 회로번호 초기화",
"modal.circuit.trestle.setting.circuit.allocation.passivity.selected.power.conditional.reset.info": "선택된 파워 컨디셔너의 회로할당을 초기화합니다.", "modal.circuit.trestle.setting.circuit.allocation.passivity.selected.power.conditional.reset.info": "선택된 파워 컨디셔너의 회로할당을 초기화합니다.",
"modal.circuit.trestle.setting.circuit.allocation.passivity.all.power.conditional.reset": "모든 회로번호 초기화", "modal.circuit.trestle.setting.circuit.allocation.passivity.all.power.conditional.reset": "모든 회로번호 초기화",
@ -178,7 +205,7 @@
"modal.roof.alloc.select.parallel": "병렬식", "modal.roof.alloc.select.parallel": "병렬식",
"modal.roof.alloc.select.stairs": "계단식", "modal.roof.alloc.select.stairs": "계단식",
"modal.roof.alloc.apply": "선택한 지붕재로 할당", "modal.roof.alloc.apply": "선택한 지붕재로 할당",
"plan.menu.estimate.docDown": "문서 다운로드", "plan.menu.estimate.docDownload": "문서 다운로드",
"plan.menu.estimate.save": "저장", "plan.menu.estimate.save": "저장",
"plan.menu.estimate.reset": "초기화", "plan.menu.estimate.reset": "초기화",
"plan.menu.estimate.copy": "견적서 복사", "plan.menu.estimate.copy": "견적서 복사",
@ -594,6 +621,7 @@
"myinfo.message.password.error": "비밀번호가 틀렸습니다.", "myinfo.message.password.error": "비밀번호가 틀렸습니다.",
"login": "로그인", "login": "로그인",
"login.auto.page.text": "자동로그인 중 입니다.", "login.auto.page.text": "자동로그인 중 입니다.",
"login.fail": "계정이 없거나 비밀번호가 잘못되었습니다.",
"login.id.save": "ID Save", "login.id.save": "ID Save",
"login.id.placeholder": "아이디를 입력해주세요.", "login.id.placeholder": "아이디를 입력해주세요.",
"login.password.placeholder": "비밀번호를 입력해주세요.", "login.password.placeholder": "비밀번호를 입력해주세요.",
@ -886,7 +914,7 @@
"estimate.detail.drawingEstimateCreateDate": "등록일", "estimate.detail.drawingEstimateCreateDate": "등록일",
"estimate.detail.lastEditDatetime": "변경일시", "estimate.detail.lastEditDatetime": "변경일시",
"estimate.detail.saleStoreId": "1차 판매점명", "estimate.detail.saleStoreId": "1차 판매점명",
"estimate.detail.estimateDate": "견적일", "estimate.detail.estimateDate": "견적작성일",
"estimate.detail.otherSaleStoreId": "2차 판매점명", "estimate.detail.otherSaleStoreId": "2차 판매점명",
"estimate.detail.noOtherSaleStoreId": "2차점 없음", "estimate.detail.noOtherSaleStoreId": "2차점 없음",
"estimate.detail.receiveUser": "담당자", "estimate.detail.receiveUser": "담당자",
@ -895,6 +923,7 @@
"estimate.detail.estimateType": "주문분류", "estimate.detail.estimateType": "주문분류",
"estimate.detail.estimateType.yjss": "住宅PKG", "estimate.detail.estimateType.yjss": "住宅PKG",
"estimate.detail.estimateType.yjod": "積上げ( YJOD )", "estimate.detail.estimateType.yjod": "積上げ( YJOD )",
"estimate.detail.agency": "2차점명",
"estimate.detail.roofCns": "지붕재・사양시공", "estimate.detail.roofCns": "지붕재・사양시공",
"estimate.detail.remarks": "비고", "estimate.detail.remarks": "비고",
"estimate.detail.fileFlg": "후일자료제출", "estimate.detail.fileFlg": "후일자료제출",
@ -918,6 +947,7 @@
"estimate.detail.header.unitPrice": "정가", "estimate.detail.header.unitPrice": "정가",
"estimate.detail.showPrice.pricingBtn": "Pricing", "estimate.detail.showPrice.pricingBtn": "Pricing",
"estimate.detail.showPrice.pricingBtn.noItemId": "Pricing이 누락된 아이템이 있습니다. 제품 선택 후 Pricing을 진행해주세요.", "estimate.detail.showPrice.pricingBtn.noItemId": "Pricing이 누락된 아이템이 있습니다. 제품 선택 후 Pricing을 진행해주세요.",
"estimate.detail.showPrice.pricingBtn.confirm": "가격등록을 초기화 하시겠습니까?",
"estimate.detail.showPrice.description1": "제품 가격 OPEN", "estimate.detail.showPrice.description1": "제품 가격 OPEN",
"estimate.detail.showPrice.description2": "추가 변경 자재", "estimate.detail.showPrice.description2": "추가 변경 자재",
"estimate.detail.showPrice.description3": "첨부필수", "estimate.detail.showPrice.description3": "첨부필수",
@ -957,6 +987,7 @@
"estimate.detail.estimateCopyPopup.close": "닫기", "estimate.detail.estimateCopyPopup.close": "닫기",
"estimate.detail.estimateCopyPopup.copyBtn": "견적복사", "estimate.detail.estimateCopyPopup.copyBtn": "견적복사",
"estimate.detail.estimateCopyPopup.copy.alertMessage": "견적서가 복사되었습니다. 복사된 물건정보로 이동합니다.", "estimate.detail.estimateCopyPopup.copy.alertMessage": "견적서가 복사되었습니다. 복사된 물건정보로 이동합니다.",
"estimate.detail.estimateCopyPopup.copy.alertMessageError": "캔버스 복사 중 오류 발생.",
"estimate.detail.productFeaturesPopup.title": "제품특이사항", "estimate.detail.productFeaturesPopup.title": "제품특이사항",
"estimate.detail.productFeaturesPopup.close": "닫기", "estimate.detail.productFeaturesPopup.close": "닫기",
"estimate.detail.productFeaturesPopup.requiredStoreId": "1차 판매점은 필수값 입니다.", "estimate.detail.productFeaturesPopup.requiredStoreId": "1차 판매점은 필수값 입니다.",
@ -1034,5 +1065,20 @@
"roof.exceed.count": "지붕재는 4개까지 선택 가능합니다.", "roof.exceed.count": "지붕재는 4개까지 선택 가능합니다.",
"outerLine.property.fix": "외벽선 속성 설정을 완료하시겠습니까?", "outerLine.property.fix": "외벽선 속성 설정을 완료하시겠습니까?",
"outerLine.property.close": "외벽선 속성 설정을 종료하시겠습니까?", "outerLine.property.close": "외벽선 속성 설정을 종료하시겠습니까?",
"want.to.complete.auxiliary.creation": "보조선 작성을 완료하시겠습니까?" "want.to.complete.auxiliary.creation": "보조선 작성을 완료하시겠습니까?",
"module.layout.setup.has.zero.value": "모듈의 열수, 단수를 입력해 주세요.",
"modal.placement.initial.setting.plan.drawing.only.number": "(※ 숫자는 [반각]입력만 가능합니다.)",
"wall.line.not.found": "외벽선이 없습니다.",
"roof.line.not.found": "지붕형상이 없습니다.",
"roof.material.can.not.delete": "할당된 배치면이 있습니다.",
"chidory.can.not.install": "치조 불가 공법입니다.",
"module.layout.setup.max.count": "모듈의 최대 단수는 {0}, 최대 열수는 {1} 입니다.",
"module.layout.setup.max.count.multiple": "모듈 {0}번의 최대 단수는 {1}, 최대 열수는 {2} 입니다.",
"roofAllocation.not.found": "할당할 지붕이 없습니다.",
"modal.module.basic.setting.module.placement.max.size.check": "지붕재별 모듈 단체의 최대 단수, 2종 혼합 단수를 확인하십시오.",
"modal.module.basic.setting.module.placement.max.row": "최대 단수",
"modal.module.basic.setting.module.placement.max.rows.multiple": "2종 혼합 최대단수",
"modal.module.basic.setting.module.placement.mix.asg.yn.error": "혼합 설치 불가능한 모듈입니다.",
"modal.module.basic.setting.module.placement.mix.asg.yn": "혼합",
"modal.module.basic.setting.module.placement.over.max.row": "{0}의 최대단수를 초과했습니다. 최대단수표를 참고해 주세요."
} }

View File

@ -0,0 +1,71 @@
import { atom } from 'recoil'
// 보조선 타입
export const AUXILIARY_LINE_TYPE = {
OUTER_LINE: 'outerLine', // 외벽선
RIGHT_ANGLE: 'rightAngle', // 직각
DOUBLE_PITCH: 'doublePitch',
ANGLE: 'angle', // 각도
DIAGONAL_LINE: 'diagonalLine', // 대각선
}
/**
* 보조선 작성에서 사용하는 recoilState
*/
export const auxiliaryLineLength1State = atom({
//길이1
key: 'auxiliaryLineLength1State',
default: 0,
})
export const auxiliaryLineLength2State = atom({
// 길이2
key: 'auxiliaryLineLength2State',
default: 0,
})
export const auxiliaryLineArrow1State = atom({
// 방향1
key: 'auxiliaryLineArrow1State',
default: '',
})
export const auxiliaryLineArrow2State = atom({
// 방향2
key: 'auxiliaryLineArrow2State',
default: '',
})
export const auxiliaryLineAngle1State = atom({
// 각도1
key: 'auxiliaryLineAngle1State',
default: 0,
})
export const auxiliaryLineAngle2State = atom({
// 각도2
key: 'auxiliaryLineAngle2State',
default: 0,
})
export const auxiliaryLineDiagonalState = atom({
// 대각선
key: 'auxiliaryLineDiagonalState',
default: 0,
})
export const auxiliaryLineTypeState = atom({
key: 'auxiliaryLineTypeState',
default: AUXILIARY_LINE_TYPE.OUTER_LINE,
})
export const auxiliaryLinePointsState = atom({
key: 'auxiliaryLinePointsState',
default: [],
})
export const auxiliaryLineFixState = atom({
key: 'auxiliaryLineFixState',
default: false,
})

View File

@ -384,3 +384,27 @@ export const isManualModuleSetupState = atom({
key: 'isManualModuleSetupState', key: 'isManualModuleSetupState',
default: false, default: false,
}) })
export const isManualModuleLayoutSetupState = atom({
key: 'isManualModuleLayoutSetupState',
default: false,
})
export const moduleSetupOptionState = atom({
key: 'moduleSetupOptionState',
default: {
isChidori: false, //치조 안함
setupLocation: 'eaves', //처마
},
})
export const toggleManualSetupModeState = atom({
key: 'toggleManualSetupModeState',
default: '',
})
export const moduleRowColArrayState = atom({
key: 'moduleRowColArrayState',
default: [],
dangerouslyAllowMutability: true,
})

6
src/store/hotkeyAtom.js Normal file
View File

@ -0,0 +1,6 @@
import { atom } from 'recoil'
export const hotkeyStore = atom({
key: 'hotkeyState',
default: [],
})

7
src/store/roofAtom.js Normal file
View File

@ -0,0 +1,7 @@
import { atom } from 'recoil'
export const roofsState = atom({
key: 'roofs',
default: null,
dangerouslyAllowMutability: true,
})

View File

@ -2,7 +2,7 @@ import { atom } from 'recoil'
export const selectedModuleState = atom({ export const selectedModuleState = atom({
key: 'selectedModuleState', key: 'selectedModuleState',
default: [], default: null,
dangerouslyAllowMutability: true, dangerouslyAllowMutability: true,
}) })

View File

@ -221,6 +221,22 @@ export const corridorDimensionSelector = selector({
const settingModalFirstOptions = get(settingModalFirstOptionsState) const settingModalFirstOptions = get(settingModalFirstOptionsState)
return settingModalFirstOptions.dimensionDisplay.find((option) => option.selected) return settingModalFirstOptions.dimensionDisplay.find((option) => option.selected)
}, },
set: ({ set }, newValue) => {
//0 : 복도치수 , 1 : 실제치수
set(settingModalFirstOptionsState, (prev) => {
return {
...prev,
dimensionDisplay: prev.dimensionDisplay.map((item, index) => {
if (index === newValue) {
return { ...item, selected: true }
} else {
return { ...item, selected: false }
}
}),
}
})
},
dangerouslyAllowMutability: true, dangerouslyAllowMutability: true,
}) })

View File

@ -340,6 +340,14 @@
&.active{ &.active{
top: calc(92.8px + 50px); top: calc(92.8px + 50px);
} }
.canvas-id{
display: flex;
align-items: center;
padding: 9.6px 20px;
font-size: 12px;
color: #fff;
background-color: #1083E3;
}
.canvas-plane-wrap{ .canvas-plane-wrap{
display: flex; display: flex;
align-items: center; align-items: center;
@ -798,28 +806,28 @@
color: #45576F; color: #45576F;
margin-left: 5px; margin-left: 5px;
} }
.drag-file-box{ .btn-area{
padding: 10px; padding-bottom: 15px;
.btn-area{ border-bottom: 1px solid #ECF0F4;
padding-bottom: 15px; .file-upload{
border-bottom: 1px solid #ECF0F4; display: inline-block;
.file-upload{ height: 30px;
display: inline-block; background-color: #94A0AD;
height: 30px; padding: 0 10px;
background-color: #94A0AD; border-radius: 2px;
padding: 0 10px; font-size: 13px;
border-radius: 2px; line-height: 30px;
font-size: 13px; color: #fff;
line-height: 30px; font-weight: 500;
color: #fff; cursor: pointer;
font-weight: 500; transition: background .15s ease-in-out;
cursor: pointer; &:hover{
transition: background .15s ease-in-out; background-color: #607F9A;
&:hover{
background-color: #607F9A;
}
} }
} }
}
.drag-file-box{
padding: 10px;
.drag-file-area{ .drag-file-area{
position: relative; position: relative;
margin-top: 15px; margin-top: 15px;

View File

@ -24,6 +24,10 @@
.ag-header-cell{ .ag-header-cell{
font-size: 13px; font-size: 13px;
color: #fff; color: #fff;
border-right: 1px solid #738596;
&:last-child{
border: none;
}
} }
.ag-header-cell-label{ .ag-header-cell-label{
justify-content: center; justify-content: center;

View File

@ -279,10 +279,11 @@ $alert-color: #101010;
border-bottom: 1px solid #424242; border-bottom: 1px solid #424242;
} }
} }
.grid-check-form-block{ .grid-check-form-flex{
display: block; display: flex;
> div{ gap: 10px;
margin-bottom: 10px; .d-check-box{
flex: 1;
} }
} }
.grid-option-overflow{ .grid-option-overflow{
@ -1313,13 +1314,13 @@ $alert-color: #101010;
.circle { .circle {
position: absolute; position: absolute;
width: 12px; width: 10px;
height: 12px; height: 10px;
border: 1px solid #fff; border: 1px solid #fff;
border-radius: 50%; border-radius: 50%;
top: 95%; top: 88%;
left: 50%; left: 50%;
transform-origin: 0 -90px; /* 중심에서 반지름 거리만큼 떨어져 위치 */ transform-origin: 0 -76px; /* 중심에서 반지름 거리만큼 떨어져 위치 */
cursor:pointer; cursor:pointer;
z-index: 3; z-index: 3;
/* 0번을 180도 위치(아래)에, 13번을 0도 위치(위)에 배치 */ /* 0번을 180도 위치(아래)에, 13번을 0도 위치(위)에 배치 */
@ -1366,8 +1367,8 @@ $alert-color: #101010;
top: 50%; top: 50%;
left: 50%; left: 50%;
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
width: 5px; width: 4px;
height: 5px; height: 4px;
background-color: #fff; background-color: #fff;
border-radius: 50%; border-radius: 50%;
} }
@ -1381,15 +1382,15 @@ $alert-color: #101010;
top: 50%; top: 50%;
left: 50%; left: 50%;
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
width: 148px; width: 121px;
height: 148px; height: 121px;
border: 4px solid #fff; border: 4px solid #fff;
border-radius: 50%; border-radius: 50%;
.compas-arr{ .compas-arr{
width: 100%; width: 100%;
height: 100%; height: 100%;
background: url(../../public/static/images/canvas/compas.svg)no-repeat center; background: url(../../public/static/images/canvas/compas.svg)no-repeat center;
background-size: 122px 122px; background-size: 100px 100px;
} }
} }
} }
@ -1441,10 +1442,10 @@ $alert-color: #101010;
.roof-module-compas{ .roof-module-compas{
margin-bottom: 24px; margin-bottom: 24px;
.compas-box-inner{ .compas-box-inner{
width: 280px; width: 235px;
height: 253px; height: 215px;
.circle{ .circle{
top: 86%; top: 85%;
// &:nth-child(1), // &:nth-child(1),
// &:nth-child(7), // &:nth-child(7),
// &:nth-child(13), // &:nth-child(13),
@ -1461,7 +1462,7 @@ $alert-color: #101010;
// } // }
// } // }
i{ i{
top: 22px; top: 19px;
} }
&.act{ &.act{
i{color: #8B8B8B;} i{color: #8B8B8B;}
@ -1482,6 +1483,10 @@ $alert-color: #101010;
.outline-form{ .outline-form{
flex: 1; flex: 1;
} }
.non-flex{
min-width: 300px;
flex: none;
}
} }
.module-box-tab{ .module-box-tab{
@ -2172,31 +2177,46 @@ $alert-color: #101010;
&.tab2{ &.tab2{
margin-top: 10px; margin-top: 10px;
gap: 15px; gap: 15px;
.eaves-keraba-table{
margin-top: 0;
}
} }
.module-flex-item{ .module-flex-item{
flex: 1; flex: 1;
.module-flex-item-tit{
font-size: 12px;
font-weight: 500;
color: #fff;
padding-bottom: 10px;
border-bottom: 1px solid #4D4D4D;
}
.flex-item-btn-wrap{ .flex-item-btn-wrap{
display: grid; display: grid;
grid-template-columns: repeat(2, 1fr); grid-template-columns: repeat(2, 1fr);
gap: 8px; gap: 8px;
margin-bottom: 24px; margin-bottom: 10px;
} }
&.non-flex{ &.non-flex{
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: flex-start; justify-content: flex-start;
flex: none; flex: none;
padding-top: 27.5px; width: 340px;
width: 260px;
} }
.flex-item-button{
margin-top: 10px;
button{
width: 100%;
}
}
}
}
.module-flex-item-tit-wrap{
display: flex;
align-items: center;
padding-bottom: 10px;
border-bottom: 1px solid #4D4D4D;
.module-flex-item-tit{
font-size: 12px;
font-weight: 500;
color: #fff;
}
button{
margin-left: auto;
} }
} }
@ -2277,4 +2297,143 @@ $alert-color: #101010;
box-shadow: 0em -2.6em 0em 0em rgba(255, 255, 255, 0.2), 1.8em -1.8em 0 0em rgba(255, 255, 255, 0.2), 2.5em 0em 0 0em rgba(255, 255, 255, 0.2), 1.75em 1.75em 0 0em rgba(255, 255, 255, 0.2), 0em 2.5em 0 0em rgba(255, 255, 255, 0.2), -1.8em 1.8em 0 0em rgba(255, 255, 255, 0.5), -2.6em 0em 0 0em rgba(255, 255, 255, 0.7), -1.8em -1.8em 0 0em #fff; box-shadow: 0em -2.6em 0em 0em rgba(255, 255, 255, 0.2), 1.8em -1.8em 0 0em rgba(255, 255, 255, 0.2), 2.5em 0em 0 0em rgba(255, 255, 255, 0.2), 1.75em 1.75em 0 0em rgba(255, 255, 255, 0.2), 0em 2.5em 0 0em rgba(255, 255, 255, 0.2), -1.8em 1.8em 0 0em rgba(255, 255, 255, 0.5), -2.6em 0em 0 0em rgba(255, 255, 255, 0.7), -1.8em -1.8em 0 0em #fff;
} }
} }
}
.roof-module-inner{
display: flex;
.compas-wrapper{
position: relative;
flex: none;
width: 300px;
padding-right: 15px;
&:before{
content: '';
position: absolute;
top: 0;
right: 10px;
width: 1px;
height: 100%;
background-color: #424242;
}
}
.compas-table-wrap{
display: flex;
flex-direction: column;
flex: 1;
}
.compas-table-box{
background-color: #3D3D3D;
padding: 10px;
.outline-form{
span{
width: auto;
}
}
.compas-grid-table{
display: grid;
gap: 10px;
grid-template-columns: repeat(2, 1fr);
.outline-form{
span{
width: 65px;
&.thin{
width: 20px;
}
}
}
}
}
}
.module-table-block-wrap{
.roof-module-table{
&.self{
table{
table-layout: fixed;
}
}
}
.self-table-radio{
display: flex;
align-items: center;
justify-content: center;
}
}
.placement-name-guide{
font-size: 11px;
margin-left: 10px;
color: #53a7eb;
font-weight: 500;
}
.hexagonal-flex-wrap{
display: flex;
gap: 10px;
.non-flex{
flex: none;
}
}
.hexagonal-radio-wrap{
padding: 17px 10px;
}
.hide-check-guide{
display: flex;
align-items: center;
font-size: 12px;
color: #fff;
margin-top: 10px;
font-weight: 500;
.arr{
width: 13px;
height: 13px;
margin-left: 10px;
background: url(../../public/static/images/canvas/hide-check-arr.svg) no-repeat center;
background-size: contain;
transform: rotate(180deg);
&.act{
transform: rotate(0deg);
}
}
}
.module-table-box{
&.hide{
overflow: hidden;
height: 0;
}
}
// 2025-05-07 지붕모듈
.module-input-area{
display: flex;
align-items: center;
margin-top: 10px;
.module-area-title{
flex: none;
font-size: 12px;
color: #fff;
font-weight: 500;
margin-right: 20px;
}
.module-input-wrap{
width: 100%;
display: flex;
.outline-form{
width: calc(33.333% - 10px);
margin-right: 15px;
&:last-child{
margin-right: 0;
}
.input-grid{
width: 100%;
}
}
}
} }

View File

@ -222,15 +222,16 @@ button{
padding: 0 10px; padding: 0 10px;
line-height: 28px; line-height: 28px;
font-family: 'Noto Sans JP', sans-serif; font-family: 'Noto Sans JP', sans-serif;
background-color: transparent; background-color: #353535;
border: 1px solid #484848; border: 1px solid #484848;
color: #fff; color: #fff;
&.blue{ &.blue{
background-color: #4C6FBF; background-color: #4C6FBF;
border: 1px solid #4C6FBF; border: 1px solid #4C6FBF;
&:hover{ &:hover{
background-color: #414E6C; background-color: #4C6FBF;
border: 1px solid #414E6C; border: 1px solid #4C6FBF;
font-weight: normal;
} }
} }
&.white{ &.white{
@ -241,13 +242,14 @@ button{
background-color: #fff; background-color: #fff;
border: 1px solid #fff; border: 1px solid #fff;
color: #101010; color: #101010;
font-weight: normal;
} }
} }
&:hover{ &:hover{
font-weight: 400; background-color: #353535;
background-color: transparent;
border: 1px solid #484848; border: 1px solid #484848;
color: #fff; color: #fff;
font-weight: normal;
} }
} }
&.self{ &.self{
@ -375,13 +377,14 @@ button{
font-size: 12px; font-size: 12px;
color: #fff; color: #fff;
line-height: 1.4; line-height: 1.4;
text-align: left;
} }
&:hover{ &:hover{
background-color: #2C2C2C; background-color: #2C2C2C;
} }
} }
&::-webkit-scrollbar { &::-webkit-scrollbar {
width: 2px; width: 5px;
background-color: transparent; background-color: transparent;
} }
@ -1021,4 +1024,23 @@ input:checked + .slider {
// alert z-index // alert z-index
.swal2-container{ .swal2-container{
z-index: 120000; z-index: 120000;
}
// textarea
.textarea-form{
width: 100%;
outline: none;
resize: none;
border: none;
border: 1px solid #eee;
min-height: 150px;
padding: 10px;
font-size: 13px;
color: #45576F;
border-radius: 2px;
font-family: "Noto Sans JP", sans-serif;
transition: border .15s ease-in-out;
&:focus{
border-color: #94a0ad;
}
} }

View File

@ -288,55 +288,56 @@
} }
// 커뮤니티 // 커뮤니티
.community_detail{ .community_detail-tit{
.community_detail-tit{ font-size: 16px;
font-size: 16px; color: #101010;
color: #101010; font-weight: 600;
font-weight: 600; padding-bottom: 14px;
padding-bottom: 14px; border-bottom: 2px solid #101010;
border-bottom: 2px solid #101010; }
} .community_detail-file-wrap{
.community_detail-file-wrap{ padding: 24px 0;
padding: 24px 0; border-bottom: 1px solid #E5E5E5;
border-bottom: 1px solid #E5E5E5; dt{
dt{
font-size: 13px;
color: #101010;
font-weight: 500;
margin-bottom: 15px;
}
dd{
font-size: 12px;
font-weight: 400;
margin-bottom: 3px;
color: #344356;
&:last-child{
margin-bottom: 0;
}
}
}
.community_detail-inner{
max-height: 300px;
overflow-y: auto;
margin-top: 20px;
margin-bottom: 20px;
font-size: 13px; font-size: 13px;
color: #101010;
font-weight: 500;
}
dd{
font-size: 12px;
font-weight: 400; font-weight: 400;
color: #45576F; margin-bottom: 3px;
line-height: 26px; color: #344356;
word-break: keep-all; &:nth-child(2){
&::-webkit-scrollbar { margin-top: 15px;
width: 4px;
background-color: transparent;
} }
&::-webkit-scrollbar-thumb { &:last-child{
background-color: #C1CCD7; margin-bottom: 0;
}
&::-webkit-scrollbar-track {
background-color: transparent;
} }
} }
} }
.community_detail-inner{
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;
}
}
// 견적 복사 // 견적 복사
.estimate-copy-info-item{ .estimate-copy-info-item{
@ -368,4 +369,117 @@
color: #999; color: #999;
} }
} }
}
// 1:1문의
.one-on-one{
.select-wrap{
width: 250px;
}
.input-wrap{
flex: 1 1 auto;
}
&.btn-area{
padding-bottom: 0;
border: none;
margin-left: 10px;
}
&.drag-file-box{
border: 1px solid #eee;
.drag-file-area{
margin-top: 0;
}
}
}
.oneonone-header-wrap{
padding-bottom: 14px;
border-bottom: 2px solid #101010;
.oneonone-title{
font-size: 16px;
color: #101010;
font-weight: 600;
margin-bottom: 14px;
}
}
.oneonone-infor{
display: flex;
align-items: center;
.profile{
position: relative;
padding-left: 26px;
padding-right: 8px;
font-size: 13px;
font-weight: 400;
color: #101010;
&::before{
content: '';
position: absolute;
top: 50%;
left: 0;
transform: translateY(-50%);
width: 24px;
height: 24px;
background: url(./../../public/static/images/sub/oneonone_profile_icon.svg)no-repeat center;
}
&::after{
content: '';
position: absolute;
top: 50%;
right: 0;
transform: translateY(-50%);
width: 1px;
height: 8px;
background-color: #CCCCCC;
}
}
.date{
padding-left: 8px;
font-size: 13px;
font-weight: 400;
color: #101010;
}
}
.oneonone-detail{
padding: 20px;
border: 1px solid #101010;
border-top: none;
.community_detail-file-wrap{
padding-top: 0;
}
.community_detail-inner{
max-height: 110px;
margin-top: 24px;
margin-bottom: 0;
}
}
.oneonone-answer{
margin-top: 8px;
padding: 20px;
border: 1px solid #101010;
.community_detail-inner{
max-height: 110px;
}
.community_detail-file-wrap{
border-top: 1px solid #D4DCE7;
padding: 16px 0 0 0;
border-bottom: none;
}
}
.answer-title-wrap{
display: flex;
align-items: center;
padding-bottom: 14px;
border-bottom: 1px solid #D4DCE7;
.answer-title{
font-size: 14px;
color: #101010;
font-weight: 600;
}
.oneonone-infor{
margin-left: auto;
}
} }

View File

@ -47,6 +47,16 @@ table{
} }
.form-flex-wrap{ .form-flex-wrap{
@include flexbox; @include flexbox;
.form-flex-select{
@include flexbox;
label{
flex: none;
margin-right: 5px;
}
.form-select{
min-width: 300px;
}
}
} }
.date-picker-wrap{ .date-picker-wrap{
width: 100%; width: 100%;
@ -236,6 +246,16 @@ table{
.d-check-box{ .d-check-box{
opacity: 0.5; opacity: 0.5;
} }
.color-wrap{
display: flex;
align-items: center;
justify-content: center;
.color-box{
width: 14px;
height: 14px;
margin-right: 8px;
}
}
} }
} }
tbody{ tbody{

View File

@ -348,15 +348,15 @@ export const calculateIntersection = (line1, line2) => {
} }
// Determine the min and max for line1 and line2 for both x and y // Determine the min and max for line1 and line2 for both x and y
const line1MinX = Math.min(line1.x1, line1.x2) const line1MinX = Math.min(line1.x1, line1.x2) - 5
const line1MaxX = Math.max(line1.x1, line1.x2) const line1MaxX = Math.max(line1.x1, line1.x2) + 5
const line2MinX = Math.min(line2.x1, line2.x2) const line2MinX = Math.min(line2.x1, line2.x2) - 5
const line2MaxX = Math.max(line2.x1, line2.x2) const line2MaxX = Math.max(line2.x1, line2.x2) + 5
const line1MinY = Math.min(line1.y1, line1.y2) const line1MinY = Math.min(line1.y1, line1.y2) - 5
const line1MaxY = Math.max(line1.y1, line1.y2) const line1MaxY = Math.max(line1.y1, line1.y2) + 5
const line2MinY = Math.min(line2.y1, line2.y2) const line2MinY = Math.min(line2.y1, line2.y2) - 5
const line2MaxY = Math.max(line2.y1, line2.y2) const line2MaxY = Math.max(line2.y1, line2.y2) + 5
// Check if the intersection X and Y are within the range of both lines // Check if the intersection X and Y are within the range of both lines
if ( if (
@ -518,14 +518,23 @@ export const sortedPointLessEightPoint = (points) => {
*/ */
// 직선의 방정식. // 직선의 방정식.
// 방정식은 ax + by + c = 0이며, 점의 좌표를 대입하여 계산된 값은 직선과 점 사이의 관계를 나타낸다. // 방정식은 ax + by + c = 0이며, 점의 좌표를 대입하여 계산된 값은 직선과 점 사이의 관계를 나타낸다.
export function isPointOnLine(line, point) { export function isPointOnLine({ x1, y1, x2, y2 }, { x, y }) {
const a = line.y2 - line.y1 /*const a = line.y2 - line.y1
const b = line.x1 - line.x2 const b = line.x1 - line.x2
const c = line.x2 * line.y1 - line.x1 * line.y2 const c = line.x2 * line.y1 - line.x1 * line.y2
const result = Math.abs(a * point.x + b * point.y + c) / 100 const result = Math.abs(a * point.x + b * point.y + c) / 100
// 점이 선 위에 있는지 확인 // 점이 선 위에 있는지 확인
return result <= 10 return result <= 10*/
// 직선 방정식 만족 여부 확인
const crossProduct = (y - y1) * (x2 - x1) - (x - x1) * (y2 - y1)
if (Math.abs(crossProduct) > 5) return false // 작은 오차 허용
// 점이 선분의 범위 내에 있는지 확인
const withinXRange = Math.min(x1, x2) <= x && x <= Math.max(x1, x2)
const withinYRange = Math.min(y1, y2) <= y && y <= Math.max(y1, y2)
return withinXRange && withinYRange
} }
/** /**
* 점과 가까운 line 찾기 * 점과 가까운 line 찾기

View File

@ -145,9 +145,19 @@ export const unescapeString = (str) => {
'&#39;': "'", '&#39;': "'",
} }
/*
1. 한번 변환은 {&quot; 변환됨 : 에러 발생 => while 변경
2. 변환할 내용이 없으면 리턴값이 undefined
if (regex.test(str)) { if (regex.test(str)) {
return str.replace(regex, (matched) => chars[matched] || matched) return str.replace(regex, (matched) => chars[matched] || matched)
} }
*/
while (regex.test(str)) {
str = str.replace(regex, (matched) => chars[matched] || matched);
}
return str
} }
export const isNullOrUndefined = (value) => { export const isNullOrUndefined = (value) => {

View File

@ -305,6 +305,9 @@ export function removeDuplicatePolygons(polygons) {
} }
export const isSamePoint = (a, b) => { export const isSamePoint = (a, b) => {
if (!a || !b) {
return false
}
return Math.abs(Math.round(a.x) - Math.round(b.x)) <= 2 && Math.abs(Math.round(a.y) - Math.round(b.y)) <= 2 return Math.abs(Math.round(a.x) - Math.round(b.x)) <= 2 && Math.abs(Math.round(a.y) - Math.round(b.y)) <= 2
} }
@ -3524,7 +3527,7 @@ export const calcLinePlaneSize = (points) => {
* @param degree * @param degree
* @returns number * @returns number
*/ */
export const calcLineActualSize = (points, degree) => { export const calcLineActualSize = (points, degree = 0) => {
const { x1, y1, x2, y2 } = points const { x1, y1, x2, y2 } = points
const planeSize = calcLinePlaneSize(points) const planeSize = calcLinePlaneSize(points)
let height = Big(Math.tan(Big(degree).times(Math.PI / 180))).times(planeSize) let height = Big(Math.tan(Big(degree).times(Math.PI / 180))).times(planeSize)

2
startscript-3000.js Normal file
View File

@ -0,0 +1,2 @@
var exec = require('child_process').exec
exec('yarn dev -p 3000', { windowsHide: true })

View File

@ -1,2 +1,2 @@
var exec = require('child_process').exec var exec = require('child_process').exec
exec('yarn start', { windowsHide: true }) exec('yarn dev -p 5000', { windowsHide: true })