Compare commits
399 Commits
4056206af7
...
88632a4619
| Author | SHA1 | Date | |
|---|---|---|---|
| 88632a4619 | |||
| 3c094b3c5a | |||
| 97725c1a3d | |||
| 7377b06e2a | |||
|
|
ee79c0e680 | ||
|
|
a3d1704390 | ||
|
|
fecc4e999a | ||
| 34a1a6d201 | |||
| 03c31c82ff | |||
| 978d74f66a | |||
| 990c0e7e29 | |||
| a428cc31e8 | |||
| baeb2be0a1 | |||
| 89b8af1533 | |||
| a75ea4c98a | |||
| 4a716ad7ad | |||
| 8cd67a92a8 | |||
| befa12b00b | |||
| d7e35dba40 | |||
| 2080c8bf20 | |||
| affef782f3 | |||
| 0ad18e4f15 | |||
| 82698a5d03 | |||
| 7cc20f33c6 | |||
| 7baa198501 | |||
| 396d8bc708 | |||
| c60da7ddc9 | |||
| a2cd08484d | |||
| 3a3ff7c156 | |||
| d548b0e1f4 | |||
| ddf326ca6b | |||
| 8b2cf6a9d3 | |||
| 41de001986 | |||
| 1385683bce | |||
| 92fd17ed71 | |||
| 320080e0c1 | |||
| d32553416c | |||
| 1aa6bc79a8 | |||
| b480345b24 | |||
| 5f726bf5db | |||
| 9a2c6adb96 | |||
| 75312f5ccf | |||
| ba94dd0579 | |||
| 384c68c1ef | |||
| dfed51a758 | |||
| 01f160fa8b | |||
| c58c1f2106 | |||
|
|
bf0e1e4cb0 | ||
|
|
340c7669af | ||
|
|
8cca1e9937 | ||
| e8adf1659f | |||
| 9786c1fbf7 | |||
| 4469691618 | |||
| 9c0403c947 | |||
| 4df39defc6 | |||
| 0efc90f135 | |||
|
|
bbd8a43864 | ||
|
|
46710533b5 | ||
| eeab13b9cd | |||
| 0acd9e422f | |||
|
|
c58146ca53 | ||
| 960d31ffdd | |||
| 93e54812c3 | |||
| 816e440ba0 | |||
| a8d9988f24 | |||
| 21943536c9 | |||
|
|
99c7759e00 | ||
| 7d9b6d5225 | |||
|
|
36d16069e0 | ||
| f912a8474e | |||
| d37b191139 | |||
| 7c9fd0f698 | |||
| 650e72d91d | |||
| fb20a1676b | |||
| b651aab959 | |||
| cf969d6f1c | |||
| 0666612c0a | |||
| 3811f224d6 | |||
|
|
fda35b6927 | ||
| a183453390 | |||
|
|
760becfb0d | ||
|
|
098fb8efc6 | ||
|
|
473f4f8d74 | ||
| c358cba0c5 | |||
| ff5c335c9b | |||
| 9999d1bae0 | |||
| c5700c62cc | |||
| 08ae9ac7b9 | |||
| c4baa119b1 | |||
| acffaa5ebd | |||
| 87d058fd07 | |||
| 61a44704fd | |||
| ee26f28446 | |||
| 71a7fab4ea | |||
| 114bf94d92 | |||
| 6dd66ee27d | |||
| 662b38aac7 | |||
| 8f2a78ef1e | |||
| 872c27734b | |||
| 098147bc6b | |||
| cee1567438 | |||
| f15ff10bf6 | |||
| 38a1907842 | |||
| 0c8374b43d | |||
| bad50e11b1 | |||
| 3d67e5eec8 | |||
|
|
20eccab581 | ||
|
|
e35cacf520 | ||
| a55fca439c | |||
| 9a536970c4 | |||
| 9f1649dc0e | |||
| 06815fc04a | |||
| 27164dc3a7 | |||
| 21634a7a8a | |||
| 77510148e3 | |||
| 4375666084 | |||
| 736328ef0f | |||
| 848f75bafa | |||
| 4b8c0510ca | |||
| 301e6c136c | |||
| 6c89e6ffa6 | |||
| cef764fd0e | |||
| a663faf359 | |||
| 5cf5ef55af | |||
| d98eba97a7 | |||
| 4a914e0aea | |||
| 102cc4d672 | |||
| 2aca4d22ec | |||
| 0f2719a2f0 | |||
|
|
8c153430cd | ||
|
|
7175cd0485 | ||
|
|
c10a46e86f | ||
|
|
d8389c1d9f | ||
| bdef681898 | |||
| 9db8b949ff | |||
| 6819196d20 | |||
| d03937cc92 | |||
| cb42ad82de | |||
| 7a0db7abfb | |||
| de39eac555 | |||
| bab3039d6e | |||
| e285210927 | |||
| dcd7ace830 | |||
| 959964c876 | |||
| f11a7d68e7 | |||
| 359c7c458f | |||
| 58d35a2881 | |||
| 1b57602cb6 | |||
| fa9663f123 | |||
| 856086a271 | |||
| 591b24974c | |||
| 0a258c3807 | |||
| cc7f1ad6af | |||
| 984836d523 | |||
| d15e4ebd53 | |||
| 95dcc69ab4 | |||
| b3254d1e5d | |||
| 992d87412c | |||
| 6a5ba73274 | |||
| 31751c26b9 | |||
| f82a355f01 | |||
|
|
4e14e1d5bf | ||
|
|
5e017ccbb7 | ||
| 445270bb80 | |||
| 9ec5249122 | |||
| 7e3820e6b1 | |||
| 1e678cfd81 | |||
| 2b956bbc3b | |||
| 37e50c14cc | |||
| 718fbc16fa | |||
| 123657431d | |||
| ce1fab884d | |||
|
|
699bfa9af4 | ||
| 5469037de9 | |||
| 81fd0168e7 | |||
| d1da73849f | |||
| 40970c5580 | |||
| cdda10c30c | |||
| 59d3bd61e2 | |||
| 4684b45883 | |||
| e14e43f778 | |||
| 5e86c98afd | |||
|
|
45a4ac84de | ||
|
|
a64eaae57d | ||
| ca0620bd23 | |||
| d52fdb23a1 | |||
| b979e6cfa4 | |||
| eb03054804 | |||
| ef607afcf3 | |||
| 2d8a7034d8 | |||
| fb9ed9949d | |||
| 4875985685 | |||
| a319df0120 | |||
|
|
8e3a3d7c06 | ||
| 0bea975130 | |||
| 553a9340f4 | |||
| eb255d5148 | |||
| 031643c092 | |||
| edb0783e26 | |||
|
|
2f6a4a3ee8 | ||
|
|
7a019730f5 | ||
| 911ec78055 | |||
| 7ca01985b6 | |||
| e60989d318 | |||
|
|
44508faf04 | ||
|
|
59539e4a60 | ||
| e9f3cb8e53 | |||
| ea34d469bb | |||
| 10d7a6476a | |||
| 8226f6cf3d | |||
| c58ff87a4c | |||
| af850fc742 | |||
| dc6b268b74 | |||
| b432c8792c | |||
| b29b76a158 | |||
| 60f8539d3f | |||
| 9f7fedab79 | |||
| 47a8274fd0 | |||
| 929304456a | |||
| 5102fa2ec0 | |||
| 25bb9ede08 | |||
|
|
3d4040e06c | ||
| 7e0e4643e2 | |||
|
|
ac9d2b593e | ||
|
|
016395ef9b | ||
| 849ba0e0d5 | |||
| c70ab4e7e9 | |||
| 149842547c | |||
| b2e5055ed1 | |||
| d35230cb9b | |||
| 9c05cde0b0 | |||
| 079f3a7ff2 | |||
| 7472a8f36e | |||
| a093d368f2 | |||
| 30061ab167 | |||
| 0badbef52c | |||
| 6dc2b8a037 | |||
| 71d92f12b8 | |||
| a05a63ebdc | |||
| f79d94c1b7 | |||
| d72444199f | |||
| fb9e8386fc | |||
| f6f385e597 | |||
| 0cb3b13519 | |||
| dc685f8594 | |||
|
|
b130d844c2 | ||
|
|
624815ba0d | ||
| 71d774bf88 | |||
| 8354770a9e | |||
| 7ccdda6405 | |||
| a4d74bb41b | |||
| 6347b99ba9 | |||
| 2b0df28ab9 | |||
| 16432f94cd | |||
| 4bd21c1662 | |||
| 530ee1dba7 | |||
| 9c54c135b7 | |||
| 936cc7bc92 | |||
|
|
2b6f679f75 | ||
| 1750ba53fd | |||
| cf290b0bc8 | |||
| 3ab5aec767 | |||
| 7286ddc97b | |||
| 3a93aed738 | |||
| d0b75f27fb | |||
| 93cda139de | |||
| 69fa493428 | |||
| b46ce583ad | |||
| 178d53ef14 | |||
| 516b323a1e | |||
|
|
6397de0e76 | ||
| 5dae6c3738 | |||
| 213b020a3d | |||
| 9957dae967 | |||
| e3ae71a161 | |||
| c4c773f929 | |||
| be881b656f | |||
| 9036502f2d | |||
| 3a076b216b | |||
| f24715b5d4 | |||
| 422a331d90 | |||
| 2a3ae5b0be | |||
| cc3bf02b17 | |||
| a8e0eaadbd | |||
| 09fd910b8b | |||
| 2c63b22507 | |||
| f9fb5b7b50 | |||
| 3dd1053eff | |||
| 249d254622 | |||
| 468296e139 | |||
| 3504e09ba2 | |||
| c3e4570f05 | |||
| adaa25dd53 | |||
| 3ac9e56def | |||
| 66b626b3ea | |||
| 61081475f7 | |||
| 88a22c1ed1 | |||
| 1255d30c3a | |||
| afa8d5d62a | |||
| b2f639a461 | |||
| 0d73df5b0e | |||
|
|
50cf8f6fc8 | ||
|
|
8508ce143a | ||
| 1ce58af019 | |||
| dd5580a3a4 | |||
| 9da2a71668 | |||
| e659a385bd | |||
| 05d4fc3fd8 | |||
| 3cd1d0fec4 | |||
| a151046943 | |||
|
|
74c956bf1e | ||
|
|
80272b1ffd | ||
|
|
1dfd6bbdc9 | ||
| 3c1bfbc110 | |||
| 93e3036aff | |||
|
|
aa5cc6aea3 | ||
| 4596f03d88 | |||
|
|
3168a29ecd | ||
|
|
4f84221dea | ||
| c888103fef | |||
| 8feefb0b7e | |||
| 16de0d8533 | |||
| 36211f04bd | |||
| 1276230fcc | |||
| 5c592e95cc | |||
| a41092b896 | |||
| a37bf6e4f7 | |||
| c25e8b2a4a | |||
| 9b8727a63a | |||
| 8853b7ae86 | |||
| 9a543c4b90 | |||
| af29c83eb4 | |||
| 6e87357466 | |||
| 0a6d58cba8 | |||
| fd962219dd | |||
| d867a8dda7 | |||
| 32a9ade07c | |||
| d4f633fb91 | |||
| 158f8226b0 | |||
| 17100beb8c | |||
| 8f782e7b17 | |||
| 0005643c94 | |||
| c25af0e186 | |||
| e19395f612 | |||
| 9f2fe5f3c8 | |||
| faf4c62328 | |||
| c6e864d4c4 | |||
| 190d5cc19c | |||
| 713b773dc0 | |||
| 72bbf33317 | |||
| ac00777cf1 | |||
| f61e50284c | |||
| cf462dc4a3 | |||
| aec7bb8c03 | |||
| bc607d4828 | |||
| a58604478d | |||
| b71fb0992f | |||
| 2bbe4ea29e | |||
| 7641148fce | |||
| 9c0c851dcb | |||
| 37f35c3a13 | |||
| 42238db254 | |||
| 7c610c8487 | |||
| cccf74e2eb | |||
| 4de742d1a3 | |||
| 72cded314b | |||
| 1a8dfa628b | |||
| 52cee232d0 | |||
| bcf0e3d1bf | |||
| 86a67420ea | |||
| 00d87f143f | |||
| 9c35900ce3 | |||
| 18c47cca2c | |||
| 52d25341de | |||
| 60449017d8 | |||
| 71d6639ebd | |||
| 342b5a37f1 | |||
| e94542f15c | |||
| 636ac163dd | |||
| 15b1cc84fd | |||
| d7b17e491f | |||
| a83f0d7017 | |||
| c42c36501a | |||
| 7a005bb161 | |||
| 44b016dc50 | |||
| 5429e5474d | |||
| cb14b3bb78 | |||
| c8823712a4 | |||
| a5a1370135 | |||
| 41ab66eb10 | |||
| 745665782c | |||
|
|
765855b361 | ||
| b4733ebfa6 | |||
| a4af9dc1ea | |||
| d0eb50313a | |||
| 03ec99dbb4 | |||
| 68d961858f | |||
| ec8d767809 | |||
| c52d372148 |
@ -10,7 +10,8 @@ SESSION_SECRET="i3iHH1yp2/2SpQSIySQ4bpyc4g0D+zCF9FAn5xUG0+Y="
|
|||||||
|
|
||||||
# NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_bV5zuYMyyIYFlOb3"
|
# NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_bV5zuYMyyIYFlOb3"
|
||||||
# NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_yAS4QDalL9jgQ7vS"
|
# NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_yAS4QDalL9jgQ7vS"
|
||||||
NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_a0FLEK6M2oTpXInK"
|
NEXT_PUBLIC_CONVERTER_DWG_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_a0FLEK6M2oTpXInK"
|
||||||
|
NEXT_PUBLIC_CONVERTER_DXF_API_URL="https://v2.convertapi.com/convert/dxf/to/png?Secret=secret_a0FLEK6M2oTpXInK"
|
||||||
|
|
||||||
NEXT_PUBLIC_Q_ORDER_AUTO_LOGIN_URL="http://q-order-stg.q-cells.jp:8120/eos/login/autoLogin"
|
NEXT_PUBLIC_Q_ORDER_AUTO_LOGIN_URL="http://q-order-stg.q-cells.jp:8120/eos/login/autoLogin"
|
||||||
NEXT_PUBLIC_Q_MUSUBI_AUTO_LOGIN_URL="http://q-musubi-stg.q-cells.jp:8120/qm/login/autoLogin"
|
NEXT_PUBLIC_Q_MUSUBI_AUTO_LOGIN_URL="http://q-musubi-stg.q-cells.jp:8120/qm/login/autoLogin"
|
||||||
@ -29,4 +30,7 @@ AWS_ACCESS_KEY_ID="AKIA3K4QWLZHFZRJOM2E"
|
|||||||
AWS_SECRET_ACCESS_KEY="Cw87TjKwnTWRKgORGxYiFU6GUTgu25eUw4eLBNcA"
|
AWS_SECRET_ACCESS_KEY="Cw87TjKwnTWRKgORGxYiFU6GUTgu25eUw4eLBNcA"
|
||||||
NEXT_PUBLIC_AWS_S3_BASE_URL="//files.hanasys.jp"
|
NEXT_PUBLIC_AWS_S3_BASE_URL="//files.hanasys.jp"
|
||||||
|
|
||||||
S3_PROFILE="dev"
|
S3_PROFILE="dev"
|
||||||
|
|
||||||
|
#logging
|
||||||
|
NEXT_PUBLIC_ENABLE_LOGGING=true
|
||||||
@ -10,7 +10,8 @@ SESSION_SECRET="i3iHH1yp2/2SpQSIySQ4bpyc4g0D+zCF9FAn5xUG0+Y="
|
|||||||
|
|
||||||
# NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_bV5zuYMyyIYFlOb3"
|
# NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_bV5zuYMyyIYFlOb3"
|
||||||
# NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_yAS4QDalL9jgQ7vS"
|
# NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_yAS4QDalL9jgQ7vS"
|
||||||
NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_a0FLEK6M2oTpXInK"
|
NEXT_PUBLIC_CONVERTER_DWG_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_a0FLEK6M2oTpXInK"
|
||||||
|
NEXT_PUBLIC_CONVERTER_DXF_API_URL="https://v2.convertapi.com/convert/dxf/to/png?Secret=secret_a0FLEK6M2oTpXInK"
|
||||||
|
|
||||||
NEXT_PUBLIC_Q_ORDER_AUTO_LOGIN_URL="http://q-order-stg.q-cells.jp:8120/eos/login/autoLogin"
|
NEXT_PUBLIC_Q_ORDER_AUTO_LOGIN_URL="http://q-order-stg.q-cells.jp:8120/eos/login/autoLogin"
|
||||||
NEXT_PUBLIC_Q_MUSUBI_AUTO_LOGIN_URL="http://q-musubi-stg.q-cells.jp:8120/qm/login/autoLogin"
|
NEXT_PUBLIC_Q_MUSUBI_AUTO_LOGIN_URL="http://q-musubi-stg.q-cells.jp:8120/qm/login/autoLogin"
|
||||||
@ -29,4 +30,7 @@ AWS_ACCESS_KEY_ID="AKIA3K4QWLZHFZRJOM2E"
|
|||||||
AWS_SECRET_ACCESS_KEY="Cw87TjKwnTWRKgORGxYiFU6GUTgu25eUw4eLBNcA"
|
AWS_SECRET_ACCESS_KEY="Cw87TjKwnTWRKgORGxYiFU6GUTgu25eUw4eLBNcA"
|
||||||
NEXT_PUBLIC_AWS_S3_BASE_URL="//files.hanasys.jp"
|
NEXT_PUBLIC_AWS_S3_BASE_URL="//files.hanasys.jp"
|
||||||
|
|
||||||
S3_PROFILE="dev"
|
S3_PROFILE="dev"
|
||||||
|
|
||||||
|
#logging
|
||||||
|
NEXT_PUBLIC_ENABLE_LOGGING=false
|
||||||
|
|||||||
@ -10,12 +10,11 @@ SESSION_SECRET="i3iHH1yp2/2SpQSIySQ4bpyc4g0D+zCF9FAn5xUG0+Y="
|
|||||||
|
|
||||||
# NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_bV5zuYMyyIYFlOb3"
|
# NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_bV5zuYMyyIYFlOb3"
|
||||||
# NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_yAS4QDalL9jgQ7vS"
|
# NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_yAS4QDalL9jgQ7vS"
|
||||||
NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_a0FLEK6M2oTpXInK"
|
NEXT_PUBLIC_CONVERTER_DWG_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_a0FLEK6M2oTpXInK"
|
||||||
|
NEXT_PUBLIC_CONVERTER_DXF_API_URL="https://v2.convertapi.com/convert/dxf/to/png?Secret=secret_a0FLEK6M2oTpXInK"
|
||||||
|
|
||||||
# NEXT_PUBLIC_Q_ORDER_AUTO_LOGIN_URL="https://q-order.q-cells.jp/eos/login/autoLogin"
|
NEXT_PUBLIC_Q_ORDER_AUTO_LOGIN_URL="https://q-order.q-cells.jp/eos/login/autoLogin"
|
||||||
# NEXT_PUBLIC_Q_MUSUBI_AUTO_LOGIN_URL="https://q-musubi.q-cells.jp/qm/login/autoLogin"
|
NEXT_PUBLIC_Q_MUSUBI_AUTO_LOGIN_URL="https://q-musubi.q-cells.jp/qm/login/autoLogin"
|
||||||
NEXT_PUBLIC_Q_ORDER_AUTO_LOGIN_URL="http://q-order-stg.q-cells.jp:8120/eos/login/autoLogin"
|
|
||||||
NEXT_PUBLIC_Q_MUSUBI_AUTO_LOGIN_URL="http://q-musubi-stg.q-cells.jp:8120/qm/login/autoLogin"
|
|
||||||
|
|
||||||
# AWS_REGION="ap-northeast-2"
|
# AWS_REGION="ap-northeast-2"
|
||||||
# AMPLIFY_BUCKET="interplug"
|
# AMPLIFY_BUCKET="interplug"
|
||||||
@ -30,4 +29,7 @@ AWS_ACCESS_KEY_ID="AKIA3K4QWLZHFZRJOM2E"
|
|||||||
AWS_SECRET_ACCESS_KEY="Cw87TjKwnTWRKgORGxYiFU6GUTgu25eUw4eLBNcA"
|
AWS_SECRET_ACCESS_KEY="Cw87TjKwnTWRKgORGxYiFU6GUTgu25eUw4eLBNcA"
|
||||||
NEXT_PUBLIC_AWS_S3_BASE_URL="//files.hanasys.jp"
|
NEXT_PUBLIC_AWS_S3_BASE_URL="//files.hanasys.jp"
|
||||||
|
|
||||||
S3_PROFILE="prd"
|
S3_PROFILE="prd"
|
||||||
|
|
||||||
|
#logging
|
||||||
|
NEXT_PUBLIC_ENABLE_LOGGING=false
|
||||||
@ -1,6 +1,6 @@
|
|||||||
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
|
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
|
||||||
|
|
||||||
## Getting Started
|
### Getting Started
|
||||||
|
|
||||||
First, run the development server:
|
First, run the development server:
|
||||||
|
|
||||||
|
|||||||
@ -55,11 +55,14 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@turf/turf": "^7.0.0",
|
"@turf/turf": "^7.0.0",
|
||||||
|
"@types/node": "^24.3.0",
|
||||||
|
"@types/react": "^19.1.11",
|
||||||
"convertapi": "^1.14.0",
|
"convertapi": "^1.14.0",
|
||||||
"postcss": "^8",
|
"postcss": "^8",
|
||||||
"prettier": "^3.3.3",
|
"prettier": "^3.3.3",
|
||||||
"react-color-palette": "^7.2.2",
|
"react-color-palette": "^7.2.2",
|
||||||
"sass": "^1.77.8",
|
"sass": "^1.77.8",
|
||||||
"tailwindcss": "^3.4.1"
|
"tailwindcss": "^3.4.1",
|
||||||
|
"typescript": "^5.9.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
public/static/images/canvas/deg/-105.png
Normal file
|
After Width: | Height: | Size: 4.9 KiB |
BIN
public/static/images/canvas/deg/-120.png
Normal file
|
After Width: | Height: | Size: 5.3 KiB |
BIN
public/static/images/canvas/deg/-135.png
Normal file
|
After Width: | Height: | Size: 4.9 KiB |
BIN
public/static/images/canvas/deg/-15.png
Normal file
|
After Width: | Height: | Size: 4.9 KiB |
BIN
public/static/images/canvas/deg/-150.png
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
public/static/images/canvas/deg/-165.png
Normal file
|
After Width: | Height: | Size: 4.9 KiB |
BIN
public/static/images/canvas/deg/-30.png
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
public/static/images/canvas/deg/-45.png
Normal file
|
After Width: | Height: | Size: 5.0 KiB |
BIN
public/static/images/canvas/deg/-60.png
Normal file
|
After Width: | Height: | Size: 5.4 KiB |
BIN
public/static/images/canvas/deg/-75.png
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
BIN
public/static/images/canvas/deg/-90.png
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
public/static/images/canvas/deg/0.png
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
public/static/images/canvas/deg/105.png
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
BIN
public/static/images/canvas/deg/120.png
Normal file
|
After Width: | Height: | Size: 5.3 KiB |
BIN
public/static/images/canvas/deg/135.png
Normal file
|
After Width: | Height: | Size: 5.1 KiB |
BIN
public/static/images/canvas/deg/15.png
Normal file
|
After Width: | Height: | Size: 4.9 KiB |
BIN
public/static/images/canvas/deg/150.png
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
public/static/images/canvas/deg/165.png
Normal file
|
After Width: | Height: | Size: 4.9 KiB |
BIN
public/static/images/canvas/deg/180.png
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
BIN
public/static/images/canvas/deg/30.png
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
public/static/images/canvas/deg/45.png
Normal file
|
After Width: | Height: | Size: 4.9 KiB |
BIN
public/static/images/canvas/deg/60.png
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
public/static/images/canvas/deg/75.png
Normal file
|
After Width: | Height: | Size: 4.9 KiB |
BIN
public/static/images/canvas/deg/90.png
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
@ -38,10 +38,14 @@ const cropImage = async (Key, width, height, left, top) => {
|
|||||||
}
|
}
|
||||||
const buffer = Buffer.concat(chunks)
|
const buffer = Buffer.concat(chunks)
|
||||||
|
|
||||||
const image = await Jimp.read(buffer)
|
let image = await Jimp.read(buffer)
|
||||||
|
|
||||||
image.autocrop({ tolerance: 0.0002, leaveBorder: 10 })
|
image.autocrop({ tolerance: 0.0002, leaveBorder: 10 })
|
||||||
return await image.getBuffer('image/png')
|
|
||||||
|
const resizedImage = await resizeImage(image).then((result) => {
|
||||||
|
return result
|
||||||
|
})
|
||||||
|
|
||||||
|
return await resizedImage.getBuffer('image/png')
|
||||||
|
|
||||||
// Convert stream to buffer
|
// Convert stream to buffer
|
||||||
// const chunks = []
|
// const chunks = []
|
||||||
@ -77,6 +81,56 @@ const cropImage = async (Key, width, height, left, top) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//크롭된 이미지를 배경 크기에 맞게 리사이즈
|
||||||
|
const resizeImage = async (image) => {
|
||||||
|
//엑셀 템플릿 너비 35.4cm, 높이 12.89cm
|
||||||
|
const convertStandardWidth = Math.round((35.4 * 96) / 2.54)
|
||||||
|
const convertStandardHeight = Math.round((12.89 * 96) / 2.54)
|
||||||
|
|
||||||
|
// 이미지를 배경의 98%까지 확대 (훨씬 더 크게)
|
||||||
|
const targetImageWidth = convertStandardWidth * 0.98
|
||||||
|
const targetImageHeight = convertStandardHeight * 0.98
|
||||||
|
|
||||||
|
const scaleX = targetImageWidth / image.bitmap.width
|
||||||
|
const scaleY = targetImageHeight / image.bitmap.height
|
||||||
|
let scale = Math.min(scaleX, scaleY) // 비율 유지하면서 최대한 크게
|
||||||
|
|
||||||
|
// scale 저장 (나중에 전체 확대에 사용)
|
||||||
|
const originalScale = scale
|
||||||
|
|
||||||
|
let finalWidth = Math.round(image.bitmap.width * scale)
|
||||||
|
let finalHeight = Math.round(image.bitmap.height * scale)
|
||||||
|
|
||||||
|
if (scale >= 0.6) {
|
||||||
|
// 실제 리사이즈 실행
|
||||||
|
image.resize({ w: finalWidth, h: finalHeight })
|
||||||
|
}
|
||||||
|
|
||||||
|
//배경 이미지를 생성
|
||||||
|
const mixedImage = new Jimp({ width: convertStandardWidth, height: convertStandardHeight, color: 0xffffffff })
|
||||||
|
|
||||||
|
//이미지를 중앙에 배치
|
||||||
|
const x = Math.floor((mixedImage.bitmap.width - image.bitmap.width) / 2)
|
||||||
|
const y = Math.floor((mixedImage.bitmap.height - image.bitmap.height) / 2)
|
||||||
|
|
||||||
|
//이미지를 배경 이미지에 합성
|
||||||
|
mixedImage.composite(image, x, y, {
|
||||||
|
opacitySource: 1, // 원본 투명도 유지
|
||||||
|
opacityDest: 1,
|
||||||
|
})
|
||||||
|
|
||||||
|
// scale이 0.8 이하인 경우 완성된 이미지를 전체적으로 확대
|
||||||
|
if (originalScale <= 0.8) {
|
||||||
|
const enlargeRatio = 1.5 // 50% 확대
|
||||||
|
const newWidth = Math.round(mixedImage.bitmap.width * enlargeRatio)
|
||||||
|
const newHeight = Math.round(mixedImage.bitmap.height * enlargeRatio)
|
||||||
|
|
||||||
|
mixedImage.resize({ w: newWidth, h: newHeight })
|
||||||
|
}
|
||||||
|
|
||||||
|
return mixedImage
|
||||||
|
}
|
||||||
|
|
||||||
export async function POST(req) {
|
export async function POST(req) {
|
||||||
try {
|
try {
|
||||||
const formData = await req.formData()
|
const formData = await req.formData()
|
||||||
|
|||||||
@ -21,6 +21,8 @@ const defaultEstimateData = {
|
|||||||
fileList: [],
|
fileList: [],
|
||||||
fileFlg: '0', //후일 자료 제출 (체크 1 노체크 0)
|
fileFlg: '0', //후일 자료 제출 (체크 1 노체크 0)
|
||||||
priceCd: '',
|
priceCd: '',
|
||||||
|
pricingFlag: false, // 가격 처리 플래그 추가
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -13,6 +13,7 @@ export const MENU = {
|
|||||||
MOVEMENT_SHAPE_UPDOWN: 'movementShapeUpdown', // 동선이동.형올림내림
|
MOVEMENT_SHAPE_UPDOWN: 'movementShapeUpdown', // 동선이동.형올림내림
|
||||||
OUTLINE_EDIT_OFFSET: 'outlineEditOffset', // 외벽선 편집 및 오프셋
|
OUTLINE_EDIT_OFFSET: 'outlineEditOffset', // 외벽선 편집 및 오프셋
|
||||||
ROOF_SHAPE_ALLOC: 'rootShapeAlloc', // 지붕면 항당
|
ROOF_SHAPE_ALLOC: 'rootShapeAlloc', // 지붕면 항당
|
||||||
|
ALL_REMOVE: 'allRemove', // 전체 삭제
|
||||||
DEFAULT: 'roofCoveringDefault', // 아무것도 선택 안할 경우
|
DEFAULT: 'roofCoveringDefault', // 아무것도 선택 안할 경우
|
||||||
}, // 지붕덮개
|
}, // 지붕덮개
|
||||||
BATCH_CANVAS: {
|
BATCH_CANVAS: {
|
||||||
@ -212,6 +213,13 @@ export const SAVE_KEY = [
|
|||||||
'endPoint',
|
'endPoint',
|
||||||
'editable',
|
'editable',
|
||||||
'isSortedPoints',
|
'isSortedPoints',
|
||||||
|
'isMultipleOf45',
|
||||||
|
'from',
|
||||||
|
'originColor',
|
||||||
|
'originWidth',
|
||||||
|
'originHeight',
|
||||||
|
'skeletonLines',
|
||||||
|
'skeleton'
|
||||||
]
|
]
|
||||||
|
|
||||||
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]
|
||||||
|
|||||||
@ -24,7 +24,8 @@ export default function WithDraggable({ isShow, children, pos = { x: 0, y: 0 },
|
|||||||
<Draggable
|
<Draggable
|
||||||
position={{ x: position.x, y: position.y }}
|
position={{ x: position.x, y: position.y }}
|
||||||
onDrag={(e, data) => handleOnDrag(e, data)}
|
onDrag={(e, data) => handleOnDrag(e, data)}
|
||||||
handle={handle === '' ? '.modal-handle' : handle}
|
handle= ''//{handle === '' ? '.modal-handle' : handle} //전체 handle
|
||||||
|
cancel="input, button, select, textarea, [contenteditable], .sort-select"
|
||||||
>
|
>
|
||||||
<div className={`modal-pop-wrap ${className}`} style={{ visibility: isHidden ? 'hidden' : 'visible' }}>
|
<div className={`modal-pop-wrap ${className}`} style={{ visibility: isHidden ? 'hidden' : 'visible' }}>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
442
src/components/common/input/CalcInput.jsx
Normal file
@ -0,0 +1,442 @@
|
|||||||
|
import React, { useState, useRef, useEffect, forwardRef } from 'react'
|
||||||
|
import { createCalculator } from '@/util/calc-utils'
|
||||||
|
import '@/styles/calc.scss'
|
||||||
|
|
||||||
|
export const CalculatorInput = forwardRef(
|
||||||
|
({ value, onChange, label, options = {}, id, className = 'calculator-input', readOnly = false, placeholder, name='', disabled = false }, ref) => {
|
||||||
|
const [showKeypad, setShowKeypad] = useState(false)
|
||||||
|
const [displayValue, setDisplayValue] = useState(value || '0')
|
||||||
|
const [hasOperation, setHasOperation] = useState(false)
|
||||||
|
const calculatorRef = useRef(createCalculator(options))
|
||||||
|
const containerRef = useRef(null)
|
||||||
|
const inputRef = useRef(null)
|
||||||
|
|
||||||
|
// 외부 ref와 내부 ref를 동기화
|
||||||
|
useEffect(() => {
|
||||||
|
if (ref) {
|
||||||
|
if (typeof ref === 'function') {
|
||||||
|
ref(inputRef.current)
|
||||||
|
} else {
|
||||||
|
ref.current = inputRef.current
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [ref])
|
||||||
|
|
||||||
|
// Sync displayValue with value prop
|
||||||
|
useEffect(() => {
|
||||||
|
setDisplayValue(value || '0')
|
||||||
|
}, [value])
|
||||||
|
|
||||||
|
// 클릭 외부 감지
|
||||||
|
useEffect(() => {
|
||||||
|
const handleClickOutside = (event) => {
|
||||||
|
if (containerRef.current && !containerRef.current.contains(event.target)) {
|
||||||
|
setShowKeypad(false)
|
||||||
|
if (hasOperation) {
|
||||||
|
// If there's an operation in progress, compute the result when losing focus
|
||||||
|
handleCompute()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('mousedown', handleClickOutside)
|
||||||
|
return () => document.removeEventListener('mousedown', handleClickOutside)
|
||||||
|
}, [value, onChange, hasOperation])
|
||||||
|
|
||||||
|
// 숫자 입력 처리 함수 수정
|
||||||
|
const handleNumber = (num) => {
|
||||||
|
const calculator = calculatorRef.current
|
||||||
|
let newDisplayValue = ''
|
||||||
|
|
||||||
|
if (hasOperation) {
|
||||||
|
// 연산자 이후 숫자 입력 시
|
||||||
|
if (calculator.currentOperand === '0' || calculator.shouldResetDisplay) {
|
||||||
|
calculator.currentOperand = num.toString()
|
||||||
|
calculator.shouldResetDisplay = false
|
||||||
|
} else {
|
||||||
|
calculator.currentOperand = (calculator.currentOperand || '') + num
|
||||||
|
}
|
||||||
|
newDisplayValue = calculator.previousOperand + calculator.operation + calculator.currentOperand
|
||||||
|
setDisplayValue(newDisplayValue)
|
||||||
|
} else {
|
||||||
|
// 첫 번째 숫자 입력 시
|
||||||
|
if (displayValue === '0' || calculator.shouldResetDisplay) {
|
||||||
|
calculator.currentOperand = num.toString()
|
||||||
|
calculator.shouldResetDisplay = false
|
||||||
|
newDisplayValue = calculator.currentOperand
|
||||||
|
setDisplayValue(newDisplayValue)
|
||||||
|
if (!hasOperation) {
|
||||||
|
onChange(calculator.currentOperand)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
calculator.currentOperand = (calculator.currentOperand || '') + num
|
||||||
|
newDisplayValue = calculator.currentOperand
|
||||||
|
setDisplayValue(newDisplayValue)
|
||||||
|
if (!hasOperation) {
|
||||||
|
onChange(newDisplayValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 커서를 텍스트 끝으로 이동하고 스크롤 처리
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
if (inputRef.current) {
|
||||||
|
inputRef.current.focus()
|
||||||
|
const len = newDisplayValue.length
|
||||||
|
inputRef.current.setSelectionRange(len, len)
|
||||||
|
// 텍스트 끝으로 스크롤
|
||||||
|
inputRef.current.scrollLeft = inputRef.current.scrollWidth
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 연산자 처리 함수 수정
|
||||||
|
const handleOperation = (operation) => {
|
||||||
|
const calculator = calculatorRef.current
|
||||||
|
let newDisplayValue = ''
|
||||||
|
|
||||||
|
// 현재 입력된 값이 없으면 이전 값 사용 (연속 연산 시)
|
||||||
|
if (!calculator.currentOperand && calculator.previousOperand) {
|
||||||
|
calculator.operation = operation
|
||||||
|
newDisplayValue = calculator.previousOperand + operation
|
||||||
|
setDisplayValue(newDisplayValue)
|
||||||
|
setHasOperation(true)
|
||||||
|
} else if (hasOperation) {
|
||||||
|
// 이미 연산자가 있는 경우, 계산 실행
|
||||||
|
const result = calculator.compute()
|
||||||
|
if (result !== undefined) {
|
||||||
|
calculator.previousOperand = result.toString()
|
||||||
|
calculator.operation = operation
|
||||||
|
calculator.currentOperand = ''
|
||||||
|
newDisplayValue = calculator.previousOperand + operation
|
||||||
|
setDisplayValue(newDisplayValue)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 첫 번째 연산자 입력 시
|
||||||
|
calculator.previousOperand = calculator.currentOperand || '0'
|
||||||
|
calculator.operation = operation
|
||||||
|
calculator.currentOperand = ''
|
||||||
|
setHasOperation(true)
|
||||||
|
newDisplayValue = calculator.previousOperand + operation
|
||||||
|
setDisplayValue(newDisplayValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 커서를 텍스트 끝으로 이동하고 스크롤 처리
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
if (inputRef.current) {
|
||||||
|
inputRef.current.focus()
|
||||||
|
const len = newDisplayValue.length
|
||||||
|
inputRef.current.setSelectionRange(len, len)
|
||||||
|
// 텍스트 끝으로 스크롤
|
||||||
|
inputRef.current.scrollLeft = inputRef.current.scrollWidth
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// AC 버튼 클릭 핸들러
|
||||||
|
const handleClear = () => {
|
||||||
|
const calculator = calculatorRef.current
|
||||||
|
const newValue = calculator.clear()
|
||||||
|
const displayValue = newValue || '0'
|
||||||
|
setDisplayValue(displayValue)
|
||||||
|
setHasOperation(false)
|
||||||
|
onChange(displayValue)
|
||||||
|
// 커서를 텍스트 끝으로 이동하고 스크롤 처리
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
if (inputRef.current) {
|
||||||
|
inputRef.current.focus()
|
||||||
|
const len = displayValue.length
|
||||||
|
inputRef.current.setSelectionRange(len, len)
|
||||||
|
// 텍스트 끝으로 스크롤
|
||||||
|
inputRef.current.scrollLeft = inputRef.current.scrollWidth
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 계산 실행 함수 수정
|
||||||
|
const handleCompute = (fromEnterKey = false) => {
|
||||||
|
const calculator = calculatorRef.current
|
||||||
|
if (!hasOperation || !calculator.currentOperand) return
|
||||||
|
|
||||||
|
const result = calculator.compute()
|
||||||
|
if (result !== undefined) {
|
||||||
|
const resultStr = result.toString()
|
||||||
|
setDisplayValue(resultStr)
|
||||||
|
setHasOperation(false)
|
||||||
|
// Only call onChange with the final result
|
||||||
|
onChange(resultStr)
|
||||||
|
|
||||||
|
// 엔터키로 호출된 경우 포커스 설정하지 않음
|
||||||
|
if (!fromEnterKey) {
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
if (inputRef.current) {
|
||||||
|
inputRef.current.focus()
|
||||||
|
const len = resultStr.length
|
||||||
|
inputRef.current.setSelectionRange(len, len)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DEL 버튼 클릭 핸들러
|
||||||
|
const handleDelete = () => {
|
||||||
|
const calculator = calculatorRef.current
|
||||||
|
const newValue = calculator.deleteNumber()
|
||||||
|
const displayValue = newValue || '0'
|
||||||
|
setDisplayValue(displayValue)
|
||||||
|
setHasOperation(!!calculator.operation)
|
||||||
|
onChange(displayValue)
|
||||||
|
// 커서를 텍스트 끝으로 이동하고 스크롤 처리
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
if (inputRef.current) {
|
||||||
|
inputRef.current.focus()
|
||||||
|
const len = displayValue.length
|
||||||
|
inputRef.current.setSelectionRange(len, len)
|
||||||
|
// 텍스트 끝으로 스크롤
|
||||||
|
inputRef.current.scrollLeft = inputRef.current.scrollWidth
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// input의 onChange 이벤트 처리 - 허용된 계산기 입력만 처리
|
||||||
|
const handleInputChange = (e) => {
|
||||||
|
if (readOnly) return
|
||||||
|
|
||||||
|
const inputValue = e.target.value
|
||||||
|
|
||||||
|
// 허용된 문자만 필터링 (숫자, 연산자, 소수점)
|
||||||
|
const filteredValue = inputValue.replace(/[^0-9+\-×÷.]/g, '')
|
||||||
|
|
||||||
|
// 연산자 연속 입력 방지
|
||||||
|
const lastChar = filteredValue[filteredValue.length - 1]
|
||||||
|
const prevChar = filteredValue[filteredValue.length - 2]
|
||||||
|
|
||||||
|
if (['+', '×', '÷'].includes(lastChar) && ['+', '-', '×', '÷', '.'].includes(prevChar)) {
|
||||||
|
// 연산자나 소수점이 연속으로 입력된 경우 이전 문자 유지
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 소수점 중복 입력 방지
|
||||||
|
const parts = filteredValue.split(/[+\-×÷]/)
|
||||||
|
if (parts[parts.length - 1].split('.').length > 2) {
|
||||||
|
// 한 숫자에 소수점이 2개 이상인 경우
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
setDisplayValue(filteredValue)
|
||||||
|
|
||||||
|
// 계산기 상태 업데이트
|
||||||
|
if (filteredValue !== displayValue) {
|
||||||
|
const calculator = calculatorRef.current
|
||||||
|
const hasOperation = /[+\-×÷]/.test(filteredValue)
|
||||||
|
|
||||||
|
if (hasOperation) {
|
||||||
|
const [operand1, operator, operand2] = filteredValue.split(/([+\-×÷])/)
|
||||||
|
calculator.previousOperand = operand1 || ''
|
||||||
|
calculator.operation = operator || ''
|
||||||
|
calculator.currentOperand = operand2 || ''
|
||||||
|
setHasOperation(true)
|
||||||
|
} else {
|
||||||
|
calculator.currentOperand = filteredValue
|
||||||
|
setHasOperation(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
onChange(filteredValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 키패드 토글 함수
|
||||||
|
const toggleKeypad = (e) => {
|
||||||
|
if (e) {
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
}
|
||||||
|
const newShowKeypad = !showKeypad
|
||||||
|
setShowKeypad(newShowKeypad)
|
||||||
|
|
||||||
|
// Show keypad 시에만 포커스 유지
|
||||||
|
if (newShowKeypad) {
|
||||||
|
setTimeout(() => {
|
||||||
|
inputRef.current?.focus()
|
||||||
|
}, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 키보드 이벤트 처리 수정
|
||||||
|
const handleKeyDown = (e) => {
|
||||||
|
if (readOnly) return
|
||||||
|
|
||||||
|
// Tab 키는 계산기 숨기고 기본 동작 허용
|
||||||
|
if (e.key === 'Tab') {
|
||||||
|
setShowKeypad(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 모든 방향키는 기본 동작 허용
|
||||||
|
if (e.key === 'ArrowLeft' || e.key === 'ArrowRight' || e.key === 'ArrowUp' || e.key === 'ArrowDown') {
|
||||||
|
setShowKeypad(true)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 계산기 허용 키들이 입력되면 키패드 표시 (엔터키 제외)
|
||||||
|
if (/^[0-9+\-×÷.=]$/.test(e.key) || e.key === 'Backspace' || e.key === 'Delete' || e.key === '*' || e.key === '/') {
|
||||||
|
setShowKeypad(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 키패드가 숨겨진 상태에서 엔터키는 페이지로 전달
|
||||||
|
if (!showKeypad && e.key === 'Enter') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
e.preventDefault()
|
||||||
|
const calculator = calculatorRef.current
|
||||||
|
const { allowDecimal } = options
|
||||||
|
|
||||||
|
if (e.key === '.') {
|
||||||
|
// allowDecimal이 false이면 소수점 입력 무시
|
||||||
|
if (!allowDecimal) return
|
||||||
|
|
||||||
|
// 소수점 입력 처리
|
||||||
|
const currentValue = displayValue.toString()
|
||||||
|
const parts = currentValue.split(/[+\-×÷]/)
|
||||||
|
const lastPart = parts[parts.length - 1]
|
||||||
|
|
||||||
|
// 이미 소수점이 있으면 무시
|
||||||
|
if (!lastPart.includes('.')) {
|
||||||
|
handleNumber(e.key)
|
||||||
|
}
|
||||||
|
} else if (/^[0-9]$/.test(e.key)) {
|
||||||
|
handleNumber(e.key)
|
||||||
|
} else {
|
||||||
|
switch (e.key) {
|
||||||
|
case '+':
|
||||||
|
case '-':
|
||||||
|
case '*':
|
||||||
|
case '/':
|
||||||
|
const opMap = { '*': '×', '/': '÷' }
|
||||||
|
handleOperation(opMap[e.key] || e.key)
|
||||||
|
break
|
||||||
|
case 'Enter':
|
||||||
|
case '=':
|
||||||
|
if (showKeypad) {
|
||||||
|
// 키패드가 보이는 상태에서 엔터키: 계산 후 키패드만 숨김
|
||||||
|
handleCompute(true) // 엔터키로 호출됨을 표시
|
||||||
|
setShowKeypad(false)
|
||||||
|
}
|
||||||
|
// 키패드가 숨겨진 상태에서 엔터키: 페이지로 전달 (preventDefault 하지 않음)
|
||||||
|
break
|
||||||
|
case 'Backspace':
|
||||||
|
case 'Delete':
|
||||||
|
handleDelete()
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'Escape':
|
||||||
|
handleClear()
|
||||||
|
setShowKeypad(false)
|
||||||
|
break
|
||||||
|
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div ref={containerRef} className="calculator-input-wrapper">
|
||||||
|
{label && (
|
||||||
|
<label htmlFor={id} className="calculator-label">
|
||||||
|
{label}
|
||||||
|
</label>
|
||||||
|
)}
|
||||||
|
<input
|
||||||
|
ref={inputRef}
|
||||||
|
type="text"
|
||||||
|
id={id}
|
||||||
|
name={name}
|
||||||
|
value={displayValue}
|
||||||
|
readOnly={readOnly}
|
||||||
|
className={className}
|
||||||
|
onClick={() => !readOnly && setShowKeypad(true)}
|
||||||
|
onFocus={() => !readOnly && setShowKeypad(true)}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
|
tabIndex={readOnly ? -1 : 0}
|
||||||
|
placeholder={placeholder}
|
||||||
|
autoComplete={'off'}
|
||||||
|
disabled={disabled}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{showKeypad && !readOnly && (
|
||||||
|
<div className="keypad-container">
|
||||||
|
<div className="keypad-grid">
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
// const newValue = calculatorRef.current.clear()
|
||||||
|
// setDisplayValue(newValue || '0')
|
||||||
|
// onChange(newValue || '0')
|
||||||
|
handleClear()
|
||||||
|
setHasOperation(false)
|
||||||
|
}}
|
||||||
|
className="btn-clear"
|
||||||
|
>
|
||||||
|
AC
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
// const newValue = calculatorRef.current.deleteNumber()
|
||||||
|
// setDisplayValue(newValue || '0')
|
||||||
|
// onChange(newValue || '0')
|
||||||
|
//setHasOperation(!!calculatorRef.current.operation)
|
||||||
|
handleDelete()
|
||||||
|
}}
|
||||||
|
className="btn-delete"
|
||||||
|
>
|
||||||
|
DEL
|
||||||
|
</button>
|
||||||
|
<button onClick={() => handleOperation('÷')} className="btn-operator">
|
||||||
|
÷
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button onClick={() => handleOperation('×')} className="btn-operator">
|
||||||
|
×
|
||||||
|
</button>
|
||||||
|
<button onClick={() => handleOperation('-')} className="btn-operator">
|
||||||
|
-
|
||||||
|
</button>
|
||||||
|
<button onClick={() => handleOperation('+')} className="btn-operator">
|
||||||
|
+
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{/* 숫자 버튼 */}
|
||||||
|
{[1, 2, 3, 4, 5, 6, 7, 8, 9].map((num) => (
|
||||||
|
<button key={num} onClick={() => handleNumber(num)} className="btn-number">
|
||||||
|
{num}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
{/* 0 버튼 */}
|
||||||
|
<button onClick={() => handleNumber('0')} className="btn-number btn-zero">
|
||||||
|
0
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
const newValue = calculatorRef.current.appendNumber('.')
|
||||||
|
onChange(newValue)
|
||||||
|
}}
|
||||||
|
className="btn-number"
|
||||||
|
>
|
||||||
|
.
|
||||||
|
</button>
|
||||||
|
{/* = 버튼 */}
|
||||||
|
<button onClick={() => handleCompute(false)} className="btn-equals">
|
||||||
|
=
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
CalculatorInput.displayName = 'CalculatorInput'
|
||||||
@ -14,6 +14,7 @@ import { useMessage } from '@/hooks/useMessage'
|
|||||||
* @param {string} targetKey - value에 있는 키
|
* @param {string} targetKey - value에 있는 키
|
||||||
* @param {string} showKey - options 있는 키중 보여줄 키
|
* @param {string} showKey - options 있는 키중 보여줄 키
|
||||||
* @param {object} params - 추가 파라미터
|
* @param {object} params - 추가 파라미터
|
||||||
|
* @param {boolean} showFirstOptionWhenEmpty - value가 빈값일 때 첫 번째 옵션을 보여줄지 여부
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export default function QSelectBox({
|
export default function QSelectBox({
|
||||||
@ -27,6 +28,7 @@ export default function QSelectBox({
|
|||||||
showKey = '',
|
showKey = '',
|
||||||
params = {},
|
params = {},
|
||||||
tagTitle = '',
|
tagTitle = '',
|
||||||
|
showFirstOptionWhenEmpty = false,
|
||||||
}) {
|
}) {
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
|
|
||||||
@ -39,7 +41,9 @@ export default function QSelectBox({
|
|||||||
if (options.length === 0) return title !== '' ? title : getMessage('selectbox.title')
|
if (options.length === 0) return title !== '' ? title : getMessage('selectbox.title')
|
||||||
if (showKey !== '' && !value) {
|
if (showKey !== '' && !value) {
|
||||||
//value가 없으면 showKey가 있으면 우선 보여준다
|
//value가 없으면 showKey가 있으면 우선 보여준다
|
||||||
// return options[0][showKey]
|
if (showFirstOptionWhenEmpty && options.length > 0) {
|
||||||
|
return options[0][showKey]
|
||||||
|
}
|
||||||
return title !== '' ? title : getMessage('selectbox.title')
|
return title !== '' ? title : getMessage('selectbox.title')
|
||||||
} else if (showKey !== '' && value) {
|
} else if (showKey !== '' && value) {
|
||||||
//value가 있으면 sourceKey와 targetKey를 비교하여 보여준다
|
//value가 있으면 sourceKey와 targetKey를 비교하여 보여준다
|
||||||
@ -48,12 +52,18 @@ export default function QSelectBox({
|
|||||||
return option[sourceKey] === value[targetKey]
|
return option[sourceKey] === value[targetKey]
|
||||||
})
|
})
|
||||||
if (!option) {
|
if (!option) {
|
||||||
|
if (showFirstOptionWhenEmpty && options.length > 0) {
|
||||||
|
return options[0][showKey]
|
||||||
|
}
|
||||||
return title !== '' ? title : getMessage('selectbox.title')
|
return title !== '' ? title : getMessage('selectbox.title')
|
||||||
} else {
|
} else {
|
||||||
return option[showKey]
|
return option[showKey]
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
//일치하는 조건이 없으면 기본값을 보여준다.
|
//일치하는 조건이 없으면 기본값을 보여준다.
|
||||||
|
if (showFirstOptionWhenEmpty && options.length > 0) {
|
||||||
|
return showKey !== '' ? options[0][showKey] : options[0].name
|
||||||
|
}
|
||||||
return title !== '' ? title : getMessage('selectbox.title')
|
return title !== '' ? title : getMessage('selectbox.title')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -74,7 +84,7 @@ export default function QSelectBox({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// value && handleClickSelectOption(value)
|
// value && handleClickSelectOption(value)
|
||||||
setSelected(handleInitState())
|
setSelected(handleInitState())
|
||||||
}, [options, value, sourceKey, targetKey, showKey])
|
}, [options, value, sourceKey, targetKey, showKey, showFirstOptionWhenEmpty])
|
||||||
|
|
||||||
useOnClickOutside(ref, handleClose)
|
useOnClickOutside(ref, handleClose)
|
||||||
|
|
||||||
|
|||||||
@ -25,6 +25,7 @@ export default function QnaDetailModal({ qnaNo, setOpen, qnaType }) {
|
|||||||
compCd : 5200,
|
compCd : 5200,
|
||||||
loginId : sessionState.userId,
|
loginId : sessionState.userId,
|
||||||
langCd : 'JA',
|
langCd : 'JA',
|
||||||
|
siteTpCd : 'QC',
|
||||||
})
|
})
|
||||||
const apiUrl = `${url}?${params.toString()}`
|
const apiUrl = `${url}?${params.toString()}`
|
||||||
|
|
||||||
|
|||||||
@ -22,7 +22,8 @@ export default function QnaRegModal({ setOpen, setReload, searchValue, selectPag
|
|||||||
const [sessionState, setSessionState] = useRecoilState(sessionStore)
|
const [sessionState, setSessionState] = useRecoilState(sessionStore)
|
||||||
const globalLocaleState = useRecoilValue(globalLocaleStore)
|
const globalLocaleState = useRecoilValue(globalLocaleStore)
|
||||||
const [files, setFiles] = useState([])
|
const [files, setFiles] = useState([])
|
||||||
const [qnaData, setQnaData] = useState([])
|
//const [qnaData, setQnaData] = useState([])
|
||||||
|
const [qnaData, setQnaData] = useState({})
|
||||||
const [closeMdFlg, setCloseMdFlg] = useState(true)
|
const [closeMdFlg, setCloseMdFlg] = useState(true)
|
||||||
const [closeSmFlg, setCloseSmFlg] = useState(true)
|
const [closeSmFlg, setCloseSmFlg] = useState(true)
|
||||||
const [hideSmFlg, setHideSmFlg] = useState(false)
|
const [hideSmFlg, setHideSmFlg] = useState(false)
|
||||||
@ -44,6 +45,10 @@ export default function QnaRegModal({ setOpen, setReload, searchValue, selectPag
|
|||||||
const [isBtnDisable, setIsBtnDisable] = useState(false);
|
const [isBtnDisable, setIsBtnDisable] = useState(false);
|
||||||
const { promiseGet, post, promisePost } = useAxios(globalLocaleState)
|
const { promiseGet, post, promisePost } = useAxios(globalLocaleState)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
console.log('qnaData updated:', qnaData);
|
||||||
|
}, [qnaData]);
|
||||||
|
|
||||||
let fileCheck = false;
|
let fileCheck = false;
|
||||||
const regPhoneNumber = (e) => {
|
const regPhoneNumber = (e) => {
|
||||||
const result = e.target.value
|
const result = e.target.value
|
||||||
@ -80,14 +85,16 @@ let fileCheck = false;
|
|||||||
//setQnaData([])
|
//setQnaData([])
|
||||||
|
|
||||||
setQnaData({
|
setQnaData({
|
||||||
...qnaData,
|
|
||||||
compCd: "5200",
|
compCd: "5200",
|
||||||
siteTpCd: "QC",
|
siteTpCd: "QC",
|
||||||
schNoticeClsCd: "QNA",
|
schNoticeClsCd: "QNA",
|
||||||
regId: sessionState.userId,
|
regId: sessionState?.userId || '',
|
||||||
storeId: sessionState.userId,
|
storeId: sessionState?.userId || '',
|
||||||
qstMail : sessionState.email
|
qstMail: sessionState?.email || '',
|
||||||
})
|
qnaClsLrgCd: '',
|
||||||
|
qnaClsMidCd: '',
|
||||||
|
qnaClsSmlCd: ''
|
||||||
|
});
|
||||||
|
|
||||||
const codeL = findCommonCode(204200)
|
const codeL = findCommonCode(204200)
|
||||||
if (codeL != null) {
|
if (codeL != null) {
|
||||||
@ -119,41 +126,42 @@ let fileCheck = false;
|
|||||||
|
|
||||||
}
|
}
|
||||||
const onChangeQnaTypeM = (e) => {
|
const onChangeQnaTypeM = (e) => {
|
||||||
|
if (!e?.clCode) return;
|
||||||
|
|
||||||
if(e === undefined || e === null) return;
|
// 중분류 코드 업데이트
|
||||||
const codeS = findCommonCode(204400)
|
setQnaData(prevState => ({
|
||||||
if (codeS != null) {
|
...prevState,
|
||||||
|
qnaClsMidCd: e.clCode,
|
||||||
let codeList = []
|
// 소분류는 초기화 (새로 선택하도록)
|
||||||
|
qnaClsSmlCd: ''
|
||||||
codeS.map((item) => {
|
}));
|
||||||
|
|
||||||
if (item.clRefChr1 === e.clCode) {
|
|
||||||
codeList.push(item);
|
|
||||||
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
setQnaData({ ...qnaData, qnaClsMidCd: e.clCode })
|
|
||||||
setCloseSmFlg(false)
|
|
||||||
setQnaTypeSmCodeList(codeList)
|
|
||||||
qnaTypeSmCodeRef.current?.setValue();
|
|
||||||
|
|
||||||
if(codeList.length > 0) {
|
|
||||||
setHideSmFlg(false)
|
|
||||||
}else{
|
|
||||||
setHideSmFlg(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
// 소분류 코드 목록 설정
|
||||||
|
const codeS = findCommonCode(204400);
|
||||||
|
if (codeS) {
|
||||||
|
const filteredCodeList = codeS.filter(item => item.clRefChr1 === e.clCode);
|
||||||
|
setQnaTypeSmCodeList(filteredCodeList);
|
||||||
|
|
||||||
|
// 소분류가 있으면 초기화, 없으면 숨김
|
||||||
|
const hasSubCategories = filteredCodeList.length > 0;
|
||||||
|
setCloseSmFlg(!hasSubCategories);
|
||||||
|
setHideSmFlg(!hasSubCategories);
|
||||||
|
} else {
|
||||||
|
setHideSmFlg(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
// 소분류 선택기 초기화
|
||||||
|
qnaTypeSmCodeRef.current?.setValue();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
const onChangeQnaTypeS = (e) => {
|
const onChangeQnaTypeS = (e) => {
|
||||||
if(e === undefined || e === null) return;
|
if (!e?.clCode) return;
|
||||||
setQnaData({ ...qnaData, qnaClsSmlCd:e.clCode})
|
|
||||||
|
setQnaData(prevState => ({
|
||||||
|
...prevState,
|
||||||
|
qnaClsSmlCd: e.clCode
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
const onFileSave = () => {
|
const onFileSave = () => {
|
||||||
|
|||||||
@ -23,6 +23,7 @@ import { usePopup } from '@/hooks/usePopup'
|
|||||||
import { useSwal } from '@/hooks/useSwal'
|
import { useSwal } from '@/hooks/useSwal'
|
||||||
import { QcastContext } from '@/app/QcastProvider'
|
import { QcastContext } from '@/app/QcastProvider'
|
||||||
import { useCanvasMenu } from '@/hooks/common/useCanvasMenu'
|
import { useCanvasMenu } from '@/hooks/common/useCanvasMenu'
|
||||||
|
import {normalizeDigits, normalizeDecimal} from '@/util/input-utils'
|
||||||
export default function Estimate({}) {
|
export default function Estimate({}) {
|
||||||
const [uniqueData, setUniqueData] = useState([])
|
const [uniqueData, setUniqueData] = useState([])
|
||||||
const [handlePricingFlag, setHandlePricingFlag] = useState(false)
|
const [handlePricingFlag, setHandlePricingFlag] = useState(false)
|
||||||
@ -137,7 +138,27 @@ export default function Estimate({}) {
|
|||||||
updatedRes = [...res]
|
updatedRes = [...res]
|
||||||
}
|
}
|
||||||
|
|
||||||
setOriginDisplayItemList(res)
|
const groupByItemGroup = (items) => {
|
||||||
|
const grouped = items.reduce((acc, item) => {
|
||||||
|
const group = item.itemGroup || '기타';
|
||||||
|
if (!acc[group]) {
|
||||||
|
acc[group] = {
|
||||||
|
label: group,
|
||||||
|
options: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
acc[group].options.push({
|
||||||
|
value: item.itemId,
|
||||||
|
label: `${item.itemNo} - ${item.itemName}`,
|
||||||
|
...item
|
||||||
|
});
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
return Object.values(grouped);
|
||||||
|
};
|
||||||
|
const groupedItems = groupByItemGroup(res);
|
||||||
|
setOriginDisplayItemList(groupedItems)
|
||||||
setDisplayItemList(updatedRes)
|
setDisplayItemList(updatedRes)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -152,6 +173,19 @@ export default function Estimate({}) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const groupStyles = {
|
||||||
|
groupHeading: (provided) => ({
|
||||||
|
...provided,
|
||||||
|
fontSize: '14px',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
color: '#333',
|
||||||
|
backgroundColor: '#f5f5f5',
|
||||||
|
padding: '8px 12px',
|
||||||
|
marginBottom: '4px',
|
||||||
|
borderBottom: '2px solid #ddd'
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// console.log('🚀 ~ Estimate ~ selectedPlan:', selectedPlan)
|
// console.log('🚀 ~ Estimate ~ selectedPlan:', selectedPlan)
|
||||||
if (selectedPlan) initEstimate(selectedPlan?.planNo?? currentPid)
|
if (selectedPlan) initEstimate(selectedPlan?.planNo?? currentPid)
|
||||||
@ -507,6 +541,7 @@ export default function Estimate({}) {
|
|||||||
icon: 'warning',
|
icon: 'warning',
|
||||||
confirmFn: () => {
|
confirmFn: () => {
|
||||||
handlePricing(showPriceCd)
|
handlePricing(showPriceCd)
|
||||||
|
setEstimateContextState({ pricingFlag:true })
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -644,11 +679,14 @@ export default function Estimate({}) {
|
|||||||
newValue = parts[0] + '.' + parts[1].substring(0, 2)
|
newValue = parts[0] + '.' + parts[1].substring(0, 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
let pkgAsp = newValue || '0'
|
let pkgAsp = normalizeDecimal(newValue || '0')
|
||||||
|
|
||||||
//현재 PKG용량값 가져오기
|
//현재 PKG용량값 가져오기
|
||||||
let totVolKw = estimateContextState.totVolKw * 1000
|
let totVolKw = estimateContextState.totVolKw * 1000
|
||||||
let pkgTotPrice = parseFloat(pkgAsp?.replaceAll(',', '')) * totVolKw * 1000
|
// let pkgTotPrice = parseFloat(pkgAsp?.replaceAll(',', '')) * totVolKw * 1000
|
||||||
|
|
||||||
|
const pkgAspNumber = Number(normalizeDecimal(pkgAsp))
|
||||||
|
const pkgTotPrice = pkgAspNumber * totVolKw * 1000
|
||||||
|
|
||||||
setEstimateContextState({
|
setEstimateContextState({
|
||||||
pkgAsp: pkgAsp,
|
pkgAsp: pkgAsp,
|
||||||
@ -662,7 +700,7 @@ export default function Estimate({}) {
|
|||||||
// 수량 변경
|
// 수량 변경
|
||||||
const onChangeAmount = (value, dispOrder, index) => {
|
const onChangeAmount = (value, dispOrder, index) => {
|
||||||
//itemChangeFlg = 1, partAdd = 0 셋팅
|
//itemChangeFlg = 1, partAdd = 0 셋팅
|
||||||
let amount = Number(value.replace(/[^0-9]/g, '').replaceAll(',', ''))
|
let amount = Number(normalizeDigits(value))
|
||||||
|
|
||||||
if (isNaN(amount)) {
|
if (isNaN(amount)) {
|
||||||
amount = '0'
|
amount = '0'
|
||||||
@ -700,7 +738,8 @@ export default function Estimate({}) {
|
|||||||
// 단가 변경
|
// 단가 변경
|
||||||
const onChangeSalePrice = (value, dispOrder, index) => {
|
const onChangeSalePrice = (value, dispOrder, index) => {
|
||||||
//itemChangeFlg, partAdd 받아온 그대로
|
//itemChangeFlg, partAdd 받아온 그대로
|
||||||
let salePrice = Number(value.replace(/[^0-9]/g, '').replaceAll(',', ''))
|
let salePrice = Number(normalizeDecimal(value))
|
||||||
|
|
||||||
if (isNaN(salePrice)) {
|
if (isNaN(salePrice)) {
|
||||||
salePrice = 0
|
salePrice = 0
|
||||||
} else {
|
} else {
|
||||||
@ -731,7 +770,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' && item.itemTpCd !== 'M12') {
|
if (item.dispCableFlg === '1' && item.itemTpCd !== 'M12' && item.itemTpCd !== 'S13') {
|
||||||
if (value !== '') {
|
if (value !== '') {
|
||||||
onChangeDisplayItem(value, item.dispOrder, index, true)
|
onChangeDisplayItem(value, item.dispOrder, index, true)
|
||||||
}
|
}
|
||||||
@ -743,7 +782,7 @@ export default function Estimate({}) {
|
|||||||
/* 케이블 select 변경시 */
|
/* 케이블 select 변경시 */
|
||||||
const onChangeDisplayDoubleCableItem = (value, itemList) => {
|
const onChangeDisplayDoubleCableItem = (value, itemList) => {
|
||||||
itemList.map((item, index) => {
|
itemList.map((item, index) => {
|
||||||
if (item.dispCableFlg === '1' && item.itemTpCd === 'M12') {
|
if (item.dispCableFlg === '1' && (item.itemTpCd === 'M12' || item.itemTpCd === 'S13')) {
|
||||||
if (value !== '') {
|
if (value !== '') {
|
||||||
onChangeDisplayItem(value, item.dispOrder, index, true)
|
onChangeDisplayItem(value, item.dispOrder, index, true)
|
||||||
}
|
}
|
||||||
@ -939,7 +978,7 @@ export default function Estimate({}) {
|
|||||||
delete item.showSalePrice
|
delete item.showSalePrice
|
||||||
delete item.showSaleTotPrice
|
delete item.showSaleTotPrice
|
||||||
if (item.delFlg === '0') {
|
if (item.delFlg === '0') {
|
||||||
let amount = Number(item.amount?.replace(/[^0-9]/g, '').replaceAll(',', '')) || 0
|
let amount = Number(normalizeDigits(item.amount)) || 0
|
||||||
let price
|
let price
|
||||||
if (amount === 0) {
|
if (amount === 0) {
|
||||||
price = 0
|
price = 0
|
||||||
@ -974,7 +1013,7 @@ export default function Estimate({}) {
|
|||||||
makeUniqueSpecialNoteCd(itemList)
|
makeUniqueSpecialNoteCd(itemList)
|
||||||
itemList.forEach((item) => {
|
itemList.forEach((item) => {
|
||||||
if (item.delFlg === '0') {
|
if (item.delFlg === '0') {
|
||||||
let amount = Number(item.amount?.replace(/[^0-9]/g, '').replaceAll(',', '')) || 0
|
let amount = Number(normalizeDigits(item.amount)) || 0
|
||||||
let salePrice
|
let salePrice
|
||||||
if (item.moduleFlg === '1') {
|
if (item.moduleFlg === '1') {
|
||||||
const volKw = (item.pnowW * amount) / 1000
|
const volKw = (item.pnowW * amount) / 1000
|
||||||
@ -1008,8 +1047,8 @@ export default function Estimate({}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
let pkgAsp = estimateContextState.pkgAsp ? Number(estimateContextState.pkgAsp.replaceAll(',', '')) : 0
|
//let pkgAsp = estimateContextState.pkgAsp ? Number(estimateContextState.pkgAsp.replaceAll(',', '')) : 0
|
||||||
|
const pkgAsp = Number(normalizeDecimal(estimateContextState.pkgAsp))
|
||||||
totals.pkgTotPrice = pkgAsp * totals.totVolKw * 1000
|
totals.pkgTotPrice = pkgAsp * totals.totVolKw * 1000
|
||||||
totals.supplyPrice = totals.addSupplyPrice + totals.pkgTotPrice
|
totals.supplyPrice = totals.addSupplyPrice + totals.pkgTotPrice
|
||||||
totals.vatPrice = totals.supplyPrice * 0.1
|
totals.vatPrice = totals.supplyPrice * 0.1
|
||||||
@ -1068,7 +1107,7 @@ export default function Estimate({}) {
|
|||||||
let dispCableFlgCnt = 0
|
let dispCableFlgCnt = 0
|
||||||
estimateContextState.itemList.forEach((item) => {
|
estimateContextState.itemList.forEach((item) => {
|
||||||
if (item.delFlg === '0') {
|
if (item.delFlg === '0') {
|
||||||
let amount = Number(item.amount?.replace(/[^0-9]/g, '').replaceAll(',', '')) || 0
|
let amount = Number(normalizeDigits(item.amount)) || 0
|
||||||
let salePrice
|
let salePrice
|
||||||
if (item.moduleFlg === '1') {
|
if (item.moduleFlg === '1') {
|
||||||
const volKw = (item.pnowW * amount) / 1000
|
const volKw = (item.pnowW * amount) / 1000
|
||||||
@ -1101,9 +1140,9 @@ export default function Estimate({}) {
|
|||||||
item.showSaleTotPrice = '0'
|
item.showSaleTotPrice = '0'
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.dispCableFlg === '1' ) {
|
if (item.dispCableFlg === '1') {
|
||||||
dispCableFlgCnt++
|
dispCableFlgCnt++
|
||||||
if(item.itemTpCd === 'M12') {
|
if(item.itemTpCd === 'M12' || item.itemTpCd === 'S13') {
|
||||||
setCableDbItem(item.itemId)
|
setCableDbItem(item.itemId)
|
||||||
}else{
|
}else{
|
||||||
setCableItem(item.itemId)
|
setCableItem(item.itemId)
|
||||||
@ -1117,9 +1156,10 @@ export default function Estimate({}) {
|
|||||||
setCableDbItem('100037')
|
setCableDbItem('100037')
|
||||||
}
|
}
|
||||||
|
|
||||||
let pkgAsp = estimateContextState.pkgAsp ? Number(estimateContextState.pkgAsp.replaceAll(',', '')) : 0
|
// let pkgAsp = estimateContextState.pkgAsp ? Number(estimateContextState.pkgAsp.replaceAll(',', '')) : 0
|
||||||
|
const pkgAsp = Number(normalizeDecimal(estimateContextState.pkgAsp))
|
||||||
totals.pkgTotPrice = pkgAsp * totals.totVolKw * 1000
|
totals.pkgTotPrice = pkgAsp * totals.totVolKw * 1000
|
||||||
|
|
||||||
totals.supplyPrice = totals.addSupplyPrice + totals.pkgTotPrice
|
totals.supplyPrice = totals.addSupplyPrice + totals.pkgTotPrice
|
||||||
totals.vatPrice = totals.supplyPrice * 0.1
|
totals.vatPrice = totals.supplyPrice * 0.1
|
||||||
totals.totPrice = totals.supplyPrice + totals.vatPrice
|
totals.totPrice = totals.supplyPrice + totals.vatPrice
|
||||||
@ -1149,7 +1189,7 @@ export default function Estimate({}) {
|
|||||||
delete item.showSalePrice
|
delete item.showSalePrice
|
||||||
delete item.showSaleTotPrice
|
delete item.showSaleTotPrice
|
||||||
if (item.delFlg === '0') {
|
if (item.delFlg === '0') {
|
||||||
let amount = Number(item.amount?.replace(/[^0-9]/g, '').replaceAll(',', '')) || 0
|
let amount = Number(normalizeDigits(item.amount)) || 0
|
||||||
let price
|
let price
|
||||||
if (amount === 0) {
|
if (amount === 0) {
|
||||||
price = 0
|
price = 0
|
||||||
@ -1175,11 +1215,7 @@ export default function Estimate({}) {
|
|||||||
|
|
||||||
if (item.dispCableFlg === '1') {
|
if (item.dispCableFlg === '1') {
|
||||||
dispCableFlgCnt++
|
dispCableFlgCnt++
|
||||||
}
|
if(item.itemTpCd === 'M12' || item.itemTpCd === 'S13') {
|
||||||
|
|
||||||
if (item.dispCableFlg === '1'){
|
|
||||||
|
|
||||||
if(item.itemTpCd === 'M12') {
|
|
||||||
setCableDbItem(item.itemId)
|
setCableDbItem(item.itemId)
|
||||||
}else{
|
}else{
|
||||||
setCableItem(item.itemId)
|
setCableItem(item.itemId)
|
||||||
@ -1437,7 +1473,7 @@ export default function Estimate({}) {
|
|||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
//주문분류
|
//주문분류
|
||||||
setHandlePricingFlag(true)
|
setHandlePricingFlag(true)
|
||||||
setEstimateContextState({ estimateType: e.target.value })
|
setEstimateContextState({ estimateType: e.target.value, setEstimateContextState })
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<label htmlFor="YJSS">{getMessage('estimate.detail.estimateType.yjss')}</label>
|
<label htmlFor="YJSS">{getMessage('estimate.detail.estimateType.yjss')}</label>
|
||||||
@ -1457,7 +1493,7 @@ export default function Estimate({}) {
|
|||||||
<label htmlFor="YJOD">{getMessage('estimate.detail.estimateType.yjod')}</label>
|
<label htmlFor="YJOD">{getMessage('estimate.detail.estimateType.yjod')}</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{session?.storeLvl === '1' && agencyCustList.length > 0 ? (
|
{session?.storeLvl === '100000' && agencyCustList.length > 0 ? ( //일시적으로 1 => 100000로 정리
|
||||||
<div className="form-flex-select ml10">
|
<div className="form-flex-select ml10">
|
||||||
<label htmlFor="">{getMessage('estimate.detail.agency')}</label>
|
<label htmlFor="">{getMessage('estimate.detail.agency')}</label>
|
||||||
<div className="select-wrap" style={{ width: '400px' }}>
|
<div className="select-wrap" style={{ width: '400px' }}>
|
||||||
@ -1995,6 +2031,7 @@ export default function Estimate({}) {
|
|||||||
classNamePrefix="custom"
|
classNamePrefix="custom"
|
||||||
placeholder="Select"
|
placeholder="Select"
|
||||||
options={originDisplayItemList}
|
options={originDisplayItemList}
|
||||||
|
styles={groupStyles}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
if (isObjectNotEmpty(e)) {
|
if (isObjectNotEmpty(e)) {
|
||||||
onChangeDisplayItem(e.itemId, item.dispOrder, index, false)
|
onChangeDisplayItem(e.itemId, item.dispOrder, index, false)
|
||||||
@ -2031,13 +2068,13 @@ export default function Estimate({}) {
|
|||||||
getOptionValue={(x) => x.clRefChr1}
|
getOptionValue={(x) => x.clRefChr1}
|
||||||
components={{
|
components={{
|
||||||
SingleValue: ({ children, ...props }) => {
|
SingleValue: ({ children, ...props }) => {
|
||||||
return <components.SingleValue {...props}>{(item.itemTpCd === 'M12')? item.itemName : props.data.clRefChr3}</components.SingleValue>
|
return <components.SingleValue {...props}>{(item.itemTpCd === 'M12' || item.itemTpCd === 'S13')? 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 (item.itemTpCd === 'M12')? item.itemId : option.clRefChr1 === item.itemId
|
return (item.itemTpCd === 'M12' || item.itemTpCd === 'S13' )? item.itemId : option.clRefChr1 === item.itemId
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import { useRecoilValue } from 'recoil'
|
|||||||
import { floorPlanObjectState, estimateState } from '@/store/floorPlanObjectAtom'
|
import { floorPlanObjectState, estimateState } from '@/store/floorPlanObjectAtom'
|
||||||
import { usePathname, useSearchParams } from 'next/navigation'
|
import { usePathname, useSearchParams } from 'next/navigation'
|
||||||
import { QcastContext } from '@/app/QcastProvider'
|
import { QcastContext } from '@/app/QcastProvider'
|
||||||
|
import { sessionStore } from '@/store/commonAtom'
|
||||||
|
|
||||||
export default function DocDownOptionPop({ planNo, setEstimatePopupOpen, docDownPopLockFlg }) {
|
export default function DocDownOptionPop({ planNo, setEstimatePopupOpen, docDownPopLockFlg }) {
|
||||||
const { setIsGlobalLoading } = useContext(QcastContext)
|
const { setIsGlobalLoading } = useContext(QcastContext)
|
||||||
@ -30,7 +31,7 @@ export default function DocDownOptionPop({ planNo, setEstimatePopupOpen, docDown
|
|||||||
// recoil 물건번호
|
// recoil 물건번호
|
||||||
const objectRecoil = useRecoilValue(floorPlanObjectState)
|
const objectRecoil = useRecoilValue(floorPlanObjectState)
|
||||||
const estimateRecoilState = useRecoilValue(estimateState)
|
const estimateRecoilState = useRecoilValue(estimateState)
|
||||||
|
const sessionState = useRecoilValue(sessionStore)
|
||||||
//문서 다운로드
|
//문서 다운로드
|
||||||
const handleFileDown = async () => {
|
const handleFileDown = async () => {
|
||||||
const url = '/api/estimate/excel-download'
|
const url = '/api/estimate/excel-download'
|
||||||
@ -67,6 +68,8 @@ export default function DocDownOptionPop({ planNo, setEstimatePopupOpen, docDown
|
|||||||
schWeightFlg: schWeightFlg,
|
schWeightFlg: schWeightFlg,
|
||||||
schDrawingFlg: defaultSchDrawingFlg,
|
schDrawingFlg: defaultSchDrawingFlg,
|
||||||
pwrGnrSimType: 'D', //default 화면에 안보여줌
|
pwrGnrSimType: 'D', //default 화면에 안보여줌
|
||||||
|
userId: sessionState.userId ? sessionState.userId : "",
|
||||||
|
saleStoreId: sessionState.storeId ? sessionState.storeId : "",
|
||||||
}
|
}
|
||||||
|
|
||||||
const options = { responseType: 'blob' }
|
const options = { responseType: 'blob' }
|
||||||
@ -130,7 +133,7 @@ export default function DocDownOptionPop({ planNo, setEstimatePopupOpen, docDown
|
|||||||
<div className="common-table">
|
<div className="common-table">
|
||||||
<table>
|
<table>
|
||||||
<colgroup>
|
<colgroup>
|
||||||
<col style={{ width: '260px' }} />
|
<col style={{ width: '220px' }} />
|
||||||
<col />
|
<col />
|
||||||
</colgroup>
|
</colgroup>
|
||||||
<tbody>
|
<tbody>
|
||||||
@ -183,7 +186,7 @@ export default function DocDownOptionPop({ planNo, setEstimatePopupOpen, docDown
|
|||||||
/>
|
/>
|
||||||
<label htmlFor="schUnitPricePdfFlg0">{getMessage('estimate.detail.docPopup.schUnitPriceFlg.pdfFlg0')}</label>
|
<label htmlFor="schUnitPricePdfFlg0">{getMessage('estimate.detail.docPopup.schUnitPriceFlg.pdfFlg0')}</label>
|
||||||
</div>
|
</div>
|
||||||
<div className="d-check-radio light ">
|
<div className="d-check-radio light mr10">
|
||||||
<input
|
<input
|
||||||
type="radio"
|
type="radio"
|
||||||
id="schUnitPricePdfFlg1"
|
id="schUnitPricePdfFlg1"
|
||||||
@ -197,6 +200,20 @@ export default function DocDownOptionPop({ planNo, setEstimatePopupOpen, docDown
|
|||||||
/>
|
/>
|
||||||
<label htmlFor="schUnitPricePdfFlg1">{getMessage('estimate.detail.docPopup.schUnitPriceFlg.pdfFlg1')}</label>
|
<label htmlFor="schUnitPricePdfFlg1">{getMessage('estimate.detail.docPopup.schUnitPriceFlg.pdfFlg1')}</label>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="d-check-radio light">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
id="schUnitPriceExcelFlg2"
|
||||||
|
name="schUnitPriceFlg"
|
||||||
|
value={'4'}
|
||||||
|
checked={schUnitPriceFlg === '4'}
|
||||||
|
onChange={(e) => {
|
||||||
|
setSchDownload('EXCEL2')
|
||||||
|
setSchUnitPriceFlg(e.target.value)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<label htmlFor="schUnitPriceExcelFlg2">{getMessage('estimate.detail.docPopup.schUnitPriceFlg.excelFlg2')}</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
import { fabric } from 'fabric'
|
import { fabric } from 'fabric'
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
import { getDirectionByPoint } from '@/util/canvas-util'
|
import { getDirectionByPoint } from '@/util/canvas-util'
|
||||||
|
import { calcLinePlaneSize } from '@/util/qpolygon-utils'
|
||||||
|
import { logger } from '@/util/logger'
|
||||||
|
|
||||||
export const QLine = fabric.util.createClass(fabric.Line, {
|
export const QLine = fabric.util.createClass(fabric.Line, {
|
||||||
type: 'QLine',
|
type: 'QLine',
|
||||||
@ -14,10 +16,11 @@ export const QLine = fabric.util.createClass(fabric.Line, {
|
|||||||
children: [],
|
children: [],
|
||||||
padding: 5,
|
padding: 5,
|
||||||
textVisible: true,
|
textVisible: true,
|
||||||
|
textBaseline: 'alphabetic',
|
||||||
initialize: function (points, options, length = 0) {
|
initialize: function (points, options, length = 0) {
|
||||||
// 소수점 전부 제거
|
// 소수점 전부 제거
|
||||||
|
|
||||||
points = points.map((point) => Number(point?.toFixed(1)))
|
points = points.map((point) => Number(Number(point)?.toFixed(1)))
|
||||||
|
|
||||||
this.callSuper('initialize', points, { ...options, selectable: options.selectable ?? true })
|
this.callSuper('initialize', points, { ...options, selectable: options.selectable ?? true })
|
||||||
if (options.id) {
|
if (options.id) {
|
||||||
@ -31,14 +34,16 @@ export const QLine = fabric.util.createClass(fabric.Line, {
|
|||||||
this.direction = options.direction ?? getDirectionByPoint({ x: this.x1, y: this.y1 }, { x: this.x2, y: this.y2 })
|
this.direction = options.direction ?? getDirectionByPoint({ x: this.x1, y: this.y1 }, { x: this.x2, y: this.y2 })
|
||||||
this.textMode = options.textMode ?? 'plane' // plane:복시도, actual:실측, none:표시안함
|
this.textMode = options.textMode ?? 'plane' // plane:복시도, actual:실측, none:표시안함
|
||||||
this.textVisible = options.textVisible ?? true
|
this.textVisible = options.textVisible ?? true
|
||||||
if (length !== 0) {
|
|
||||||
this.length = length
|
|
||||||
} else {
|
|
||||||
this.setLength()
|
|
||||||
}
|
|
||||||
|
|
||||||
this.startPoint = { x: this.x1, y: this.y1 }
|
this.startPoint = { x: this.x1, y: this.y1 }
|
||||||
this.endPoint = { x: this.x2, y: this.y2 }
|
this.endPoint = { x: this.x2, y: this.y2 }
|
||||||
|
try {
|
||||||
|
this.setLength()
|
||||||
|
} catch (e) {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.setLength()
|
||||||
|
}, 100)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
init: function () {
|
init: function () {
|
||||||
@ -66,23 +71,14 @@ export const QLine = fabric.util.createClass(fabric.Line, {
|
|||||||
},
|
},
|
||||||
|
|
||||||
setLength() {
|
setLength() {
|
||||||
if (this.attributes?.actualSize !== undefined && this.attributes?.planeSize !== undefined) {
|
// Ensure all required properties are valid numbers
|
||||||
if (this.textMode === 'plane') {
|
const { x1, y1, x2, y2 } = this;
|
||||||
this.length = this.attributes.planeSize / 10
|
if (isNaN(x1) || isNaN(y1) || isNaN(x2) || isNaN(y2)) {
|
||||||
} else if (this.textMode === 'actual') {
|
logger.error('Invalid coordinates in QLine:', { x1, y1, x2, y2 });
|
||||||
this.length = this.attributes.actualSize / 10
|
this.length = 0;
|
||||||
}
|
return;
|
||||||
} else {
|
|
||||||
const scaleX = this.scaleX
|
|
||||||
const scaleY = this.scaleY
|
|
||||||
const x1 = this.left
|
|
||||||
const y1 = this.top
|
|
||||||
const x2 = this.left + this.width * scaleX
|
|
||||||
const y2 = this.top + this.height * scaleY
|
|
||||||
const dx = x2 - x1
|
|
||||||
const dy = y2 - y1
|
|
||||||
this.length = Number(Math.sqrt(dx * dx + dy * dy).toFixed(1))
|
|
||||||
}
|
}
|
||||||
|
this.length = calcLinePlaneSize({ x1, y1, x2, y2 }) / 10;
|
||||||
},
|
},
|
||||||
|
|
||||||
addLengthText() {
|
addLengthText() {
|
||||||
@ -182,4 +178,78 @@ export const QLine = fabric.util.createClass(fabric.Line, {
|
|||||||
}
|
}
|
||||||
return this
|
return this
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setCoords: function () {
|
||||||
|
// 부모 클래스의 setCoords 호출
|
||||||
|
this.callSuper('setCoords')
|
||||||
|
|
||||||
|
// QLine의 경우 추가 처리 - 항상 강제로 재계산
|
||||||
|
if (this.canvas) {
|
||||||
|
// 모든 좌표 관련 캐시 초기화
|
||||||
|
delete this.oCoords
|
||||||
|
delete this.aCoords
|
||||||
|
delete this.__corner
|
||||||
|
|
||||||
|
// 다시 부모 setCoords 호출
|
||||||
|
this.callSuper('setCoords')
|
||||||
|
|
||||||
|
// 한 번 더 강제로 bounding rect 재계산
|
||||||
|
this._clearCache && this._clearCache()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
containsPoint: function (point) {
|
||||||
|
// 먼저 좌표 업데이트
|
||||||
|
this.setCoords()
|
||||||
|
|
||||||
|
// 캔버스 줌과 viewport transform 고려한 좌표 변환
|
||||||
|
let localPoint = point
|
||||||
|
if (this.canvas) {
|
||||||
|
const vpt = this.canvas.viewportTransform
|
||||||
|
if (vpt) {
|
||||||
|
// viewport transform 역변환
|
||||||
|
const inverted = fabric.util.invertTransform(vpt)
|
||||||
|
localPoint = fabric.util.transformPoint(point, inverted)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 기본 boundingRect 사용하되 줌을 고려하여 선택 영역 조정
|
||||||
|
const boundingRect = this.getBoundingRect(true)
|
||||||
|
|
||||||
|
// 선의 방향 판단
|
||||||
|
const dx = Math.abs(this.x2 - this.x1)
|
||||||
|
const dy = Math.abs(this.y2 - this.y1)
|
||||||
|
const isVertical = dx < dy && dx < 10
|
||||||
|
const isDiagonal = dx > 10 && dy > 10
|
||||||
|
|
||||||
|
// 줌 레벨에 따른 선택 영역 조정
|
||||||
|
const zoom = this.canvas ? this.canvas.getZoom() : 1
|
||||||
|
const baseMultiplier = 1 // 줌이 클수록 선택 영역을 줄임
|
||||||
|
|
||||||
|
let reducedWidth, reducedHeight
|
||||||
|
|
||||||
|
if (isDiagonal) {
|
||||||
|
reducedWidth = Math.max(boundingRect.width / 2, 10 * baseMultiplier)
|
||||||
|
reducedHeight = Math.max(boundingRect.height / 2, 10 * baseMultiplier)
|
||||||
|
} else if (isVertical) {
|
||||||
|
reducedWidth = Math.max(boundingRect.width * 2, 20 * baseMultiplier)
|
||||||
|
reducedHeight = boundingRect.height
|
||||||
|
} else {
|
||||||
|
reducedWidth = boundingRect.width
|
||||||
|
reducedHeight = Math.max(boundingRect.height * 2, 20 * baseMultiplier)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 축소된 영역의 중심점 계산
|
||||||
|
const centerX = boundingRect.left + boundingRect.width / 2
|
||||||
|
const centerY = boundingRect.top + boundingRect.height / 2
|
||||||
|
|
||||||
|
// 축소된 영역의 경계 계산
|
||||||
|
const left = centerX - reducedWidth / 2
|
||||||
|
const top = centerY - reducedHeight / 2
|
||||||
|
const right = centerX + reducedWidth / 2
|
||||||
|
const bottom = centerY + reducedHeight / 2
|
||||||
|
|
||||||
|
// 점이 축소된 영역 내에 있는지 확인
|
||||||
|
return localPoint.x >= left && localPoint.x <= right && localPoint.y >= top && localPoint.y <= bottom
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@ -2,10 +2,11 @@ import { fabric } from 'fabric'
|
|||||||
import { v4 as uuidv4 } from 'uuid'
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
import { QLine } from '@/components/fabric/QLine'
|
import { QLine } from '@/components/fabric/QLine'
|
||||||
import { distanceBetweenPoints, findTopTwoIndexesByDistance, getDirectionByPoint, sortedPointLessEightPoint, sortedPoints } from '@/util/canvas-util'
|
import { distanceBetweenPoints, findTopTwoIndexesByDistance, getDirectionByPoint, sortedPointLessEightPoint, sortedPoints } from '@/util/canvas-util'
|
||||||
import { calculateAngle, drawRidgeRoof, drawShedRoof, toGeoJSON } from '@/util/qpolygon-utils'
|
import { calculateAngle, drawGableRoof, drawRidgeRoof, drawShedRoof, toGeoJSON } from '@/util/qpolygon-utils'
|
||||||
import * as turf from '@turf/turf'
|
import * as turf from '@turf/turf'
|
||||||
import { LINE_TYPE, POLYGON_TYPE } from '@/common/common'
|
import { LINE_TYPE, POLYGON_TYPE } from '@/common/common'
|
||||||
import Big from 'big.js'
|
import Big from 'big.js'
|
||||||
|
import { drawSkeletonRidgeRoof } from '@/util/skeleton-utils'
|
||||||
|
|
||||||
export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
||||||
type: 'QPolygon',
|
type: 'QPolygon',
|
||||||
@ -87,6 +88,10 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
|||||||
this.initLines()
|
this.initLines()
|
||||||
this.init()
|
this.init()
|
||||||
this.setShape()
|
this.setShape()
|
||||||
|
const originWidth = this.originWidth ?? this.width
|
||||||
|
const originHeight = this.originHeight ?? this.height
|
||||||
|
this.originWidth = this.angle === 90 || this.angle === 270 ? originHeight : originWidth
|
||||||
|
this.originHeight = this.angle === 90 || this.angle === 270 ? originWidth : originHeight
|
||||||
},
|
},
|
||||||
|
|
||||||
setShape() {
|
setShape() {
|
||||||
@ -126,11 +131,13 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
|||||||
this.on('moving', () => {
|
this.on('moving', () => {
|
||||||
this.initLines()
|
this.initLines()
|
||||||
this.addLengthText()
|
this.addLengthText()
|
||||||
|
this.setCoords()
|
||||||
})
|
})
|
||||||
|
|
||||||
this.on('modified', (e) => {
|
this.on('modified', (e) => {
|
||||||
this.initLines()
|
this.initLines()
|
||||||
this.addLengthText()
|
this.addLengthText()
|
||||||
|
this.setCoords()
|
||||||
})
|
})
|
||||||
|
|
||||||
this.on('selected', () => {
|
this.on('selected', () => {
|
||||||
@ -210,8 +217,26 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
|||||||
line.startPoint = point
|
line.startPoint = point
|
||||||
line.endPoint = nextPoint
|
line.endPoint = nextPoint
|
||||||
this.lines.push(line)
|
this.lines.push(line)
|
||||||
|
this.calculateDegree()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
calculateDegree() {
|
||||||
|
const degrees = []
|
||||||
|
// polygon.lines를 순회하며 각도를 구해 출력
|
||||||
|
this.lines.forEach((line, idx) => {
|
||||||
|
const dx = line.x2 - line.x1
|
||||||
|
const dy = line.y2 - line.y1
|
||||||
|
const rad = Math.atan2(dy, dx)
|
||||||
|
const degree = (rad * 180) / Math.PI
|
||||||
|
degrees.push(degree)
|
||||||
|
})
|
||||||
|
|
||||||
|
function isMultipleOf45(degree, epsilon = 1) {
|
||||||
|
return Math.abs(degree % 45) <= epsilon || Math.abs((degree % 45) - 45) <= epsilon
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isMultipleOf45 = degrees.every((degree) => isMultipleOf45(degree))
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 보조선 그리기
|
* 보조선 그리기
|
||||||
@ -219,9 +244,20 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
|||||||
*/
|
*/
|
||||||
drawHelpLine(settingModalFirstOptions) {
|
drawHelpLine(settingModalFirstOptions) {
|
||||||
/* innerLines 초기화 */
|
/* innerLines 초기화 */
|
||||||
this.innerLines.forEach((line) => {
|
this.canvas
|
||||||
this.canvas.remove(line)
|
.getObjects()
|
||||||
})
|
.filter(
|
||||||
|
(obj) =>
|
||||||
|
obj.parentId === this.id &&
|
||||||
|
obj.name !== POLYGON_TYPE.WALL &&
|
||||||
|
obj.name !== POLYGON_TYPE.ROOF &&
|
||||||
|
obj.name !== 'lengthText' &&
|
||||||
|
obj.name !== 'outerLine' &&
|
||||||
|
obj.name !== 'baseLine',
|
||||||
|
// && obj.name !== 'outerLinePoint',
|
||||||
|
)
|
||||||
|
.forEach((obj) => this.canvas.remove(obj))
|
||||||
|
this.innerLines = []
|
||||||
this.canvas.renderAll()
|
this.canvas.renderAll()
|
||||||
|
|
||||||
let textMode = 'plane'
|
let textMode = 'plane'
|
||||||
@ -241,50 +277,76 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
const types = []
|
const types = this.lines.map((line) => line.attributes.type)
|
||||||
this.lines.forEach((line) => types.push(line.attributes.type))
|
|
||||||
|
|
||||||
const gableType = [LINE_TYPE.WALLLINE.GABLE, LINE_TYPE.WALLLINE.JERKINHEAD]
|
const isGableRoof = function (types) {
|
||||||
|
if (!types.includes(LINE_TYPE.WALLLINE.GABLE)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
const gableTypes = [LINE_TYPE.WALLLINE.GABLE, LINE_TYPE.WALLLINE.JERKINHEAD]
|
||||||
|
const oddTypes = types.filter((type, i) => i % 2 === 0)
|
||||||
|
const evenTypes = types.filter((type, i) => i % 2 === 1)
|
||||||
|
|
||||||
const hasShed = types.includes(LINE_TYPE.WALLLINE.SHED)
|
const oddAllEaves = oddTypes.every((type) => type === LINE_TYPE.WALLLINE.EAVES)
|
||||||
|
const evenAllGable = evenTypes.every((type) => gableTypes.includes(type))
|
||||||
|
const evenAllEaves = evenTypes.every((type) => type === LINE_TYPE.WALLLINE.EAVES)
|
||||||
|
const oddAllGable = oddTypes.every((type) => gableTypes.includes(type))
|
||||||
|
|
||||||
if (hasShed) {
|
return (oddAllEaves && evenAllGable) || (evenAllEaves && oddAllGable)
|
||||||
const sheds = this.lines.filter((line) => line.attributes !== undefined && line.attributes.type === LINE_TYPE.WALLLINE.SHED)
|
}
|
||||||
const areLinesParallel = function (line1, line2) {
|
|
||||||
const angle1 = calculateAngle(line1.startPoint, line1.endPoint)
|
const isShedRoof = function (types, lines) {
|
||||||
const angle2 = calculateAngle(line2.startPoint, line2.endPoint)
|
const gableTypes = [LINE_TYPE.WALLLINE.GABLE, LINE_TYPE.WALLLINE.JERKINHEAD]
|
||||||
return angle1 === angle2
|
if (!types.includes(LINE_TYPE.WALLLINE.SHED)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
const shedLines = lines.filter((line) => line.attributes?.type === LINE_TYPE.WALLLINE.SHED)
|
||||||
|
const areShedLinesParallel = function (shedLines) {
|
||||||
|
return shedLines.every((shed, i) => {
|
||||||
|
const nextShed = shedLines[(i + 1) % shedLines.length]
|
||||||
|
const angle1 = calculateAngle(shed.startPoint, shed.endPoint)
|
||||||
|
const angle2 = calculateAngle(nextShed.startPoint, nextShed.endPoint)
|
||||||
|
return angle1 === angle2
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (!areShedLinesParallel(shedLines)) {
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
let isShedRoof = true
|
const getParallelEavesLines = function (shedLines, lines) {
|
||||||
sheds.forEach((shed, i) => {
|
const eavesLines = lines.filter((line) => line.attributes?.type === LINE_TYPE.WALLLINE.EAVES)
|
||||||
isShedRoof = areLinesParallel(shed, sheds[(i + 1) % sheds.length])
|
|
||||||
})
|
const referenceAngle = calculateAngle(shedLines[0].startPoint, shedLines[0].endPoint)
|
||||||
if (isShedRoof) {
|
|
||||||
const eaves = this.lines
|
return eavesLines.filter((line) => {
|
||||||
.filter((line) => line.attributes !== undefined && line.attributes.type === LINE_TYPE.WALLLINE.EAVES)
|
const eavesAngle = calculateAngle(line.startPoint, line.endPoint)
|
||||||
.filter((line) => {
|
return Math.abs(referenceAngle - eavesAngle) === 180
|
||||||
const angle1 = calculateAngle(sheds[0].startPoint, sheds[0].endPoint)
|
})
|
||||||
const angle2 = calculateAngle(line.startPoint, line.endPoint)
|
|
||||||
if (Math.abs(angle1 - angle2) === 180) {
|
|
||||||
return line
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if (eaves.length > 0) {
|
|
||||||
const gables = this.lines.filter((line) => sheds.includes(line) === false && eaves.includes(line) === false)
|
|
||||||
const isGable = gables.every((line) => gableType.includes(line.attributes.type))
|
|
||||||
if (isGable) {
|
|
||||||
drawShedRoof(this.id, this.canvas, textMode)
|
|
||||||
} else {
|
|
||||||
drawRidgeRoof(this.id, this.canvas, textMode)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
drawRidgeRoof(this.id, this.canvas, textMode)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
drawRidgeRoof(this.id, this.canvas, textMode)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const parallelEaves = getParallelEavesLines(shedLines, lines)
|
||||||
|
if (parallelEaves.length === 0) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const remainingLines = lines.filter((line) => !shedLines.includes(line) && !parallelEaves.includes(line))
|
||||||
|
return remainingLines.every((line) => gableTypes.includes(line.attributes.type))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (types.every((type) => type === LINE_TYPE.WALLLINE.EAVES)) {
|
||||||
|
// 용마루 -- straight-skeleton
|
||||||
|
console.log('용마루 지붕')
|
||||||
|
drawRidgeRoof(this.id, this.canvas, textMode)
|
||||||
|
//drawSkeletonRidgeRoof(this.id, this.canvas, textMode);
|
||||||
|
} else if (isGableRoof(types)) {
|
||||||
|
// A형, B형 박공 지붕
|
||||||
|
console.log('패턴 지붕')
|
||||||
|
drawGableRoof(this.id, this.canvas, textMode)
|
||||||
|
} else if (isShedRoof(types, this.lines)) {
|
||||||
|
console.log('한쪽흐름 지붕')
|
||||||
|
drawShedRoof(this.id, this.canvas, textMode)
|
||||||
} else {
|
} else {
|
||||||
|
console.log('변별로 설정')
|
||||||
drawRidgeRoof(this.id, this.canvas, textMode)
|
drawRidgeRoof(this.id, this.canvas, textMode)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -314,9 +376,27 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
|||||||
const dy = Big(end.y).minus(Big(start.y))
|
const dy = Big(end.y).minus(Big(start.y))
|
||||||
const length = dx.pow(2).plus(dy.pow(2)).sqrt().times(10).round().toNumber()
|
const length = dx.pow(2).plus(dy.pow(2)).sqrt().times(10).round().toNumber()
|
||||||
|
|
||||||
|
const direction = getDirectionByPoint(start, end)
|
||||||
|
|
||||||
|
let left, top
|
||||||
|
|
||||||
|
if (direction === 'bottom') {
|
||||||
|
left = (start.x + end.x) / 2 - 50
|
||||||
|
top = (start.y + end.y) / 2
|
||||||
|
} else if (direction === 'top') {
|
||||||
|
left = (start.x + end.x) / 2 + 30
|
||||||
|
top = (start.y + end.y) / 2
|
||||||
|
} else if (direction === 'left') {
|
||||||
|
left = (start.x + end.x) / 2
|
||||||
|
top = (start.y + end.y) / 2 - 30
|
||||||
|
} else if (direction === 'right') {
|
||||||
|
left = (start.x + end.x) / 2
|
||||||
|
top = (start.y + end.y) / 2 + 30
|
||||||
|
}
|
||||||
|
|
||||||
let midPoint
|
let midPoint
|
||||||
|
|
||||||
midPoint = new fabric.Point((start.x + end.x) / 2, (start.y + end.y) / 2)
|
midPoint = new fabric.Point(left, top)
|
||||||
|
|
||||||
const degree = Big(Math.atan2(dy.toNumber(), dx.toNumber())).times(180).div(Math.PI).toNumber()
|
const degree = Big(Math.atan2(dy.toNumber(), dx.toNumber())).times(180).div(Math.PI).toNumber()
|
||||||
|
|
||||||
@ -369,7 +449,15 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
|||||||
this.canvas = canvas
|
this.canvas = canvas
|
||||||
},
|
},
|
||||||
fillCellABType(
|
fillCellABType(
|
||||||
cell = { width: 50, height: 100, padding: 5, wallDirection: 'left', referenceDirection: 'none', startIndex: -1, isCellCenter: false },
|
cell = {
|
||||||
|
width: 50,
|
||||||
|
height: 100,
|
||||||
|
padding: 5,
|
||||||
|
wallDirection: 'left',
|
||||||
|
referenceDirection: 'none',
|
||||||
|
startIndex: -1,
|
||||||
|
isCellCenter: false,
|
||||||
|
},
|
||||||
) {
|
) {
|
||||||
const points = this.points
|
const points = this.points
|
||||||
|
|
||||||
@ -671,13 +759,119 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
|||||||
|
|
||||||
return intersects % 2 === 1
|
return intersects % 2 === 1
|
||||||
},
|
},
|
||||||
|
|
||||||
|
inPolygonImproved(point) {
|
||||||
|
const vertices = this.getCurrentPoints()
|
||||||
|
let inside = false
|
||||||
|
const testX = Number(point.x.toFixed(this.toFixed))
|
||||||
|
const testY = Number(point.y.toFixed(this.toFixed))
|
||||||
|
|
||||||
|
for (let i = 0, j = vertices.length - 1; i < vertices.length; j = i++) {
|
||||||
|
const xi = Number(vertices[i].x.toFixed(this.toFixed))
|
||||||
|
const yi = Number(vertices[i].y.toFixed(this.toFixed))
|
||||||
|
const xj = Number(vertices[j].x.toFixed(this.toFixed))
|
||||||
|
const yj = Number(vertices[j].y.toFixed(this.toFixed))
|
||||||
|
|
||||||
|
// 점이 정점 위에 있는지 확인
|
||||||
|
if (Math.abs(xi - testX) <= 0.01 && Math.abs(yi - testY) <= 0.01) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 점이 선분 위에 있는지 확인
|
||||||
|
if (this.isPointOnSegment(point, { x: xi, y: yi }, { x: xj, y: yj })) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ray casting 알고리즘 - 부동소수점 정밀도 개선
|
||||||
|
if (yi > testY !== yj > testY) {
|
||||||
|
const denominator = yj - yi
|
||||||
|
if (Math.abs(denominator) > 1e-10) {
|
||||||
|
// 0으로 나누기 방지
|
||||||
|
const intersection = ((xj - xi) * (testY - yi)) / denominator + xi
|
||||||
|
if (testX < intersection) {
|
||||||
|
inside = !inside
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return inside
|
||||||
|
},
|
||||||
|
|
||||||
|
isPointOnSegment(point, segStart, segEnd) {
|
||||||
|
const tolerance = 0.1
|
||||||
|
const dxSegment = segEnd.x - segStart.x
|
||||||
|
const dySegment = segEnd.y - segStart.y
|
||||||
|
const dxPoint = point.x - segStart.x
|
||||||
|
const dyPoint = point.y - segStart.y
|
||||||
|
|
||||||
|
// 벡터의 외적을 계산하여 점이 선분 위에 있는지 확인
|
||||||
|
const crossProduct = Math.abs(dxPoint * dySegment - dyPoint * dxSegment)
|
||||||
|
|
||||||
|
if (crossProduct > tolerance) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 점이 선분의 범위 내에 있는지 확인
|
||||||
|
const dotProduct = dxPoint * dxSegment + dyPoint * dySegment
|
||||||
|
const squaredLength = dxSegment * dxSegment + dySegment * dySegment
|
||||||
|
|
||||||
|
return dotProduct >= 0 && dotProduct <= squaredLength
|
||||||
|
},
|
||||||
|
setCoords: function () {
|
||||||
|
// 부모 클래스의 setCoords 호출
|
||||||
|
this.callSuper('setCoords')
|
||||||
|
|
||||||
|
// QPolygon의 경우 추가 처리 - 항상 강제로 재계산
|
||||||
|
if (this.canvas) {
|
||||||
|
// 모든 좌표 관련 캐시 초기화
|
||||||
|
delete this.oCoords
|
||||||
|
delete this.aCoords
|
||||||
|
delete this.__corner
|
||||||
|
|
||||||
|
// 다시 부모 setCoords 호출
|
||||||
|
this.callSuper('setCoords')
|
||||||
|
|
||||||
|
// 한 번 더 강제로 bounding rect 재계산
|
||||||
|
this._clearCache && this._clearCache()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
containsPoint: function (point) {
|
containsPoint: function (point) {
|
||||||
|
// 먼저 좌표 업데이트
|
||||||
|
this.setCoords()
|
||||||
|
|
||||||
|
// 캔버스 줌과 viewport transform 고려한 좌표 변환
|
||||||
|
let localPoint = point
|
||||||
|
if (this.canvas) {
|
||||||
|
const vpt = this.canvas.viewportTransform
|
||||||
|
if (vpt) {
|
||||||
|
// viewport transform 역변환
|
||||||
|
const inverted = fabric.util.invertTransform(vpt)
|
||||||
|
localPoint = fabric.util.transformPoint(point, inverted)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 오브젝트의 transform matrix를 고려한 좌표 변환
|
||||||
|
const matrix = this.calcTransformMatrix()
|
||||||
|
const invertedMatrix = fabric.util.invertTransform(matrix)
|
||||||
|
const transformedPoint = fabric.util.transformPoint(localPoint, invertedMatrix)
|
||||||
|
|
||||||
|
// pathOffset을 고려한 최종 좌표 계산
|
||||||
|
const pathOffset = this.get('pathOffset')
|
||||||
|
const finalPoint = {
|
||||||
|
x: Number((transformedPoint.x + pathOffset.x).toFixed(this.toFixed)),
|
||||||
|
y: Number((transformedPoint.y + pathOffset.y).toFixed(this.toFixed)),
|
||||||
|
}
|
||||||
|
|
||||||
if (this.name === POLYGON_TYPE.ROOF && this.isFixed) {
|
if (this.name === POLYGON_TYPE.ROOF && this.isFixed) {
|
||||||
const isInside = this.inPolygon(point)
|
const isInside = this.inPolygonImproved(finalPoint)
|
||||||
this.set('selectable', isInside)
|
if (!this.selectable) {
|
||||||
|
this.set('selectable', isInside)
|
||||||
|
}
|
||||||
return isInside
|
return isInside
|
||||||
} else {
|
} else {
|
||||||
return this.callSuper('containsPoint', point)
|
return this.inPolygonImproved(finalPoint)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { useContext, useEffect, useRef } from 'react'
|
import { useContext, useEffect, useRef } from 'react'
|
||||||
|
|
||||||
import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil'
|
import { useRecoilValue, useResetRecoilState } from 'recoil'
|
||||||
|
|
||||||
import QContextMenu from '@/components/common/context-menu/QContextMenu'
|
import QContextMenu from '@/components/common/context-menu/QContextMenu'
|
||||||
import PanelBatchStatistics from '@/components/floor-plan/modal/panelBatch/PanelBatchStatistics'
|
import PanelBatchStatistics from '@/components/floor-plan/modal/panelBatch/PanelBatchStatistics'
|
||||||
@ -12,8 +12,8 @@ import { usePlan } from '@/hooks/usePlan'
|
|||||||
import { useContextMenu } from '@/hooks/useContextMenu'
|
import { useContextMenu } from '@/hooks/useContextMenu'
|
||||||
import { useCanvasConfigInitialize } from '@/hooks/common/useCanvasConfigInitialize'
|
import { useCanvasConfigInitialize } from '@/hooks/common/useCanvasConfigInitialize'
|
||||||
import { currentMenuState } from '@/store/canvasAtom'
|
import { currentMenuState } from '@/store/canvasAtom'
|
||||||
import { roofMaterialsAtom, totalDisplaySelector } from '@/store/settingAtom'
|
import { totalDisplaySelector } from '@/store/settingAtom'
|
||||||
import { MENU, POLYGON_TYPE } from '@/common/common'
|
import { POLYGON_TYPE } from '@/common/common'
|
||||||
import { FloorPlanContext } from '@/app/floor-plan/FloorPlanProvider'
|
import { FloorPlanContext } from '@/app/floor-plan/FloorPlanProvider'
|
||||||
import { QcastContext } from '@/app/QcastProvider'
|
import { QcastContext } from '@/app/QcastProvider'
|
||||||
import {
|
import {
|
||||||
@ -30,8 +30,6 @@ 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 { ROOF_MATERIAL_LAYOUT } from '@/components/floor-plan/modal/placementShape/PlacementShapeSetting'
|
|
||||||
import { useMasterController } from '@/hooks/common/useMasterController'
|
|
||||||
import { hotkeyStore } from '@/store/hotkeyAtom'
|
import { hotkeyStore } from '@/store/hotkeyAtom'
|
||||||
import { usePopup } from '@/hooks/usePopup'
|
import { usePopup } from '@/hooks/usePopup'
|
||||||
|
|
||||||
@ -70,10 +68,29 @@ export default function CanvasFrame() {
|
|||||||
canvas?.renderAll() // 캔버스를 다시 그립니다.
|
canvas?.renderAll() // 캔버스를 다시 그립니다.
|
||||||
|
|
||||||
if (canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE).length > 0) {
|
if (canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE).length > 0) {
|
||||||
setSelectedMenu('module')
|
setTimeout(() => {
|
||||||
|
setSelectedMenu('module')
|
||||||
|
}, 500)
|
||||||
|
} else if (canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.WALL).length > 0) {
|
||||||
|
setSelectedMenu('outline')
|
||||||
} else {
|
} else {
|
||||||
setSelectedMenu('surface')
|
setTimeout(() => {
|
||||||
|
setSelectedMenu('surface')
|
||||||
|
}, 500)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const roofs = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
|
||||||
|
|
||||||
|
roofs.forEach((roof) => {
|
||||||
|
const auxiliaryLines = canvas
|
||||||
|
.getObjects()
|
||||||
|
.filter((obj) => obj.name === 'auxiliaryLine' && roof.inPolygonImproved(obj.startPoint) && roof.inPolygonImproved(obj.endPoint))
|
||||||
|
|
||||||
|
auxiliaryLines.forEach((auxiliaryLine) => {
|
||||||
|
roof.innerLines.push(auxiliaryLine)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
initEvent()
|
initEvent()
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
@ -124,12 +141,12 @@ export default function CanvasFrame() {
|
|||||||
/**
|
/**
|
||||||
* 캔버스가 있을 경우 핫키 이벤트 처리
|
* 캔버스가 있을 경우 핫키 이벤트 처리
|
||||||
* hotkeyStore에 핫키 이벤트 리스너 추가
|
* hotkeyStore에 핫키 이벤트 리스너 추가
|
||||||
*
|
*
|
||||||
* const hotkeys = [
|
* const hotkeys = [
|
||||||
{ key: 'c', fn: () => asdf() },
|
{ key: 'c', fn: () => asdf() },
|
||||||
{ key: 'v', fn: () => qwer() },
|
{ key: 'v', fn: () => qwer() },
|
||||||
]
|
]
|
||||||
setHotkeyStore(hotkeys)
|
setHotkeyStore(hotkeys)
|
||||||
*/
|
*/
|
||||||
const hotkeyState = useRecoilValue(hotkeyStore)
|
const hotkeyState = useRecoilValue(hotkeyStore)
|
||||||
const hotkeyHandlerRef = useRef(null)
|
const hotkeyHandlerRef = useRef(null)
|
||||||
|
|||||||
@ -118,7 +118,7 @@ export default function CanvasMenu(props) {
|
|||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
objectNo: objectNo,
|
objectNo: objectNo,
|
||||||
planNo: selectedPlan.planNo,
|
planNo: selectedPlan?.planNo ? selectedPlan.planNo : pid,
|
||||||
schDownload: donwloadType,
|
schDownload: donwloadType,
|
||||||
schDrawingFlg: drawingFlg,
|
schDrawingFlg: drawingFlg,
|
||||||
pwrGnrSimType: pwrGnrSimTypeRecoil.type,
|
pwrGnrSimType: pwrGnrSimTypeRecoil.type,
|
||||||
@ -196,6 +196,14 @@ export default function CanvasMenu(props) {
|
|||||||
text: getMessage('module.delete.confirm'),
|
text: getMessage('module.delete.confirm'),
|
||||||
type: 'confirm',
|
type: 'confirm',
|
||||||
confirmFn: () => {
|
confirmFn: () => {
|
||||||
|
const roofs = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
|
||||||
|
roofs.forEach((roof) => {
|
||||||
|
roof.set({
|
||||||
|
stroke: 'black',
|
||||||
|
strokeWidth: 3,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
//해당 메뉴 이동시 배치면 삭제
|
//해당 메뉴 이동시 배치면 삭제
|
||||||
|
|
||||||
setAllModuleSurfaceIsComplete(false)
|
setAllModuleSurfaceIsComplete(false)
|
||||||
@ -238,10 +246,13 @@ export default function CanvasMenu(props) {
|
|||||||
await reloadCanvasStatus(objectNo, currentCanvasPlan?.planNo ?? pid)
|
await reloadCanvasStatus(objectNo, currentCanvasPlan?.planNo ?? pid)
|
||||||
break
|
break
|
||||||
case 'estimate':
|
case 'estimate':
|
||||||
if (!isAllComplete()) {
|
if (selectedMenu !== 'simulation') {
|
||||||
swalFire({ text: getMessage('estimate.menu.move.valid1') })
|
if (!isAllComplete()) {
|
||||||
return
|
swalFire({ text: getMessage('estimate.menu.move.valid1') })
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setIsGlobalLoading(true)
|
setIsGlobalLoading(true)
|
||||||
promiseGet({ url: `/api/estimate/${objectNo}/${selectedPlan?.planNo ?? pid}/detail` }).then((res) => {
|
promiseGet({ url: `/api/estimate/${objectNo}/${selectedPlan?.planNo ?? pid}/detail` }).then((res) => {
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
@ -312,7 +323,6 @@ export default function CanvasMenu(props) {
|
|||||||
const settingsModalOptions = useRecoilState(settingModalFirstOptionsState)
|
const settingsModalOptions = useRecoilState(settingModalFirstOptionsState)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log(selectedMenu)
|
|
||||||
if (selectedMenu === 'placement') {
|
if (selectedMenu === 'placement') {
|
||||||
onClickPlacementInitialMenu()
|
onClickPlacementInitialMenu()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,8 +8,12 @@ import { OUTER_LINE_TYPE } from '@/store/outerLineAtom'
|
|||||||
import OuterLineWall from '@/components/floor-plan/modal/lineTypes/OuterLineWall'
|
import OuterLineWall from '@/components/floor-plan/modal/lineTypes/OuterLineWall'
|
||||||
import { useAuxiliaryDrawing } from '@/hooks/roofcover/useAuxiliaryDrawing'
|
import { useAuxiliaryDrawing } from '@/hooks/roofcover/useAuxiliaryDrawing'
|
||||||
import { usePopup } from '@/hooks/usePopup'
|
import { usePopup } from '@/hooks/usePopup'
|
||||||
|
import { useEffect } from 'react'
|
||||||
|
import { useRecoilValue } from 'recoil'
|
||||||
|
import { canvasState } from '@/store/canvasAtom'
|
||||||
|
|
||||||
export default function AuxiliaryDrawing({ id, pos = { x: 50, y: 230 } }) {
|
export default function AuxiliaryDrawing({ id, pos = { x: 50, y: 230 } }) {
|
||||||
|
const canvas = useRecoilValue(canvasState)
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
const { closePopup } = usePopup()
|
const { closePopup } = usePopup()
|
||||||
|
|
||||||
@ -52,6 +56,15 @@ export default function AuxiliaryDrawing({ id, pos = { x: 50, y: 230 } }) {
|
|||||||
cutAuxiliary,
|
cutAuxiliary,
|
||||||
} = useAuxiliaryDrawing(id)
|
} = useAuxiliaryDrawing(id)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
const auxiliaryLines = canvas.getObjects().filter((line) => line.name === 'auxiliaryLine' && !line.isAuxiliaryFixed) // 보조선이 그려져 있는 경우 물어본다.
|
||||||
|
if (auxiliaryLines.length > 0) {
|
||||||
|
handleFix()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
const outerLineProps = {
|
const outerLineProps = {
|
||||||
length1,
|
length1,
|
||||||
setLength1,
|
setLength1,
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import { useState } from 'react'
|
|||||||
import { currentObjectState } from '@/store/canvasAtom'
|
import { currentObjectState } from '@/store/canvasAtom'
|
||||||
import { useAuxiliaryDrawing } from '@/hooks/roofcover/useAuxiliaryDrawing'
|
import { useAuxiliaryDrawing } from '@/hooks/roofcover/useAuxiliaryDrawing'
|
||||||
import { useSwal } from '@/hooks/useSwal'
|
import { useSwal } from '@/hooks/useSwal'
|
||||||
|
import { normalizeDigits } from '@/util/input-utils'
|
||||||
|
|
||||||
export default function AuxiliaryEdit(props) {
|
export default function AuxiliaryEdit(props) {
|
||||||
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
||||||
@ -40,15 +41,15 @@ export default function AuxiliaryEdit(props) {
|
|||||||
if (currentObject) {
|
if (currentObject) {
|
||||||
copy(
|
copy(
|
||||||
currentObject,
|
currentObject,
|
||||||
arrow2 ? (arrow2 === '←' ? Number(+horizonSize / 10) * -1 : Number(+horizonSize / 10)) : 0,
|
arrow2 ? (arrow2 === '←' ? (Number(normalizeDigits(horizonSize)) / 10) * -1 : Number(normalizeDigits(horizonSize)) / 10) : 0,
|
||||||
arrow1 ? (arrow1 === '↑' ? Number(+verticalSize / 10) * -1 : Number(+verticalSize / 10)) : 0,
|
arrow1 ? (arrow1 === '↑' ? (Number(normalizeDigits(verticalSize)) / 10) * -1 : Number(normalizeDigits(verticalSize)) / 10) : 0,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
move(
|
move(
|
||||||
currentObject,
|
currentObject,
|
||||||
arrow2 ? (arrow2 === '←' ? Number(+horizonSize / 10) * -1 : Number(+horizonSize / 10)) : 0,
|
arrow2 ? (arrow2 === '←' ? (Number(normalizeDigits(horizonSize)) / 10) * -1 : Number(normalizeDigits(horizonSize)) / 10) : 0,
|
||||||
arrow1 ? (arrow1 === '↑' ? Number(+verticalSize / 10) * -1 : Number(+verticalSize / 10)) : 0,
|
arrow1 ? (arrow1 === '↑' ? (Number(normalizeDigits(verticalSize)) / 10) * -1 : Number(normalizeDigits(verticalSize)) / 10) : 0,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,7 +66,7 @@ export default function AuxiliaryEdit(props) {
|
|||||||
<p className="mb5">{getMessage('length')}</p>
|
<p className="mb5">{getMessage('length')}</p>
|
||||||
<div className="input-move-wrap mb5">
|
<div className="input-move-wrap mb5">
|
||||||
<div className="input-move">
|
<div className="input-move">
|
||||||
<input type="text" className="input-origin" value={verticalSize} onChange={(e) => setVerticalSize(e.target.value)} />
|
<input type="text" className="input-origin" value={verticalSize} onChange={(e) => setVerticalSize(normalizeDigits(e.target.value))} />
|
||||||
</div>
|
</div>
|
||||||
<span>mm</span>
|
<span>mm</span>
|
||||||
<div className="direction-move-wrap">
|
<div className="direction-move-wrap">
|
||||||
@ -87,7 +88,7 @@ export default function AuxiliaryEdit(props) {
|
|||||||
</div>
|
</div>
|
||||||
<div className="input-move-wrap">
|
<div className="input-move-wrap">
|
||||||
<div className="input-move">
|
<div className="input-move">
|
||||||
<input type="text" className="input-origin" value={horizonSize} onChange={(e) => setHorizonSize(e.target.value)} />
|
<input type="text" className="input-origin" value={horizonSize} onChange={(e) => setHorizonSize(normalizeDigits(e.target.value))} />
|
||||||
</div>
|
</div>
|
||||||
<span>mm</span>
|
<span>mm</span>
|
||||||
<div className="direction-move-wrap">
|
<div className="direction-move-wrap">
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import { canvasState, currentObjectState } from '@/store/canvasAtom'
|
|||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import Big from 'big.js'
|
import Big from 'big.js'
|
||||||
import { calcLineActualSize, calcLinePlaneSize } from '@/util/qpolygon-utils'
|
import { calcLineActualSize, calcLinePlaneSize } from '@/util/qpolygon-utils'
|
||||||
|
import { normalizeDigits } from '@/util/input-utils'
|
||||||
|
|
||||||
export default function AuxiliarySize(props) {
|
export default function AuxiliarySize(props) {
|
||||||
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
||||||
@ -42,7 +43,8 @@ export default function AuxiliarySize(props) {
|
|||||||
if (checkedRadio === 2) setValue2(value)
|
if (checkedRadio === 2) setValue2(value)
|
||||||
setSize(0)
|
setSize(0)
|
||||||
} else {
|
} else {
|
||||||
value = Big(value.replace(/[^0-9]/g, ''))
|
//value = Big(value.replace(/[^0-9]/g, ''))
|
||||||
|
value = Big(normalizeDigits(value))
|
||||||
if (checkedRadio === 1) setValue1(value.toNumber())
|
if (checkedRadio === 1) setValue1(value.toNumber())
|
||||||
if (checkedRadio === 2) setValue2(value.toNumber())
|
if (checkedRadio === 2) setValue2(value.toNumber())
|
||||||
setSize(value.div(10).toNumber())
|
setSize(value.div(10).toNumber())
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import { useDebounceValue } from 'usehooks-ts'
|
|||||||
import { moduleSelectionDataState } from '@/store/selectedModuleOptions'
|
import { moduleSelectionDataState } from '@/store/selectedModuleOptions'
|
||||||
import { useCanvasPopupStatusController } from '@/hooks/common/useCanvasPopupStatusController'
|
import { useCanvasPopupStatusController } from '@/hooks/common/useCanvasPopupStatusController'
|
||||||
import { isObjectNotEmpty } from '@/util/common-utils'
|
import { isObjectNotEmpty } from '@/util/common-utils'
|
||||||
|
import { normalizeDecimal} from '@/util/input-utils'
|
||||||
|
|
||||||
export default function Module({ setTabNum }) {
|
export default function Module({ setTabNum }) {
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
@ -188,7 +189,7 @@ export default function Module({ setTabNum }) {
|
|||||||
type="text"
|
type="text"
|
||||||
className="input-origin block"
|
className="input-origin block"
|
||||||
value={inputInstallHeight}
|
value={inputInstallHeight}
|
||||||
onChange={(e) => setInputInstallHeight(e.target.value)}
|
onChange={(e) => setInputInstallHeight(normalizeDecimal(e.target.value))}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<span className="thin">m</span>
|
<span className="thin">m</span>
|
||||||
@ -225,7 +226,7 @@ export default function Module({ setTabNum }) {
|
|||||||
type="text"
|
type="text"
|
||||||
className="input-origin block"
|
className="input-origin block"
|
||||||
value={inputVerticalSnowCover}
|
value={inputVerticalSnowCover}
|
||||||
onChange={(e) => setInputVerticalSnowCover(e.target.value)}
|
onChange={(e) => setInputVerticalSnowCover(normalizeDecimal(e.target.value))}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<span className="thin">cm</span>
|
<span className="thin">cm</span>
|
||||||
|
|||||||
@ -7,15 +7,21 @@ import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
|
|||||||
import QSelectBox from '@/components/common/select/QSelectBox'
|
import QSelectBox from '@/components/common/select/QSelectBox'
|
||||||
import { roofsState } from '@/store/roofAtom'
|
import { roofsState } from '@/store/roofAtom'
|
||||||
import { useModuleBasicSetting } from '@/hooks/module/useModuleBasicSetting'
|
import { useModuleBasicSetting } from '@/hooks/module/useModuleBasicSetting'
|
||||||
|
import { useCommonCode } from '@/hooks/common/useCommonCode'
|
||||||
import Swal from 'sweetalert2'
|
import Swal from 'sweetalert2'
|
||||||
|
import { normalizeDecimal} from '@/util/input-utils'
|
||||||
|
|
||||||
export const Orientation = forwardRef((props, ref) => {
|
export const Orientation = forwardRef((props, ref) => {
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
|
const { findCommonCode } = useCommonCode()
|
||||||
const [hasAnglePassivity, setHasAnglePassivity] = useState(false)
|
const [hasAnglePassivity, setHasAnglePassivity] = useState(false)
|
||||||
const basicSetting = useRecoilValue(basicSettingState)
|
const basicSetting = useRecoilValue(basicSettingState)
|
||||||
const [addedRoofs, setAddedRoofs] = useRecoilState(addedRoofsState) //지붕재 선택
|
const [addedRoofs, setAddedRoofs] = useRecoilState(addedRoofsState) //지붕재 선택
|
||||||
const [roofsStore, setRoofsStore] = useRecoilState(roofsState)
|
const [roofsStore, setRoofsStore] = useRecoilState(roofsState)
|
||||||
const [roofTab, setRoofTab] = useState(0) //지붕재 탭
|
const [roofTab, setRoofTab] = useState(0) //지붕재 탭
|
||||||
|
const [selectedModuleSeries, setSelectedModuleSeries] = useState(null)
|
||||||
|
const [moduleSeriesList, setModuleSeriesList] = useState([])
|
||||||
|
const [filteredModuleList, setFilteredModuleList] = useState([])
|
||||||
const {
|
const {
|
||||||
roofs,
|
roofs,
|
||||||
setRoofs,
|
setRoofs,
|
||||||
@ -65,8 +71,13 @@ export const Orientation = forwardRef((props, ref) => {
|
|||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const allOption = {
|
||||||
|
moduleSerCd: 'ALL',
|
||||||
|
moduleSerNm: getMessage("board.sub.total") || 'ALL'
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (basicSetting.roofSizeSet == '3') {
|
if (basicSetting.roofSizeSet === '3') {
|
||||||
restoreModuleInstArea()
|
restoreModuleInstArea()
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
@ -79,9 +90,22 @@ export const Orientation = forwardRef((props, ref) => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (selectedModules) {
|
if (selectedModules) {
|
||||||
setSelectedModules(moduleList.find((module) => module.itemId === selectedModules.itemId))
|
const foundModule = moduleList.find((module) => module.itemId === selectedModules.itemId)
|
||||||
|
if (foundModule) {
|
||||||
|
setSelectedModules(foundModule)
|
||||||
|
|
||||||
|
// 선택된 모듈의 시리즈로 업데이트 (시리즈 목록이 있는 경우에만)
|
||||||
|
if (moduleSeriesList.length > 0 && foundModule.moduleSerCd) {
|
||||||
|
const currentSeries = moduleSeriesList.find(series => series.moduleSerCd === foundModule.moduleSerCd)
|
||||||
|
if (currentSeries && (!selectedModuleSeries || selectedModuleSeries.moduleSerCd !== currentSeries.moduleSerCd)) {
|
||||||
|
//setSelectedModuleSeries(currentSeries)
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
setSelectedModuleSeries(allOption)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, [selectedModules])
|
}, [selectedModules, moduleList, moduleSeriesList])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (selectedSurfaceType) {
|
if (selectedSurfaceType) {
|
||||||
@ -163,7 +187,7 @@ export const Orientation = forwardRef((props, ref) => {
|
|||||||
title: getMessage('module.not.found'),
|
title: getMessage('module.not.found'),
|
||||||
icon: 'warning',
|
icon: 'warning',
|
||||||
})
|
})
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -177,9 +201,10 @@ export const Orientation = forwardRef((props, ref) => {
|
|||||||
setInputCompasDeg('-0')
|
setInputCompasDeg('-0')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (Number(e) >= -180 && Number(e) <= 180) {
|
const n = Number(normalizeDecimal(e))
|
||||||
if (numberCheck(Number(e))) {
|
if (n >= -180 && n <= 180) {
|
||||||
setInputCompasDeg(Number(e))
|
if (numberCheck(n)) {
|
||||||
|
setInputCompasDeg(n)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setInputCompasDeg(compasDeg)
|
setInputCompasDeg(compasDeg)
|
||||||
@ -204,6 +229,41 @@ export const Orientation = forwardRef((props, ref) => {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleChangeModuleSeries = (e) => {
|
||||||
|
resetRoofs()
|
||||||
|
setSelectedModuleSeries(e)
|
||||||
|
|
||||||
|
// 선택된 시리즈에 맞는 모듈 목록 필터링 및 첫 번째 모듈 선택
|
||||||
|
if (e && moduleList.length > 0) {
|
||||||
|
let filtered
|
||||||
|
|
||||||
|
if (e.moduleSerCd === 'ALL') {
|
||||||
|
// "전체" 선택 시 모든 모듈 표시
|
||||||
|
filtered = moduleList
|
||||||
|
} else {
|
||||||
|
// 특정 시리즈 선택 시 해당 시리즈 모듈만 표시
|
||||||
|
//filtered = moduleList.filter(module => module.moduleSerCd === e.moduleSerCd)
|
||||||
|
filtered = moduleList.filter(module => module && module.moduleSerCd && module.moduleSerCd === e.moduleSerCd)
|
||||||
|
}
|
||||||
|
|
||||||
|
setFilteredModuleList(filtered)
|
||||||
|
|
||||||
|
// 필터링된 목록의 첫 번째 모듈을 자동 선택
|
||||||
|
if (filtered.length > 0) {
|
||||||
|
const firstModule = filtered[0]
|
||||||
|
setSelectedModules(firstModule)
|
||||||
|
// 상위 컴포넌트의 handleChangeModule 호출
|
||||||
|
if (handleChangeModule) {
|
||||||
|
handleChangeModule(firstModule)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 모듈 리스트가 비어있는 경우
|
||||||
|
setFilteredModuleList([])
|
||||||
|
setSelectedModules(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const handleChangeModule = (e) => {
|
const handleChangeModule = (e) => {
|
||||||
resetRoofs()
|
resetRoofs()
|
||||||
setSelectedModules(e)
|
setSelectedModules(e)
|
||||||
@ -262,6 +322,71 @@ export const Orientation = forwardRef((props, ref) => {
|
|||||||
setRoofsStore(newRoofs)
|
setRoofsStore(newRoofs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 모듈시리즈 목록 생성 및 commonCode와 매핑
|
||||||
|
useEffect(() => {
|
||||||
|
if (moduleList.length > 0 && moduleSeriesList.length === 0) {
|
||||||
|
const moduleSeriesCodes = findCommonCode(207100) || []
|
||||||
|
|
||||||
|
// moduleList에서 고유한 moduleSerCd 추출
|
||||||
|
const uniqueSeriesCd = [...new Set(moduleList.map(module => module.moduleSerCd).filter(Boolean))]
|
||||||
|
|
||||||
|
if (uniqueSeriesCd.length > 0) {
|
||||||
|
// moduleSerCd와 commonCode를 매핑하여 기본 moduleSeriesList 생성
|
||||||
|
const mappedSeries = uniqueSeriesCd.map(serCd => {
|
||||||
|
const matchedCode = moduleSeriesCodes.find(code => code.clCode === serCd)
|
||||||
|
return {
|
||||||
|
moduleSerCd: serCd,
|
||||||
|
moduleSerNm: matchedCode ? matchedCode.clCodeNm : serCd
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// "전체" 옵션을 맨 앞에 추가
|
||||||
|
const seriesList = [allOption, ...mappedSeries]
|
||||||
|
setModuleSeriesList(seriesList)
|
||||||
|
|
||||||
|
// 현재 선택된 모듈이 있으면 해당 모듈의 시리즈를 찾아서 선택
|
||||||
|
if (selectedModules && selectedModules.moduleSerCd) {
|
||||||
|
const currentSeries = seriesList.find(series => series.moduleSerCd === selectedModules.moduleSerCd)
|
||||||
|
if (currentSeries) {
|
||||||
|
setSelectedModuleSeries(currentSeries)
|
||||||
|
} else {
|
||||||
|
setSelectedModuleSeries(allOption)
|
||||||
|
// "ALL"이 선택되면 자동으로 모듈 필터링 및 선택 실행
|
||||||
|
setTimeout(() => handleChangeModuleSeries(allOption), 0)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 선택된 모듈이 없으면 "전체"를 기본 선택
|
||||||
|
setSelectedModuleSeries(allOption)
|
||||||
|
// "ALL"이 선택되면 자동으로 모듈 필터링 및 선택 실행
|
||||||
|
setTimeout(() => handleChangeModuleSeries(allOption), 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [moduleList, selectedModules])
|
||||||
|
|
||||||
|
// 초기 로딩 시에만 필터링된 모듈 목록 설정
|
||||||
|
useEffect(() => {
|
||||||
|
if (moduleList.length > 0 && filteredModuleList.length === 0 && selectedModuleSeries) {
|
||||||
|
let filtered
|
||||||
|
|
||||||
|
if (selectedModuleSeries.moduleSerCd === 'ALL') {
|
||||||
|
// "전체" 선택 시 모든 모듈 표시
|
||||||
|
filtered = moduleList
|
||||||
|
} else {
|
||||||
|
// 특정 시리즈 선택 시 해당 시리즈 모듈만 표시
|
||||||
|
filtered = moduleList.filter(module => module.moduleSerCd === selectedModuleSeries.moduleSerCd)
|
||||||
|
}
|
||||||
|
|
||||||
|
setFilteredModuleList(filtered)
|
||||||
|
|
||||||
|
if (filtered.length > 0 && !selectedModules) {
|
||||||
|
setSelectedModules(filtered[0])
|
||||||
|
}
|
||||||
|
} else if (moduleList.length === 0 && filteredModuleList.length === 0 && selectedModuleSeries) {
|
||||||
|
// 모듈 리스트가 비어있는 경우 빈 배열로 설정
|
||||||
|
setFilteredModuleList([])
|
||||||
|
}
|
||||||
|
}, [moduleList, selectedModuleSeries]);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="properties-setting-wrap">
|
<div className="properties-setting-wrap">
|
||||||
@ -275,31 +400,31 @@ export const Orientation = forwardRef((props, ref) => {
|
|||||||
{Array.from({ length: 180 / 15 }).map((dot, index) => (
|
{Array.from({ length: 180 / 15 }).map((dot, index) => (
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={index}
|
||||||
className={`circle ${getDegreeInOrientation(inputCompasDeg) === -1 * (-15 * index + 180) || (index === 0 && inputCompasDeg >= 172 && index === 0 && inputCompasDeg <= 180) || (inputCompasDeg === -180 && index === 0) ? 'act' : ''}`}
|
className={`circle ${getDegreeInOrientation(inputCompasDeg) === -15 * index + 180 || (index === 0 && inputCompasDeg >= 172 && index === 0 && inputCompasDeg <= 180) || (inputCompasDeg === -180 && index === 0) ? 'act' : ''}`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (index === 0) {
|
if (index === 0) {
|
||||||
setInputCompasDeg(180)
|
setInputCompasDeg(180)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
setInputCompasDeg(-1 * (-15 * index + 180))
|
setInputCompasDeg(-15 * index + 180)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{index === 0 && <i>180°</i>}
|
{index === 0 && <i>180°</i>}
|
||||||
{index === 6 && <i>-90°</i>}
|
{index === 6 && <i>90°</i>}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
{Array.from({ length: 180 / 15 }).map((dot, index) => (
|
{Array.from({ length: 180 / 15 }).map((dot, index) => (
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={index}
|
||||||
className={`circle ${inputCompasDeg !== 180 && getDegreeInOrientation(inputCompasDeg) === 15 * index ? 'act' : ''}`}
|
className={`circle ${inputCompasDeg !== 180 && getDegreeInOrientation(inputCompasDeg) === -1 * 15 * index ? 'act' : ''}`}
|
||||||
onClick={() => setInputCompasDeg(15 * index)}
|
onClick={() => setInputCompasDeg(15 * index * -1)}
|
||||||
>
|
>
|
||||||
{index === 0 && <i>0°</i>}
|
{index === 0 && <i>0°</i>}
|
||||||
{index === 6 && <i>90°</i>}
|
{index === 6 && <i>-90°</i>}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
<div className="compas">
|
<div className="compas">
|
||||||
<div className="compas-arr" style={{ transform: `rotate(${getDegreeInOrientation(inputCompasDeg)}deg)` }}></div>
|
<div className="compas-arr" style={{ transform: `rotate(${-1 * getDegreeInOrientation(inputCompasDeg)}deg)` }}></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -328,16 +453,32 @@ export const Orientation = forwardRef((props, ref) => {
|
|||||||
<div className="compas-table-wrap">
|
<div className="compas-table-wrap">
|
||||||
<div className="compas-table-box mb10">
|
<div className="compas-table-box mb10">
|
||||||
<div className="outline-form mb10">
|
<div className="outline-form mb10">
|
||||||
<span>{getMessage('modal.module.basic.setting.module.setting')}</span>
|
<span>{getMessage('modal.module.basic.setting.module.series.setting')}</span>
|
||||||
<div className="grid-select">
|
<div className="grid-select">
|
||||||
{moduleList && (
|
<div className="grid-select">
|
||||||
<QSelectBox
|
<QSelectBox
|
||||||
options={moduleList}
|
options={moduleSeriesList.length > 0 ? moduleSeriesList : [allOption]}
|
||||||
|
value={selectedModuleSeries}
|
||||||
|
targetKey={'moduleSerCd'}
|
||||||
|
sourceKey={'moduleSerCd'}
|
||||||
|
showKey={'moduleSerNm'}
|
||||||
|
onChange={(e) => handleChangeModuleSeries(e)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="outline-form mb10">
|
||||||
|
<span>{getMessage('modal.module.basic.setting.module.setting2')}</span>
|
||||||
|
<div className="grid-select">
|
||||||
|
{filteredModuleList && (
|
||||||
|
<QSelectBox
|
||||||
|
options={filteredModuleList}
|
||||||
value={selectedModules}
|
value={selectedModules}
|
||||||
targetKey={'itemId'}
|
targetKey={'itemId'}
|
||||||
sourceKey={'itemId'}
|
sourceKey={'itemId'}
|
||||||
showKey={'itemNm'}
|
showKey={'itemNm'}
|
||||||
onChange={(e) => handleChangeModule(e)}
|
onChange={(e) => handleChangeModule(e)}
|
||||||
|
showFirstOptionWhenEmpty = {true}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@ -388,18 +529,18 @@ export const Orientation = forwardRef((props, ref) => {
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
{basicSetting && basicSetting.roofSizeSet == '3' && (
|
{basicSetting && basicSetting.roofSizeSet === '3' && (
|
||||||
<div className="outline-form mt15">
|
<div className="outline-form mt15">
|
||||||
<span>{getMessage('modal.module.basic.setting.module.placement.area')}</span>
|
<span>{getMessage('modal.module.basic.setting.module.placement.area')}</span>
|
||||||
<div className="input-grid mr10" style={{ width: '60px' }}>
|
<div className="input-grid mr10" style={{ width: '60px' }}>
|
||||||
<input type="number" className="input-origin block" value={inputMargin} onChange={(e) => setInputMargin(e.target.value)} />
|
<input type="text" className="input-origin block" value={inputMargin} onChange={(e) => setInputMargin(normalizeDecimal(e.target.value))} />
|
||||||
</div>
|
</div>
|
||||||
<span className="thin">m</span>
|
<span className="thin">m</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{basicSetting && basicSetting.roofSizeSet != '3' && (
|
{basicSetting && basicSetting.roofSizeSet !== '3' && (
|
||||||
<div className="compas-table-box">
|
<div className="compas-table-box">
|
||||||
<div className="compas-grid-table">
|
<div className="compas-grid-table">
|
||||||
<div className="outline-form">
|
<div className="outline-form">
|
||||||
@ -421,10 +562,10 @@ export const Orientation = forwardRef((props, ref) => {
|
|||||||
<span>{getMessage('modal.module.basic.setting.module.fitting.height')}</span>
|
<span>{getMessage('modal.module.basic.setting.module.fitting.height')}</span>
|
||||||
<div className="input-grid mr10">
|
<div className="input-grid mr10">
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="text"
|
||||||
className="input-origin block"
|
className="input-origin block"
|
||||||
value={inputInstallHeight}
|
value={inputInstallHeight}
|
||||||
onChange={(e) => handleChangeInstallHeight(e.target.value)}
|
onChange={(e) => handleChangeInstallHeight(normalizeDecimal(e.target.value))}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<span className="thin">m</span>
|
<span className="thin">m</span>
|
||||||
@ -449,10 +590,10 @@ export const Orientation = forwardRef((props, ref) => {
|
|||||||
<span>{getMessage('modal.module.basic.setting.module.standard.snowfall.amount')}</span>
|
<span>{getMessage('modal.module.basic.setting.module.standard.snowfall.amount')}</span>
|
||||||
<div className="input-grid mr10">
|
<div className="input-grid mr10">
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="text"
|
||||||
className="input-origin block"
|
className="input-origin block"
|
||||||
value={inputVerticalSnowCover}
|
value={inputVerticalSnowCover}
|
||||||
onChange={(e) => handleChangeVerticalSnowCover(e.target.value)}
|
onChange={(e) => handleChangeVerticalSnowCover(normalizeDecimal(e.target.value))}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<span className="thin">cm</span>
|
<span className="thin">cm</span>
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import {
|
|||||||
import { useRecoilState, useRecoilValue, useResetRecoilState, useSetRecoilState } from 'recoil'
|
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'
|
||||||
|
import { normalizeDigits } from '@/util/input-utils'
|
||||||
import Image from 'next/image'
|
import Image from 'next/image'
|
||||||
|
|
||||||
const Placement = forwardRef((props, refs) => {
|
const Placement = forwardRef((props, refs) => {
|
||||||
@ -155,7 +156,7 @@ const Placement = forwardRef((props, refs) => {
|
|||||||
newLayoutSetup[index] = {
|
newLayoutSetup[index] = {
|
||||||
...newLayoutSetup[index],
|
...newLayoutSetup[index],
|
||||||
moduleId: itemId,
|
moduleId: itemId,
|
||||||
[e.target.name]: Number(e.target.value),
|
[e.target.name]: Number(normalizeDigits(e.target.value)),
|
||||||
}
|
}
|
||||||
props.setLayoutSetup(newLayoutSetup)
|
props.setLayoutSetup(newLayoutSetup)
|
||||||
}
|
}
|
||||||
@ -168,6 +169,7 @@ const Placement = forwardRef((props, refs) => {
|
|||||||
<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' ? (
|
||||||
@ -180,6 +182,7 @@ const Placement = forwardRef((props, refs) => {
|
|||||||
)}
|
)}
|
||||||
</th>
|
</th>
|
||||||
))}
|
))}
|
||||||
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{selectedModules?.itemList &&
|
{selectedModules?.itemList &&
|
||||||
@ -215,7 +218,7 @@ const Placement = forwardRef((props, refs) => {
|
|||||||
className="input-origin block"
|
className="input-origin block"
|
||||||
name="row"
|
name="row"
|
||||||
value={props.layoutSetup[index]?.row ?? 1}
|
value={props.layoutSetup[index]?.row ?? 1}
|
||||||
defaultValue={0}
|
//defaultValue={0}
|
||||||
onChange={(e) => handleLayoutSetup(e, item.itemId, index)}
|
onChange={(e) => handleLayoutSetup(e, item.itemId, index)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -227,7 +230,7 @@ const Placement = forwardRef((props, refs) => {
|
|||||||
className="input-origin block"
|
className="input-origin block"
|
||||||
name="col"
|
name="col"
|
||||||
value={props.layoutSetup[index]?.col ?? 1}
|
value={props.layoutSetup[index]?.col ?? 1}
|
||||||
defaultValue={0}
|
//defaultValue={0}
|
||||||
onChange={(e) => handleLayoutSetup(e, item.itemId, index)}
|
onChange={(e) => handleLayoutSetup(e, item.itemId, index)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import { moduleSelectionDataState, selectedModuleState } from '@/store/selectedM
|
|||||||
import { forwardRef, useContext, useEffect, useImperativeHandle, useRef, useState } from 'react'
|
import { forwardRef, useContext, useEffect, useImperativeHandle, useRef, useState } from 'react'
|
||||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||||
import Swal from 'sweetalert2'
|
import Swal from 'sweetalert2'
|
||||||
|
import { normalizeDigits } from '@/util/input-utils'
|
||||||
|
|
||||||
const Trestle = forwardRef((props, ref) => {
|
const Trestle = forwardRef((props, ref) => {
|
||||||
const { tabNum, setTabNum, trestleTrigger, roofs, setRoofs, moduleSelectionData, setModuleSelectionData, setRoofsStore } = props
|
const { tabNum, setTabNum, trestleTrigger, roofs, setRoofs, moduleSelectionData, setModuleSelectionData, setRoofsStore } = props
|
||||||
@ -17,6 +18,12 @@ const Trestle = forwardRef((props, ref) => {
|
|||||||
const currentAngleType = useRecoilValue(currentAngleTypeSelector)
|
const currentAngleType = useRecoilValue(currentAngleTypeSelector)
|
||||||
const pitchText = useRecoilValue(pitchTextSelector)
|
const pitchText = useRecoilValue(pitchTextSelector)
|
||||||
const [selectedRoof, setSelectedRoof] = useState(null)
|
const [selectedRoof, setSelectedRoof] = useState(null)
|
||||||
|
const [isAutoSelecting, setIsAutoSelecting] = useState(false) // 자동 선택 중인지 상태
|
||||||
|
const [autoSelectTimeout, setAutoSelectTimeout] = useState(null) // 타임아웃 참조
|
||||||
|
const autoSelectTimeoutRef = useRef(null)
|
||||||
|
|
||||||
|
// 공통 타임아웃 설정 (밀리초)
|
||||||
|
const AUTO_SELECT_TIMEOUT = 500 // API 호출 완료 대기 시간
|
||||||
const {
|
const {
|
||||||
trestleState,
|
trestleState,
|
||||||
trestleDetail,
|
trestleDetail,
|
||||||
@ -58,15 +65,28 @@ const Trestle = forwardRef((props, ref) => {
|
|||||||
const { restoreModuleInstArea } = useModuleBasicSetting()
|
const { restoreModuleInstArea } = useModuleBasicSetting()
|
||||||
const [flag, setFlag] = useState(false)
|
const [flag, setFlag] = useState(false)
|
||||||
const tempModuleSelectionData = useRef(null)
|
const tempModuleSelectionData = useRef(null)
|
||||||
|
const [autoSelectStep, setAutoSelectStep] = useState(null) // 'raftBase', 'trestle', 'constMthd', 'roofBase', 'construction'
|
||||||
|
const prevHajebichiRef = useRef();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (roofs && !selectedRoof) {
|
if (roofs && roofs.length > 0 && !selectedRoof) {
|
||||||
|
console.log("roofs:::::", roofs.length)
|
||||||
|
setLengthBase(roofs[0].length);
|
||||||
setSelectedRoof(roofs[0])
|
setSelectedRoof(roofs[0])
|
||||||
}
|
}
|
||||||
|
if (selectedRoof && selectedRoof.lenAuth === "C") {
|
||||||
|
onChangeLength(selectedRoof.length);
|
||||||
|
}else if (selectedRoof && ["C", "R"].includes(selectedRoof.raftAuth) && roofs && roofs.length > 0) {
|
||||||
|
onChangeRaftBase(roofs[0]);
|
||||||
|
}else if (selectedRoof && ["C", "R"].includes(selectedRoof.roofPchAuth) && roofs && roofs.length > 0 &&
|
||||||
|
roofs[0].hajebichi !== prevHajebichiRef.current ) {
|
||||||
|
prevHajebichiRef.current = roofs[0].hajebichi;
|
||||||
|
onChangeHajebichi(roofs[0].hajebichi);
|
||||||
|
}
|
||||||
|
|
||||||
//모듈 설치 영역 복구
|
//모듈 설치 영역 복구
|
||||||
restoreModuleInstArea()
|
restoreModuleInstArea()
|
||||||
}, [roofs])
|
}, [roofs, selectedRoof]) // selectedRoof 추가
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (flag && moduleSelectionData) {
|
if (flag && moduleSelectionData) {
|
||||||
@ -100,41 +120,86 @@ const Trestle = forwardRef((props, ref) => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (trestleList.length > 0) {
|
if (trestleList.length > 0) {
|
||||||
setSelectedTrestle(trestleList.find((trestle) => trestle.trestleMkrCd === trestleState?.trestleMkrCd) ?? null)
|
const existingTrestle = trestleList.find(
|
||||||
|
(trestle) => trestle.trestleMkrCd === trestleState?.trestleMkrCd
|
||||||
|
);
|
||||||
|
if (existingTrestle) {
|
||||||
|
setSelectedTrestle(existingTrestle)
|
||||||
|
} else if (autoSelectStep === 'trestle') {
|
||||||
|
// 자동 선택: 첫 번째 가대메이커 선택
|
||||||
|
console.log('Auto selecting first trestle:', trestleList[0])
|
||||||
|
const firstTrestle = trestleList[0]
|
||||||
|
onChangeTrestleMaker(firstTrestle)
|
||||||
|
// setAutoSelectStep은 onChangeTrestleMaker 내부에서 처리됨
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
setSelectedTrestle(null)
|
setSelectedTrestle(null)
|
||||||
}
|
}
|
||||||
}, [trestleList])
|
}, [trestleList, autoSelectStep])
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (roofBaseList.length > 0) {
|
|
||||||
setSelectedRoofBase(roofBaseList.find((roofBase) => roofBase.roofBaseCd === trestleState?.roofBaseCd) ?? null)
|
|
||||||
} else {
|
|
||||||
setSelectedRoofBase(null)
|
|
||||||
}
|
|
||||||
}, [roofBaseList])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (constMthdList.length > 0) {
|
if (constMthdList.length > 0) {
|
||||||
setSelectedConstMthd(constMthdList.find((constMthd) => constMthd.constMthdCd === trestleState?.constMthdCd) ?? null)
|
const existingConstMthd = constMthdList.find((constMthd) => constMthd.constMthdCd === trestleState?.constMthdCd)
|
||||||
|
if (existingConstMthd) {
|
||||||
|
setSelectedConstMthd(existingConstMthd)
|
||||||
|
} else if (autoSelectStep === 'constMthd') {
|
||||||
|
// 자동 선택: 첫 번째 공법 선택
|
||||||
|
const firstConstMthd = constMthdList[0]
|
||||||
|
onChangeConstMthd(firstConstMthd)
|
||||||
|
setAutoSelectStep('roofBase') // 다음 단계로 설정
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
setSelectedConstMthd(null)
|
setSelectedConstMthd(null)
|
||||||
}
|
}
|
||||||
}, [constMthdList])
|
}, [constMthdList, autoSelectStep])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (roofBaseList.length > 0) {
|
||||||
|
const existingRoofBase = roofBaseList.find((roofBase) => roofBase.roofBaseCd === trestleState?.roofBaseCd)
|
||||||
|
if (existingRoofBase) {
|
||||||
|
setSelectedRoofBase(existingRoofBase)
|
||||||
|
} else if (autoSelectStep === 'roofBase') {
|
||||||
|
// 자동 선택: 첫 번째 지붕밑바탕 선택
|
||||||
|
const firstRoofBase = roofBaseList[0]
|
||||||
|
onChangeRoofBase(firstRoofBase)
|
||||||
|
setAutoSelectStep('construction') // 다음 단계로 설정
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setSelectedRoofBase(null)
|
||||||
|
}
|
||||||
|
}, [roofBaseList, autoSelectStep])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (constructionList.length > 0) {
|
if (constructionList.length > 0) {
|
||||||
setSelectedConstruction(constructionList.find((construction) => construction.constTp === trestleState?.construction?.constTp) ?? null)
|
const existingConstruction = constructionList.find((construction) => construction.constTp === trestleState.constTp)
|
||||||
|
if (existingConstruction) {
|
||||||
|
setSelectedConstruction(existingConstruction)
|
||||||
|
} else if (autoSelectStep === 'construction') {
|
||||||
|
// 자동 선택: 첫 번째 가능한 construction 선택
|
||||||
|
const availableConstructions = constructionList.filter((construction) => construction.constPossYn === 'Y')
|
||||||
|
if (availableConstructions.length > 0) {
|
||||||
|
const firstConstruction = availableConstructions[0]
|
||||||
|
const firstIndex = constructionList.findIndex((construction) => construction.constTp === firstConstruction.constTp)
|
||||||
|
handleConstruction(firstIndex)
|
||||||
|
setAutoSelectStep(null) // 자동 선택 완료
|
||||||
|
} else {
|
||||||
|
Swal.fire({
|
||||||
|
title: getMessage('modal.module.basic.settting.module.error4', [selectedRoof?.nameJp]),
|
||||||
|
icon: 'warning',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (constructionList.filter((construction) => construction.constPossYn === 'Y').length === 0) {
|
if (constructionList.filter((construction) => construction.constPossYn === 'Y').length === 0) {
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
title: getMessage('modal.module.basic.settting.module.error4', [selectedRoof?.nameJp]), // 시공법법을 선택해주세요.
|
title: getMessage('modal.module.basic.settting.module.error4', [selectedRoof?.nameJp]),
|
||||||
icon: 'warning',
|
icon: 'warning',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setSelectedConstruction(null)
|
setSelectedConstruction(null)
|
||||||
}
|
}
|
||||||
}, [constructionList])
|
}, [constructionList, autoSelectStep])
|
||||||
|
|
||||||
const getConstructionState = (index) => {
|
const getConstructionState = (index) => {
|
||||||
if (constructionList && constructionList.length > 0) {
|
if (constructionList && constructionList.length > 0) {
|
||||||
@ -151,6 +216,13 @@ const Trestle = forwardRef((props, ref) => {
|
|||||||
|
|
||||||
const onChangeLength = (e) => {
|
const onChangeLength = (e) => {
|
||||||
setLengthBase(e)
|
setLengthBase(e)
|
||||||
|
// 다음 단계들 초기화
|
||||||
|
setSelectedRaftBase(null)
|
||||||
|
setSelectedTrestle(null)
|
||||||
|
setSelectedConstMthd(null)
|
||||||
|
setSelectedRoofBase(null)
|
||||||
|
setSelectedConstruction(null)
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: 'SET_LENGTH',
|
type: 'SET_LENGTH',
|
||||||
roof: {
|
roof: {
|
||||||
@ -160,10 +232,24 @@ const Trestle = forwardRef((props, ref) => {
|
|||||||
raft: selectedRaftBase?.clCode,
|
raft: selectedRaftBase?.clCode,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 자동으로 첫 번째 서까래 간격 선택
|
||||||
|
if (raftBaseList.length > 0) {
|
||||||
|
|
||||||
|
const inx = raftBaseList.findIndex((raft) => raft.clCode === selectedRoof?.raft) ?? 0
|
||||||
|
const firstRaftBase = raftBaseList[inx]
|
||||||
|
onChangeRaftBase(firstRaftBase)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const onChangeRaftBase = (e) => {
|
const onChangeRaftBase = (e) => {
|
||||||
setSelectedRaftBase(e)
|
setSelectedRaftBase(e)
|
||||||
|
// 다음 단계들 초기화
|
||||||
|
setSelectedTrestle(null)
|
||||||
|
setSelectedConstMthd(null)
|
||||||
|
setSelectedRoofBase(null)
|
||||||
|
setSelectedConstruction(null)
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: 'SET_RAFT_BASE',
|
type: 'SET_RAFT_BASE',
|
||||||
roof: {
|
roof: {
|
||||||
@ -172,58 +258,115 @@ const Trestle = forwardRef((props, ref) => {
|
|||||||
raft: e.clCode,
|
raft: e.clCode,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 다음 단계(가대메이커) 자동 선택 설정 - 지연 실행
|
||||||
|
setTimeout(() => {
|
||||||
|
setAutoSelectStep('trestle')
|
||||||
|
}, AUTO_SELECT_TIMEOUT) // API 호출 완료를 위한 더 긴 지연
|
||||||
}
|
}
|
||||||
|
|
||||||
const onChangeHajebichi = (e) => {
|
const onChangeHajebichi = (e) => {
|
||||||
setHajebichi(e)
|
setHajebichi(e)
|
||||||
|
// 다음 단계들 초기화
|
||||||
|
setSelectedTrestle(null)
|
||||||
|
setSelectedConstMthd(null)
|
||||||
|
setSelectedRoofBase(null)
|
||||||
|
setSelectedConstruction(null)
|
||||||
|
|
||||||
|
// roofs 배열에서 selectedRoof.index와 같은 인덱스의 지붕 객체 업데이트
|
||||||
|
if (selectedRoof && selectedRoof.index !== undefined) {
|
||||||
|
const updatedRoofs = roofs.map((roof, index) => (index === selectedRoof.index ? { ...roof, hajebichi: Number(e) } : roof))
|
||||||
|
setRoofs(updatedRoofs)
|
||||||
|
}
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: 'SET_HAJEBICHI',
|
type: 'SET_HAJEBICHI',
|
||||||
roof: {
|
roof: {
|
||||||
moduleTpCd: selectedModules.itemTp ?? '',
|
moduleTpCd: selectedModules.itemTp ?? '',
|
||||||
roofMatlCd: selectedRoof?.roofMatlCd ?? '',
|
roofMatlCd: selectedRoof?.roofMatlCd ?? '',
|
||||||
raft: selectedRaftBase?.clCode,
|
raft: selectedRaftBase?.clCode ?? selectedRoof?.roofBaseCd,
|
||||||
hajebichi: e,
|
hajebichi: e,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 다음 단계(가대메이커) 자동 선택 설정 - 지연 실행
|
||||||
|
setTimeout(() => {
|
||||||
|
setAutoSelectStep('trestle')
|
||||||
|
}, AUTO_SELECT_TIMEOUT)
|
||||||
}
|
}
|
||||||
|
|
||||||
const onChangeTrestleMaker = (e) => {
|
const onChangeTrestleMaker = (e) => {
|
||||||
setSelectedTrestle(e)
|
setSelectedTrestle(e)
|
||||||
|
// 다음 단계들 초기화
|
||||||
|
setSelectedConstMthd(null)
|
||||||
|
setSelectedRoofBase(null)
|
||||||
|
setSelectedConstruction(null)
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: 'SET_TRESTLE_MAKER',
|
type: 'SET_TRESTLE_MAKER',
|
||||||
roof: {
|
roof: {
|
||||||
moduleTpCd: selectedModules.itemTp ?? '',
|
moduleTpCd: selectedModules.itemTp ?? '',
|
||||||
roofMatlCd: selectedRoof?.roofMatlCd ?? '',
|
roofMatlCd: selectedRoof?.roofMatlCd ?? '',
|
||||||
raft: selectedRaftBase?.clCode,
|
raft: selectedRaftBase?.clCode ?? selectedRoof?.roofBaseCd,
|
||||||
|
//hajebichi: selectedRaftBase?.hajebichi ?? selectedRoof?.hajebichi,
|
||||||
trestleMkrCd: e.trestleMkrCd,
|
trestleMkrCd: e.trestleMkrCd,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// API 호출 완료 후 다음 단계(공법) 자동 선택 설정
|
||||||
|
setTimeout(() => {
|
||||||
|
setAutoSelectStep('constMthd')
|
||||||
|
}, AUTO_SELECT_TIMEOUT)
|
||||||
}
|
}
|
||||||
|
|
||||||
const onChangeConstMthd = (e) => {
|
const onChangeConstMthd = (e) => {
|
||||||
setSelectedConstMthd(e)
|
setSelectedConstMthd(e)
|
||||||
|
// 다음 단계 초기화
|
||||||
|
setSelectedRoofBase(null)
|
||||||
|
setSelectedConstruction(null)
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: 'SET_CONST_MTHD',
|
type: 'SET_CONST_MTHD',
|
||||||
roof: {
|
roof: {
|
||||||
moduleTpCd: selectedModules.itemTp ?? '',
|
moduleTpCd: selectedModules.itemTp ?? '',
|
||||||
roofMatlCd: selectedRoof?.roofMatlCd ?? '',
|
roofMatlCd: selectedRoof?.roofMatlCd ?? '',
|
||||||
raft: selectedRaftBase?.clCode,
|
raft: selectedRaftBase?.clCode ?? selectedRoof?.roofBaseCd,
|
||||||
trestleMkrCd: selectedTrestle.trestleMkrCd,
|
//hajebichi: selectedRaftBase?.hajebichi ?? selectedRoof?.hajebichi,
|
||||||
|
trestleMkrCd: selectedTrestle?.trestleMkrCd,
|
||||||
constMthdCd: e.constMthdCd,
|
constMthdCd: e.constMthdCd,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 기존 타임아웃 취소
|
||||||
|
if (autoSelectTimeoutRef.current) {
|
||||||
|
clearTimeout(autoSelectTimeoutRef.current)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 자동 선택 중 상태 활성화
|
||||||
|
setIsAutoSelecting(true)
|
||||||
|
|
||||||
|
// API 호출 완료 후 다음 단계(지붕밑바탕) 자동 선택 설정
|
||||||
|
const timeoutId = setTimeout(() => {
|
||||||
|
setAutoSelectStep('roofBase')
|
||||||
|
setIsAutoSelecting(false)
|
||||||
|
}, AUTO_SELECT_TIMEOUT)
|
||||||
|
|
||||||
|
autoSelectTimeoutRef.current = timeoutId
|
||||||
}
|
}
|
||||||
|
|
||||||
const onChangeRoofBase = (e) => {
|
const onChangeRoofBase = (e) => {
|
||||||
setSelectedRoofBase(e)
|
setSelectedRoofBase(e)
|
||||||
|
setSelectedConstruction(null)
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: 'SET_ROOF_BASE',
|
type: 'SET_ROOF_BASE',
|
||||||
roof: {
|
roof: {
|
||||||
moduleTpCd: selectedModules.itemTp ?? '',
|
moduleTpCd: selectedModules.itemTp ?? '',
|
||||||
roofMatlCd: selectedRoof?.roofMatlCd ?? '',
|
roofMatlCd: selectedRoof?.roofMatlCd ?? '',
|
||||||
raft: selectedRaftBase?.clCode,
|
raft: selectedRaftBase?.clCode ?? selectedRoof?.roofBaseCd,
|
||||||
trestleMkrCd: selectedTrestle.trestleMkrCd,
|
//hajebichi: selectedRaftBase?.hajebichi ?? selectedRoof?.hajebichi,
|
||||||
constMthdCd: selectedConstMthd.constMthdCd,
|
trestleMkrCd: selectedTrestle?.trestleMkrCd,
|
||||||
|
constMthdCd: selectedConstMthd?.constMthdCd,
|
||||||
roofBaseCd: e.roofBaseCd,
|
roofBaseCd: e.roofBaseCd,
|
||||||
illuminationTp: managementState?.surfaceTypeValue ?? '',
|
illuminationTp: managementState?.surfaceTypeValue ?? '',
|
||||||
instHt: managementState?.installHeight ?? '',
|
instHt: managementState?.installHeight ?? '',
|
||||||
@ -233,6 +376,11 @@ const Trestle = forwardRef((props, ref) => {
|
|||||||
roofPitch: Math.round(hajebichi ?? 0),
|
roofPitch: Math.round(hajebichi ?? 0),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// API 호출 완료 후 다음 단계(construction) 자동 선택 설정
|
||||||
|
setTimeout(() => {
|
||||||
|
setAutoSelectStep('construction')
|
||||||
|
}, AUTO_SELECT_TIMEOUT)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleConstruction = (index) => {
|
const handleConstruction = (index) => {
|
||||||
@ -242,7 +390,8 @@ const Trestle = forwardRef((props, ref) => {
|
|||||||
roof: {
|
roof: {
|
||||||
moduleTpCd: selectedModules.itemTp ?? '',
|
moduleTpCd: selectedModules.itemTp ?? '',
|
||||||
roofMatlCd: selectedRoof?.roofMatlCd ?? '',
|
roofMatlCd: selectedRoof?.roofMatlCd ?? '',
|
||||||
raft: selectedRaftBase?.clCode,
|
raft: selectedRaftBase?.clCode ?? selectedRoof?.roofBaseCd,
|
||||||
|
//hajebichi: selectedRaftBase?.hajebichi ?? selectedRoof?.hajebichi,
|
||||||
trestleMkrCd: selectedTrestle.trestleMkrCd,
|
trestleMkrCd: selectedTrestle.trestleMkrCd,
|
||||||
constMthdCd: selectedConstMthd.constMthdCd,
|
constMthdCd: selectedConstMthd.constMthdCd,
|
||||||
roofBaseCd: selectedRoofBase.roofBaseCd,
|
roofBaseCd: selectedRoofBase.roofBaseCd,
|
||||||
@ -279,7 +428,7 @@ const Trestle = forwardRef((props, ref) => {
|
|||||||
ridgeMargin,
|
ridgeMargin,
|
||||||
kerabaMargin,
|
kerabaMargin,
|
||||||
roofIndex: selectedRoof.index,
|
roofIndex: selectedRoof.index,
|
||||||
raft: selectedRaftBase?.clCode,
|
raft: selectedRaftBase?.clCode ?? selectedRoof?.roofBaseCd,
|
||||||
trestle: {
|
trestle: {
|
||||||
hajebichi: hajebichi,
|
hajebichi: hajebichi,
|
||||||
length: lengthBase,
|
length: lengthBase,
|
||||||
@ -316,8 +465,8 @@ const Trestle = forwardRef((props, ref) => {
|
|||||||
ridgeMargin,
|
ridgeMargin,
|
||||||
kerabaMargin,
|
kerabaMargin,
|
||||||
roofIndex: roof.index,
|
roofIndex: roof.index,
|
||||||
raft: selectedRaftBase?.clCode,
|
raft: selectedRaftBase?.clCode ?? selectedRoof?.raft ?? '',
|
||||||
hajebichi: hajebichi,
|
//hajebichi: selectedRaftBase?.hajebichi ?? selectedRoof?.hajebichi ?? 0,
|
||||||
trestle: {
|
trestle: {
|
||||||
length: lengthBase,
|
length: lengthBase,
|
||||||
hajebichi: hajebichi,
|
hajebichi: hajebichi,
|
||||||
@ -327,7 +476,8 @@ const Trestle = forwardRef((props, ref) => {
|
|||||||
...selectedRoofBase,
|
...selectedRoofBase,
|
||||||
},
|
},
|
||||||
construction: {
|
construction: {
|
||||||
...constructionList.find((data) => data.constTp === trestleState.constTp),
|
//...constructionList.find((data) => newAddedRoofs[index].construction.constTp === data.constTp),
|
||||||
|
...constructionList.find((data) => trestleState.constTp === data.constTp),
|
||||||
cvrYn,
|
cvrYn,
|
||||||
snowGdPossYn,
|
snowGdPossYn,
|
||||||
cvrChecked,
|
cvrChecked,
|
||||||
@ -401,7 +551,7 @@ const Trestle = forwardRef((props, ref) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (['C', 'R'].includes(roof.roofPchAuth)) {
|
if (['C', 'R'].includes(roof.roofPchAuth)) {
|
||||||
if (!roof?.roofPchBase) {
|
if (!roof?.hajebichi) {
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
title: getMessage('modal.module.basic.settting.module.error7', [roof.nameJp]), // 하제비치를 입력해주세요.
|
title: getMessage('modal.module.basic.settting.module.error7', [roof.nameJp]), // 하제비치를 입력해주세요.
|
||||||
icon: 'warning',
|
icon: 'warning',
|
||||||
@ -551,7 +701,19 @@ const Trestle = forwardRef((props, ref) => {
|
|||||||
type="text"
|
type="text"
|
||||||
className="input-origin block"
|
className="input-origin block"
|
||||||
value={lengthBase}
|
value={lengthBase}
|
||||||
onChange={(e) => onChangeLength(e.target.value)}
|
onChange={(e) => {
|
||||||
|
const v = e.target.value
|
||||||
|
if (v === '') {
|
||||||
|
onChangeLength('')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const n = Number(normalizeDigits(v))
|
||||||
|
if (Number.isNaN(n)) {
|
||||||
|
onChangeLength('')
|
||||||
|
} else {
|
||||||
|
onChangeLength(n)
|
||||||
|
}
|
||||||
|
}}
|
||||||
disabled={selectedRoof.lenAuth === 'R'}
|
disabled={selectedRoof.lenAuth === 'R'}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -574,6 +736,8 @@ const Trestle = forwardRef((props, ref) => {
|
|||||||
showKey={'clCodeNm'}
|
showKey={'clCodeNm'}
|
||||||
disabled={selectedRoof.raftAuth === 'R'}
|
disabled={selectedRoof.raftAuth === 'R'}
|
||||||
onChange={(e) => onChangeRaftBase(e)}
|
onChange={(e) => onChangeRaftBase(e)}
|
||||||
|
showFirstOptionWhenEmpty={true}
|
||||||
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@ -591,7 +755,19 @@ const Trestle = forwardRef((props, ref) => {
|
|||||||
type="text"
|
type="text"
|
||||||
className="input-origin block"
|
className="input-origin block"
|
||||||
disabled={selectedRoof.roofPchAuth === 'R'}
|
disabled={selectedRoof.roofPchAuth === 'R'}
|
||||||
onChange={(e) => onChangeHajebichi(e.target.value)}
|
onChange={(e) => {
|
||||||
|
const v = e.target.value
|
||||||
|
if (v === '') {
|
||||||
|
onChangeHajebichi('')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const n = Number(normalizeDigits(v))
|
||||||
|
if (Number.isNaN(n)) {
|
||||||
|
onChangeHajebichi('')
|
||||||
|
} else {
|
||||||
|
onChangeHajebichi(n)
|
||||||
|
}
|
||||||
|
}}
|
||||||
value={hajebichi}
|
value={hajebichi}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -612,6 +788,7 @@ const Trestle = forwardRef((props, ref) => {
|
|||||||
targetKey={'trestleMkrCd'}
|
targetKey={'trestleMkrCd'}
|
||||||
showKey={'trestleMkrCdJp'}
|
showKey={'trestleMkrCdJp'}
|
||||||
onChange={(e) => onChangeTrestleMaker(e)}
|
onChange={(e) => onChangeTrestleMaker(e)}
|
||||||
|
showFirstOptionWhenEmpty={true}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@ -630,6 +807,7 @@ const Trestle = forwardRef((props, ref) => {
|
|||||||
targetKey={'constMthdCd'}
|
targetKey={'constMthdCd'}
|
||||||
showKey={'constMthdCdJp'}
|
showKey={'constMthdCdJp'}
|
||||||
onChange={(e) => onChangeConstMthd(e)}
|
onChange={(e) => onChangeConstMthd(e)}
|
||||||
|
showFirstOptionWhenEmpty={true}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@ -648,6 +826,7 @@ const Trestle = forwardRef((props, ref) => {
|
|||||||
showKey={'roofBaseCdJp'}
|
showKey={'roofBaseCdJp'}
|
||||||
value={selectedRoofBase}
|
value={selectedRoofBase}
|
||||||
onChange={(e) => onChangeRoofBase(e)}
|
onChange={(e) => onChangeRoofBase(e)}
|
||||||
|
showFirstOptionWhenEmpty={true}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,16 +1,13 @@
|
|||||||
import WithDraggable from '@/components/common/draggable/WithDraggable'
|
import WithDraggable from '@/components/common/draggable/WithDraggable'
|
||||||
import { useState, useEffect, useContext, useRef } from 'react'
|
import { useContext, useEffect, useRef, useState } from 'react'
|
||||||
import PowerConditionalSelect from '@/components/floor-plan/modal/circuitTrestle/step/PowerConditionalSelect'
|
import PowerConditionalSelect from '@/components/floor-plan/modal/circuitTrestle/step/PowerConditionalSelect'
|
||||||
import StepUp from '@/components/floor-plan/modal/circuitTrestle/step/StepUp'
|
import StepUp from '@/components/floor-plan/modal/circuitTrestle/step/StepUp'
|
||||||
import { useMessage } from '@/hooks/useMessage'
|
import { useMessage } from '@/hooks/useMessage'
|
||||||
import { usePopup } from '@/hooks/usePopup'
|
import { usePopup } from '@/hooks/usePopup'
|
||||||
import PassivityCircuitAllocation from './step/type/PassivityCircuitAllocation'
|
import PassivityCircuitAllocation from './step/type/PassivityCircuitAllocation'
|
||||||
import { useMasterController } from '@/hooks/common/useMasterController'
|
import { useMasterController } from '@/hooks/common/useMasterController'
|
||||||
import { correntObjectNoState } from '@/store/settingAtom'
|
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||||
import { useRecoilValue } from 'recoil'
|
|
||||||
import { GlobalDataContext } from '@/app/GlobalDataProvider'
|
import { GlobalDataContext } from '@/app/GlobalDataProvider'
|
||||||
import { useRecoilState } from 'recoil'
|
|
||||||
import { makersState, modelsState, modelState, pcsCheckState, selectedMakerState, selectedModelsState, seriesState } from '@/store/circuitTrestleAtom'
|
|
||||||
import { POLYGON_TYPE } from '@/common/common'
|
import { POLYGON_TYPE } from '@/common/common'
|
||||||
import { useSwal } from '@/hooks/useSwal'
|
import { useSwal } from '@/hooks/useSwal'
|
||||||
import { canvasState, canvasZoomState } from '@/store/canvasAtom'
|
import { canvasState, canvasZoomState } from '@/store/canvasAtom'
|
||||||
@ -21,9 +18,7 @@ import { v4 as uuidv4 } from 'uuid'
|
|||||||
|
|
||||||
import { useEstimate } from '@/hooks/useEstimate'
|
import { useEstimate } from '@/hooks/useEstimate'
|
||||||
import { useCircuitTrestle } from '@/hooks/useCirCuitTrestle'
|
import { useCircuitTrestle } from '@/hooks/useCirCuitTrestle'
|
||||||
import { useCanvasPopupStatusController } from '@/hooks/common/useCanvasPopupStatusController'
|
|
||||||
import { useImgLoader } from '@/hooks/floorPlan/useImgLoader'
|
import { useImgLoader } from '@/hooks/floorPlan/useImgLoader'
|
||||||
import { usePlan } from '@/hooks/usePlan'
|
|
||||||
import { QcastContext } from '@/app/QcastProvider'
|
import { QcastContext } from '@/app/QcastProvider'
|
||||||
import { fabric } from 'fabric'
|
import { fabric } from 'fabric'
|
||||||
import { fontSelector } from '@/store/fontAtom'
|
import { fontSelector } from '@/store/fontAtom'
|
||||||
@ -107,12 +102,84 @@ export default function CircuitTrestleSetting({ id }) {
|
|||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
const capture = async (type) => {
|
||||||
|
beforeCapture(type)
|
||||||
|
|
||||||
|
await handleCanvasToPng(type)
|
||||||
|
|
||||||
|
afterCapture(type)
|
||||||
|
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
resolve(true)
|
||||||
|
}, 1000)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// 캡쳐 전 처리
|
// 캡쳐 전 처리
|
||||||
const beforeCapture = () => {
|
const beforeCapture = (type) => {
|
||||||
// setCanvasZoom(100)
|
setCanvasZoom(100)
|
||||||
const x = canvas.width / 2
|
canvas.set({ zoom: 1 })
|
||||||
const y = canvas.height / 2
|
|
||||||
canvas.zoomToPoint(new fabric.Point(x, y), 0.5)
|
// roof 객체들을 찾아서 중앙점 계산
|
||||||
|
const roofs = canvas.getObjects().filter((obj) => obj.name === 'roof' && !obj.wall)
|
||||||
|
|
||||||
|
if (roofs.length > 0) {
|
||||||
|
// 모든 roof의 x, y 좌표를 수집
|
||||||
|
const allPoints = []
|
||||||
|
roofs.forEach((roof) => {
|
||||||
|
if (roof.getCurrentPoints()) {
|
||||||
|
roof.getCurrentPoints().forEach((point) => {
|
||||||
|
allPoints.push({ x: point.x, y: point.y })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (allPoints.length > 0) {
|
||||||
|
// 모든 점들의 중앙값 계산
|
||||||
|
const minX = Math.min(...allPoints.map((p) => p.x))
|
||||||
|
const maxX = Math.max(...allPoints.map((p) => p.x))
|
||||||
|
const minY = Math.min(...allPoints.map((p) => p.y))
|
||||||
|
const maxY = Math.max(...allPoints.map((p) => p.y))
|
||||||
|
|
||||||
|
const centerX = (minX + maxX) / 2
|
||||||
|
const centerY = (minY + maxY) / 2
|
||||||
|
|
||||||
|
// 캔버스 중앙으로 이동하기 위한 오프셋 계산
|
||||||
|
const canvasWidth = canvas.getWidth()
|
||||||
|
const canvasHeight = canvas.getHeight()
|
||||||
|
const offsetX = canvasWidth / 2 - centerX
|
||||||
|
const offsetY = canvasHeight / 2 - centerY
|
||||||
|
|
||||||
|
canvas.viewportTransform = [1, 0, 0, 1, offsetX, offsetY]
|
||||||
|
} else {
|
||||||
|
canvas.viewportTransform = [1, 0, 0, 1, 0, 0]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
canvas.viewportTransform = [1, 0, 0, 1, 0, 0]
|
||||||
|
}
|
||||||
|
|
||||||
|
const modules = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE)
|
||||||
|
const circuitNumberTexts = canvas.getObjects().filter((obj) => obj.name === 'circuitNumber')
|
||||||
|
|
||||||
|
if (type === 2) {
|
||||||
|
modules.forEach((module) => {
|
||||||
|
module.set({ originColor: module.fill, fill: null, strokeWidth: 2 })
|
||||||
|
})
|
||||||
|
circuitNumberTexts.forEach((text) => {
|
||||||
|
text.set({ visible: false })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas.renderAll()
|
||||||
|
|
||||||
|
// roof polygon들의 중간점 계산
|
||||||
|
const roofPolygons = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
|
||||||
|
let x, y
|
||||||
|
x = canvas.width / 2
|
||||||
|
y = canvas.height / 2
|
||||||
|
|
||||||
|
canvas.zoomToPoint(new fabric.Point(x, y), 0.4)
|
||||||
changeFontSize('lengthText', '28')
|
changeFontSize('lengthText', '28')
|
||||||
changeFontSize('circuitNumber', '28')
|
changeFontSize('circuitNumber', '28')
|
||||||
changeFontSize('flowText', '28')
|
changeFontSize('flowText', '28')
|
||||||
@ -120,13 +187,27 @@ export default function CircuitTrestleSetting({ id }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 캡쳐 후 처리
|
// 캡쳐 후 처리
|
||||||
const afterCapture = () => {
|
const afterCapture = (type) => {
|
||||||
setCanvasZoom(100)
|
setCanvasZoom(100)
|
||||||
canvas.set({ zoom: 1 })
|
canvas.set({ zoom: 1 })
|
||||||
canvas.viewportTransform = [1, 0, 0, 1, 0, 0]
|
canvas.viewportTransform = [1, 0, 0, 1, 0, 0]
|
||||||
changeFontSize('lengthText', lengthText.fontSize.value)
|
changeFontSize('lengthText', lengthText.fontSize.value)
|
||||||
changeFontSize('circuitNumber', circuitNumberText.fontSize.value)
|
changeFontSize('circuitNumber', circuitNumberText.fontSize.value)
|
||||||
changeFontSize('flowText', flowText.fontSize.value)
|
changeFontSize('flowText', flowText.fontSize.value)
|
||||||
|
|
||||||
|
const modules = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE)
|
||||||
|
const circuitNumberTexts = canvas.getObjects().filter((obj) => obj.name === 'circuitNumber')
|
||||||
|
if (type === 2) {
|
||||||
|
modules.forEach((module) => {
|
||||||
|
module.set({
|
||||||
|
fill: module.originColor,
|
||||||
|
strokeWidth: 0.3,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
circuitNumberTexts.forEach((text) => {
|
||||||
|
text.set({ visible: true })
|
||||||
|
})
|
||||||
|
}
|
||||||
canvas.renderAll()
|
canvas.renderAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -387,9 +468,7 @@ export default function CircuitTrestleSetting({ id }) {
|
|||||||
.map((obj) => {
|
.map((obj) => {
|
||||||
obj.pcses = getStepUpListData()
|
obj.pcses = getStepUpListData()
|
||||||
})
|
})
|
||||||
beforeCapture()
|
await capture(1)
|
||||||
handleCanvasToPng(1)
|
|
||||||
afterCapture()
|
|
||||||
|
|
||||||
//회로할당 저장 시 result=null인 경우에도 회로번호 텍스트 표시 유지 처리
|
//회로할당 저장 시 result=null인 경우에도 회로번호 텍스트 표시 유지 처리
|
||||||
|
|
||||||
@ -406,9 +485,7 @@ export default function CircuitTrestleSetting({ id }) {
|
|||||||
const result = await getEstimateData()
|
const result = await getEstimateData()
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
beforeCapture()
|
await capture(2)
|
||||||
handleCanvasToPng(2)
|
|
||||||
afterCapture()
|
|
||||||
// 견적서 저장
|
// 견적서 저장
|
||||||
await saveEstimate(result)
|
await saveEstimate(result)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -74,11 +74,9 @@ export default function PowerConditionalSelect(props) {
|
|||||||
]
|
]
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (makers.length === 0) {
|
getPcsMakerList().then((res) => {
|
||||||
getPcsMakerList().then((res) => {
|
setMakers(res.data)
|
||||||
setMakers(res.data)
|
})
|
||||||
})
|
|
||||||
}
|
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const onCheckSeries = (data) => {
|
const onCheckSeries = (data) => {
|
||||||
@ -202,6 +200,7 @@ export default function PowerConditionalSelect(props) {
|
|||||||
const param = {
|
const param = {
|
||||||
pcsMkrCd: option.pcsMkrCd,
|
pcsMkrCd: option.pcsMkrCd,
|
||||||
mixMatlNo: moduleSelectionData.module.mixMatlNo,
|
mixMatlNo: moduleSelectionData.module.mixMatlNo,
|
||||||
|
moduleMatlCds: moduleSelectionData.module.itemList.map((item) => item.itemId).join(','),
|
||||||
}
|
}
|
||||||
|
|
||||||
getPcsMakerList(param).then((res) => {
|
getPcsMakerList(param).then((res) => {
|
||||||
|
|||||||
@ -109,6 +109,7 @@ export default function StepUp(props) {
|
|||||||
/** 캔버스에 회로 정보 적용 */
|
/** 캔버스에 회로 정보 적용 */
|
||||||
// 병설일때 pcs 있으면 setSubOpsions, 없으면 setMainOptions
|
// 병설일때 pcs 있으면 setSubOpsions, 없으면 setMainOptions
|
||||||
console.log('stepUpListData', stepUpListData)
|
console.log('stepUpListData', stepUpListData)
|
||||||
|
let mChk = 0;
|
||||||
stepUpListData[0].pcsItemList.forEach((pcsItem, index) => {
|
stepUpListData[0].pcsItemList.forEach((pcsItem, index) => {
|
||||||
const optionList = formatOptionCodes(pcsItem.optionList)
|
const optionList = formatOptionCodes(pcsItem.optionList)
|
||||||
if (isMultiOptions()) {
|
if (isMultiOptions()) {
|
||||||
@ -164,6 +165,8 @@ export default function StepUp(props) {
|
|||||||
targetModule.pcsItemId = module.pcsItemId
|
targetModule.pcsItemId = module.pcsItemId
|
||||||
targetModule.circuitNumber = module.circuit
|
targetModule.circuitNumber = module.circuit
|
||||||
canvas.add(moduleCircuitText)
|
canvas.add(moduleCircuitText)
|
||||||
|
} else {
|
||||||
|
mChk++;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -172,6 +175,10 @@ export default function StepUp(props) {
|
|||||||
|
|
||||||
canvas.renderAll()
|
canvas.renderAll()
|
||||||
setModuleStatisticsData()
|
setModuleStatisticsData()
|
||||||
|
|
||||||
|
if (mChk > 0) {
|
||||||
|
swalFire({ text: getMessage('modal.circuit.trestle.setting.step.up.allocation.module.over.count') })
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
swalFire({ text: getMessage('common.message.send.error') })
|
swalFire({ text: getMessage('common.message.send.error') })
|
||||||
}
|
}
|
||||||
@ -467,7 +474,7 @@ export default function StepUp(props) {
|
|||||||
module.pcsItemId = null
|
module.pcsItemId = null
|
||||||
})
|
})
|
||||||
|
|
||||||
/** 선택된 모듈 목록 추가 */
|
/** 선택된 모듈 목록 추가 */
|
||||||
selectedData.roofSurfaceList.forEach((roofSurface) => {
|
selectedData.roofSurfaceList.forEach((roofSurface) => {
|
||||||
const targetSurface = canvas.getObjects().filter((obj) => obj.id === roofSurface.roofSurfaceId)[0]
|
const targetSurface = canvas.getObjects().filter((obj) => obj.id === roofSurface.roofSurfaceId)[0]
|
||||||
const moduleIds = targetSurface.modules.map((module) => {
|
const moduleIds = targetSurface.modules.map((module) => {
|
||||||
@ -516,7 +523,7 @@ export default function StepUp(props) {
|
|||||||
|
|
||||||
canvas.renderAll()
|
canvas.renderAll()
|
||||||
setModuleStatisticsData()
|
setModuleStatisticsData()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 현재 선택된 값들을 가져오는 함수 추가
|
* 현재 선택된 값들을 가져오는 함수 추가
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import { selectedModuleState } from '@/store/selectedModuleOptions'
|
|||||||
import { circuitNumDisplaySelector } from '@/store/settingAtom'
|
import { circuitNumDisplaySelector } from '@/store/settingAtom'
|
||||||
import { useContext, useEffect, useState } from 'react'
|
import { useContext, useEffect, useState } from 'react'
|
||||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||||
|
import { normalizeDigits } from '@/util/input-utils'
|
||||||
|
|
||||||
export default function PassivityCircuitAllocation(props) {
|
export default function PassivityCircuitAllocation(props) {
|
||||||
const {
|
const {
|
||||||
@ -580,7 +581,20 @@ export default function PassivityCircuitAllocation(props) {
|
|||||||
value={circuitNumber}
|
value={circuitNumber}
|
||||||
min={1}
|
min={1}
|
||||||
max={99}
|
max={99}
|
||||||
onChange={(e) => setCircuitNumber(e.target.value)}
|
onChange={(e) => {
|
||||||
|
const v = e.target.value
|
||||||
|
if (v === '') {
|
||||||
|
setCircuitNumber('')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const n = Number(normalizeDigits(v))
|
||||||
|
if (Number.isNaN(n)) {
|
||||||
|
setCircuitNumber('')
|
||||||
|
} else {
|
||||||
|
const clamped = Math.max(1, Math.min(99, n))
|
||||||
|
setCircuitNumber(clamped)
|
||||||
|
}
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<button className="btn-frame roof" onClick={() => handleCircuitNumberFix()}>
|
<button className="btn-frame roof" onClick={() => handleCircuitNumberFix()}>
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import { defaultSlope } from '@/store/commonAtom'
|
|||||||
import { re } from 'mathjs'
|
import { re } from 'mathjs'
|
||||||
import { basicSettingState } from '@/store/settingAtom'
|
import { basicSettingState } from '@/store/settingAtom'
|
||||||
import { getChonByDegree, getDegreeByChon } from '@/util/canvas-util'
|
import { getChonByDegree, getDegreeByChon } from '@/util/canvas-util'
|
||||||
|
import { normalizeDigits } from '@/util/input-utils'
|
||||||
|
|
||||||
export default function DimensionLineSetting(props) {
|
export default function DimensionLineSetting(props) {
|
||||||
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
||||||
@ -144,8 +145,13 @@ export default function DimensionLineSetting(props) {
|
|||||||
defaultValue={''}
|
defaultValue={''}
|
||||||
value={changeLength}
|
value={changeLength}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
console.log(e.target)
|
const v = e.target.value
|
||||||
setChangeLength(e.target.value)
|
if (v === '') {
|
||||||
|
setChangeLength('')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const n = Number(normalizeDigits(v))
|
||||||
|
setChangeLength(Number.isNaN(n) ? '' : n)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import Image from 'next/image'
|
|||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { useRecoilValue } from 'recoil'
|
import { useRecoilValue } from 'recoil'
|
||||||
import { ANGLE_TYPE, currentAngleTypeSelector } from '@/store/canvasAtom'
|
import { ANGLE_TYPE, currentAngleTypeSelector } from '@/store/canvasAtom'
|
||||||
|
import { normalizeDecimalLimit, normalizeDigits } from '@/util/input-utils'
|
||||||
|
|
||||||
export default function Eaves({ pitchRef, offsetRef, widthRef, radioTypeRef, pitchText }) {
|
export default function Eaves({ pitchRef, offsetRef, widthRef, radioTypeRef, pitchText }) {
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
@ -20,7 +21,17 @@ export default function Eaves({ pitchRef, offsetRef, widthRef, radioTypeRef, pit
|
|||||||
{getMessage('slope')}
|
{getMessage('slope')}
|
||||||
</span>
|
</span>
|
||||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||||
<input type="text" className="input-origin block" defaultValue={currentAngleType === ANGLE_TYPE.SLOPE ? 4 : 21.8} ref={pitchRef} />
|
<input
|
||||||
|
type="text"
|
||||||
|
className="input-origin block"
|
||||||
|
defaultValue={currentAngleType === ANGLE_TYPE.SLOPE ? 4 : 21.8}
|
||||||
|
ref={pitchRef}
|
||||||
|
onChange={(e) => {
|
||||||
|
const v = normalizeDecimalLimit(e.target.value, 2)
|
||||||
|
e.target.value = v
|
||||||
|
if (pitchRef?.current) pitchRef.current.value = v
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<span className="thin">{pitchText}</span>
|
<span className="thin">{pitchText}</span>
|
||||||
</div>
|
</div>
|
||||||
@ -29,7 +40,17 @@ export default function Eaves({ pitchRef, offsetRef, widthRef, radioTypeRef, pit
|
|||||||
{getMessage('offset')}
|
{getMessage('offset')}
|
||||||
</span>
|
</span>
|
||||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||||
<input type="text" className="input-origin block" defaultValue={500} ref={offsetRef} />
|
<input
|
||||||
|
type="text"
|
||||||
|
className="input-origin block"
|
||||||
|
defaultValue={500}
|
||||||
|
ref={offsetRef}
|
||||||
|
onChange={(e) => {
|
||||||
|
const v = normalizeDigits(e.target.value)
|
||||||
|
e.target.value = v
|
||||||
|
if (offsetRef?.current) offsetRef.current.value = v
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<span className="thin">mm</span>
|
<span className="thin">mm</span>
|
||||||
</div>
|
</div>
|
||||||
@ -45,7 +66,9 @@ export default function Eaves({ pitchRef, offsetRef, widthRef, radioTypeRef, pit
|
|||||||
</div>
|
</div>
|
||||||
<div className="eaves-keraba-td">
|
<div className="eaves-keraba-td">
|
||||||
<div className={`eaves-keraba-ico ${type === '1' ? 'act' : ''}`}>
|
<div className={`eaves-keraba-ico ${type === '1' ? 'act' : ''}`}>
|
||||||
<Image src="/static/images/canvas/eaves_icon01.svg" alt="react" width={30} height={30} />
|
<div style={{ width: 30, height: 30, position: 'relative' }}>
|
||||||
|
<Image src="/static/images/canvas/eaves_icon01.svg" alt="react" fill style={{ objectFit: 'contain' }} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -58,7 +81,9 @@ export default function Eaves({ pitchRef, offsetRef, widthRef, radioTypeRef, pit
|
|||||||
</div>
|
</div>
|
||||||
<div className="eaves-keraba-td">
|
<div className="eaves-keraba-td">
|
||||||
<div className={`eaves-keraba-ico ${type === '2' ? 'act' : ''}`}>
|
<div className={`eaves-keraba-ico ${type === '2' ? 'act' : ''}`}>
|
||||||
<Image src="/static/images/canvas/eaves_icon02.svg" alt="react" width={30} height={30} />
|
<div style={{ width: 30, height: 30, position: 'relative' }}>
|
||||||
|
<Image src="/static/images/canvas/eaves_icon02.svg" alt="react" fill style={{ objectFit: 'contain' }} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -66,14 +91,27 @@ export default function Eaves({ pitchRef, offsetRef, widthRef, radioTypeRef, pit
|
|||||||
<div className="eaves-keraba-th">
|
<div className="eaves-keraba-th">
|
||||||
<div className="outline-form">
|
<div className="outline-form">
|
||||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||||
<input type="number" min={0} className="input-origin block" defaultValue={500} ref={widthRef} readOnly={type === '1'} />
|
<input
|
||||||
|
type="text"
|
||||||
|
className="input-origin block"
|
||||||
|
defaultValue={500}
|
||||||
|
ref={widthRef}
|
||||||
|
readOnly={type === '1'}
|
||||||
|
onChange={(e) => {
|
||||||
|
const v = normalizeDigits(e.target.value)
|
||||||
|
e.target.value = v
|
||||||
|
if (widthRef?.current) widthRef.current.value = v
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<span className="thin">mm</span>
|
<span className="thin">mm</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="eaves-keraba-td">
|
<div className="eaves-keraba-td">
|
||||||
<div className="eaves-keraba-ico ">
|
<div className="eaves-keraba-ico ">
|
||||||
<Image src="/static/images/canvas/eaves_icon03.svg" alt="react" width={30} height={30} />
|
<div style={{ width: 30, height: 30, position: 'relative' }}>
|
||||||
|
<Image src="/static/images/canvas/eaves_icon03.svg" alt="react" fill style={{ objectFit: 'contain' }} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -37,7 +37,9 @@ export default function Gable({ pitchRef, offsetRef, widthRef, radioTypeRef, pit
|
|||||||
</div>
|
</div>
|
||||||
<div className="eaves-keraba-td">
|
<div className="eaves-keraba-td">
|
||||||
<div className={`eaves-keraba-ico ${type === '1' ? 'act' : ''}`}>
|
<div className={`eaves-keraba-ico ${type === '1' ? 'act' : ''}`}>
|
||||||
<Image src="/static/images/canvas/eaves_icon04.svg" alt="react" width={30} height={30} />
|
<div style={{ width: 30, height: 30, position: 'relative' }}>
|
||||||
|
<Image src="/static/images/canvas/eaves_icon04.svg" alt="react" fill style={{ objectFit: 'contain' }} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -50,7 +52,9 @@ export default function Gable({ pitchRef, offsetRef, widthRef, radioTypeRef, pit
|
|||||||
</div>
|
</div>
|
||||||
<div className="eaves-keraba-td">
|
<div className="eaves-keraba-td">
|
||||||
<div className={`eaves-keraba-ico ${type === '2' ? 'act' : ''}`}>
|
<div className={`eaves-keraba-ico ${type === '2' ? 'act' : ''}`}>
|
||||||
<Image src="/static/images/canvas/eaves_icon09.svg" alt="react" width={30} height={30} />
|
<div style={{ width: 30, height: 30, position: 'relative' }}>
|
||||||
|
<Image src="/static/images/canvas/eaves_icon09.svg" alt="react" fill style={{ objectFit: 'contain' }} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -74,7 +78,9 @@ export default function Gable({ pitchRef, offsetRef, widthRef, radioTypeRef, pit
|
|||||||
</div>
|
</div>
|
||||||
<div className="eaves-keraba-td">
|
<div className="eaves-keraba-td">
|
||||||
<div className="eaves-keraba-ico ">
|
<div className="eaves-keraba-ico ">
|
||||||
<Image src="/static/images/canvas/eaves_icon05.svg" alt="react" width={30} height={30} />
|
<div style={{ width: 30, height: 30, position: 'relative' }}>
|
||||||
|
<Image src="/static/images/canvas/eaves_icon05.svg" alt="react" fill style={{ objectFit: 'contain' }} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -92,7 +98,9 @@ export default function Gable({ pitchRef, offsetRef, widthRef, radioTypeRef, pit
|
|||||||
</div>
|
</div>
|
||||||
<div className="eaves-keraba-td">
|
<div className="eaves-keraba-td">
|
||||||
<div className="eaves-keraba-ico ">
|
<div className="eaves-keraba-ico ">
|
||||||
<Image src="/static/images/canvas/eaves_icon10.svg" alt="react" width={30} height={30} />
|
<div style={{ width: 30, height: 30, position: 'relative' }}>
|
||||||
|
<Image src="/static/images/canvas/eaves_icon10.svg" alt="react" fill style={{ objectFit: 'contain' }} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -23,7 +23,9 @@ export default function WallMerge({ offsetRef, radioTypeRef }) {
|
|||||||
</div>
|
</div>
|
||||||
<div className="eaves-keraba-td">
|
<div className="eaves-keraba-td">
|
||||||
<div className={`eaves-keraba-ico ${type === '1' ? 'act' : ''}`}>
|
<div className={`eaves-keraba-ico ${type === '1' ? 'act' : ''}`}>
|
||||||
<Image src="/static/images/canvas/eaves_icon06.svg" alt="react" width={30} height={30} />
|
<div style={{ width: 30, height: 30, position: 'relative' }}>
|
||||||
|
<Image src="/static/images/canvas/eaves_icon06.svg" alt="react" fill style={{ objectFit: 'contain' }} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -36,7 +38,9 @@ export default function WallMerge({ offsetRef, radioTypeRef }) {
|
|||||||
</div>
|
</div>
|
||||||
<div className="eaves-keraba-td">
|
<div className="eaves-keraba-td">
|
||||||
<div className={`eaves-keraba-ico ${type === '2' ? 'act' : ''}`}>
|
<div className={`eaves-keraba-ico ${type === '2' ? 'act' : ''}`}>
|
||||||
<Image src="/static/images/canvas/eaves_icon07.svg" alt="react" width={30} height={30} />
|
<div style={{ width: 30, height: 30, position: 'relative' }}>
|
||||||
|
<Image src="/static/images/canvas/eaves_icon07.svg" alt="react" fill style={{ objectFit: 'contain' }} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -54,7 +58,9 @@ export default function WallMerge({ offsetRef, radioTypeRef }) {
|
|||||||
</div>
|
</div>
|
||||||
<div className="eaves-keraba-td">
|
<div className="eaves-keraba-td">
|
||||||
<div className="eaves-keraba-ico ">
|
<div className="eaves-keraba-ico ">
|
||||||
<Image src="/static/images/canvas/eaves_icon08.svg" alt="react" width={30} height={30} />
|
<div style={{ width: 30, height: 30, position: 'relative' }}>
|
||||||
|
<Image src="/static/images/canvas/eaves_icon08.svg" alt="react" fill style={{ objectFit: 'contain' }} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -8,6 +8,7 @@ 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'
|
import { useSurfaceShapeBatch } from '@/hooks/surface/useSurfaceShapeBatch'
|
||||||
|
import { useRoofFn } from '@/hooks/common/useRoofFn'
|
||||||
|
|
||||||
const FLOW_DIRECTION_TYPE = {
|
const FLOW_DIRECTION_TYPE = {
|
||||||
EIGHT_AZIMUTH: 'eightAzimuth',
|
EIGHT_AZIMUTH: 'eightAzimuth',
|
||||||
@ -19,6 +20,7 @@ export default function FlowDirectionSetting(props) {
|
|||||||
const { id, pos = contextPopupPosition, target } = props
|
const { id, pos = contextPopupPosition, target } = props
|
||||||
const canvas = useRecoilValue(canvasState)
|
const canvas = useRecoilValue(canvasState)
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
|
const { setSurfaceShapePattern } = useRoofFn()
|
||||||
|
|
||||||
const { changeSurfaceLineType } = useSurfaceShapeBatch({})
|
const { changeSurfaceLineType } = useSurfaceShapeBatch({})
|
||||||
|
|
||||||
@ -32,15 +34,40 @@ export default function FlowDirectionSetting(props) {
|
|||||||
const [flowDirection, setFlowDirection] = useState(target.direction)
|
const [flowDirection, setFlowDirection] = useState(target.direction)
|
||||||
const { closePopup } = usePopup()
|
const { closePopup } = usePopup()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let newCompassDeg = 0
|
||||||
|
if ([-15, 0, 15].includes(compasDeg)) {
|
||||||
|
newCompassDeg = 0
|
||||||
|
} else if ([30, 45, 60].includes(compasDeg)) {
|
||||||
|
newCompassDeg = 45
|
||||||
|
} else if ([75, 90, 105].includes(compasDeg)) {
|
||||||
|
newCompassDeg = 90
|
||||||
|
} else if ([120, 135, 150].includes(compasDeg)) {
|
||||||
|
newCompassDeg = 135
|
||||||
|
} else if ([165, 180, -165].includes(compasDeg)) {
|
||||||
|
newCompassDeg = 180
|
||||||
|
} else if ([-120, -135, -150].includes(compasDeg)) {
|
||||||
|
newCompassDeg = -135
|
||||||
|
} else if ([-105, -90, -75].includes(compasDeg)) {
|
||||||
|
newCompassDeg = -90
|
||||||
|
} else if ([-60, -45, -30].includes(compasDeg)) {
|
||||||
|
newCompassDeg = -45
|
||||||
|
} else {
|
||||||
|
newCompassDeg = ''
|
||||||
|
}
|
||||||
|
const newOrientation = orientations.find((item) => item.value === newCompassDeg)
|
||||||
|
setSelectedOrientation(newOrientation)
|
||||||
|
}, [compasDeg])
|
||||||
|
|
||||||
const orientations = [
|
const orientations = [
|
||||||
{ name: `${getMessage('commons.none')}`, value: 0 },
|
{ name: `${getMessage('commons.none')}`, value: '' },
|
||||||
{ name: `${getMessage('commons.south')}`, value: 360 },
|
{ name: `${getMessage('commons.south')}`, value: 0 },
|
||||||
{ name: `${getMessage('commons.south')}${getMessage('commons.east')}`, value: 315 },
|
{ name: `${getMessage('commons.south')}${getMessage('commons.east')}`, value: 45 },
|
||||||
{ name: `${getMessage('commons.south')}${getMessage('commons.west')}`, value: 45 },
|
{ name: `${getMessage('commons.south')}${getMessage('commons.west')}`, value: -45 },
|
||||||
{ name: `${getMessage('commons.east')}`, value: 270 },
|
{ name: `${getMessage('commons.east')}`, value: 90 },
|
||||||
{ name: `${getMessage('commons.west')}`, value: 90 },
|
{ name: `${getMessage('commons.west')}`, value: -90 },
|
||||||
{ name: `${getMessage('commons.north')}${getMessage('commons.east')}`, value: 225 },
|
{ name: `${getMessage('commons.north')}${getMessage('commons.east')}`, value: 135 },
|
||||||
{ name: `${getMessage('commons.north')}${getMessage('commons.west')}`, value: 135 },
|
{ name: `${getMessage('commons.north')}${getMessage('commons.west')}`, value: -135 },
|
||||||
{ name: `${getMessage('commons.north')}`, value: 180 },
|
{ name: `${getMessage('commons.north')}`, value: 180 },
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -54,6 +81,7 @@ export default function FlowDirectionSetting(props) {
|
|||||||
surfaceCompass: orientation,
|
surfaceCompass: orientation,
|
||||||
surfaceCompassType: type,
|
surfaceCompassType: type,
|
||||||
})
|
})
|
||||||
|
setSurfaceShapePattern(roof, null, null, roof.roofMaterial)
|
||||||
drawDirectionArrow(roof)
|
drawDirectionArrow(roof)
|
||||||
canvas?.renderAll()
|
canvas?.renderAll()
|
||||||
changeSurfaceLineType(roof)
|
changeSurfaceLineType(roof)
|
||||||
@ -109,6 +137,11 @@ export default function FlowDirectionSetting(props) {
|
|||||||
value={selectedOrientation}
|
value={selectedOrientation}
|
||||||
options={orientations}
|
options={orientations}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
|
if (e.value === '') {
|
||||||
|
setCompasDeg(null)
|
||||||
|
setSelectedOrientation(e)
|
||||||
|
return
|
||||||
|
}
|
||||||
setType(FLOW_DIRECTION_TYPE.EIGHT_AZIMUTH)
|
setType(FLOW_DIRECTION_TYPE.EIGHT_AZIMUTH)
|
||||||
setSelectedOrientation(e)
|
setSelectedOrientation(e)
|
||||||
setCompasDeg(e.value)
|
setCompasDeg(e.value)
|
||||||
@ -137,31 +170,32 @@ export default function FlowDirectionSetting(props) {
|
|||||||
<div className="draw-flow-wrap">
|
<div className="draw-flow-wrap">
|
||||||
<div className="compas-box">
|
<div className="compas-box">
|
||||||
<div className="compas-box-inner">
|
<div className="compas-box-inner">
|
||||||
{Array.from({ length: 180 / 15 + 1 }).map((dot, index) => (
|
{Array.from({ length: 180 / 15 }).map((dot, index) => (
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={index}
|
||||||
className={`circle ${compasDeg === 15 * (12 + index) && type === FLOW_DIRECTION_TYPE.TWENTY_FOUR_AZIMUTH ? 'act' : ''}`}
|
className={`circle ${compasDeg === -15 * index + 180 ? 'act' : ''}`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
if (index === 0) {
|
||||||
|
setCompasDeg(180)
|
||||||
|
return
|
||||||
|
}
|
||||||
setType(FLOW_DIRECTION_TYPE.TWENTY_FOUR_AZIMUTH)
|
setType(FLOW_DIRECTION_TYPE.TWENTY_FOUR_AZIMUTH)
|
||||||
setCompasDeg(15 * (12 + index))
|
setCompasDeg(-15 * index + 180)
|
||||||
}}
|
}}
|
||||||
></div>
|
></div>
|
||||||
))}
|
))}
|
||||||
{Array.from({ length: 180 / 15 - 1 }).map((dot, index) => (
|
{Array.from({ length: 180 / 15 }).map((dot, index) => (
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={index}
|
||||||
className={`circle ${compasDeg === 15 * (index + 1) && type === FLOW_DIRECTION_TYPE.TWENTY_FOUR_AZIMUTH ? 'act' : ''}`}
|
className={`circle ${compasDeg === -1 * 15 * index ? 'act' : ''}`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setType(FLOW_DIRECTION_TYPE.TWENTY_FOUR_AZIMUTH)
|
setType(FLOW_DIRECTION_TYPE.TWENTY_FOUR_AZIMUTH)
|
||||||
setCompasDeg(15 * (index + 1))
|
setCompasDeg(15 * index * -1)
|
||||||
}}
|
}}
|
||||||
></div>
|
></div>
|
||||||
))}
|
))}
|
||||||
<div className="compas">
|
<div className="compas">
|
||||||
<div
|
<div className="compas-arr" style={{ transform: `${`rotate(${-1 * compasDeg}deg)`}` }}></div>
|
||||||
className="compas-arr"
|
|
||||||
style={{ transform: `${type === FLOW_DIRECTION_TYPE.TWENTY_FOUR_AZIMUTH && `rotate(${compasDeg}deg)`}` }}
|
|
||||||
></div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -4,10 +4,10 @@ import { useEffect, useState } from 'react'
|
|||||||
import { useMessage } from '@/hooks/useMessage'
|
import { useMessage } from '@/hooks/useMessage'
|
||||||
import { canvasState } from '@/store/canvasAtom'
|
import { canvasState } from '@/store/canvasAtom'
|
||||||
import { useRecoilValue } from 'recoil'
|
import { useRecoilValue } from 'recoil'
|
||||||
import { onlyNumberInputChange } from '@/util/input-utils'
|
|
||||||
import { usePopup } from '@/hooks/usePopup'
|
import { usePopup } from '@/hooks/usePopup'
|
||||||
import { useCanvasSetting } from '@/hooks/option/useCanvasSetting'
|
import { useCanvasSetting } from '@/hooks/option/useCanvasSetting'
|
||||||
import { useSwal } from '@/hooks/useSwal'
|
import { useSwal } from '@/hooks/useSwal'
|
||||||
|
import { normalizeDigits } from '@/util/input-utils'
|
||||||
|
|
||||||
const TYPE = {
|
const TYPE = {
|
||||||
DOT: 'DOT',
|
DOT: 'DOT',
|
||||||
@ -218,7 +218,7 @@ export default function DotLineGrid(props) {
|
|||||||
className="input-origin"
|
className="input-origin"
|
||||||
name={`horizontalInterval`}
|
name={`horizontalInterval`}
|
||||||
value={copyCurrentSetting.INTERVAL.horizontalInterval}
|
value={copyCurrentSetting.INTERVAL.horizontalInterval}
|
||||||
onChange={(e) => onlyNumberInputChange(e, changeInput)}
|
onChange={(e) => changeInput(normalizeDigits(e.target.value), e)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<span>mm</span>
|
<span>mm</span>
|
||||||
@ -231,7 +231,7 @@ export default function DotLineGrid(props) {
|
|||||||
className="input-origin"
|
className="input-origin"
|
||||||
name={`verticalInterval`}
|
name={`verticalInterval`}
|
||||||
value={copyCurrentSetting.INTERVAL.verticalInterval}
|
value={copyCurrentSetting.INTERVAL.verticalInterval}
|
||||||
onChange={(e) => onlyNumberInputChange(e, changeInput)}
|
onChange={(e) => changeInput(normalizeDigits(e.target.value), e)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<span>mm</span>
|
<span>mm</span>
|
||||||
@ -258,7 +258,7 @@ export default function DotLineGrid(props) {
|
|||||||
className="input-origin"
|
className="input-origin"
|
||||||
name={`ratioInterval`}
|
name={`ratioInterval`}
|
||||||
value={copyCurrentSetting.INTERVAL.ratioInterval}
|
value={copyCurrentSetting.INTERVAL.ratioInterval}
|
||||||
onChange={(e) => onlyNumberInputChange(e, changeInput)}
|
onChange={(e) => changeInput(normalizeDigits(e.target.value), e)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<span>mm</span>
|
<span>mm</span>
|
||||||
|
|||||||
@ -5,9 +5,9 @@ import { useRecoilValue } from 'recoil'
|
|||||||
import { contextPopupPositionState } from '@/store/popupAtom'
|
import { contextPopupPositionState } from '@/store/popupAtom'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { canvasState, currentObjectState } from '@/store/canvasAtom'
|
import { canvasState, currentObjectState } from '@/store/canvasAtom'
|
||||||
import { useGrid } from '@/hooks/common/useGrid'
|
|
||||||
import { gridColorState } from '@/store/gridAtom'
|
import { gridColorState } from '@/store/gridAtom'
|
||||||
import { gridDisplaySelector } from '@/store/settingAtom'
|
import { gridDisplaySelector } from '@/store/settingAtom'
|
||||||
|
|
||||||
const GRID_PADDING = 5
|
const GRID_PADDING = 5
|
||||||
export default function GridCopy(props) {
|
export default function GridCopy(props) {
|
||||||
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
||||||
@ -25,10 +25,10 @@ export default function GridCopy(props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const copy = (object, length) => {
|
const copy = (object, length) => {
|
||||||
const lineStartX = object.direction === 'vertical' ? object.x1 + length : 0
|
const lineStartX = object.direction === 'vertical' ? object.x1 + length : object.x1
|
||||||
const lineEndX = object.direction === 'vertical' ? object.x2 + length : canvas.width
|
const lineEndX = object.direction === 'vertical' ? object.x2 + length : object.x2
|
||||||
const lineStartY = object.direction === 'vertical' ? 0 : object.y1 + length
|
const lineStartY = object.direction === 'vertical' ? object.y1 : object.y1 + length
|
||||||
const lineEndY = object.direction === 'vertical' ? canvas.width : object.y1 + length
|
const lineEndY = object.direction === 'vertical' ? object.y2 : object.y1 + length
|
||||||
|
|
||||||
const line = new fabric.Line([lineStartX, lineStartY, lineEndX, lineEndY], {
|
const line = new fabric.Line([lineStartX, lineStartY, lineEndX, lineEndY], {
|
||||||
stroke: gridColor,
|
stroke: gridColor,
|
||||||
|
|||||||
@ -3,11 +3,10 @@ import { useMessage } from '@/hooks/useMessage'
|
|||||||
import { usePopup } from '@/hooks/usePopup'
|
import { usePopup } from '@/hooks/usePopup'
|
||||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||||
import { contextPopupPositionState } from '@/store/popupAtom'
|
import { contextPopupPositionState } from '@/store/popupAtom'
|
||||||
import { useCanvas } from '@/hooks/useCanvas'
|
|
||||||
import { canvasState, currentObjectState } from '@/store/canvasAtom'
|
import { canvasState, currentObjectState } from '@/store/canvasAtom'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { useSwal } from '@/hooks/useSwal'
|
import { useSwal } from '@/hooks/useSwal'
|
||||||
import { set } from 'react-hook-form'
|
import { normalizeDigits } from '@/util/input-utils'
|
||||||
|
|
||||||
export default function GridMove(props) {
|
export default function GridMove(props) {
|
||||||
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
||||||
@ -52,15 +51,15 @@ export default function GridMove(props) {
|
|||||||
.forEach((grid) => {
|
.forEach((grid) => {
|
||||||
move(
|
move(
|
||||||
grid,
|
grid,
|
||||||
arrow2 === '←' ? (Number(horizonSize) * -1) / 10 : Number(horizonSize) / 10,
|
arrow2 === '←' ? (Number(normalizeDigits(horizonSize)) * -1) / 10 : Number(normalizeDigits(horizonSize)) / 10,
|
||||||
arrow1 === '↑' ? (Number(verticalSize) * -1) / 10 : Number(verticalSize) / 10,
|
arrow1 === '↑' ? (Number(normalizeDigits(verticalSize)) * -1) / 10 : Number(normalizeDigits(verticalSize)) / 10,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
move(
|
move(
|
||||||
currentObject,
|
currentObject,
|
||||||
arrow2 === '←' ? (Number(horizonSize) * -1) / 10 : Number(horizonSize) / 10,
|
arrow2 === '←' ? (Number(normalizeDigits(horizonSize)) * -1) / 10 : Number(normalizeDigits(horizonSize)) / 10,
|
||||||
arrow1 === '↑' ? (Number(verticalSize) * -1) / 10 : Number(verticalSize) / 10,
|
arrow1 === '↑' ? (Number(normalizeDigits(verticalSize)) * -1) / 10 : Number(normalizeDigits(verticalSize)) / 10,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
canvas.renderAll()
|
canvas.renderAll()
|
||||||
@ -70,10 +69,10 @@ export default function GridMove(props) {
|
|||||||
const move = (object, x, y) => {
|
const move = (object, x, y) => {
|
||||||
object.set({
|
object.set({
|
||||||
...object,
|
...object,
|
||||||
x1: object.direction === 'vertical' ? object.x1 + x : 0,
|
x1: object.direction === 'vertical' ? object.x1 + x : object.x1,
|
||||||
x2: object.direction === 'vertical' ? object.x1 + x : canvas.width,
|
x2: object.direction === 'vertical' ? object.x1 + x : object.x2,
|
||||||
y1: object.direction === 'vertical' ? 0 : object.y1 + y,
|
y1: object.direction === 'vertical' ? object.y1 : object.y1 + y,
|
||||||
y2: object.direction === 'vertical' ? canvas.height : object.y1 + y,
|
y2: object.direction === 'vertical' ? object.y2 : object.y1 + y,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,7 +98,7 @@ export default function GridMove(props) {
|
|||||||
type="text"
|
type="text"
|
||||||
className="input-origin"
|
className="input-origin"
|
||||||
value={verticalSize}
|
value={verticalSize}
|
||||||
onChange={(e) => setVerticalSize(e.target.value)}
|
onChange={(e) => setVerticalSize(normalizeDigits(e.target.value))}
|
||||||
readOnly={!isAll && currentObject?.direction === 'vertical'}
|
readOnly={!isAll && currentObject?.direction === 'vertical'}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -127,7 +126,7 @@ export default function GridMove(props) {
|
|||||||
type="text"
|
type="text"
|
||||||
className="input-origin"
|
className="input-origin"
|
||||||
value={horizonSize}
|
value={horizonSize}
|
||||||
onChange={(e) => setHorizonSize(e.target.value)}
|
onChange={(e) => setHorizonSize(normalizeDigits(e.target.value))}
|
||||||
readOnly={!isAll && currentObject?.direction === 'horizontal'}
|
readOnly={!isAll && currentObject?.direction === 'horizontal'}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import Image from 'next/image'
|
import Image from 'next/image'
|
||||||
import { useMessage } from '@/hooks/useMessage'
|
import { useMessage } from '@/hooks/useMessage'
|
||||||
import { onlyNumberInputChange, onlyNumberWithDotInputChange } from '@/util/input-utils'
|
import { normalizeDecimalLimit, normalizeDigits } from '@/util/input-utils'
|
||||||
|
|
||||||
export default function Angle({ props }) {
|
export default function Angle({ props }) {
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
@ -20,7 +20,7 @@ export default function Angle({ props }) {
|
|||||||
value={angle1}
|
value={angle1}
|
||||||
ref={angle1Ref}
|
ref={angle1Ref}
|
||||||
onFocus={(e) => (angle1Ref.current.value = '')}
|
onFocus={(e) => (angle1Ref.current.value = '')}
|
||||||
onChange={(e) => onlyNumberWithDotInputChange(e, setAngle1)}
|
onChange={(e) => setAngle1(normalizeDecimalLimit(e.target.value, 2))}
|
||||||
placeholder="45"
|
placeholder="45"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -40,7 +40,7 @@ export default function Angle({ props }) {
|
|||||||
value={length1}
|
value={length1}
|
||||||
ref={length1Ref}
|
ref={length1Ref}
|
||||||
onFocus={(e) => (length1Ref.current.value = '')}
|
onFocus={(e) => (length1Ref.current.value = '')}
|
||||||
onChange={(e) => onlyNumberInputChange(e, setLength1)}
|
onChange={(e) => setLength1(normalizeDigits(e.target.value))}
|
||||||
placeholder="3000"
|
placeholder="3000"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { useMessage } from '@/hooks/useMessage'
|
import { useMessage } from '@/hooks/useMessage'
|
||||||
import { onlyNumberInputChange } from '@/util/input-utils'
|
import { normalizeDigits } from '@/util/input-utils'
|
||||||
|
|
||||||
export default function Diagonal({ props }) {
|
export default function Diagonal({ props }) {
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
@ -36,7 +36,7 @@ export default function Diagonal({ props }) {
|
|||||||
value={outerLineDiagonalLength}
|
value={outerLineDiagonalLength}
|
||||||
ref={outerLineDiagonalLengthRef}
|
ref={outerLineDiagonalLengthRef}
|
||||||
onFocus={(e) => (outerLineDiagonalLengthRef.current.value = '')}
|
onFocus={(e) => (outerLineDiagonalLengthRef.current.value = '')}
|
||||||
onChange={(e) => onlyNumberInputChange(e, setOuterLineDiagonalLength)}
|
onChange={(e) => setOuterLineDiagonalLength(normalizeDigits(e.target.value))}
|
||||||
placeholder="3000"
|
placeholder="3000"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -58,7 +58,7 @@ export default function Diagonal({ props }) {
|
|||||||
value={length1}
|
value={length1}
|
||||||
ref={length1Ref}
|
ref={length1Ref}
|
||||||
onFocus={(e) => (length1Ref.current.value = '')}
|
onFocus={(e) => (length1Ref.current.value = '')}
|
||||||
onChange={(e) => onlyNumberInputChange(e, setLength1)}
|
onChange={(e) => setLength1(normalizeDigits(e.target.value))}
|
||||||
placeholder="3000"
|
placeholder="3000"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -115,7 +115,7 @@ export default function Diagonal({ props }) {
|
|||||||
className="input-origin block"
|
className="input-origin block"
|
||||||
value={length2}
|
value={length2}
|
||||||
ref={length2Ref}
|
ref={length2Ref}
|
||||||
onChange={(e) => onlyNumberInputChange(e, setLength2)}
|
onChange={(e) => setLength2(normalizeDigits(e.target.value))}
|
||||||
readOnly={true}
|
readOnly={true}
|
||||||
placeholder="3000"
|
placeholder="3000"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { useMessage } from '@/hooks/useMessage'
|
import { useMessage } from '@/hooks/useMessage'
|
||||||
import { onlyNumberInputChange, onlyNumberWithDotInputChange } from '@/util/input-utils'
|
import { normalizeDecimalLimit, normalizeDigits } from '@/util/input-utils'
|
||||||
import { getDegreeByChon } from '@/util/canvas-util'
|
import { getDegreeByChon } from '@/util/canvas-util'
|
||||||
|
|
||||||
export default function DoublePitch({ props }) {
|
export default function DoublePitch({ props }) {
|
||||||
@ -56,7 +56,7 @@ export default function DoublePitch({ props }) {
|
|||||||
value={angle1}
|
value={angle1}
|
||||||
ref={angle1Ref}
|
ref={angle1Ref}
|
||||||
onFocus={(e) => (angle1Ref.current.value = '')}
|
onFocus={(e) => (angle1Ref.current.value = '')}
|
||||||
onChange={(e) => onlyNumberWithDotInputChange(e, setAngle1)}
|
onChange={(e) => setAngle1(normalizeDecimalLimit(e.target.value, 2))}
|
||||||
placeholder="45"
|
placeholder="45"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -73,7 +73,7 @@ export default function DoublePitch({ props }) {
|
|||||||
value={length1}
|
value={length1}
|
||||||
ref={length1Ref}
|
ref={length1Ref}
|
||||||
onFocus={(e) => (length1Ref.current.value = '')}
|
onFocus={(e) => (length1Ref.current.value = '')}
|
||||||
onChange={(e) => onlyNumberInputChange(e, setLength1)}
|
onChange={(e) => setLength1(normalizeDigits(e.target.value))}
|
||||||
placeholder="3000"
|
placeholder="3000"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -132,7 +132,8 @@ export default function DoublePitch({ props }) {
|
|||||||
ref={angle2Ref}
|
ref={angle2Ref}
|
||||||
onFocus={(e) => (angle2Ref.current.value = '')}
|
onFocus={(e) => (angle2Ref.current.value = '')}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
onlyNumberWithDotInputChange(e, setAngle2)
|
const v = normalizeDecimalLimit(e.target.value, 2)
|
||||||
|
setAngle2(v)
|
||||||
setLength2(getLength2())
|
setLength2(getLength2())
|
||||||
}}
|
}}
|
||||||
placeholder="45"
|
placeholder="45"
|
||||||
@ -156,7 +157,7 @@ export default function DoublePitch({ props }) {
|
|||||||
value={length2}
|
value={length2}
|
||||||
ref={length2Ref}
|
ref={length2Ref}
|
||||||
onFocus={(e) => (length2Ref.current.value = '')}
|
onFocus={(e) => (length2Ref.current.value = '')}
|
||||||
onChange={(e) => onlyNumberInputChange(e, setLength2)}
|
onChange={(e) => setLength2(normalizeDigits(e.target.value))}
|
||||||
readOnly={true}
|
readOnly={true}
|
||||||
placeholder="3000"
|
placeholder="3000"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -1,30 +1,91 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { useMessage } from '@/hooks/useMessage'
|
import { useMessage } from '@/hooks/useMessage'
|
||||||
import { onlyNumberInputChange } from '@/util/input-utils'
|
import { normalizeDigits } from '@/util/input-utils'
|
||||||
|
import { getChonByDegree, getDegreeByChon } from '@/util/canvas-util'
|
||||||
|
import { CalculatorInput } from '@/components/common/input/CalcInput'
|
||||||
|
import { useEffect, useRef } from 'react'
|
||||||
|
|
||||||
export default function OuterLineWall({ props }) {
|
export default function OuterLineWall({ props }) {
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
|
|
||||||
const { length1, setLength1, length1Ref, arrow1, setArrow1 } = props
|
const { length1, setLength1, length1Ref, arrow1, setArrow1 } = props
|
||||||
|
|
||||||
|
// 키보드 입력 처리
|
||||||
|
useEffect(() => {
|
||||||
|
const handleKeyDown = (e) => {
|
||||||
|
// 계산기 키패드가 보이는지 확인
|
||||||
|
const keypadVisible = document.querySelector('.keypad-container')
|
||||||
|
|
||||||
|
// 계산기 키패드가 보이면 계산기가 처리하도록 함
|
||||||
|
if (keypadVisible) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 이미 계산기 input에 포커스가 있으면 계산기가 처리하도록 함
|
||||||
|
if (document.activeElement && document.activeElement.classList.contains('calculator-input')) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 엔터키는 계산기가 숨겨진 상태에서만 페이지가 처리
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
// 엔터키를 페이지 레벨로 전달 (useOuterLineWall.js에서 처리)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 숫자 키가 입력되면 계산기 input에 포커스만 주기
|
||||||
|
if (/^[0-9+\-*\/=.]$/.test(e.key) || e.key === 'Backspace' || e.key === 'Delete') {
|
||||||
|
const calcInput = document.querySelector('.calculator-input')
|
||||||
|
if (calcInput) {
|
||||||
|
// 포커스만 주고 이벤트는 preventDefault 하지 않음
|
||||||
|
calcInput.focus()
|
||||||
|
calcInput.click()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// capture: true로 설정하여 다른 핸들러보다 먼저 실행
|
||||||
|
document.addEventListener('keydown', handleKeyDown, { capture: true })
|
||||||
|
return () => document.removeEventListener('keydown', handleKeyDown, { capture: true })
|
||||||
|
}, [])
|
||||||
return (
|
return (
|
||||||
<div className="outline-wrap">
|
<div className="outline-wrap">
|
||||||
<div className="outline-inner">
|
<div className="outline-inner">
|
||||||
<div className="outline-form">
|
<div className="outline-form">
|
||||||
<span className="mr10">{getMessage('straight.line')}</span>
|
<span className="mr10">{getMessage('straight.line')}</span>
|
||||||
<div className="input-grid" style={{ width: '63px' }}>
|
<div className="input-grid" style={{ width: '63px' }}>
|
||||||
<input
|
{/*<input*/}
|
||||||
type="text"
|
{/* type="text"*/}
|
||||||
className="input-origin block"
|
{/* className="input-origin block"*/}
|
||||||
|
{/* value={length1}*/}
|
||||||
|
{/* ref={length1Ref}*/}
|
||||||
|
{/* onFocus={(e) => {*/}
|
||||||
|
{/* if (length1Ref.current.value === '0') {*/}
|
||||||
|
{/* length1Ref.current.value = ''*/}
|
||||||
|
{/* }*/}
|
||||||
|
{/* }}*/}
|
||||||
|
{/* onChange={(e) => setLength1(normalizeDigits(e.target.value))}*/}
|
||||||
|
{/* placeholder="3000"*/}
|
||||||
|
{/*/>*/}
|
||||||
|
|
||||||
|
<CalculatorInput
|
||||||
|
id="length1-calc"
|
||||||
|
label=""
|
||||||
|
className="input-origin block calculator-input"
|
||||||
|
readOnly={false}
|
||||||
value={length1}
|
value={length1}
|
||||||
|
onChange={(value) => setLength1(value)}
|
||||||
|
options={{
|
||||||
|
allowNegative: false,
|
||||||
|
allowDecimal: false //(index !== 0),
|
||||||
|
}}
|
||||||
|
placeholder={'3000'}
|
||||||
ref={length1Ref}
|
ref={length1Ref}
|
||||||
onFocus={(e) => {
|
onFocus={() => {
|
||||||
if (length1Ref.current.value === '0') {
|
if (length1Ref.current && length1Ref.current.value === '0') {
|
||||||
length1Ref.current.value = ''
|
length1Ref.current.value = ''
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
onChange={(e) => onlyNumberInputChange(e, setLength1)}
|
|
||||||
placeholder="3000"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<button className="reset-btn" onClick={() => setLength1(0)}></button>
|
<button className="reset-btn" onClick={() => setLength1(0)}></button>
|
||||||
|
|||||||
@ -1,9 +1,21 @@
|
|||||||
import { useMessage } from '@/hooks/useMessage'
|
import { useMessage } from '@/hooks/useMessage'
|
||||||
import { onlyNumberInputChange } from '@/util/input-utils'
|
import { normalizeDigits } from '@/util/input-utils'
|
||||||
|
|
||||||
export default function RightAngle({ props }) {
|
export default function RightAngle({ props }) {
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
const { length1, setLength1, length1Ref, length2, setLength2, length2Ref, arrow1, setArrow1, arrow2, setArrow2 } = props
|
const { length1, setLength1, length1Ref, length2, setLength2, length2Ref, arrow1, setArrow1, arrow2, setArrow2 } = props
|
||||||
|
|
||||||
|
const handleClickArrow = (arrow) => {
|
||||||
|
const arrowType = arrow === '↑' ? 'ArrowUp' : arrow === '↓' ? 'ArrowDown' : arrow === '←' ? 'ArrowLeft' : arrow === '→' ? 'ArrowRight' : ''
|
||||||
|
setArrow2(arrow)
|
||||||
|
const originLeng2Val = length2Ref.current.value
|
||||||
|
if (originLeng2Val === '') {
|
||||||
|
length2Ref.current.value = '0'
|
||||||
|
}
|
||||||
|
length2Ref.current.focus()
|
||||||
|
length2Ref.current.value = originLeng2Val
|
||||||
|
document.dispatchEvent(new KeyboardEvent('keydown', { key: arrowType }))
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div className="outline-wrap">
|
<div className="outline-wrap">
|
||||||
<div className="outline-inner">
|
<div className="outline-inner">
|
||||||
@ -16,7 +28,7 @@ export default function RightAngle({ props }) {
|
|||||||
value={length1}
|
value={length1}
|
||||||
ref={length1Ref}
|
ref={length1Ref}
|
||||||
onFocus={(e) => (length1Ref.current.value = '')}
|
onFocus={(e) => (length1Ref.current.value = '')}
|
||||||
onChange={(e) => onlyNumberInputChange(e, setLength1)}
|
onChange={(e) => setLength1(normalizeDigits(e.target.value))}
|
||||||
placeholder="3000"
|
placeholder="3000"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -72,7 +84,7 @@ export default function RightAngle({ props }) {
|
|||||||
value={length2}
|
value={length2}
|
||||||
ref={length2Ref}
|
ref={length2Ref}
|
||||||
onFocus={(e) => (length2Ref.current.value = '')}
|
onFocus={(e) => (length2Ref.current.value = '')}
|
||||||
onChange={(e) => onlyNumberInputChange(e, setLength2)}
|
onChange={(e) => setLength2(normalizeDigits(e.target.value))}
|
||||||
placeholder="3000"
|
placeholder="3000"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -89,29 +101,25 @@ export default function RightAngle({ props }) {
|
|||||||
<button
|
<button
|
||||||
className={`direction up ${arrow2 === '↑' ? 'act' : ''}`}
|
className={`direction up ${arrow2 === '↑' ? 'act' : ''}`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setArrow2('↑')
|
handleClickArrow('↑')
|
||||||
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' }))
|
|
||||||
}}
|
}}
|
||||||
></button>
|
></button>
|
||||||
<button
|
<button
|
||||||
className={`direction down ${arrow2 === '↓' ? 'act' : ''}`}
|
className={`direction down ${arrow2 === '↓' ? 'act' : ''}`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setArrow2('↓')
|
handleClickArrow('↓')
|
||||||
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' }))
|
|
||||||
}}
|
}}
|
||||||
></button>
|
></button>
|
||||||
<button
|
<button
|
||||||
className={`direction left ${arrow2 === '←' ? 'act' : ''}`}
|
className={`direction left ${arrow2 === '←' ? 'act' : ''}`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setArrow2('←')
|
handleClickArrow('←')
|
||||||
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowLeft' }))
|
|
||||||
}}
|
}}
|
||||||
></button>
|
></button>
|
||||||
<button
|
<button
|
||||||
className={`direction right ${arrow2 === '→' ? 'act' : ''}`}
|
className={`direction right ${arrow2 === '→' ? 'act' : ''}`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setArrow2('→')
|
handleClickArrow('→')
|
||||||
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowRight' }))
|
|
||||||
}}
|
}}
|
||||||
></button>
|
></button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -4,10 +4,7 @@ import { contextPopupPositionState } from '@/store/popupAtom'
|
|||||||
import { usePopup } from '@/hooks/usePopup'
|
import { usePopup } from '@/hooks/usePopup'
|
||||||
import { useMessage } from '@/hooks/useMessage'
|
import { useMessage } from '@/hooks/useMessage'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { polygonToTurfPolygon } from '@/util/canvas-util'
|
|
||||||
import { deepCopyArray } from '@/util/common-utils'
|
|
||||||
import { canvasState } from '@/store/canvasAtom'
|
import { canvasState } from '@/store/canvasAtom'
|
||||||
import * as turf from '@turf/turf'
|
|
||||||
import { POLYGON_TYPE } from '@/common/common'
|
import { POLYGON_TYPE } from '@/common/common'
|
||||||
import { useModule } from '@/hooks/module/useModule'
|
import { useModule } from '@/hooks/module/useModule'
|
||||||
import { useSwal } from '@/hooks/useSwal'
|
import { useSwal } from '@/hooks/useSwal'
|
||||||
@ -41,7 +38,15 @@ export default function PanelEdit(props) {
|
|||||||
|
|
||||||
//팝업 닫을때 선택 해제
|
//팝업 닫을때 선택 해제
|
||||||
return () => {
|
return () => {
|
||||||
|
const modules = canvas.getObjects().filter((obj) => obj.name === 'module')
|
||||||
|
modules.forEach((obj) => {
|
||||||
|
obj.set({
|
||||||
|
stroke: 'black',
|
||||||
|
strokeWidth: 0.3,
|
||||||
|
})
|
||||||
|
})
|
||||||
canvas?.discardActiveObject() //선택해제
|
canvas?.discardActiveObject() //선택해제
|
||||||
|
canvas.renderAll()
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
|||||||
@ -12,11 +12,12 @@ export default function FlowLine({ FLOW_LINE_REF }) {
|
|||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
const [type, setType] = useState(FLOW_LINE_TYPE.DOWN_LEFT)
|
const [type, setType] = useState(FLOW_LINE_TYPE.DOWN_LEFT)
|
||||||
const [filledInput, setFilledInput] = useState('')
|
const [filledInput, setFilledInput] = useState('')
|
||||||
|
const [pointerInput, setPointerInput] = useState('100')
|
||||||
const currentObject = useRecoilValue(currentObjectState)
|
const currentObject = useRecoilValue(currentObjectState)
|
||||||
const handleFocus = () => {
|
const handleFocus = () => {
|
||||||
if (currentObject === null) {
|
if (currentObject === null) {
|
||||||
FLOW_LINE_REF.POINTER_INPUT_REF.current.value = ''
|
setPointerInput('')
|
||||||
FLOW_LINE_REF.FILLED_INPUT_REF.current.value = ''
|
setFilledInput('')
|
||||||
FLOW_LINE_REF.FILLED_INPUT_REF.current.blur()
|
FLOW_LINE_REF.FILLED_INPUT_REF.current.blur()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -35,7 +36,7 @@ export default function FlowLine({ FLOW_LINE_REF }) {
|
|||||||
<div className="outline-form">
|
<div className="outline-form">
|
||||||
<span>{getMessage('modal.movement.flow.line.position')}</span>
|
<span>{getMessage('modal.movement.flow.line.position')}</span>
|
||||||
<div className="input-grid mr5">
|
<div className="input-grid mr5">
|
||||||
<input type="text" className="input-origin block" defaultValue={100} readOnly={true} ref={FLOW_LINE_REF.POINTER_INPUT_REF} />
|
<input type="text" className="input-origin block" value={pointerInput} readOnly={true} ref={FLOW_LINE_REF.POINTER_INPUT_REF} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="moving-tab-content">
|
<div className="moving-tab-content">
|
||||||
@ -71,7 +72,6 @@ export default function FlowLine({ FLOW_LINE_REF }) {
|
|||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
className="input-origin block"
|
className="input-origin block"
|
||||||
defaultValue={100}
|
|
||||||
ref={FLOW_LINE_REF.FILLED_INPUT_REF}
|
ref={FLOW_LINE_REF.FILLED_INPUT_REF}
|
||||||
value={filledInput}
|
value={filledInput}
|
||||||
onFocus={handleFocus}
|
onFocus={handleFocus}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { useMessage } from '@/hooks/useMessage'
|
|||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { useRecoilValue } from 'recoil'
|
import { useRecoilValue } from 'recoil'
|
||||||
import { currentObjectState } from '@/store/canvasAtom'
|
import { currentObjectState } from '@/store/canvasAtom'
|
||||||
|
import { normalizeDigits } from '@/util/input-utils'
|
||||||
|
|
||||||
const UP_DOWN_TYPE = {
|
const UP_DOWN_TYPE = {
|
||||||
UP: 'up',
|
UP: 'up',
|
||||||
@ -11,7 +12,7 @@ const UP_DOWN_TYPE = {
|
|||||||
export default function Updown({ UP_DOWN_REF }) {
|
export default function Updown({ UP_DOWN_REF }) {
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
const [type, setType] = useState(UP_DOWN_TYPE.UP)
|
const [type, setType] = useState(UP_DOWN_TYPE.UP)
|
||||||
const [filledInput, setFilledInput] = useState('')
|
const [filledInput, setFilledInput] = useState('100')
|
||||||
const currentObject = useRecoilValue(currentObjectState)
|
const currentObject = useRecoilValue(currentObjectState)
|
||||||
const handleFocus = () => {
|
const handleFocus = () => {
|
||||||
if (currentObject === null) {
|
if (currentObject === null) {
|
||||||
@ -22,7 +23,8 @@ export default function Updown({ UP_DOWN_REF }) {
|
|||||||
}
|
}
|
||||||
const handleInput = (e) => {
|
const handleInput = (e) => {
|
||||||
const value = e.target.value.replace(/^0+/, '')
|
const value = e.target.value.replace(/^0+/, '')
|
||||||
setFilledInput(value.replace(/[^0-9]/g, ''))
|
//setFilledInput(value.replace(/[^0-9]/g, ''))
|
||||||
|
setFilledInput(normalizeDigits(value))
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -69,7 +71,6 @@ export default function Updown({ UP_DOWN_REF }) {
|
|||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
className="input-origin block"
|
className="input-origin block"
|
||||||
defaultValue={100}
|
|
||||||
ref={UP_DOWN_REF.FILLED_INPUT_REF}
|
ref={UP_DOWN_REF.FILLED_INPUT_REF}
|
||||||
value={filledInput}
|
value={filledInput}
|
||||||
onFocus={handleFocus}
|
onFocus={handleFocus}
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import Shadow from '@/components/floor-plan/modal/object/type/Shadow'
|
|||||||
import TriangleDormer from '@/components/floor-plan/modal/object/type/TriangleDormer'
|
import TriangleDormer from '@/components/floor-plan/modal/object/type/TriangleDormer'
|
||||||
import PentagonDormer from '@/components/floor-plan/modal/object/type/PentagonDormer'
|
import PentagonDormer from '@/components/floor-plan/modal/object/type/PentagonDormer'
|
||||||
import { usePopup } from '@/hooks/usePopup'
|
import { usePopup } from '@/hooks/usePopup'
|
||||||
|
import { normalizeDecimalLimit, normalizeDigits } from '@/util/input-utils'
|
||||||
|
|
||||||
export default function ObjectSetting({ id, pos = { x: 50, y: 230 } }) {
|
export default function ObjectSetting({ id, pos = { x: 50, y: 230 } }) {
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
@ -61,20 +62,20 @@ export default function ObjectSetting({ id, pos = { x: 50, y: 230 } }) {
|
|||||||
if (buttonAct === 1 || buttonAct === 2) {
|
if (buttonAct === 1 || buttonAct === 2) {
|
||||||
applyOpeningAndShadow(objectPlacement, buttonAct)
|
applyOpeningAndShadow(objectPlacement, buttonAct)
|
||||||
} else {
|
} else {
|
||||||
const height = dormerPlacement.heightRef.current !== null ? dormerPlacement.heightRef.current.value / 10 : 0
|
const height = dormerPlacement.heightRef.current !== null ? Number(normalizeDigits(dormerPlacement.heightRef.current.value)) / 10 : 0
|
||||||
const width = dormerPlacement.widthRef.current !== null ? dormerPlacement.widthRef.current.value / 10 : 0 //triangle일때는 없음
|
const width = dormerPlacement.widthRef.current !== null ? Number(normalizeDigits(dormerPlacement.widthRef.current.value)) / 10 : 0 //triangle일때는 없음
|
||||||
const pitch = dormerPlacement.pitchRef.current !== null ? Number(dormerPlacement.pitchRef.current.value) : 0
|
const pitch = dormerPlacement.pitchRef.current !== null ? Number(normalizeDecimalLimit(dormerPlacement.pitchRef.current.value, 2)) : 0
|
||||||
const offsetRef =
|
const offsetRef =
|
||||||
dormerPlacement.offsetRef.current !== null
|
dormerPlacement.offsetRef.current !== null
|
||||||
? dormerPlacement.offsetRef.current.value === ''
|
? dormerPlacement.offsetRef.current.value === ''
|
||||||
? 0
|
? 0
|
||||||
: parseInt(dormerPlacement.offsetRef.current.value) / 10
|
: Number(normalizeDigits(dormerPlacement.offsetRef.current.value)) / 10
|
||||||
: 0
|
: 0
|
||||||
const offsetWidthRef =
|
const offsetWidthRef =
|
||||||
dormerPlacement.offsetWidthRef.current !== null
|
dormerPlacement.offsetWidthRef.current !== null
|
||||||
? dormerPlacement.offsetWidthRef.current.value === ''
|
? dormerPlacement.offsetWidthRef.current.value === ''
|
||||||
? 0
|
? 0
|
||||||
: parseInt(dormerPlacement.offsetWidthRef.current.value) / 10
|
: Number(normalizeDigits(dormerPlacement.offsetWidthRef.current.value)) / 10
|
||||||
: 0
|
: 0
|
||||||
const directionRef = dormerPlacement.directionRef.current
|
const directionRef = dormerPlacement.directionRef.current
|
||||||
|
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import { useSurfaceShapeBatch } from '@/hooks/surface/useSurfaceShapeBatch'
|
|||||||
|
|
||||||
export default function SizeSetting(props) {
|
export default function SizeSetting(props) {
|
||||||
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
||||||
const [settingTarget, setSettingTarget] = useState(1)
|
const [settingTarget, setSettingTarget] = useState(props.side || 1)
|
||||||
const { id, pos = contextPopupPosition, target } = props
|
const { id, pos = contextPopupPosition, target } = props
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
const { closePopup } = usePopup()
|
const { closePopup } = usePopup()
|
||||||
@ -47,11 +47,11 @@ export default function SizeSetting(props) {
|
|||||||
<div className="size-option-top">
|
<div className="size-option-top">
|
||||||
<div className="size-option-wrap">
|
<div className="size-option-wrap">
|
||||||
<div className="size-option mb5">
|
<div className="size-option mb5">
|
||||||
<input type="text" className="input-origin mr5" value={target?.width.toFixed(0) * 10} readOnly={true} />
|
<input type="text" className="input-origin mr5" value={(target?.originWidth * 10).toFixed(0)} readOnly={true} />
|
||||||
<span className="normal-font">mm</span>
|
<span className="normal-font">mm</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="size-option">
|
<div className="size-option">
|
||||||
<input type="text" className="input-origin mr5" defaultValue={target?.width.toFixed(0) * 10} ref={widthRef} />
|
<input type="text" className="input-origin mr5" defaultValue={(target?.originWidth * 10).toFixed(0)} ref={widthRef} />
|
||||||
<span className="normal-font">mm</span>
|
<span className="normal-font">mm</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -60,11 +60,11 @@ export default function SizeSetting(props) {
|
|||||||
<div className="size-option-side">
|
<div className="size-option-side">
|
||||||
<div className="size-option-wrap">
|
<div className="size-option-wrap">
|
||||||
<div className="size-option mb5">
|
<div className="size-option mb5">
|
||||||
<input type="text" className="input-origin mr5" value={target?.height.toFixed(0) * 10} readOnly={true} />
|
<input type="text" className="input-origin mr5" value={(target?.originHeight * 10).toFixed(0)} readOnly={true} />
|
||||||
<span className="normal-font">mm</span>
|
<span className="normal-font">mm</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="size-option">
|
<div className="size-option">
|
||||||
<input type="text" className="input-origin mr5" defaultValue={target?.height.toFixed(0) * 10} ref={heightRef} />
|
<input type="text" className="input-origin mr5" defaultValue={(target?.originHeight * 10).toFixed(0)} ref={heightRef} />
|
||||||
<span className="normal-font">mm</span>
|
<span className="normal-font">mm</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -14,12 +14,14 @@ import { useCommonCode } from '@/hooks/common/useCommonCode'
|
|||||||
import QSelectBox from '@/components/common/select/QSelectBox'
|
import QSelectBox from '@/components/common/select/QSelectBox'
|
||||||
import { globalLocaleStore } from '@/store/localeAtom'
|
import { globalLocaleStore } from '@/store/localeAtom'
|
||||||
|
|
||||||
import { onlyNumberInputChange } from '@/util/input-utils'
|
|
||||||
import { getChonByDegree, getDegreeByChon } from '@/util/canvas-util'
|
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'
|
import { usePlan } from '@/hooks/usePlan'
|
||||||
|
import { normalizeDecimal, normalizeDigits } from '@/util/input-utils'
|
||||||
|
import { logger } from '@/util/logger'
|
||||||
|
import { CalculatorInput } from '@/components/common/input/CalcInput'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 지붕 레이아웃
|
* 지붕 레이아웃
|
||||||
@ -182,7 +184,7 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla
|
|||||||
*/
|
*/
|
||||||
const changeInput = (value, e) => {
|
const changeInput = (value, e) => {
|
||||||
const { name } = e.target
|
const { name } = e.target
|
||||||
setCurrentRoof({ ...currentRoof, [name]: Number(value) })
|
setCurrentRoof({ ...currentRoof, [name]: Number(normalizeDecimal(value)) })
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -207,6 +209,7 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla
|
|||||||
* 배치면초기설정 저장 버튼 클릭
|
* 배치면초기설정 저장 버튼 클릭
|
||||||
*/
|
*/
|
||||||
const handleSaveBtn = async () => {
|
const handleSaveBtn = async () => {
|
||||||
|
|
||||||
const roofInfo = {
|
const roofInfo = {
|
||||||
...currentRoof,
|
...currentRoof,
|
||||||
planNo: basicSetting.planNo,
|
planNo: basicSetting.planNo,
|
||||||
@ -214,7 +217,7 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla
|
|||||||
width: roofRef.width.current?.value,
|
width: roofRef.width.current?.value,
|
||||||
length: roofRef.length.current?.value,
|
length: roofRef.length.current?.value,
|
||||||
hajebichi: roofRef.hajebichi.current?.value,
|
hajebichi: roofRef.hajebichi.current?.value,
|
||||||
raft: roofRef.rafter.current?.value,
|
//raft: roofRef.rafter.current?.value,
|
||||||
selected: true,
|
selected: true,
|
||||||
layout: currentRoof.layout,
|
layout: currentRoof.layout,
|
||||||
index: 0,
|
index: 0,
|
||||||
@ -224,7 +227,7 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla
|
|||||||
newAddedRoofs[0] = { ...roofInfo }
|
newAddedRoofs[0] = { ...roofInfo }
|
||||||
setAddedRoofs(newAddedRoofs)
|
setAddedRoofs(newAddedRoofs)
|
||||||
|
|
||||||
console.log('save Info', {
|
logger.debug('save Info', {
|
||||||
...basicSetting,
|
...basicSetting,
|
||||||
selectedRoofMaterial: {
|
selectedRoofMaterial: {
|
||||||
...newAddedRoofs[0],
|
...newAddedRoofs[0],
|
||||||
@ -324,16 +327,51 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla
|
|||||||
</div>
|
</div>
|
||||||
</span>
|
</span>
|
||||||
<div className="input-grid mr5">
|
<div className="input-grid mr5">
|
||||||
<input
|
{/* <input
|
||||||
type="number"
|
type="text"
|
||||||
className="input-origin block"
|
className="input-origin block"
|
||||||
readOnly={currentRoof?.roofAngleSet !== item.value}
|
readOnly={currentRoof?.roofAngleSet !== item.value}
|
||||||
value={index === 0 ? currentRoof?.pitch : currentRoof?.angle}
|
value={index === 0 ? currentRoof?.pitch || '0' : currentRoof?.angle || '0'}
|
||||||
onChange={(e) =>
|
onChange={(e) => {
|
||||||
index === 0
|
const v = normalizeDecimal(e.target.value)
|
||||||
? setCurrentRoof({ ...currentRoof, pitch: e.target.value, angle: getDegreeByChon(e.target.value) })
|
e.target.value = v
|
||||||
: setCurrentRoof({ ...currentRoof, pitch: getChonByDegree(e.target.value), angle: e.target.value })
|
if (index === 0) {
|
||||||
}
|
const num = v === '' ? '' : Number(v)
|
||||||
|
setCurrentRoof({ ...currentRoof, pitch: num === '' ? '' : num, angle: num === '' ? '' : getDegreeByChon(num) })
|
||||||
|
} else {
|
||||||
|
const num = v === '' ? '' : Number(v)
|
||||||
|
setCurrentRoof({ ...currentRoof, pitch: num === '' ? '' : getChonByDegree(num), angle: num === '' ? '' : num })
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/> */}
|
||||||
|
<CalculatorInput
|
||||||
|
id=""
|
||||||
|
name=""
|
||||||
|
label=""
|
||||||
|
className="input-origin block"
|
||||||
|
readOnly={currentRoof?.roofAngleSet !== item.value}
|
||||||
|
value={index === 0 ? currentRoof?.pitch || '0' : currentRoof?.angle || '0'}
|
||||||
|
onChange={(value) => {
|
||||||
|
if (index === 0) {
|
||||||
|
const num = value === '' ? '' : Number(value)
|
||||||
|
setCurrentRoof(prev => ({
|
||||||
|
...prev,
|
||||||
|
pitch: num === '' ? '' : num,
|
||||||
|
angle: num === '' ? '' : getDegreeByChon(num),
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
const num = value === '' ? '' : Number(value)
|
||||||
|
setCurrentRoof( prev => ({
|
||||||
|
...prev,
|
||||||
|
pitch: num === '' ? '' : getChonByDegree(num),
|
||||||
|
angle: num === '' ? '' : num,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
options={{
|
||||||
|
allowNegative: false,
|
||||||
|
allowDecimal: true //(index !== 0),
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<span className="thin">{index === 0 ? '寸' : '度'}</span>
|
<span className="thin">{index === 0 ? '寸' : '度'}</span>
|
||||||
@ -375,15 +413,33 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla
|
|||||||
<div className="flex-ment">
|
<div className="flex-ment">
|
||||||
<span>W</span>
|
<span>W</span>
|
||||||
<div className="input-grid" style={{ width: '84px' }}>
|
<div className="input-grid" style={{ width: '84px' }}>
|
||||||
<input
|
{/*<input*/}
|
||||||
type="text"
|
{/* type="text"*/}
|
||||||
|
{/* className="input-origin block"*/}
|
||||||
|
{/* name={`width`}*/}
|
||||||
|
{/* ref={roofRef.width}*/}
|
||||||
|
{/* value={parseInt(currentRoof?.width)}*/}
|
||||||
|
{/* onChange={(e) => changeInput(normalizeDigits(e.target.value), e)}*/}
|
||||||
|
{/* readOnly={currentRoof?.widAuth === 'R'}*/}
|
||||||
|
{/* disabled={currentRoof?.roofSizeSet === '3'}*/}
|
||||||
|
{/*/>*/}
|
||||||
|
|
||||||
|
<CalculatorInput
|
||||||
|
id=""
|
||||||
|
name={'width'}
|
||||||
|
label=""
|
||||||
className="input-origin block"
|
className="input-origin block"
|
||||||
name={`width`}
|
|
||||||
ref={roofRef.width}
|
ref={roofRef.width}
|
||||||
value={parseInt(currentRoof?.width)}
|
value={currentRoof?.width||0}
|
||||||
onChange={(e) => onlyNumberInputChange(e, changeInput)}
|
onChange={(value) => {
|
||||||
|
setCurrentRoof({ ...currentRoof, value })
|
||||||
|
}}
|
||||||
readOnly={currentRoof?.widAuth === 'R'}
|
readOnly={currentRoof?.widAuth === 'R'}
|
||||||
disabled={currentRoof?.roofSizeSet === '3'}
|
disabled={currentRoof?.roofSizeSet === '3'}
|
||||||
|
options={{
|
||||||
|
allowNegative: false,
|
||||||
|
allowDecimal: false //(index !== 0),
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -392,15 +448,33 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla
|
|||||||
<div className="flex-ment">
|
<div className="flex-ment">
|
||||||
<span>L</span>
|
<span>L</span>
|
||||||
<div className="input-grid" style={{ width: '84px' }}>
|
<div className="input-grid" style={{ width: '84px' }}>
|
||||||
<input
|
{/*<input*/}
|
||||||
type="text"
|
{/* type="text"*/}
|
||||||
|
{/* className="input-origin block"*/}
|
||||||
|
{/* name={`length`}*/}
|
||||||
|
{/* ref={roofRef.length}*/}
|
||||||
|
{/* value={parseInt(currentRoof?.length)}*/}
|
||||||
|
{/* onChange={(e) => changeInput(normalizeDigits(e.target.value), e)}*/}
|
||||||
|
{/* readOnly={currentRoof?.lenAuth === 'R'}*/}
|
||||||
|
{/* disabled={currentRoof?.roofSizeSet === '3'}*/}
|
||||||
|
{/*/>*/}
|
||||||
|
|
||||||
|
<CalculatorInput
|
||||||
|
id=""
|
||||||
|
name={'length'}
|
||||||
|
label=""
|
||||||
className="input-origin block"
|
className="input-origin block"
|
||||||
name={`length`}
|
|
||||||
ref={roofRef.length}
|
ref={roofRef.length}
|
||||||
value={parseInt(currentRoof?.length)}
|
value={currentRoof?.length||0}
|
||||||
onChange={(e) => onlyNumberInputChange(e, changeInput)}
|
onChange={(value) => {
|
||||||
|
setCurrentRoof({ ...currentRoof, value })
|
||||||
|
}}
|
||||||
readOnly={currentRoof?.lenAuth === 'R'}
|
readOnly={currentRoof?.lenAuth === 'R'}
|
||||||
disabled={currentRoof?.roofSizeSet === '3'}
|
disabled={currentRoof?.roofSizeSet === '3'}
|
||||||
|
options={{
|
||||||
|
allowNegative: false,
|
||||||
|
allowDecimal: false //(index !== 0),
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -412,11 +486,8 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla
|
|||||||
<div className="select-wrap" style={{ width: '160px' }}>
|
<div className="select-wrap" style={{ width: '160px' }}>
|
||||||
<QSelectBox
|
<QSelectBox
|
||||||
options={raftCodes}
|
options={raftCodes}
|
||||||
title={
|
title={raftCodes?.find((r) => r.clCode === (currentRoof.raft ?? currentRoof?.raftBaseCd))?.clCodeNm}
|
||||||
raftCodes?.find((r) => r.clCode === (currentRoof?.raft === undefined ? currentRoof?.raftBaseCd : currentRoof?.raft))
|
value={currentRoof?.raft ?? currentRoof?.raftBaseCd}
|
||||||
.clCodeNm
|
|
||||||
}
|
|
||||||
value={currentRoof?.raft === undefined ? currentRoof?.raftBaseCd : currentRoof?.raft}
|
|
||||||
onChange={(e) => handleRafterChange(e.clCode)}
|
onChange={(e) => handleRafterChange(e.clCode)}
|
||||||
sourceKey="clCode"
|
sourceKey="clCode"
|
||||||
targetKey={currentRoof?.raft ? 'raft' : 'raftBaseCd'}
|
targetKey={currentRoof?.raft ? 'raft' : 'raftBaseCd'}
|
||||||
@ -431,16 +502,34 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla
|
|||||||
<div className="flex-ment">
|
<div className="flex-ment">
|
||||||
<span>{getMessage('hajebichi')}</span>
|
<span>{getMessage('hajebichi')}</span>
|
||||||
<div className="input-grid" style={{ width: '84px' }}>
|
<div className="input-grid" style={{ width: '84px' }}>
|
||||||
<input
|
{/*<input*/}
|
||||||
type="text"
|
{/* type="text"*/}
|
||||||
|
{/* className="input-origin block"*/}
|
||||||
|
{/* name={`hajebichi`}*/}
|
||||||
|
{/* ref={roofRef.hajebichi}*/}
|
||||||
|
{/* value={parseInt(currentRoof?.hajebichi)}*/}
|
||||||
|
{/* onChange={(e) => changeInput(normalizeDigits(e.target.value), e)}*/}
|
||||||
|
{/* readOnly={currentRoof?.roofPchAuth === 'R'}*/}
|
||||||
|
{/* disabled={currentRoof?.roofSizeSet === '3'}*/}
|
||||||
|
{/*/>*/}
|
||||||
|
<CalculatorInput
|
||||||
|
id=""
|
||||||
|
name={'hajebichi'}
|
||||||
|
label=""
|
||||||
className="input-origin block"
|
className="input-origin block"
|
||||||
name={`hajebichi`}
|
|
||||||
ref={roofRef.hajebichi}
|
ref={roofRef.hajebichi}
|
||||||
value={parseInt(currentRoof?.hajebichi)}
|
value={currentRoof?.hajebichi||0}
|
||||||
onChange={(e) => onlyNumberInputChange(e, changeInput)}
|
onChange={(value) => {
|
||||||
|
setCurrentRoof({ ...currentRoof, value })
|
||||||
|
}}
|
||||||
readOnly={currentRoof?.roofPchAuth === 'R'}
|
readOnly={currentRoof?.roofPchAuth === 'R'}
|
||||||
disabled={currentRoof?.roofSizeSet === '3'}
|
disabled={currentRoof?.roofSizeSet === '3'}
|
||||||
|
options={{
|
||||||
|
allowNegative: false,
|
||||||
|
allowDecimal: false //(index !== 0),
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import { useEffect, useState } from 'react'
|
|||||||
import { currentObjectState } from '@/store/canvasAtom'
|
import { currentObjectState } from '@/store/canvasAtom'
|
||||||
import { useRoofAllocationSetting } from '@/hooks/roofcover/useRoofAllocationSetting'
|
import { useRoofAllocationSetting } from '@/hooks/roofcover/useRoofAllocationSetting'
|
||||||
import { useSwal } from '@/hooks/useSwal'
|
import { useSwal } from '@/hooks/useSwal'
|
||||||
|
import { normalizeDigits } from '@/util/input-utils'
|
||||||
|
|
||||||
export default function ActualSizeSetting(props) {
|
export default function ActualSizeSetting(props) {
|
||||||
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
||||||
@ -27,7 +28,7 @@ export default function ActualSizeSetting(props) {
|
|||||||
|
|
||||||
const handleFinish = () => {
|
const handleFinish = () => {
|
||||||
swalFire({
|
swalFire({
|
||||||
text: '완료 하시겠습니까?',
|
text: getMessage("modal.roof.allocation.auxiliary.accept"),
|
||||||
type: 'confirm',
|
type: 'confirm',
|
||||||
confirmFn: () => {
|
confirmFn: () => {
|
||||||
handleAlloc()
|
handleAlloc()
|
||||||
@ -37,7 +38,7 @@ export default function ActualSizeSetting(props) {
|
|||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
swalFire({
|
swalFire({
|
||||||
text: '완료 하시겠습니까?',
|
text: getMessage("modal.roof.allocation.auxiliary.accept"),
|
||||||
type: 'confirm',
|
type: 'confirm',
|
||||||
confirmFn: () => {
|
confirmFn: () => {
|
||||||
handleAlloc()
|
handleAlloc()
|
||||||
@ -87,7 +88,7 @@ export default function ActualSizeSetting(props) {
|
|||||||
<div className="eaves-keraba-td">
|
<div className="eaves-keraba-td">
|
||||||
<div className="outline-form">
|
<div className="outline-form">
|
||||||
<div className="input-grid mr5">
|
<div className="input-grid mr5">
|
||||||
<input type="text" className="input-origin block" value={actualSize} onChange={(e) => setActualSize(Number(e.target.value))} />
|
<input type="text" className="input-origin block" value={actualSize} onChange={(e) => setActualSize(Number(normalizeDigits(e.target.value)))} />
|
||||||
</div>
|
</div>
|
||||||
<span className="thin">mm</span>
|
<span className="thin">mm</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import { useCanvasSetting } from '@/hooks/option/useCanvasSetting'
|
|||||||
import { useCommonCode } from '@/hooks/common/useCommonCode'
|
import { useCommonCode } from '@/hooks/common/useCommonCode'
|
||||||
import { globalLocaleStore } from '@/store/localeAtom'
|
import { globalLocaleStore } from '@/store/localeAtom'
|
||||||
import { currentAngleTypeSelector, pitchTextSelector } from '@/store/canvasAtom'
|
import { currentAngleTypeSelector, pitchTextSelector } from '@/store/canvasAtom'
|
||||||
|
import { normalizeDecimalLimit, normalizeDigits } from '@/util/input-utils'
|
||||||
|
|
||||||
export default function ContextRoofAllocationSetting(props) {
|
export default function ContextRoofAllocationSetting(props) {
|
||||||
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
||||||
@ -85,7 +86,7 @@ export default function ContextRoofAllocationSetting(props) {
|
|||||||
return (
|
return (
|
||||||
<div className="grid-option-box" key={index}>
|
<div className="grid-option-box" key={index}>
|
||||||
<div className="d-check-radio pop no-text">
|
<div className="d-check-radio pop no-text">
|
||||||
<input type="radio" name="radio01" checked={roof.selected && 'checked'} readOnly={true} />
|
<input type="radio" name="radio01" checked={!!roof.selected} readOnly={true} />
|
||||||
<label
|
<label
|
||||||
htmlFor="ra01"
|
htmlFor="ra01"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
@ -151,6 +152,7 @@ export default function ContextRoofAllocationSetting(props) {
|
|||||||
defaultValue={roof.width}
|
defaultValue={roof.width}
|
||||||
readOnly={roof.widAuth === 'R'}
|
readOnly={roof.widAuth === 'R'}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
|
e.target.value = normalizeDigits(e.target.value)
|
||||||
handleChangeInput(e, 'width', index)
|
handleChangeInput(e, 'width', index)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@ -169,6 +171,7 @@ export default function ContextRoofAllocationSetting(props) {
|
|||||||
defaultValue={roof.length}
|
defaultValue={roof.length}
|
||||||
readOnly={roof.lenAuth === 'R'}
|
readOnly={roof.lenAuth === 'R'}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
|
e.target.value = normalizeDigits(e.target.value)
|
||||||
handleChangeInput(e, 'length', index)
|
handleChangeInput(e, 'length', index)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@ -186,9 +189,12 @@ export default function ContextRoofAllocationSetting(props) {
|
|||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
className="input-origin block"
|
className="input-origin block"
|
||||||
value={parseInt(roof.hajebichi)}
|
value={roof.hajebichi ?? ''}
|
||||||
readOnly={roof.roofPchAuth === 'R'}
|
readOnly={roof.roofPchAuth === 'R'}
|
||||||
onChange={(e) => handleChangeInput(e, 'hajebichi', index)}
|
onChange={(e) => {
|
||||||
|
e.target.value = normalizeDigits(e.target.value)
|
||||||
|
handleChangeInput(e, 'hajebichi', index)
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -202,11 +208,10 @@ export default function ContextRoofAllocationSetting(props) {
|
|||||||
type="text"
|
type="text"
|
||||||
className="input-origin block"
|
className="input-origin block"
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
// handleChangeInput(e, currentAngleType === 'slope' ? 'pitch' : 'angle', index)
|
e.target.value = normalizeDecimalLimit(e.target.value, 2)
|
||||||
handleChangePitch(e, index)
|
handleChangePitch(e, index)
|
||||||
}}
|
}}
|
||||||
value={currentAngleType === 'slope' ? roof.pitch : roof.angle}
|
value={currentAngleType === 'slope' ? (roof.pitch ?? '') : (roof.angle ?? '')}
|
||||||
defaultValue={currentAngleType === 'slope' ? roof.pitch : roof.angle}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<span className="absol">{pitchText}</span>
|
<span className="absol">{pitchText}</span>
|
||||||
|
|||||||
@ -13,7 +13,7 @@ import { globalLocaleStore } from '@/store/localeAtom'
|
|||||||
import { useRoofShapeSetting } from '@/hooks/roofcover/useRoofShapeSetting'
|
import { useRoofShapeSetting } from '@/hooks/roofcover/useRoofShapeSetting'
|
||||||
import { currentAngleTypeSelector, pitchTextSelector } from '@/store/canvasAtom'
|
import { currentAngleTypeSelector, pitchTextSelector } from '@/store/canvasAtom'
|
||||||
import { getDegreeByChon } from '@/util/canvas-util'
|
import { getDegreeByChon } from '@/util/canvas-util'
|
||||||
import { onlyNumberWithDotInputChange } from '@/util/input-utils'
|
import { normalizeDecimalLimit, normalizeDigits } from '@/util/input-utils'
|
||||||
|
|
||||||
export default function RoofAllocationSetting(props) {
|
export default function RoofAllocationSetting(props) {
|
||||||
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
||||||
@ -86,7 +86,7 @@ export default function RoofAllocationSetting(props) {
|
|||||||
return (
|
return (
|
||||||
<div className="grid-option-box" key={index}>
|
<div className="grid-option-box" key={index}>
|
||||||
<div className="d-check-radio pop no-text">
|
<div className="d-check-radio pop no-text">
|
||||||
<input type="radio" name="radio01" checked={roof.selected && 'checked'} readOnly={true} />
|
<input type="radio" name="radio01" checked={!!roof.selected} readOnly />
|
||||||
<label
|
<label
|
||||||
htmlFor="ra01"
|
htmlFor="ra01"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
@ -151,7 +151,10 @@ export default function RoofAllocationSetting(props) {
|
|||||||
type="text"
|
type="text"
|
||||||
className="input-origin block"
|
className="input-origin block"
|
||||||
defaultValue={roof.width}
|
defaultValue={roof.width}
|
||||||
onChange={(e) => handleChangeInput(e, 'width', index)}
|
onChange={(e) => {
|
||||||
|
e.target.value = normalizeDigits(e.target.value)
|
||||||
|
handleChangeInput(e, 'width', index)
|
||||||
|
}}
|
||||||
readOnly={roof.widAuth === 'R'}
|
readOnly={roof.widAuth === 'R'}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -167,7 +170,10 @@ export default function RoofAllocationSetting(props) {
|
|||||||
type="text"
|
type="text"
|
||||||
className="input-origin block"
|
className="input-origin block"
|
||||||
defaultValue={roof.length}
|
defaultValue={roof.length}
|
||||||
onChange={(e) => handleChangeInput(e, 'length', index)}
|
onChange={(e) => {
|
||||||
|
e.target.value = normalizeDigits(e.target.value)
|
||||||
|
handleChangeInput(e, 'length', index)
|
||||||
|
}}
|
||||||
readOnly={roof.lenAuth === 'R'}
|
readOnly={roof.lenAuth === 'R'}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -184,8 +190,11 @@ export default function RoofAllocationSetting(props) {
|
|||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
className="input-origin block"
|
className="input-origin block"
|
||||||
onChange={(e) => handleChangeInput(e, 'hajebichi', index)}
|
onChange={(e) => {
|
||||||
value={parseInt(roof.hajebichi)}
|
e.target.value = normalizeDigits(e.target.value)
|
||||||
|
handleChangeInput(e, 'hajebichi', index)
|
||||||
|
}}
|
||||||
|
value={roof.hajebichi ?? ''}
|
||||||
readOnly={roof.roofPchAuth === 'R'}
|
readOnly={roof.roofPchAuth === 'R'}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -200,10 +209,10 @@ export default function RoofAllocationSetting(props) {
|
|||||||
type="text"
|
type="text"
|
||||||
className="input-origin block"
|
className="input-origin block"
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
|
e.target.value = normalizeDecimalLimit(e.target.value, 2)
|
||||||
handleChangePitch(e, index)
|
handleChangePitch(e, index)
|
||||||
}}
|
}}
|
||||||
value={currentAngleType === 'slope' ? roof.pitch : roof.angle}
|
value={currentAngleType === 'slope' ? (roof.pitch ?? '') : (roof.angle ?? '')}
|
||||||
defaultValue={currentAngleType === 'slope' ? roof.pitch : roof.angle}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<span className="absol">{pitchText}</span>
|
<span className="absol">{pitchText}</span>
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { useMessage } from '@/hooks/useMessage'
|
import { useMessage } from '@/hooks/useMessage'
|
||||||
import { onlyNumberInputChange, onlyNumberWithDotInputChange } from '@/util/input-utils'
|
import { normalizeDecimalLimit, normalizeDigits } from '@/util/input-utils'
|
||||||
|
|
||||||
export default function Direction({ pitch, setPitch, eavesOffset, setEavesOffset, gableOffset, setGableOffset, shedWidth, setShedWidth, pitchText }) {
|
export default function Direction({ pitch, setPitch, eavesOffset, setEavesOffset, gableOffset, setGableOffset, shedWidth, setShedWidth, pitchText }) {
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
@ -10,7 +10,12 @@ export default function Direction({ pitch, setPitch, eavesOffset, setEavesOffset
|
|||||||
{getMessage('slope')}
|
{getMessage('slope')}
|
||||||
</span>
|
</span>
|
||||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||||
<input type="text" className="input-origin block" value={pitch} onChange={(e) => onlyNumberWithDotInputChange(e, setPitch)} />
|
<input
|
||||||
|
type="text"
|
||||||
|
className="input-origin block"
|
||||||
|
value={pitch}
|
||||||
|
onChange={(e) => setPitch(normalizeDecimalLimit(e.target.value, 2))}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<span className="thin">{pitchText}</span>
|
<span className="thin">{pitchText}</span>
|
||||||
</div>
|
</div>
|
||||||
@ -19,7 +24,12 @@ export default function Direction({ pitch, setPitch, eavesOffset, setEavesOffset
|
|||||||
{getMessage('eaves.offset')}
|
{getMessage('eaves.offset')}
|
||||||
</span>
|
</span>
|
||||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||||
<input type="text" className="input-origin block" value={eavesOffset} onChange={(e) => onlyNumberInputChange(e, setEavesOffset)} />
|
<input
|
||||||
|
type="text"
|
||||||
|
className="input-origin block"
|
||||||
|
value={eavesOffset}
|
||||||
|
onChange={(e) => setEavesOffset(normalizeDigits(e.target.value))}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<span className="thin">mm</span>
|
<span className="thin">mm</span>
|
||||||
</div>
|
</div>
|
||||||
@ -28,7 +38,12 @@ export default function Direction({ pitch, setPitch, eavesOffset, setEavesOffset
|
|||||||
{getMessage('gable.offset')}
|
{getMessage('gable.offset')}
|
||||||
</span>
|
</span>
|
||||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||||
<input type="text" className="input-origin block" value={gableOffset} onChange={(e) => onlyNumberInputChange(e, setGableOffset)} />
|
<input
|
||||||
|
type="text"
|
||||||
|
className="input-origin block"
|
||||||
|
value={gableOffset}
|
||||||
|
onChange={(e) => setGableOffset(normalizeDigits(e.target.value))}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<span className="thin">mm</span>
|
<span className="thin">mm</span>
|
||||||
</div>
|
</div>
|
||||||
@ -37,7 +52,12 @@ export default function Direction({ pitch, setPitch, eavesOffset, setEavesOffset
|
|||||||
{getMessage('windage.width')}
|
{getMessage('windage.width')}
|
||||||
</span>
|
</span>
|
||||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||||
<input type="text" className="input-origin block" value={shedWidth} onChange={(e) => onlyNumberInputChange(e, setShedWidth)} />
|
<input
|
||||||
|
type="text"
|
||||||
|
className="input-origin block"
|
||||||
|
value={shedWidth}
|
||||||
|
onChange={(e) => setShedWidth(normalizeDigits(e.target.value))}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<span className="thin">mm</span>
|
<span className="thin">mm</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { useMessage } from '@/hooks/useMessage'
|
import { useMessage } from '@/hooks/useMessage'
|
||||||
import { onlyNumberInputChange, onlyNumberWithDotInputChange } from '@/util/input-utils'
|
import { normalizeDecimalLimit, normalizeDigits } from '@/util/input-utils'
|
||||||
|
|
||||||
export default function Pattern(props) {
|
export default function Pattern(props) {
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
@ -11,7 +11,7 @@ export default function Pattern(props) {
|
|||||||
{getMessage('slope')}
|
{getMessage('slope')}
|
||||||
</span>
|
</span>
|
||||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||||
<input type="text" className="input-origin block" value={pitch} onChange={(e) => onlyNumberWithDotInputChange(e, setPitch)} />
|
<input type="text" className="input-origin block" value={pitch} onChange={(e) => setPitch(normalizeDecimalLimit(e.target.value, 2))} />
|
||||||
</div>
|
</div>
|
||||||
<span className="thin"> {pitchText}</span>
|
<span className="thin"> {pitchText}</span>
|
||||||
</div>
|
</div>
|
||||||
@ -20,7 +20,7 @@ export default function Pattern(props) {
|
|||||||
{getMessage('eaves.offset')}
|
{getMessage('eaves.offset')}
|
||||||
</span>
|
</span>
|
||||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||||
<input type="text" className="input-origin block" value={eavesOffset} onChange={(e) => onlyNumberInputChange(e, setEavesOffset)} />
|
<input type="text" className="input-origin block" value={eavesOffset} onChange={(e) => setEavesOffset(normalizeDigits(e.target.value))} />
|
||||||
</div>
|
</div>
|
||||||
<span className="thin">mm</span>
|
<span className="thin">mm</span>
|
||||||
</div>
|
</div>
|
||||||
@ -29,7 +29,7 @@ export default function Pattern(props) {
|
|||||||
{getMessage('gable.offset')}
|
{getMessage('gable.offset')}
|
||||||
</span>
|
</span>
|
||||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||||
<input type="text" className="input-origin block" value={gableOffset} onChange={(e) => onlyNumberInputChange(e, setGableOffset)} />
|
<input type="text" className="input-origin block" value={gableOffset} onChange={(e) => setGableOffset(normalizeDigits(e.target.value))} />
|
||||||
</div>
|
</div>
|
||||||
<span className="thin">mm</span>
|
<span className="thin">mm</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { useMessage } from '@/hooks/useMessage'
|
import { useMessage } from '@/hooks/useMessage'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { onlyNumberInputChange, onlyNumberWithDotInputChange } from '@/util/input-utils'
|
import { normalizeDecimalLimit, normalizeDigits } from '@/util/input-utils'
|
||||||
|
|
||||||
export default function Ridge(props) {
|
export default function Ridge(props) {
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
@ -13,7 +13,7 @@ export default function Ridge(props) {
|
|||||||
{getMessage('slope')}
|
{getMessage('slope')}
|
||||||
</span>
|
</span>
|
||||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||||
<input type="text" className="input-origin block" value={pitch} onChange={(e) => onlyNumberWithDotInputChange(e, setPitch)} />
|
<input type="text" className="input-origin block" value={pitch} onChange={(e) => setPitch(normalizeDecimalLimit(e.target.value, 2))} />
|
||||||
</div>
|
</div>
|
||||||
<span className="thin">{pitchText}</span>
|
<span className="thin">{pitchText}</span>
|
||||||
</div>
|
</div>
|
||||||
@ -22,7 +22,7 @@ export default function Ridge(props) {
|
|||||||
{getMessage('eaves.offset')}
|
{getMessage('eaves.offset')}
|
||||||
</span>
|
</span>
|
||||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||||
<input type="text" className="input-origin block" value={eavesOffset} onChange={(e) => onlyNumberInputChange(e, setEavesOffset)} />
|
<input type="text" className="input-origin block" value={eavesOffset} onChange={(e) => setEavesOffset(normalizeDigits(e.target.value))} />
|
||||||
</div>
|
</div>
|
||||||
<span className="thin">mm</span>
|
<span className="thin">mm</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { useMessage } from '@/hooks/useMessage'
|
import { useMessage } from '@/hooks/useMessage'
|
||||||
import { onlyNumberInputChange, onlyNumberWithDotInputChange } from '@/util/input-utils'
|
import { normalizeDecimalLimit, normalizeDigits } from '@/util/input-utils'
|
||||||
|
|
||||||
export default function Eaves({ pitch, setPitch, eavesOffset, setEavesOffset, pitchText }) {
|
export default function Eaves({ pitch, setPitch, eavesOffset, setEavesOffset, pitchText }) {
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
@ -10,7 +10,7 @@ export default function Eaves({ pitch, setPitch, eavesOffset, setEavesOffset, pi
|
|||||||
{getMessage('slope')}
|
{getMessage('slope')}
|
||||||
</span>
|
</span>
|
||||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||||
<input type="text" className="input-origin block" value={pitch} onChange={(e) => onlyNumberWithDotInputChange(e, setPitch)} />
|
<input type="text" className="input-origin block" value={pitch} onChange={(e) => setPitch(normalizeDecimalLimit(e.target.value, 2))} />
|
||||||
</div>
|
</div>
|
||||||
<span className="thin">{pitchText}</span>
|
<span className="thin">{pitchText}</span>
|
||||||
</div>
|
</div>
|
||||||
@ -19,7 +19,7 @@ export default function Eaves({ pitch, setPitch, eavesOffset, setEavesOffset, pi
|
|||||||
{getMessage('eaves.offset')}
|
{getMessage('eaves.offset')}
|
||||||
</span>
|
</span>
|
||||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||||
<input type="text" className="input-origin block" value={eavesOffset} onChange={(e) => onlyNumberInputChange(e, setEavesOffset)} />
|
<input type="text" className="input-origin block" value={eavesOffset} onChange={(e) => setEavesOffset(normalizeDigits(e.target.value))} />
|
||||||
</div>
|
</div>
|
||||||
<span className="thin">mm</span>
|
<span className="thin">mm</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { useMessage } from '@/hooks/useMessage'
|
import { useMessage } from '@/hooks/useMessage'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { onlyNumberInputChange } from '@/util/input-utils'
|
import { normalizeDigits } from '@/util/input-utils'
|
||||||
|
|
||||||
export default function Gable({ gableOffset, setGableOffset }) {
|
export default function Gable({ gableOffset, setGableOffset }) {
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
@ -10,7 +10,7 @@ export default function Gable({ gableOffset, setGableOffset }) {
|
|||||||
<div className="outline-form">
|
<div className="outline-form">
|
||||||
<span className="mr10">{getMessage('gable.offset')}</span>
|
<span className="mr10">{getMessage('gable.offset')}</span>
|
||||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||||
<input type="text" className="input-origin block" value={gableOffset} onChange={(e) => onlyNumberInputChange(e, setGableOffset)} />
|
<input type="text" className="input-origin block" value={gableOffset} onChange={(e) => setGableOffset(normalizeDigits(e.target.value))} />
|
||||||
</div>
|
</div>
|
||||||
<span className="thin">mm</span>
|
<span className="thin">mm</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { useMessage } from '@/hooks/useMessage'
|
import { useMessage } from '@/hooks/useMessage'
|
||||||
import { onlyNumberInputChange, onlyNumberWithDotInputChange } from '@/util/input-utils'
|
import { normalizeDecimalLimit, normalizeDigits } from '@/util/input-utils'
|
||||||
|
|
||||||
export default function HipAndGable({ pitch, setPitch, eavesOffset, setEavesOffset, hipAndGableWidth, setHipAndGableWidth, pitchText }) {
|
export default function HipAndGable({ pitch, setPitch, eavesOffset, setEavesOffset, hipAndGableWidth, setHipAndGableWidth, pitchText }) {
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
@ -10,7 +10,7 @@ export default function HipAndGable({ pitch, setPitch, eavesOffset, setEavesOffs
|
|||||||
{getMessage('slope')}
|
{getMessage('slope')}
|
||||||
</span>
|
</span>
|
||||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||||
<input type="text" className="input-origin block" value={pitch} onChange={(e) => onlyNumberWithDotInputChange(e, setPitch)} />
|
<input type="text" className="input-origin block" value={pitch} onChange={(e) => setPitch(normalizeDecimalLimit(e.target.value, 2))} />
|
||||||
</div>
|
</div>
|
||||||
<span className="thin">{pitchText}</span>
|
<span className="thin">{pitchText}</span>
|
||||||
</div>
|
</div>
|
||||||
@ -19,7 +19,7 @@ export default function HipAndGable({ pitch, setPitch, eavesOffset, setEavesOffs
|
|||||||
{getMessage('eaves.offset')}
|
{getMessage('eaves.offset')}
|
||||||
</span>
|
</span>
|
||||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||||
<input type="text" className="input-origin block" value={eavesOffset} onChange={(e) => onlyNumberInputChange(e, setEavesOffset)} />
|
<input type="text" className="input-origin block" value={eavesOffset} onChange={(e) => setEavesOffset(normalizeDigits(e.target.value))} />
|
||||||
</div>
|
</div>
|
||||||
<span className="thin">mm</span>
|
<span className="thin">mm</span>
|
||||||
</div>
|
</div>
|
||||||
@ -32,7 +32,7 @@ export default function HipAndGable({ pitch, setPitch, eavesOffset, setEavesOffs
|
|||||||
type="text"
|
type="text"
|
||||||
className="input-origin block"
|
className="input-origin block"
|
||||||
value={hipAndGableWidth}
|
value={hipAndGableWidth}
|
||||||
onChange={(e) => onlyNumberInputChange(e, setHipAndGableWidth)}
|
onChange={(e) => setHipAndGableWidth(normalizeDigits(e.target.value))}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<span className="thin">mm</span>
|
<span className="thin">mm</span>
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { useMessage } from '@/hooks/useMessage'
|
import { useMessage } from '@/hooks/useMessage'
|
||||||
import { onlyNumberInputChange, onlyNumberWithDotInputChange } from '@/util/input-utils'
|
import { normalizeDecimalLimit, normalizeDigits } from '@/util/input-utils'
|
||||||
|
|
||||||
export default function Jerkinhead({
|
export default function Jerkinhead({
|
||||||
gableOffset,
|
gableOffset,
|
||||||
@ -18,7 +18,7 @@ export default function Jerkinhead({
|
|||||||
{getMessage('gable.offset')}
|
{getMessage('gable.offset')}
|
||||||
</span>
|
</span>
|
||||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||||
<input type="text" className="input-origin block" value={gableOffset} onChange={(e) => onlyNumberInputChange(e, setGableOffset)} />
|
<input type="text" className="input-origin block" value={gableOffset} onChange={(e) => setGableOffset(normalizeDigits(e.target.value))} />
|
||||||
</div>
|
</div>
|
||||||
<span className="thin">mm</span>
|
<span className="thin">mm</span>
|
||||||
</div>
|
</div>
|
||||||
@ -27,7 +27,7 @@ export default function Jerkinhead({
|
|||||||
{getMessage('jerkinhead.width')}
|
{getMessage('jerkinhead.width')}
|
||||||
</span>
|
</span>
|
||||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||||
<input type="text" className="input-origin block" value={jerkinHeadWidth} onChange={(e) => onlyNumberInputChange(e, setJerkinHeadWidth)} />
|
<input type="text" className="input-origin block" value={jerkinHeadWidth} onChange={(e) => setJerkinHeadWidth(normalizeDigits(e.target.value))} />
|
||||||
</div>
|
</div>
|
||||||
<span className="thin">mm</span>
|
<span className="thin">mm</span>
|
||||||
</div>
|
</div>
|
||||||
@ -40,7 +40,7 @@ export default function Jerkinhead({
|
|||||||
type="text"
|
type="text"
|
||||||
className="input-origin block"
|
className="input-origin block"
|
||||||
value={jerkinHeadPitch}
|
value={jerkinHeadPitch}
|
||||||
onChange={(e) => onlyNumberWithDotInputChange(e, setJerkinHeadPitch)}
|
onChange={(e) => setJerkinHeadPitch(normalizeDecimalLimit(e.target.value, 2))}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<span className="thin">{pitchText}</span>
|
<span className="thin">{pitchText}</span>
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { useMessage } from '@/hooks/useMessage'
|
import { useMessage } from '@/hooks/useMessage'
|
||||||
import { onlyNumberInputChange, onlyNumberWithDotInputChange } from '@/util/input-utils'
|
import { normalizeDecimalLimit, normalizeDigits } from '@/util/input-utils'
|
||||||
|
|
||||||
export default function Shed({ shedWidth, setShedWidth, shedPitch, setShedPitch, pitchText }) {
|
export default function Shed({ shedWidth, setShedWidth, shedPitch, setShedPitch, pitchText }) {
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
@ -8,14 +8,14 @@ export default function Shed({ shedWidth, setShedWidth, shedPitch, setShedPitch,
|
|||||||
<div className="outline-form mb10">
|
<div className="outline-form mb10">
|
||||||
<span className="mr10">{getMessage('slope')}</span>
|
<span className="mr10">{getMessage('slope')}</span>
|
||||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||||
<input type="text" className="input-origin block" value={shedPitch} onChange={(e) => onlyNumberWithDotInputChange(e, setShedPitch)} />
|
<input type="text" className="input-origin block" value={shedPitch} onChange={(e) => setShedPitch(normalizeDecimalLimit(e.target.value, 2))} />
|
||||||
</div>
|
</div>
|
||||||
<span className="thin">{pitchText}</span>
|
<span className="thin">{pitchText}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="outline-form">
|
<div className="outline-form">
|
||||||
<span className="mr10">{getMessage('shed.width')}</span>
|
<span className="mr10">{getMessage('shed.width')}</span>
|
||||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||||
<input type="text" className="input-origin block" value={shedWidth} onChange={(e) => onlyNumberInputChange(e, setShedWidth)} />
|
<input type="text" className="input-origin block" value={shedWidth} onChange={(e) => setShedWidth(normalizeDigits(e.target.value))} />
|
||||||
</div>
|
</div>
|
||||||
<span className="thin">mm</span>
|
<span className="thin">mm</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { useMessage } from '@/hooks/useMessage'
|
import { useMessage } from '@/hooks/useMessage'
|
||||||
import { onlyNumberInputChange } from '@/util/input-utils'
|
import { normalizeDigits } from '@/util/input-utils'
|
||||||
|
|
||||||
export default function Wall({ sleeveOffset, setSleeveOffset, hasSleeve, setHasSleeve }) {
|
export default function Wall({ sleeveOffset, setSleeveOffset, hasSleeve, setHasSleeve }) {
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
@ -29,7 +29,7 @@ export default function Wall({ sleeveOffset, setSleeveOffset, hasSleeve, setHasS
|
|||||||
type="text"
|
type="text"
|
||||||
className="input-origin block"
|
className="input-origin block"
|
||||||
value={sleeveOffset}
|
value={sleeveOffset}
|
||||||
onChange={(e) => onlyNumberInputChange(e, setSleeveOffset)}
|
onChange={(e) => setSleeveOffset(normalizeDigits(e.target.value))}
|
||||||
readOnly={hasSleeve === '0'}
|
readOnly={hasSleeve === '0'}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import { usePopup } from '@/hooks/usePopup'
|
|||||||
import WithDraggable from '@/components/common/draggable/WithDraggable'
|
import WithDraggable from '@/components/common/draggable/WithDraggable'
|
||||||
import { canvasState } from '@/store/canvasAtom'
|
import { canvasState } from '@/store/canvasAtom'
|
||||||
import { useCanvasSetting } from '@/hooks/option/useCanvasSetting'
|
import { useCanvasSetting } from '@/hooks/option/useCanvasSetting'
|
||||||
import { onlyNumberInputChange } from '@/util/input-utils'
|
import { normalizeDigits } from '@/util/input-utils'
|
||||||
|
|
||||||
export default function PlanSizeSetting(props) {
|
export default function PlanSizeSetting(props) {
|
||||||
const { setIsShow, horizon, vertical, id, pos = { x: 985, y: 180 }, settingsData, setSettingsData, settingsDataSave, setSettingsDataSave } = props
|
const { setIsShow, horizon, vertical, id, pos = { x: 985, y: 180 }, settingsData, setSettingsData, settingsDataSave, setSettingsDataSave } = props
|
||||||
@ -24,15 +24,15 @@ export default function PlanSizeSetting(props) {
|
|||||||
setPlanSizeSettingMode((prev) => {
|
setPlanSizeSettingMode((prev) => {
|
||||||
return {
|
return {
|
||||||
...prev,
|
...prev,
|
||||||
originHorizon: Number(planSizeSettingMode.originHorizon),
|
originHorizon: Number(normalizeDigits(planSizeSettingMode.originHorizon)),
|
||||||
originVertical: Number(planSizeSettingMode.originVertical),
|
originVertical: Number(normalizeDigits(planSizeSettingMode.originVertical)),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
setSettingsData({
|
setSettingsData({
|
||||||
...settingsData,
|
...settingsData,
|
||||||
originHorizon: Number(planSizeSettingMode.originHorizon),
|
originHorizon: Number(normalizeDigits(planSizeSettingMode.originHorizon)),
|
||||||
originVertical: Number(planSizeSettingMode.originVertical),
|
originVertical: Number(normalizeDigits(planSizeSettingMode.originVertical)),
|
||||||
})
|
})
|
||||||
|
|
||||||
canvas.setWidth(planSizeSettingMode.originHorizon)
|
canvas.setWidth(planSizeSettingMode.originHorizon)
|
||||||
@ -46,14 +46,15 @@ export default function PlanSizeSetting(props) {
|
|||||||
const changeInput = (value, e) => {
|
const changeInput = (value, e) => {
|
||||||
const { name } = e.target
|
const { name } = e.target
|
||||||
|
|
||||||
if (Number(value) > 100000) {
|
let n = Number(normalizeDigits(value))
|
||||||
value = 100000
|
if (n > 100000) {
|
||||||
|
n = 100000
|
||||||
}
|
}
|
||||||
|
|
||||||
setPlanSizeSettingMode((prev) => {
|
setPlanSizeSettingMode((prev) => {
|
||||||
return {
|
return {
|
||||||
...prev,
|
...prev,
|
||||||
[name]: Number(value) / 10,
|
[name]: n / 10,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -77,7 +78,7 @@ export default function PlanSizeSetting(props) {
|
|||||||
className="input-origin block"
|
className="input-origin block"
|
||||||
name={`originHorizon`}
|
name={`originHorizon`}
|
||||||
value={planSizeSettingMode.originHorizon * 10}
|
value={planSizeSettingMode.originHorizon * 10}
|
||||||
onChange={(e) => onlyNumberInputChange(e, changeInput)}
|
onChange={(e) => changeInput(normalizeDigits(e.target.value), e)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<span className="thin">mm</span>
|
<span className="thin">mm</span>
|
||||||
@ -90,7 +91,7 @@ export default function PlanSizeSetting(props) {
|
|||||||
className="input-origin block"
|
className="input-origin block"
|
||||||
name={`originVertical`}
|
name={`originVertical`}
|
||||||
value={planSizeSettingMode.originVertical * 10}
|
value={planSizeSettingMode.originVertical * 10}
|
||||||
onChange={(e) => onlyNumberInputChange(e, changeInput)}
|
onChange={(e) => changeInput(normalizeDigits(e.target.value), e)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<span className="thin">mm</span>
|
<span className="thin">mm</span>
|
||||||
|
|||||||
@ -25,6 +25,7 @@ import { isObjectNotEmpty } from '@/util/common-utils'
|
|||||||
import { roofMaterialsAtom } from '@/store/settingAtom'
|
import { roofMaterialsAtom } from '@/store/settingAtom'
|
||||||
import { useMasterController } from '@/hooks/common/useMasterController'
|
import { useMasterController } from '@/hooks/common/useMasterController'
|
||||||
import { ROOF_MATERIAL_LAYOUT } from '@/components/floor-plan/modal/placementShape/PlacementShapeSetting'
|
import { ROOF_MATERIAL_LAYOUT } from '@/components/floor-plan/modal/placementShape/PlacementShapeSetting'
|
||||||
|
import { useSwal } from '@/hooks/useSwal'
|
||||||
|
|
||||||
export const ToggleonMouse = (e, act, target) => {
|
export const ToggleonMouse = (e, act, target) => {
|
||||||
const listWrap = e.target.closest(target)
|
const listWrap = e.target.closest(target)
|
||||||
@ -75,6 +76,8 @@ export default function Header(props) {
|
|||||||
const [commonCode, setCommonCode] = useRecoilState(commonCodeState)
|
const [commonCode, setCommonCode] = useRecoilState(commonCodeState)
|
||||||
const { promiseGet } = useAxios()
|
const { promiseGet } = useAxios()
|
||||||
|
|
||||||
|
const { swalFire } = useSwal()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 지붕재 목록 Header에서 조회
|
* 지붕재 목록 Header에서 조회
|
||||||
*/
|
*/
|
||||||
@ -130,17 +133,21 @@ export default function Header(props) {
|
|||||||
{ id: 1, name: 'HANASYS 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: 'HANASYS 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' },
|
||||||
|
{ id: 4, name: 'Q.PARTNERS', link: `https://q-partners.q-cells.jp/qcast_login.php`, 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: 'HANASYS 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: 3, name: 'Q.PARTNERS', link: `https://q-partners.q-cells.jp/qcast_login.php`, target: '_blank' },
|
||||||
]
|
]
|
||||||
: [
|
: [
|
||||||
{ id: 0, name: getMessage('site.header.link1'), target: '_blank' },
|
{ id: 0, name: getMessage('site.header.link1'), target: '_blank' },
|
||||||
{ id: 1, name: 'HANASYS 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' },
|
||||||
|
{ id: 3, name: 'Q.PARTNERS', link: `https://q-partners.q-cells.jp/qcast_login.php`, target: '_blank' },
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
onChangeSelect({ id: 0, name: getMessage('site.header.link1') })
|
onChangeSelect({ id: 0, name: getMessage('site.header.link1') })
|
||||||
@ -232,11 +239,32 @@ export default function Header(props) {
|
|||||||
key={`${menu.id}`}
|
key={`${menu.id}`}
|
||||||
href={menu.url}
|
href={menu.url}
|
||||||
replace={true}
|
replace={true}
|
||||||
onClick={() => {
|
onClick={(e) => {
|
||||||
// moveHome()
|
|
||||||
removeStuffRecoil(menu)
|
if(pathName === '/floor-plan') {
|
||||||
if (pathName === '/' && menu.id !== 8) {
|
e.preventDefault() // 기본 네비게이션 방지
|
||||||
window.location.reload()
|
e.stopPropagation() // 이벤트 전파 방지
|
||||||
|
swalFire({
|
||||||
|
text : getMessage('common.link.confirm'), // 적절한 확인 메시지
|
||||||
|
type : 'confirm',
|
||||||
|
confirmFn: () => {
|
||||||
|
// 확인 버튼 클릭 시 실행
|
||||||
|
removeStuffRecoil(menu)
|
||||||
|
if (pathName === '/' && menu.id !== 8) {
|
||||||
|
window.location.reload()
|
||||||
|
} else {
|
||||||
|
router.push(menu.url)
|
||||||
|
}
|
||||||
|
router.push(menu.url)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
removeStuffRecoil(menu)
|
||||||
|
if (pathName === '/' && menu.id !== 8) {
|
||||||
|
window.location.reload()
|
||||||
|
} else {
|
||||||
|
router.push(menu.url)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -258,9 +286,24 @@ export default function Header(props) {
|
|||||||
scroll={false}
|
scroll={false}
|
||||||
href={m.url}
|
href={m.url}
|
||||||
replace={true}
|
replace={true}
|
||||||
onClick={() => {
|
onClick={(e) => {
|
||||||
removeStuffRecoil(m)
|
if(pathName === '/floor-plan') {
|
||||||
}}
|
e.preventDefault() // 기본 네비게이션 방지
|
||||||
|
e.stopPropagation() // 이벤트 전파 방지
|
||||||
|
swalFire({
|
||||||
|
text: getMessage('common.link.confirm'), // 적절한 확인 메시지
|
||||||
|
type: 'confirm',
|
||||||
|
confirmFn: () => {
|
||||||
|
// 확인 버튼 클릭 시 기존 로직 실행
|
||||||
|
removeStuffRecoil(m)
|
||||||
|
router.push(m.url)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
removeStuffRecoil(m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{getMessage(m.name)}
|
{getMessage(m.name)}
|
||||||
</Link>
|
</Link>
|
||||||
@ -284,14 +327,28 @@ export default function Header(props) {
|
|||||||
<h1 className="logo">
|
<h1 className="logo">
|
||||||
<Link
|
<Link
|
||||||
href={'/'}
|
href={'/'}
|
||||||
onClick={() => {
|
onClick={(e) => {
|
||||||
setStuffSearch({
|
setStuffSearch({
|
||||||
...stuffSearch,
|
...stuffSearch,
|
||||||
code: 'DELETE',
|
code: 'DELETE',
|
||||||
})
|
})
|
||||||
if (pathName === '/') {
|
if (pathName === '/') {
|
||||||
window.location.reload()
|
window.location.reload()
|
||||||
|
} else if(pathName === '/floor-plan') {
|
||||||
|
e.preventDefault() // 기본 네비게이션 방지
|
||||||
|
e.stopPropagation() // 이벤트 전파 방지
|
||||||
|
swalFire({
|
||||||
|
text: getMessage('common.link.confirm'), // 적절한 확인 메시지
|
||||||
|
type: 'confirm',
|
||||||
|
confirmFn: () => {
|
||||||
|
// 확인 버튼 클릭 시 기존 로직 실행
|
||||||
|
//removeStuffRecoil(m)
|
||||||
|
router.push('/')
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}}
|
}}
|
||||||
></Link>
|
></Link>
|
||||||
</h1>
|
</h1>
|
||||||
|
|||||||
@ -22,6 +22,7 @@ import { stuffSearchState } from '@/store/stuffAtom'
|
|||||||
import { QcastContext } from '@/app/QcastProvider'
|
import { QcastContext } from '@/app/QcastProvider'
|
||||||
import { useCanvasMenu } from '@/hooks/common/useCanvasMenu'
|
import { useCanvasMenu } from '@/hooks/common/useCanvasMenu'
|
||||||
import { useSwal } from '@/hooks/useSwal'
|
import { useSwal } from '@/hooks/useSwal'
|
||||||
|
import { sanitizeIntegerInputEvent } from '@/util/input-utils'
|
||||||
|
|
||||||
export default function StuffDetail() {
|
export default function StuffDetail() {
|
||||||
const [stuffSearch, setStuffSearch] = useRecoilState(stuffSearchState)
|
const [stuffSearch, setStuffSearch] = useRecoilState(stuffSearchState)
|
||||||
@ -1239,10 +1240,10 @@ export default function StuffDetail() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//주소
|
//주소
|
||||||
if (!formData.address) {
|
// if (!formData.address) {
|
||||||
fieldNm = getMessage('stuff.detail.address')
|
// fieldNm = getMessage('stuff.detail.address')
|
||||||
errors = fieldNm
|
// errors = fieldNm
|
||||||
}
|
// }
|
||||||
|
|
||||||
//도도부현
|
//도도부현
|
||||||
if (!formData.prefId || formData.prefId === '0') {
|
if (!formData.prefId || formData.prefId === '0') {
|
||||||
@ -1634,13 +1635,15 @@ export default function StuffDetail() {
|
|||||||
|
|
||||||
// 숫자만 입력 가능
|
// 숫자만 입력 가능
|
||||||
const handleKeyUp = (e) => {
|
const handleKeyUp = (e) => {
|
||||||
let input = e.target
|
// let input = e.target
|
||||||
input.value = input.value.replace(/[^0-9]/g, '')
|
// input.value = input.value.replace(/[^0-9]/g, '')
|
||||||
|
sanitizeIntegerInputEvent(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleBlur = (e) => {
|
const handleBlur = (e) => {
|
||||||
let input = e.target
|
// let input = e.target
|
||||||
input.value = input.value.replace(/[^0-9]/g, '')
|
// input.value = input.value.replace(/[^0-9]/g, '')
|
||||||
|
sanitizeIntegerInputEvent(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 그리드 더블 클릭 해당플랜의 도면작성 화면으로 이동
|
// 그리드 더블 클릭 해당플랜의 도면작성 화면으로 이동
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import { useMessage } from '@/hooks/useMessage'
|
|||||||
import { isNotEmptyArray } from '@/util/common-utils'
|
import { isNotEmptyArray } from '@/util/common-utils'
|
||||||
import { useSwal } from '@/hooks/useSwal'
|
import { useSwal } from '@/hooks/useSwal'
|
||||||
import { QcastContext } from '@/app/QcastProvider'
|
import { QcastContext } from '@/app/QcastProvider'
|
||||||
|
import { sanitizeIntegerInputEvent } from '@/util/input-utils'
|
||||||
export default function FindAddressPop(props) {
|
export default function FindAddressPop(props) {
|
||||||
const globalLocaleState = useRecoilValue(globalLocaleStore)
|
const globalLocaleState = useRecoilValue(globalLocaleStore)
|
||||||
|
|
||||||
@ -113,12 +114,12 @@ export default function FindAddressPop(props) {
|
|||||||
|
|
||||||
//숫자만
|
//숫자만
|
||||||
const handleKeyUp = (e) => {
|
const handleKeyUp = (e) => {
|
||||||
let input = e.target
|
// let input = e.target
|
||||||
input.value = input.value.replace(/[^0-9]/g, '')
|
// input.value = input.value.replace(/[^0-9]/g, '')
|
||||||
|
// if (e.key === 'Enter') {
|
||||||
if (e.key === 'Enter') {
|
// searchPostNum()
|
||||||
searchPostNum()
|
// }
|
||||||
}
|
sanitizeIntegerInputEvent(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
//그리드에서 선택한 우편정보
|
//그리드에서 선택한 우편정보
|
||||||
|
|||||||
@ -11,6 +11,8 @@ import { isObjectNotEmpty, queryStringFormatter } from '@/util/common-utils'
|
|||||||
import QPagination from '@/components/common/pagination/QPagination'
|
import QPagination from '@/components/common/pagination/QPagination'
|
||||||
import { useSwal } from '@/hooks/useSwal'
|
import { useSwal } from '@/hooks/useSwal'
|
||||||
import { QcastContext } from '@/app/QcastProvider'
|
import { QcastContext } from '@/app/QcastProvider'
|
||||||
|
import { sanitizeDecimalInputEvent } from '@/util/input-utils'
|
||||||
|
|
||||||
export default function PlanRequestPop(props) {
|
export default function PlanRequestPop(props) {
|
||||||
const [pageNo, setPageNo] = useState(1) //현재 페이지 번호
|
const [pageNo, setPageNo] = useState(1) //현재 페이지 번호
|
||||||
const [pageSize, setPageSize] = useState(20) //페이지 당 게시물 개수
|
const [pageSize, setPageSize] = useState(20) //페이지 당 게시물 개수
|
||||||
@ -233,16 +235,16 @@ export default function PlanRequestPop(props) {
|
|||||||
|
|
||||||
// 숫자만 입력 가능
|
// 숫자만 입력 가능
|
||||||
const handleKeyUp = (e) => {
|
const handleKeyUp = (e) => {
|
||||||
let input = e.target
|
sanitizeIntegerInputEvent(e)
|
||||||
input.value = input.value.replace(/[^0-9]/g, '')
|
|
||||||
if (e.key === 'Enter') {
|
if (e.key === 'Enter') {
|
||||||
onSubmit(pageNo, 'S')
|
onSubmit(pageNo, 'S')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleBlur = (e) => {
|
const handleBlur = (e) => {
|
||||||
let input = e.target
|
// let input = e.target
|
||||||
input.value = input.value.replace(/[^0-9]/g, '')
|
// input.value = input.value.replace(/[^0-9]/g, '')
|
||||||
|
sanitizeIntegerInputEvent(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 엔터 이벤트
|
// 엔터 이벤트
|
||||||
|
|||||||
@ -18,7 +18,7 @@ import { usePlan } from '@/hooks/usePlan'
|
|||||||
import { usePopup } from '@/hooks/usePopup'
|
import { usePopup } from '@/hooks/usePopup'
|
||||||
|
|
||||||
import { QcastContext } from '@/app/QcastProvider'
|
import { QcastContext } from '@/app/QcastProvider'
|
||||||
|
import { useCanvasMenu } from '@/hooks/common/useCanvasMenu'
|
||||||
export default function Simulator() {
|
export default function Simulator() {
|
||||||
// global 로딩바
|
// global 로딩바
|
||||||
const { setIsGlobalLoading } = useContext(QcastContext)
|
const { setIsGlobalLoading } = useContext(QcastContext)
|
||||||
@ -34,6 +34,7 @@ export default function Simulator() {
|
|||||||
|
|
||||||
const { get } = useAxios()
|
const { get } = useAxios()
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
|
const { setSelectedMenu } = useCanvasMenu()
|
||||||
|
|
||||||
// 차트 관련
|
// 차트 관련
|
||||||
const [chartData, setChartData] = useState([])
|
const [chartData, setChartData] = useState([])
|
||||||
@ -48,34 +49,25 @@ export default function Simulator() {
|
|||||||
return isNaN(num) ? 0 : num
|
return isNaN(num) ? 0 : num
|
||||||
}),
|
}),
|
||||||
|
|
||||||
backgroundColor: [
|
backgroundColor: (context) => {
|
||||||
'rgba(255, 99, 132, 0.2)',
|
const chart = context.chart
|
||||||
'rgba(54, 162, 235, 0.2)',
|
const { ctx, chartArea } = chart
|
||||||
'rgba(255, 206, 86, 0.2)',
|
|
||||||
'rgba(75, 192, 192, 0.2)',
|
if (!chartArea) {
|
||||||
'rgba(153, 102, 255, 0.2)',
|
// This case happens on initial chart load
|
||||||
'rgba(255, 159, 64, 0.2)',
|
return null
|
||||||
'rgba(0, 99, 132, 0.2)',
|
}
|
||||||
'rgba(0, 162, 235, 0.2)',
|
|
||||||
'rgba(0, 206, 86, 0.2)',
|
const gradient = ctx.createLinearGradient(0, chartArea.bottom, 0, chartArea.top)
|
||||||
'rgba(0, 192, 192, 0.2)',
|
gradient.addColorStop(0, '#4FC3F7') // Light blue at bottom
|
||||||
'rgba(0, 102, 255, 0.2)',
|
gradient.addColorStop(0.3, '#2FA8E0') // Original blue
|
||||||
'rgba(0, 159, 64, 0.2)',
|
gradient.addColorStop(0.7, '#1976D2') // Medium blue
|
||||||
],
|
gradient.addColorStop(1, '#0D47A1') // Dark blue at top
|
||||||
borderColor: [
|
|
||||||
'rgba(255, 99, 132, 0.2)',
|
|
||||||
'rgba(54, 162, 235, 0.2)',
|
return gradient
|
||||||
'rgba(255, 206, 86, 0.2)',
|
},
|
||||||
'rgba(75, 192, 192, 0.2)',
|
borderColor: '#2FA8E0' ,
|
||||||
'rgba(153, 102, 255, 0.2)',
|
|
||||||
'rgba(255, 159, 64, 0.2)',
|
|
||||||
'rgba(0, 99, 132, 0.2)',
|
|
||||||
'rgba(0, 162, 235, 0.2)',
|
|
||||||
'rgba(0, 206, 86, 0.2)',
|
|
||||||
'rgba(0, 192, 192, 0.2)',
|
|
||||||
'rgba(0, 102, 255, 0.2)',
|
|
||||||
'rgba(0, 159, 64, 0.2)',
|
|
||||||
],
|
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -103,6 +95,7 @@ export default function Simulator() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
setSelectedMenu('simulation')
|
||||||
/* 초기화 작업 */
|
/* 초기화 작업 */
|
||||||
setChartData([])
|
setChartData([])
|
||||||
setObjectDetail({})
|
setObjectDetail({})
|
||||||
@ -193,10 +186,10 @@ export default function Simulator() {
|
|||||||
setChartData(hatsudenryouAll)
|
setChartData(hatsudenryouAll)
|
||||||
break
|
break
|
||||||
case 'B':
|
case 'B':
|
||||||
setChartData(hatsudenryouAllSnow)
|
setChartData(hatsudenryouPeakcutAll)
|
||||||
break
|
break
|
||||||
case 'C':
|
case 'C':
|
||||||
setChartData(hatsudenryouPeakcutAll)
|
setChartData(hatsudenryouAllSnow)
|
||||||
break
|
break
|
||||||
case 'D':
|
case 'D':
|
||||||
setChartData(hatsudenryouPeakcutAllSnow)
|
setChartData(hatsudenryouPeakcutAllSnow)
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import getConfigs from './config.common'
|
import getConfigs from './config.common'
|
||||||
|
|
||||||
// 환경마다 달라져야 할 변수, 값들을 정의합니다. (여기는 development 환경에 맞는 값을 지정합니다.)
|
// 환경마다 달라져야 할 변수, 값들을 정의합니다. (여기는 development 환경에 맞는 값을 지정합니다.)
|
||||||
const baseUrl = 'https://dev.hanssys.jp'
|
const baseUrl = 'https://dev.hanasys.jp'
|
||||||
const mode = 'development'
|
const mode = 'development'
|
||||||
|
|
||||||
// 환경마다 달라져야 할 값들을 getConfig 함수에 전달합니다.
|
// 환경마다 달라져야 할 값들을 getConfig 함수에 전달합니다.
|
||||||
|
|||||||
@ -101,6 +101,7 @@ export function useCanvasPopupStatusController(param = 1) {
|
|||||||
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, '\"'),
|
popupStatus: JSON.stringify(arg).replace(/"/g, '\"'),
|
||||||
|
//hajebichi: arg.roofConstructions?.[0]?.addRoof?.hajebichi || '',
|
||||||
}
|
}
|
||||||
postFetcher(`/api/v1/canvas-popup-status`, params)
|
postFetcher(`/api/v1/canvas-popup-status`, params)
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,17 +1,18 @@
|
|||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil'
|
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||||
import { wordDisplaySelector } from '@/store/settingAtom'
|
import { wordDisplaySelector } from '@/store/settingAtom'
|
||||||
import { useEvent } from '@/hooks/useEvent'
|
import { useEvent } from '@/hooks/useEvent'
|
||||||
import { checkLineOrientation, getDistance } from '@/util/canvas-util'
|
import { checkLineOrientation, getDistance } from '@/util/canvas-util'
|
||||||
import { commonUtilsState, dimensionLineSettingsState } from '@/store/commonUtilsAtom'
|
import { commonUtilsState, dimensionLineSettingsState } from '@/store/commonUtilsAtom'
|
||||||
import { fontSelector } from '@/store/fontAtom'
|
import { fontSelector } from '@/store/fontAtom'
|
||||||
import { canvasState, currentMenuState } from '@/store/canvasAtom'
|
import { canvasState } from '@/store/canvasAtom'
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
import { usePopup } from '@/hooks/usePopup'
|
import { usePopup } from '@/hooks/usePopup'
|
||||||
import Distance from '@/components/floor-plan/modal/distance/Distance'
|
import Distance from '@/components/floor-plan/modal/distance/Distance'
|
||||||
import { usePolygon } from '@/hooks/usePolygon'
|
import { usePolygon } from '@/hooks/usePolygon'
|
||||||
import { useObjectBatch } from '@/hooks/object/useObjectBatch'
|
import { useObjectBatch } from '@/hooks/object/useObjectBatch'
|
||||||
import { BATCH_TYPE } from '@/common/common'
|
import { BATCH_TYPE } from '@/common/common'
|
||||||
|
import { useMouse } from '@/hooks/useMouse'
|
||||||
|
|
||||||
export function useCommonUtils() {
|
export function useCommonUtils() {
|
||||||
const canvas = useRecoilValue(canvasState)
|
const canvas = useRecoilValue(canvasState)
|
||||||
@ -25,6 +26,7 @@ export function useCommonUtils() {
|
|||||||
const { addPopup, closeAll, targetClose } = usePopup()
|
const { addPopup, closeAll, targetClose } = usePopup()
|
||||||
const { drawDirectionArrow, addLengthText } = usePolygon()
|
const { drawDirectionArrow, addLengthText } = usePolygon()
|
||||||
const { applyDormers } = useObjectBatch({})
|
const { applyDormers } = useObjectBatch({})
|
||||||
|
const { getIntersectMousePoint } = useMouse()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
commonTextMode()
|
commonTextMode()
|
||||||
@ -213,7 +215,7 @@ export function useCommonUtils() {
|
|||||||
addCanvasMouseEventListener('mouse:down', (e) => {
|
addCanvasMouseEventListener('mouse:down', (e) => {
|
||||||
let groupObjects = []
|
let groupObjects = []
|
||||||
|
|
||||||
const pointer = canvas.getPointer(e.e)
|
const pointer = getIntersectMousePoint(e)
|
||||||
|
|
||||||
let point
|
let point
|
||||||
|
|
||||||
@ -654,7 +656,11 @@ export function useCommonUtils() {
|
|||||||
clonedObj.setCoords()
|
clonedObj.setCoords()
|
||||||
clonedObj.fire('modified')
|
clonedObj.fire('modified')
|
||||||
clonedObj.fire('polygonMoved')
|
clonedObj.fire('polygonMoved')
|
||||||
clonedObj.set({ direction: obj.direction, directionText: obj.directionText, roofMaterial: obj.roofMaterial })
|
clonedObj.set({
|
||||||
|
direction: obj.direction,
|
||||||
|
directionText: obj.directionText,
|
||||||
|
roofMaterial: obj.roofMaterial,
|
||||||
|
})
|
||||||
|
|
||||||
obj.lines.forEach((line, index) => {
|
obj.lines.forEach((line, index) => {
|
||||||
clonedObj.lines[index].set({ attributes: line.attributes })
|
clonedObj.lines[index].set({ attributes: line.attributes })
|
||||||
|
|||||||
@ -1,15 +1,18 @@
|
|||||||
import { useRecoilValue } from 'recoil'
|
import { useRecoilValue } from 'recoil'
|
||||||
import { canvasState, dotLineGridSettingState } from '@/store/canvasAtom'
|
import { canvasState, canvasZoomState, dotLineGridSettingState } from '@/store/canvasAtom'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { gridColorState } from '@/store/gridAtom'
|
import { gridColorState } from '@/store/gridAtom'
|
||||||
import { gridDisplaySelector } from '@/store/settingAtom'
|
import { gridDisplaySelector } from '@/store/settingAtom'
|
||||||
|
|
||||||
const GRID_PADDING = 5
|
const GRID_PADDING = 5
|
||||||
|
|
||||||
export function useGrid() {
|
export function useGrid() {
|
||||||
const canvas = useRecoilValue(canvasState)
|
const canvas = useRecoilValue(canvasState)
|
||||||
|
|
||||||
const dotLineGridSetting = useRecoilValue(dotLineGridSettingState)
|
const dotLineGridSetting = useRecoilValue(dotLineGridSettingState)
|
||||||
const gridColor = useRecoilValue(gridColorState)
|
const gridColor = useRecoilValue(gridColorState)
|
||||||
const isGridDisplay = useRecoilValue(gridDisplaySelector)
|
const isGridDisplay = useRecoilValue(gridDisplaySelector)
|
||||||
|
const zoom = useRecoilValue(canvasZoomState)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!canvas) {
|
if (!canvas) {
|
||||||
@ -90,14 +93,32 @@ export function useGrid() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (patternData.lineGridDisplay) {
|
if (patternData.lineGridDisplay) {
|
||||||
for (let i = 0; i < 5000 / patternData.gridVertical + 1; i++) {
|
// 캔버스의 실제 보이는 영역 계산
|
||||||
|
const canvasWidth = canvas.getWidth()
|
||||||
|
const canvasHeight = canvas.getHeight()
|
||||||
|
const currentZoom = canvas.getZoom()
|
||||||
|
const viewportTransform = canvas.viewportTransform
|
||||||
|
|
||||||
|
const visibleLeft = -viewportTransform[4] / currentZoom
|
||||||
|
const visibleTop = -viewportTransform[5] / currentZoom
|
||||||
|
const visibleRight = visibleLeft + canvasWidth / currentZoom
|
||||||
|
const visibleBottom = visibleTop + canvasHeight / currentZoom
|
||||||
|
|
||||||
|
// 여유 공간 추가
|
||||||
|
const padding = 200
|
||||||
|
const gridLeft = visibleLeft - padding
|
||||||
|
const gridTop = visibleTop - padding
|
||||||
|
const gridRight = visibleRight + padding
|
||||||
|
const gridBottom = visibleBottom + padding
|
||||||
|
|
||||||
|
// 가로선 생성 (수평선)
|
||||||
|
const horizontalGridRange = gridBottom - gridTop
|
||||||
|
const horizontalGridCount = Math.ceil(horizontalGridRange / patternData.gridVertical) + 2
|
||||||
|
|
||||||
|
for (let i = 0; i < horizontalGridCount; i++) {
|
||||||
|
const y = gridTop + i * patternData.gridVertical
|
||||||
const horizontalLine = new fabric.Line(
|
const horizontalLine = new fabric.Line(
|
||||||
[
|
[gridLeft, y, gridRight, y],
|
||||||
-1500,
|
|
||||||
-1500 + i * patternData.gridVertical - patternData.gridVertical / 2,
|
|
||||||
3000,
|
|
||||||
-1500 + i * patternData.gridVertical - patternData.gridVertical / 2,
|
|
||||||
],
|
|
||||||
{
|
{
|
||||||
stroke: gridColor,
|
stroke: gridColor,
|
||||||
strokeWidth: 1,
|
strokeWidth: 1,
|
||||||
@ -118,14 +139,14 @@ export function useGrid() {
|
|||||||
canvas.add(horizontalLine)
|
canvas.add(horizontalLine)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < 5000 / patternData.gridHorizon + 1; i++) {
|
// 세로선 생성 (수직선)
|
||||||
|
const verticalGridRange = gridRight - gridLeft
|
||||||
|
const verticalGridCount = Math.ceil(verticalGridRange / patternData.gridHorizon) + 2
|
||||||
|
|
||||||
|
for (let i = 0; i < verticalGridCount; i++) {
|
||||||
|
const x = gridLeft + i * patternData.gridHorizon
|
||||||
const verticalLine = new fabric.Line(
|
const verticalLine = new fabric.Line(
|
||||||
[
|
[x, gridTop, x, gridBottom],
|
||||||
-1500 + i * patternData.gridHorizon - patternData.gridHorizon / 2,
|
|
||||||
-1500,
|
|
||||||
-1500 + i * patternData.gridHorizon - patternData.gridHorizon / 2,
|
|
||||||
3000,
|
|
||||||
],
|
|
||||||
{
|
{
|
||||||
stroke: gridColor,
|
stroke: gridColor,
|
||||||
strokeWidth: 1,
|
strokeWidth: 1,
|
||||||
@ -148,7 +169,7 @@ export function useGrid() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
canvas.renderAll()
|
canvas.renderAll()
|
||||||
}, [dotLineGridSetting])
|
}, [dotLineGridSetting, zoom])
|
||||||
|
|
||||||
const move = (object, x, y) => {
|
const move = (object, x, y) => {
|
||||||
object.set({
|
object.set({
|
||||||
|
|||||||
@ -6,7 +6,6 @@ import EavesGableEdit from '@/components/floor-plan/modal/eavesGable/EavesGableE
|
|||||||
import MovementSetting from '@/components/floor-plan/modal/movement/MovementSetting'
|
import MovementSetting from '@/components/floor-plan/modal/movement/MovementSetting'
|
||||||
import WallLineOffsetSetting from '@/components/floor-plan/modal/wallLineOffset/WallLineOffsetSetting'
|
import WallLineOffsetSetting from '@/components/floor-plan/modal/wallLineOffset/WallLineOffsetSetting'
|
||||||
import RoofAllocationSetting from '@/components/floor-plan/modal/roofAllocation/RoofAllocationSetting'
|
import RoofAllocationSetting from '@/components/floor-plan/modal/roofAllocation/RoofAllocationSetting'
|
||||||
import Slope from '@/components/floor-plan/modal/Slope'
|
|
||||||
import PlacementShapeDrawing from '@/components/floor-plan/modal/placementShape/PlacementShapeDrawing'
|
import PlacementShapeDrawing from '@/components/floor-plan/modal/placementShape/PlacementShapeDrawing'
|
||||||
import PlacementSurfaceSetting from '@/components/floor-plan/modal/placementSurface/PlacementSurfaceSetting'
|
import PlacementSurfaceSetting from '@/components/floor-plan/modal/placementSurface/PlacementSurfaceSetting'
|
||||||
import ObjectSetting from '@/components/floor-plan/modal/object/ObjectSetting'
|
import ObjectSetting from '@/components/floor-plan/modal/object/ObjectSetting'
|
||||||
@ -20,9 +19,8 @@ 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 { useOrientation } from '@/hooks/module/useOrientation'
|
import { useOrientation } from '@/hooks/module/useOrientation'
|
||||||
import { corridorDimensionSelector, settingModalFirstOptionsState } from '@/store/settingAtom'
|
import { corridorDimensionSelector } from '@/store/settingAtom'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 메뉴 처리 훅
|
* 메뉴 처리 훅
|
||||||
@ -33,12 +31,13 @@ export default function useMenu() {
|
|||||||
const currentMenu = useRecoilValue(currentMenuState)
|
const currentMenu = useRecoilValue(currentMenuState)
|
||||||
const canvas = useRecoilValue(canvasState)
|
const canvas = useRecoilValue(canvasState)
|
||||||
const [popupId, setPopupId] = useState(uuidv4())
|
const [popupId, setPopupId] = useState(uuidv4())
|
||||||
const { addPopup } = usePopup()
|
const { addPopup, closeAll } = usePopup()
|
||||||
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 [corridorDimension, setCorridorDimension] = useRecoilState(corridorDimensionSelector)
|
||||||
const handleMenu = (type) => {
|
const handleMenu = (type) => {
|
||||||
|
closeAll()
|
||||||
if (type === 'outline') {
|
if (type === 'outline') {
|
||||||
// 지붕 덮개 메뉴의 경우는 복도치수로 적용한다.
|
// 지붕 덮개 메뉴의 경우는 복도치수로 적용한다.
|
||||||
setCorridorDimension(0)
|
setCorridorDimension(0)
|
||||||
@ -67,6 +66,9 @@ export default function useMenu() {
|
|||||||
case MENU.ROOF_COVERING.ROOF_SHAPE_ALLOC:
|
case MENU.ROOF_COVERING.ROOF_SHAPE_ALLOC:
|
||||||
addPopup(popupId, 1, <RoofAllocationSetting id={popupId} />)
|
addPopup(popupId, 1, <RoofAllocationSetting id={popupId} />)
|
||||||
break
|
break
|
||||||
|
case MENU.ROOF_COVERING.ALL_REMOVE:
|
||||||
|
deleteAllSurfacesAndObjects()
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -9,6 +9,8 @@ import { deleteBackGroundImage, setBackGroundImage } from '@/lib/imageActions'
|
|||||||
import { settingModalFirstOptionsState } from '@/store/settingAtom'
|
import { settingModalFirstOptionsState } from '@/store/settingAtom'
|
||||||
import { popSpinnerState } from '@/store/popupAtom'
|
import { popSpinnerState } from '@/store/popupAtom'
|
||||||
import Config from '@/config/config.export'
|
import Config from '@/config/config.export'
|
||||||
|
import { useMessage } from '@/hooks/useMessage'
|
||||||
|
import { logger } from '@/util/logger'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 배경 이미지 관리
|
* 배경 이미지 관리
|
||||||
@ -23,7 +25,9 @@ import Config from '@/config/config.export'
|
|||||||
* @returns {object}
|
* @returns {object}
|
||||||
*/
|
*/
|
||||||
export function useRefFiles() {
|
export function useRefFiles() {
|
||||||
const converterUrl = process.env.NEXT_PUBLIC_CONVERTER_API_URL
|
const converterDwgUrl = process.env.NEXT_PUBLIC_CONVERTER_DWG_API_URL
|
||||||
|
const converterDxfUrl = process.env.NEXT_PUBLIC_CONVERTER_DXF_API_URL
|
||||||
|
const { getMessage } = useMessage()
|
||||||
const [refImage, setRefImage] = useState(null)
|
const [refImage, setRefImage] = useState(null)
|
||||||
const [refFileMethod, setRefFileMethod] = useState('1')
|
const [refFileMethod, setRefFileMethod] = useState('1')
|
||||||
const [mapPositionAddress, setMapPositionAddress] = useState('')
|
const [mapPositionAddress, setMapPositionAddress] = useState('')
|
||||||
@ -50,12 +54,12 @@ export function useRefFiles() {
|
|||||||
* @param {*} file
|
* @param {*} file
|
||||||
*/
|
*/
|
||||||
const handleRefFile = (file) => {
|
const handleRefFile = (file) => {
|
||||||
console.log('handleRefFile', file)
|
logger.log('handleRefFile', file)
|
||||||
console.log('refImage', refImage)
|
logger.log('refImage', refImage)
|
||||||
|
|
||||||
if (refImage) {
|
if (refImage) {
|
||||||
swalFire({
|
swalFire({
|
||||||
text: '파일을 변경하시겠습니까?',
|
text: getMessage('common.message.ref.file.change'),
|
||||||
type: 'confirm',
|
type: 'confirm',
|
||||||
confirmFn: () => {
|
confirmFn: () => {
|
||||||
refFileSetting(file)
|
refFileSetting(file)
|
||||||
@ -71,15 +75,29 @@ export function useRefFiles() {
|
|||||||
* @param {File} file
|
* @param {File} file
|
||||||
*/
|
*/
|
||||||
const refFileSetting = (file) => {
|
const refFileSetting = (file) => {
|
||||||
console.log('🚀 ~ refFileSetting ~ file:', file)
|
logger.log('🚀 ~ refFileSetting ~ file:', file)
|
||||||
|
|
||||||
|
setPopSpinnerStore(true)
|
||||||
|
const newOption1 = settingModalFirstOptions.option1.map((option) => ({
|
||||||
|
...option,
|
||||||
|
selected: option.column === 'imageDisplay' ? true : option.selected,
|
||||||
|
}))
|
||||||
|
|
||||||
|
setSettingModalFirstOptions((prev) => ({
|
||||||
|
...prev,
|
||||||
|
option1: newOption1,
|
||||||
|
}))
|
||||||
|
|
||||||
if (file.name.split('.').pop() === 'dwg') {
|
if (file.name.split('.').pop() === 'dwg') {
|
||||||
handleUploadConvertRefFile(file)
|
handleUploadConvertRefFile(file, converterDwgUrl);
|
||||||
|
} else if (file.name.split('.').pop() === 'dxf') {
|
||||||
|
handleUploadConvertRefFile(file, converterDxfUrl);
|
||||||
} else {
|
} else {
|
||||||
if (file && ['image/png', 'image/jpg', 'image/jpeg', 'image/bmp', 'image/gif'].includes(file.type)) {
|
if (file && ['image/png', 'image/jpg', 'image/jpeg', 'image/bmp', 'image/gif'].includes(file.type)) {
|
||||||
handleUploadImageRefFile(file)
|
handleUploadImageRefFile(file)
|
||||||
} else {
|
} else {
|
||||||
swalFire({
|
swalFire({
|
||||||
text: '이미지가 아닙니다.',
|
text: getMessage('common.message.ref.file.noImage'),
|
||||||
type: 'alert',
|
type: 'alert',
|
||||||
icon: 'error',
|
icon: 'error',
|
||||||
})
|
})
|
||||||
@ -92,12 +110,12 @@ export function useRefFiles() {
|
|||||||
*/
|
*/
|
||||||
const handleFileDelete = async () => {
|
const handleFileDelete = async () => {
|
||||||
swalFire({
|
swalFire({
|
||||||
text: '삭제하시겠습니까?',
|
text: getMessage('common.message.data.delete'),
|
||||||
type: 'confirm',
|
type: 'confirm',
|
||||||
confirmFn: async () => {
|
confirmFn: async () => {
|
||||||
setPopSpinnerStore(true)
|
setPopSpinnerStore(true)
|
||||||
console.log('🚀 ~ handleFileDelete ~ handleFileDelete:', refImage)
|
logger.log('🚀 ~ handleFileDelete ~ handleFileDelete:', refImage)
|
||||||
console.log('🚀 ~ handleFileDelete ~ currentCanvasPlan.bgImageName:', currentCanvasPlan.bgImageName)
|
logger.log('🚀 ~ handleFileDelete ~ currentCanvasPlan.bgImageName:', currentCanvasPlan.bgImageName)
|
||||||
await del({ url: `${Config().baseUrl}/api/image/upload?fileName=${currentCanvasPlan.bgImageName}` })
|
await del({ url: `${Config().baseUrl}/api/image/upload?fileName=${currentCanvasPlan.bgImageName}` })
|
||||||
setCurrentBgImage(null)
|
setCurrentBgImage(null)
|
||||||
await deleteBackGroundImage({
|
await deleteBackGroundImage({
|
||||||
@ -114,11 +132,11 @@ export function useRefFiles() {
|
|||||||
*/
|
*/
|
||||||
const handleAddressDelete = async () => {
|
const handleAddressDelete = async () => {
|
||||||
swalFire({
|
swalFire({
|
||||||
text: '삭제하시겠습니까?',
|
text: getMessage('common.message.data.delete'),
|
||||||
type: 'confirm',
|
type: 'confirm',
|
||||||
confirmFn: async () => {
|
confirmFn: async () => {
|
||||||
console.log('🚀 ~ handleAddressDelete ~ handleAddressDelete:', refImage)
|
logger.log('🚀 ~ handleAddressDelete ~ handleAddressDelete:', refImage)
|
||||||
console.log('🚀 ~ handleAddressDelete ~ currentCanvasPlan.bgImageName:', currentCanvasPlan.bgImageName)
|
logger.log('🚀 ~ handleAddressDelete ~ currentCanvasPlan.bgImageName:', currentCanvasPlan.bgImageName)
|
||||||
await del({ url: `${Config().baseUrl}/api/image/map?fileName=${currentCanvasPlan.bgImageName}` })
|
await del({ url: `${Config().baseUrl}/api/image/map?fileName=${currentCanvasPlan.bgImageName}` })
|
||||||
setMapPositionAddress('')
|
setMapPositionAddress('')
|
||||||
setCurrentBgImage(null)
|
setCurrentBgImage(null)
|
||||||
@ -134,7 +152,7 @@ export function useRefFiles() {
|
|||||||
* 주소로 구글 맵 이미지 다운로드하여 캔버스 배경으로 로드
|
* 주소로 구글 맵 이미지 다운로드하여 캔버스 배경으로 로드
|
||||||
*/
|
*/
|
||||||
const handleMapImageDown = async () => {
|
const handleMapImageDown = async () => {
|
||||||
console.log('🚀 ~ handleMapImageDown ~ handleMapImageDown:')
|
debugger; logger.log('🚀 ~ handleMapImageDown ~ handleMapImageDown:')
|
||||||
if (queryRef.current.value === '' || queryRef.current.value === null) {
|
if (queryRef.current.value === '' || queryRef.current.value === null) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -152,7 +170,7 @@ export function useRefFiles() {
|
|||||||
const res = await get({
|
const res = await get({
|
||||||
url: `${Config().baseUrl}/api/image/map?q=${queryRef.current.value}&fileNm=${currentCanvasPlan.id}&zoom=20`,
|
url: `${Config().baseUrl}/api/image/map?q=${queryRef.current.value}&fileNm=${currentCanvasPlan.id}&zoom=20`,
|
||||||
})
|
})
|
||||||
console.log('🚀 ~ handleMapImageDown ~ res:', res)
|
logger.log('🚀 ~ handleMapImageDown ~ res:', res)
|
||||||
setCurrentBgImage(`${process.env.NEXT_PUBLIC_AWS_S3_BASE_URL}/${res.fileName}`)
|
setCurrentBgImage(`${process.env.NEXT_PUBLIC_AWS_S3_BASE_URL}/${res.fileName}`)
|
||||||
|
|
||||||
await setBackGroundImage({
|
await setBackGroundImage({
|
||||||
@ -170,9 +188,11 @@ export function useRefFiles() {
|
|||||||
// if (!currentBgImage) {
|
// if (!currentBgImage) {
|
||||||
// return
|
// return
|
||||||
// }
|
// }
|
||||||
console.log('🚀 ~ useEffect ~ currentBgImage:', currentBgImage)
|
logger.log('🚀 ~ useEffect ~ currentBgImage:', currentBgImage)
|
||||||
if (currentBgImage) {
|
if (currentBgImage) {
|
||||||
handleBackImageLoadToCanvas(currentBgImage)
|
handleBackImageLoadToCanvas(currentBgImage, () => {
|
||||||
|
logger.log('Background image loaded successfully')
|
||||||
|
})
|
||||||
setCurrentCanvasPlan((prev) => ({
|
setCurrentCanvasPlan((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
// bgImageName: refImage?.name ?? null,
|
// bgImageName: refImage?.name ?? null,
|
||||||
@ -194,16 +214,16 @@ export function useRefFiles() {
|
|||||||
* @param {*} file
|
* @param {*} file
|
||||||
*/
|
*/
|
||||||
const handleUploadImageRefFile = async (file) => {
|
const handleUploadImageRefFile = async (file) => {
|
||||||
setPopSpinnerStore(true)
|
// setPopSpinnerStore(true)
|
||||||
const newOption1 = settingModalFirstOptions.option1.map((option) => ({
|
// const newOption1 = settingModalFirstOptions.option1.map((option) => ({
|
||||||
...option,
|
// ...option,
|
||||||
selected: option.column === 'imageDisplay' ? true : option.selected,
|
// selected: option.column === 'imageDisplay' ? true : option.selected,
|
||||||
}))
|
// }))
|
||||||
|
//
|
||||||
setSettingModalFirstOptions((prev) => ({
|
// setSettingModalFirstOptions((prev) => ({
|
||||||
...prev,
|
// ...prev,
|
||||||
option1: newOption1,
|
// option1: newOption1,
|
||||||
}))
|
// }))
|
||||||
|
|
||||||
const formData = new FormData()
|
const formData = new FormData()
|
||||||
formData.append('file', file)
|
formData.append('file', file)
|
||||||
@ -212,7 +232,7 @@ export function useRefFiles() {
|
|||||||
url: `${Config().baseUrl}/api/image/upload`,
|
url: `${Config().baseUrl}/api/image/upload`,
|
||||||
data: formData,
|
data: formData,
|
||||||
})
|
})
|
||||||
console.log('🚀 ~ handleUploadImageRefFile ~ res:', res)
|
logger.log('🚀 ~ handleUploadImageRefFile ~ res:', res)
|
||||||
setCurrentBgImage(`${process.env.NEXT_PUBLIC_AWS_S3_BASE_URL}/${res.fileName}`)
|
setCurrentBgImage(`${process.env.NEXT_PUBLIC_AWS_S3_BASE_URL}/${res.fileName}`)
|
||||||
setRefImage(file)
|
setRefImage(file)
|
||||||
|
|
||||||
@ -222,7 +242,7 @@ export function useRefFiles() {
|
|||||||
// imagePath: `${process.env.NEXT_PUBLIC_HOST_URL}${res.filePath}`,
|
// imagePath: `${process.env.NEXT_PUBLIC_HOST_URL}${res.filePath}`,
|
||||||
imagePath: `${res.filePath}`,
|
imagePath: `${res.filePath}`,
|
||||||
}
|
}
|
||||||
console.log('🚀 ~ handleUploadImageRefFile ~ params:', params)
|
logger.log('🚀 ~ handleUploadImageRefFile ~ params:', params)
|
||||||
await setBackGroundImage(params)
|
await setBackGroundImage(params)
|
||||||
setPopSpinnerStore(false)
|
setPopSpinnerStore(false)
|
||||||
}
|
}
|
||||||
@ -230,14 +250,15 @@ export function useRefFiles() {
|
|||||||
/**
|
/**
|
||||||
* RefFile이 캐드 도면 파일일 경우 변환하여 이미지로 저장
|
* RefFile이 캐드 도면 파일일 경우 변환하여 이미지로 저장
|
||||||
* @param {*} file
|
* @param {*} file
|
||||||
|
* @param converterUrl
|
||||||
*/
|
*/
|
||||||
const handleUploadConvertRefFile = async (file) => {
|
const handleUploadConvertRefFile = async (file, converterUrl) => {
|
||||||
const formData = new FormData()
|
const formData = new FormData()
|
||||||
formData.append('file', file)
|
formData.append('file', file)
|
||||||
|
|
||||||
/** 캐드 도면 파일 변환 */
|
/** 캐드 도면 파일 변환 */
|
||||||
const res = await post({ url: converterUrl, data: formData })
|
const res = await post({ url: converterUrl, data: formData })
|
||||||
console.log('🚀 ~ handleUploadConvertRefFile ~ res:', res)
|
logger.log('🚀 ~ handleUploadConvertRefFile ~ res:', res)
|
||||||
|
|
||||||
// Convert Base64 to Blob
|
// Convert Base64 to Blob
|
||||||
const base64Data = res.Files[0].FileData
|
const base64Data = res.Files[0].FileData
|
||||||
@ -269,7 +290,7 @@ export function useRefFiles() {
|
|||||||
url: `${Config().baseUrl}/api/image/cad`,
|
url: `${Config().baseUrl}/api/image/cad`,
|
||||||
data: newFormData,
|
data: newFormData,
|
||||||
})
|
})
|
||||||
console.log('🚀 ~ handleUploadConvertRefFile ~ result:', result)
|
logger.log('🚀 ~ handleUploadConvertRefFile ~ result:', result)
|
||||||
|
|
||||||
setCurrentBgImage(`${process.env.NEXT_PUBLIC_AWS_S3_BASE_URL}/${result.fileName}`)
|
setCurrentBgImage(`${process.env.NEXT_PUBLIC_AWS_S3_BASE_URL}/${result.fileName}`)
|
||||||
// setCurrentBgImage(result.filePath)
|
// setCurrentBgImage(result.filePath)
|
||||||
@ -281,8 +302,9 @@ export function useRefFiles() {
|
|||||||
// imagePath: `${process.env.NEXT_PUBLIC_HOST_URL}${res.filePath}`,
|
// imagePath: `${process.env.NEXT_PUBLIC_HOST_URL}${res.filePath}`,
|
||||||
imagePath: `${result.filePath}`,
|
imagePath: `${result.filePath}`,
|
||||||
}
|
}
|
||||||
console.log('🚀 ~ handleUploadImageRefFile ~ params:', params)
|
logger.log('🚀 ~ handleUploadImageRefFile ~ params:', params)
|
||||||
await setBackGroundImage(params)
|
await setBackGroundImage(params)
|
||||||
|
setPopSpinnerStore(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -6,6 +6,8 @@ import { POLYGON_TYPE } from '@/common/common'
|
|||||||
import { useEvent } from '@/hooks/useEvent'
|
import { useEvent } from '@/hooks/useEvent'
|
||||||
import { useLine } from '@/hooks/useLine'
|
import { useLine } from '@/hooks/useLine'
|
||||||
import { outerLinePointsState } from '@/store/outerLineAtom'
|
import { outerLinePointsState } from '@/store/outerLineAtom'
|
||||||
|
import { usePolygon } from '@/hooks/usePolygon'
|
||||||
|
import { useText } from '@/hooks/useText'
|
||||||
|
|
||||||
const ROOF_COLOR = {
|
const ROOF_COLOR = {
|
||||||
0: 'rgb(199,240,213)',
|
0: 'rgb(199,240,213)',
|
||||||
@ -13,6 +15,7 @@ const ROOF_COLOR = {
|
|||||||
2: 'rgb(187,204,255)',
|
2: 'rgb(187,204,255)',
|
||||||
3: 'rgb(228,202,255)',
|
3: 'rgb(228,202,255)',
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useRoofFn() {
|
export function useRoofFn() {
|
||||||
const canvas = useRecoilValue(canvasState)
|
const canvas = useRecoilValue(canvasState)
|
||||||
const selectedRoofMaterial = useRecoilValue(selectedRoofMaterialSelector)
|
const selectedRoofMaterial = useRecoilValue(selectedRoofMaterialSelector)
|
||||||
@ -20,6 +23,8 @@ export function useRoofFn() {
|
|||||||
const { addCanvasMouseEventListener, initEvent } = useEvent()
|
const { addCanvasMouseEventListener, initEvent } = useEvent()
|
||||||
const resetPoints = useResetRecoilState(outerLinePointsState)
|
const resetPoints = useResetRecoilState(outerLinePointsState)
|
||||||
const { addPitchText } = useLine()
|
const { addPitchText } = useLine()
|
||||||
|
const { setPolygonLinesActualSize } = usePolygon()
|
||||||
|
const { changeCorridorDimensionText } = useText()
|
||||||
|
|
||||||
//면형상 선택 클릭시 지붕 패턴 입히기
|
//면형상 선택 클릭시 지붕 패턴 입히기
|
||||||
function setSurfaceShapePattern(polygon, mode = 'onlyBorder', trestleMode = false, roofMaterial, isForceChange = false, isDisplay = false) {
|
function setSurfaceShapePattern(polygon, mode = 'onlyBorder', trestleMode = false, roofMaterial, isForceChange = false, isDisplay = false) {
|
||||||
@ -27,6 +32,9 @@ export function useRoofFn() {
|
|||||||
if (!polygon) {
|
if (!polygon) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if (polygon.wall) {
|
||||||
|
return
|
||||||
|
}
|
||||||
if (polygon.points.length < 3) {
|
if (polygon.points.length < 3) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -44,6 +52,7 @@ export function useRoofFn() {
|
|||||||
|
|
||||||
let width = (roofMaterial.width || 226) / 10
|
let width = (roofMaterial.width || 226) / 10
|
||||||
let height = (roofMaterial.length || 158) / 10
|
let height = (roofMaterial.length || 158) / 10
|
||||||
|
|
||||||
const index = roofMaterial.index ?? 0
|
const index = roofMaterial.index ?? 0
|
||||||
let roofStyle = 2
|
let roofStyle = 2
|
||||||
const inputPatternSize = { width: width, height: height } //임시 사이즈
|
const inputPatternSize = { width: width, height: height } //임시 사이즈
|
||||||
@ -169,6 +178,8 @@ export function useRoofFn() {
|
|||||||
polygon.set('fill', null)
|
polygon.set('fill', null)
|
||||||
polygon.set('fill', pattern)
|
polygon.set('fill', pattern)
|
||||||
polygon.roofMaterial = roofMaterial
|
polygon.roofMaterial = roofMaterial
|
||||||
|
setPolygonLinesActualSize(polygon)
|
||||||
|
changeCorridorDimensionText()
|
||||||
polygon.canvas?.renderAll()
|
polygon.canvas?.renderAll()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e)
|
console.log(e)
|
||||||
@ -202,7 +213,7 @@ export function useRoofFn() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const roof = roofBase[0]
|
const roof = roofBase[0]
|
||||||
const wall = roof.wall
|
const wall = canvas.getObjects().find((obj) => obj.name === POLYGON_TYPE.WALL && obj.attributes?.roofId === roof.id)
|
||||||
|
|
||||||
const checkPolygon = new fabric.Polygon(roof.points, {
|
const checkPolygon = new fabric.Polygon(roof.points, {
|
||||||
name: 'moveRoofPolygon',
|
name: 'moveRoofPolygon',
|
||||||
@ -230,16 +241,11 @@ export function useRoofFn() {
|
|||||||
const texts = canvas.getObjects().filter((obj) => obj.type === 'text' && (obj.attributes?.roofId === roof.id || obj.parentId === roof.id))
|
const texts = canvas.getObjects().filter((obj) => obj.type === 'text' && (obj.attributes?.roofId === roof.id || obj.parentId === roof.id))
|
||||||
texts.forEach((text) => canvas.remove(text))
|
texts.forEach((text) => canvas.remove(text))
|
||||||
|
|
||||||
const outerLines = canvas.getObjects().filter((obj) => obj.type === 'QLine' && (obj.attributes?.roofId === roof.id || obj.parentId === roof.id))
|
|
||||||
|
|
||||||
const allRoofObject = canvas
|
const allRoofObject = canvas
|
||||||
.getObjects()
|
.getObjects()
|
||||||
.filter(
|
.filter((obj) => obj !== roof && (obj.attributes?.roofId === roof.id || obj.parentId === roof.id || obj.parentId === wall.id))
|
||||||
(obj) => obj !== roof && /*obj !== wall &&*/ (obj.attributes?.roofId === roof.id || obj.parentId === roof.id || obj.parentId === wall.id),
|
|
||||||
)
|
|
||||||
|
|
||||||
// // Calculate the movement delta
|
|
||||||
|
|
||||||
|
/** 지붕이 움직인 만큼의 delta를 구한다. */
|
||||||
const originalRoofLeft = roof.left
|
const originalRoofLeft = roof.left
|
||||||
const originalRoofTop = roof.top
|
const originalRoofTop = roof.top
|
||||||
|
|
||||||
@ -308,7 +314,15 @@ export function useRoofFn() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function convertAbsolutePoint(area) {
|
function convertAbsolutePoint(area) {
|
||||||
return area.points.map((p) => fabric.util.transformPoint({ x: p.x - area.pathOffset.x, y: p.y - area.pathOffset.y }, area.calcTransformMatrix()))
|
return area.points.map((p) =>
|
||||||
|
fabric.util.transformPoint(
|
||||||
|
{
|
||||||
|
x: p.x - area.pathOffset.x,
|
||||||
|
y: p.y - area.pathOffset.y,
|
||||||
|
},
|
||||||
|
area.calcTransformMatrix(),
|
||||||
|
),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const removeOuterLines = (currentMousePos) => {
|
const removeOuterLines = (currentMousePos) => {
|
||||||
|
|||||||
37
src/hooks/common/useTurf.js
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import * as turf from '@turf/turf'
|
||||||
|
|
||||||
|
export const useTurf = () => {
|
||||||
|
/**
|
||||||
|
* 모듈이 배치면 안에 있는지 확인
|
||||||
|
* @param {*} module
|
||||||
|
* @param {*} surface
|
||||||
|
* @param spare
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const checkModuleDisjointSurface = (module, surface, spare = 1) => {
|
||||||
|
// 표면 영역을 spare만큼 수동 확장
|
||||||
|
const expandedSurface = {
|
||||||
|
type: 'Polygon',
|
||||||
|
coordinates: [
|
||||||
|
surface.geometry.coordinates[0].map(([x, y]) => {
|
||||||
|
// 각 점을 바깥쪽으로 2 단위씩 이동
|
||||||
|
const coords = surface.geometry.coordinates[0]
|
||||||
|
const centerX = coords.slice(0, -1).reduce((sum, [x, y]) => sum + x, 0) / (coords.length - 1)
|
||||||
|
const centerY = coords.slice(0, -1).reduce((sum, [x, y]) => sum + y, 0) / (coords.length - 1)
|
||||||
|
|
||||||
|
return [x < centerX ? x - spare : x + spare, y < centerY ? y - spare : y + spare]
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
const isWithin = turf.booleanContains(expandedSurface, module) || turf.booleanWithin(module, expandedSurface)
|
||||||
|
|
||||||
|
const isContact = turf.booleanIntersects(module, expandedSurface) && !turf.booleanOverlap(module, expandedSurface)
|
||||||
|
|
||||||
|
return isWithin || isContact
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
checkModuleDisjointSurface,
|
||||||
|
}
|
||||||
|
}
|
||||||