Compare commits
No commits in common. "feature/design-remake" and "main" have entirely different histories.
feature/de
...
main
@ -1,36 +1,11 @@
|
|||||||
NEXT_PUBLIC_RUN_MODE="development"
|
NEXT_PUBLIC_API_SERVER_PATH="http://1.248.227.176:38080"
|
||||||
|
|
||||||
NEXT_PUBLIC_API_SERVER_PATH="https://dev-api.hanasys.jp"
|
NEXT_PUBLIC_HOST_URL="http://1.248.227.176:4000"
|
||||||
|
|
||||||
NEXT_PUBLIC_HOST_URL="//1.248.227.176:4000"
|
|
||||||
|
|
||||||
NEXT_PUBLIC_API_HOST_URL="https://dev.hanasys.jp"
|
|
||||||
|
|
||||||
SESSION_SECRET="i3iHH1yp2/2SpQSIySQ4bpyc4g0D+zCF9FAn5xUG0+Y="
|
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_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"
|
||||||
|
|
||||||
# 테스트용
|
|
||||||
# AWS_REGION="ap-northeast-2"
|
|
||||||
# AMPLIFY_BUCKET="interplug"
|
|
||||||
# AWS_ACCESS_KEY_ID="AKIAVWMWJCUXFHEAZ4FR"
|
|
||||||
# AWS_SECRET_ACCESS_KEY="NDzSvPUo4/ErpPOEs1eZAnoUBilc1FL7YaoHkqe4"
|
|
||||||
# NEXT_PUBLIC_AWS_S3_BASE_URL="https://interplug.s3.ap-northeast-2.amazonaws.com"
|
|
||||||
|
|
||||||
# 실제 일본 서버
|
|
||||||
AWS_REGION="ap-northeast-1"
|
|
||||||
AMPLIFY_BUCKET="files.hanasys.jp"
|
|
||||||
AWS_ACCESS_KEY_ID="AKIA3K4QWLZHFZRJOM2E"
|
|
||||||
AWS_SECRET_ACCESS_KEY="Cw87TjKwnTWRKgORGxYiFU6GUTgu25eUw4eLBNcA"
|
|
||||||
NEXT_PUBLIC_AWS_S3_BASE_URL="//files.hanasys.jp"
|
|
||||||
|
|
||||||
S3_PROFILE="dev"
|
|
||||||
|
|
||||||
#logging
|
|
||||||
NEXT_PUBLIC_ENABLE_LOGGING=true
|
|
||||||
@ -1,32 +0,0 @@
|
|||||||
NEXT_PUBLIC_RUN_MODE="local.dev"
|
|
||||||
|
|
||||||
NEXT_PUBLIC_API_SERVER_PATH="https://dev-api.hanasys.jp"
|
|
||||||
|
|
||||||
NEXT_PUBLIC_HOST_URL="//1.248.227.176:4000"
|
|
||||||
|
|
||||||
NEXT_PUBLIC_API_HOST_URL="http://1.248.227.176:5000"
|
|
||||||
|
|
||||||
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_yAS4QDalL9jgQ7vS"
|
|
||||||
NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/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_MUSUBI_AUTO_LOGIN_URL="http://q-musubi-stg.q-cells.jp:8120/qm/login/autoLogin"
|
|
||||||
|
|
||||||
# 테스트용
|
|
||||||
# AWS_REGION="ap-northeast-2"
|
|
||||||
# AMPLIFY_BUCKET="interplug"
|
|
||||||
# AWS_ACCESS_KEY_ID="AKIAVWMWJCUXFHEAZ4FR"
|
|
||||||
# AWS_SECRET_ACCESS_KEY="NDzSvPUo4/ErpPOEs1eZAnoUBilc1FL7YaoHkqe4"
|
|
||||||
# NEXT_PUBLIC_AWS_S3_BASE_URL="https://interplug.s3.ap-northeast-2.amazonaws.com"
|
|
||||||
|
|
||||||
# 실제 일본 서버
|
|
||||||
AWS_REGION="ap-northeast-1"
|
|
||||||
AMPLIFY_BUCKET="files.hanasys.jp"
|
|
||||||
AWS_ACCESS_KEY_ID="AKIA3K4QWLZHFZRJOM2E"
|
|
||||||
AWS_SECRET_ACCESS_KEY="Cw87TjKwnTWRKgORGxYiFU6GUTgu25eUw4eLBNcA"
|
|
||||||
NEXT_PUBLIC_AWS_S3_BASE_URL="//files.hanasys.jp"
|
|
||||||
|
|
||||||
S3_PROFILE="dev"
|
|
||||||
@ -1,36 +0,0 @@
|
|||||||
NEXT_PUBLIC_RUN_MODE="local"
|
|
||||||
|
|
||||||
NEXT_PUBLIC_API_SERVER_PATH="https://dev-api.hanasys.jp"
|
|
||||||
|
|
||||||
NEXT_PUBLIC_HOST_URL="//1.248.227.176:4000"
|
|
||||||
|
|
||||||
NEXT_PUBLIC_API_HOST_URL="http://localhost:3000"
|
|
||||||
|
|
||||||
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_yAS4QDalL9jgQ7vS"
|
|
||||||
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_MUSUBI_AUTO_LOGIN_URL="http://q-musubi-stg.q-cells.jp:8120/qm/login/autoLogin"
|
|
||||||
|
|
||||||
# 테스트용
|
|
||||||
# AWS_REGION="ap-northeast-2"
|
|
||||||
# AMPLIFY_BUCKET="interplug"
|
|
||||||
# AWS_ACCESS_KEY_ID="AKIAVWMWJCUXFHEAZ4FR"
|
|
||||||
# AWS_SECRET_ACCESS_KEY="NDzSvPUo4/ErpPOEs1eZAnoUBilc1FL7YaoHkqe4"
|
|
||||||
# NEXT_PUBLIC_AWS_S3_BASE_URL="https://interplug.s3.ap-northeast-2.amazonaws.com"
|
|
||||||
|
|
||||||
# 실제 일본 서버
|
|
||||||
AWS_REGION="ap-northeast-1"
|
|
||||||
AMPLIFY_BUCKET="files.hanasys.jp"
|
|
||||||
AWS_ACCESS_KEY_ID="AKIA3K4QWLZHFZRJOM2E"
|
|
||||||
AWS_SECRET_ACCESS_KEY="Cw87TjKwnTWRKgORGxYiFU6GUTgu25eUw4eLBNcA"
|
|
||||||
NEXT_PUBLIC_AWS_S3_BASE_URL="//files.hanasys.jp"
|
|
||||||
|
|
||||||
S3_PROFILE="dev"
|
|
||||||
|
|
||||||
#logging
|
|
||||||
NEXT_PUBLIC_ENABLE_LOGGING=false
|
|
||||||
@ -1,35 +1,13 @@
|
|||||||
NEXT_PUBLIC_RUN_MODE="production"
|
|
||||||
|
|
||||||
NEXT_PUBLIC_API_SERVER_PATH="https://api.hanasys.jp/"
|
NEXT_PUBLIC_API_SERVER_PATH="https://api.hanasys.jp/"
|
||||||
|
|
||||||
NEXT_PUBLIC_HOST_URL="//1.248.227.176:4000"
|
NEXT_PUBLIC_HOST_URL="http://1.248.227.176:4000"
|
||||||
|
|
||||||
NEXT_PUBLIC_API_HOST_URL="https://www.hanasys.jp"
|
|
||||||
|
|
||||||
SESSION_SECRET="i3iHH1yp2/2SpQSIySQ4bpyc4g0D+zCF9FAn5xUG0+Y="
|
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_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"
|
||||||
# AWS_REGION="ap-northeast-2"
|
NEXT_PUBLIC_Q_MUSUBI_AUTO_LOGIN_URL="http://q-musubi-stg.q-cells.jp:8120/qm/login/autoLogin"
|
||||||
# AMPLIFY_BUCKET="interplug"
|
|
||||||
# AWS_ACCESS_KEY_ID="AKIAVWMWJCUXFHEAZ4FR"
|
|
||||||
# AWS_SECRET_ACCESS_KEY="NDzSvPUo4/ErpPOEs1eZAnoUBilc1FL7YaoHkqe4"
|
|
||||||
# NEXT_PUBLIC_AWS_S3_BASE_URL="//files.hanasys.jp"
|
|
||||||
|
|
||||||
# 실제 일본 서버
|
|
||||||
AWS_REGION="ap-northeast-1"
|
|
||||||
AMPLIFY_BUCKET="files.hanasys.jp"
|
|
||||||
AWS_ACCESS_KEY_ID="AKIA3K4QWLZHFZRJOM2E"
|
|
||||||
AWS_SECRET_ACCESS_KEY="Cw87TjKwnTWRKgORGxYiFU6GUTgu25eUw4eLBNcA"
|
|
||||||
NEXT_PUBLIC_AWS_S3_BASE_URL="//files.hanasys.jp"
|
|
||||||
|
|
||||||
S3_PROFILE="prd"
|
|
||||||
|
|
||||||
#logging
|
|
||||||
NEXT_PUBLIC_ENABLE_LOGGING=false
|
|
||||||
1
.gitignore
vendored
@ -43,4 +43,3 @@ yarn.lock
|
|||||||
package-lock.json
|
package-lock.json
|
||||||
pnpm-lock.yaml
|
pnpm-lock.yaml
|
||||||
certificates
|
certificates
|
||||||
.ai
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
[일감번호] : [제목]
|
|
||||||
|
|
||||||
[작업내용] :
|
|
||||||
@ -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:
|
||||||
|
|
||||||
@ -34,5 +34,3 @@ You can check out [the Next.js GitHub repository](https://github.com/vercel/next
|
|||||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
||||||
|
|
||||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
|
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
|
||||||
|
|
||||||
deploy test
|
|
||||||
|
|||||||
@ -1,13 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
apps: [
|
|
||||||
{
|
|
||||||
name: 'qcast-front-development',
|
|
||||||
script: 'node_modules/next/dist/bin/next',
|
|
||||||
instances: 1,
|
|
||||||
exec_mode: 'fork',
|
|
||||||
env: {
|
|
||||||
PORT: 5010,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
apps: [
|
|
||||||
{
|
|
||||||
name: 'qcast-front-local-development',
|
|
||||||
script: 'node_modules/next/dist/bin/next',
|
|
||||||
instances: 1,
|
|
||||||
exec_mode: 'fork',
|
|
||||||
env: {
|
|
||||||
PORT: 5000,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
apps: [
|
|
||||||
{
|
|
||||||
name: 'qcast-front-production',
|
|
||||||
script: 'node_modules/next/dist/bin/next',
|
|
||||||
instances: 2,
|
|
||||||
exec_mode: 'cluster',
|
|
||||||
env: {
|
|
||||||
NODE_ENV: 'production',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
25
package.json
@ -3,34 +3,27 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "env-cmd -f .env.localhost next dev",
|
"dev": "next dev",
|
||||||
"local:dev": "env-cmd -f .env.local.dev next dev",
|
"build": "next build",
|
||||||
"build": "env-cmd -f .env.production next build",
|
"start": "next start -p 5000",
|
||||||
"build:dev": "env-cmd -f .env.development next build",
|
"start:dev": "next start -p 5010",
|
||||||
"build:local.dev": "env-cmd -f .env.local.dev next build",
|
|
||||||
"start:cluster1": "env-cmd -f .env.production next start -p 5000",
|
|
||||||
"start:cluster2": "env-cmd -f .env.production next start -p 5001",
|
|
||||||
"start:dev": "env-cmd -f .env.development next start -p 5010",
|
|
||||||
"lint": "next lint",
|
"lint": "next lint",
|
||||||
"serve": "node server.js"
|
"serve": "node server.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-s3": "^3.772.0",
|
|
||||||
"ag-grid-react": "^32.0.2",
|
"ag-grid-react": "^32.0.2",
|
||||||
"axios": "^1.7.8",
|
"axios": "^1.7.8",
|
||||||
"big.js": "^6.2.2",
|
"big.js": "^6.2.2",
|
||||||
"chart.js": "^4.4.6",
|
"chart.js": "^4.4.6",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
"env-cmd": "^10.1.0",
|
"fabric": "^5.3.0",
|
||||||
"fabric": "^5.5.2",
|
|
||||||
"framer-motion": "^11.2.13",
|
"framer-motion": "^11.2.13",
|
||||||
"fs": "^0.0.1-security",
|
"fs": "^0.0.1-security",
|
||||||
"iron-session": "^8.0.2",
|
"iron-session": "^8.0.2",
|
||||||
"jimp": "^1.6.0",
|
|
||||||
"js-cookie": "^3.0.5",
|
"js-cookie": "^3.0.5",
|
||||||
"mathjs": "^13.0.2",
|
"mathjs": "^13.0.2",
|
||||||
"mssql": "^11.0.1",
|
"mssql": "^11.0.1",
|
||||||
"next": "14.2.28",
|
"next": "14.2.21",
|
||||||
"next-international": "^1.2.4",
|
"next-international": "^1.2.4",
|
||||||
"react": "^18",
|
"react": "^18",
|
||||||
"react-chartjs-2": "^5.2.0",
|
"react-chartjs-2": "^5.2.0",
|
||||||
@ -44,7 +37,6 @@
|
|||||||
"react-responsive-modal": "^6.4.2",
|
"react-responsive-modal": "^6.4.2",
|
||||||
"react-select": "^5.8.1",
|
"react-select": "^5.8.1",
|
||||||
"recoil": "^0.7.7",
|
"recoil": "^0.7.7",
|
||||||
"sharp": "^0.33.5",
|
|
||||||
"sqlite": "^5.1.1",
|
"sqlite": "^5.1.1",
|
||||||
"sqlite3": "^5.1.7",
|
"sqlite3": "^5.1.7",
|
||||||
"sweetalert2": "^11.14.1",
|
"sweetalert2": "^11.14.1",
|
||||||
@ -55,14 +47,11 @@
|
|||||||
},
|
},
|
||||||
"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"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
apps: [
|
|
||||||
{
|
|
||||||
name: 'qcast-front-production-1',
|
|
||||||
script: 'node_modules/next/dist/bin/next',
|
|
||||||
instances: 1,
|
|
||||||
exec_mode: 'fork',
|
|
||||||
env: {
|
|
||||||
PORT: 5000,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
apps: [
|
|
||||||
{
|
|
||||||
name: 'qcast-front-production-2',
|
|
||||||
script: 'node_modules/next/dist/bin/next',
|
|
||||||
instances: 1,
|
|
||||||
exec_mode: 'fork',
|
|
||||||
env: {
|
|
||||||
PORT: 5001,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 5.4 KiB |
|
Before Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 4.0 KiB |
@ -1,3 +0,0 @@
|
|||||||
<svg width="16" height="14" viewBox="0 0 16 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M8 0L15.7942 13.5H0.205771L8 0Z" fill="white"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 160 B |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 13 KiB |
@ -1,4 +0,0 @@
|
|||||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<circle cx="12" cy="12" r="8.25" stroke="#101010" stroke-width="1.5"/>
|
|
||||||
<path d="M7.94995 16.5C10.0485 14.302 13.9289 14.1986 16.05 16.5M14.2455 9.75C14.2455 10.9926 13.2367 12 11.9923 12C10.7479 12 9.73912 10.9926 9.73912 9.75C9.73912 8.50736 10.7479 7.5 11.9923 7.5C13.2367 7.5 14.2455 8.50736 14.2455 9.75Z" stroke="#101010" stroke-width="1.5" stroke-linecap="round"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 474 B |
@ -1,13 +1,21 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { createContext, useState } from 'react'
|
import { createContext, useEffect, useState } from 'react'
|
||||||
|
import { useLocalStorage } from 'usehooks-ts'
|
||||||
|
|
||||||
export const GlobalDataContext = createContext(null)
|
export const GlobalDataContext = createContext(null)
|
||||||
|
|
||||||
const GlobalDataProvider = ({ children }) => {
|
const GlobalDataProvider = ({ children }) => {
|
||||||
const [managementState, setManagementState] = useState(null)
|
const [managementState, setManagementState] = useState(null)
|
||||||
|
const [managementStateLoaded, setManagementStateLoaded] = useLocalStorage('managementStateLoaded', null)
|
||||||
|
|
||||||
return <GlobalDataContext.Provider value={{ managementState, setManagementState }}>{children}</GlobalDataContext.Provider>
|
useEffect(() => {
|
||||||
|
if (managementState !== null) {
|
||||||
|
setManagementStateLoaded(managementState)
|
||||||
|
}
|
||||||
|
}, [managementState])
|
||||||
|
|
||||||
|
return <GlobalDataContext.Provider value={{ managementState, setManagementState, managementStateLoaded }}>{children}</GlobalDataContext.Provider>
|
||||||
}
|
}
|
||||||
|
|
||||||
export default GlobalDataProvider
|
export default GlobalDataProvider
|
||||||
|
|||||||
@ -1,70 +0,0 @@
|
|||||||
import { NextResponse } from 'next/server'
|
|
||||||
import { S3Client, PutObjectCommand, DeleteObjectCommand } from '@aws-sdk/client-s3'
|
|
||||||
|
|
||||||
const Bucket = process.env.AMPLIFY_BUCKET
|
|
||||||
const s3 = new S3Client({
|
|
||||||
region: process.env.AWS_REGION,
|
|
||||||
credentials: {
|
|
||||||
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
|
|
||||||
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const uploadImage = async (file) => {
|
|
||||||
console.log('🚀 ~ uploadImage ~ file:', file)
|
|
||||||
const Body = Buffer.from(await file.arrayBuffer())
|
|
||||||
const Key = `cads/${file.name}`
|
|
||||||
const ContentType = 'image/png'
|
|
||||||
|
|
||||||
await s3.send(
|
|
||||||
new PutObjectCommand({
|
|
||||||
Bucket,
|
|
||||||
Key,
|
|
||||||
Body,
|
|
||||||
ContentType,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
|
||||||
filePath: `https://${process.env.AMPLIFY_BUCKET}.s3.${process.env.AWS_REGION}.amazonaws.com/${Key}`,
|
|
||||||
fileName: Key,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function POST(req) {
|
|
||||||
try {
|
|
||||||
const formData = await req.formData()
|
|
||||||
const file = formData.get('file')
|
|
||||||
|
|
||||||
const result = await uploadImage(file)
|
|
||||||
|
|
||||||
return NextResponse.json(result)
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error)
|
|
||||||
return NextResponse.json({ error: 'Failed to upload image' }, { status: 500 })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function DELETE(req) {
|
|
||||||
try {
|
|
||||||
const searchParams = req.nextUrl.searchParams
|
|
||||||
const Key = `cads/${searchParams.get('fileName')}`
|
|
||||||
console.log('🚀 ~ DELETE ~ Key:', Key)
|
|
||||||
|
|
||||||
if (!Key) {
|
|
||||||
return NextResponse.json({ error: 'fileName parameter is required' }, { status: 400 })
|
|
||||||
}
|
|
||||||
|
|
||||||
await s3.send(
|
|
||||||
new DeleteObjectCommand({
|
|
||||||
Bucket,
|
|
||||||
Key,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
return NextResponse.json({ message: '이미지 삭제 성공' }, { status: 200 })
|
|
||||||
} catch (error) {
|
|
||||||
console.error('S3 Delete Error:', error)
|
|
||||||
return NextResponse.json({ error: 'Failed to delete image' }, { status: 500 })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,203 +0,0 @@
|
|||||||
import { NextResponse } from 'next/server'
|
|
||||||
import { DeleteObjectCommand, GetObjectCommand, PutObjectCommand, S3Client } from '@aws-sdk/client-s3'
|
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
|
||||||
import { Jimp } from 'jimp'
|
|
||||||
|
|
||||||
const Bucket = process.env.AMPLIFY_BUCKET
|
|
||||||
const s3 = new S3Client({
|
|
||||||
region: process.env.AWS_REGION,
|
|
||||||
credentials: {
|
|
||||||
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
|
|
||||||
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const checkArea = (obj) => {
|
|
||||||
const { width, height, left, top } = obj
|
|
||||||
|
|
||||||
if (left < 0 || top < 0 || width > 1600 || height > 1000) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
const cropImage = async (Key, width, height, left, top) => {
|
|
||||||
try {
|
|
||||||
// Get the image from S3
|
|
||||||
const { Body } = await s3.send(
|
|
||||||
new GetObjectCommand({
|
|
||||||
Bucket,
|
|
||||||
Key,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
const chunks = []
|
|
||||||
for await (const chunk of Body) {
|
|
||||||
chunks.push(chunk)
|
|
||||||
}
|
|
||||||
const buffer = Buffer.concat(chunks)
|
|
||||||
|
|
||||||
let image = await Jimp.read(buffer)
|
|
||||||
image.autocrop({ tolerance: 0.0002, leaveBorder: 10 })
|
|
||||||
|
|
||||||
const resizedImage = await resizeImage(image).then((result) => {
|
|
||||||
return result
|
|
||||||
})
|
|
||||||
|
|
||||||
return await resizedImage.getBuffer('image/png')
|
|
||||||
|
|
||||||
// Convert stream to buffer
|
|
||||||
// const chunks = []
|
|
||||||
// for await (const chunk of Body) {
|
|
||||||
// chunks.push(chunk)
|
|
||||||
// }
|
|
||||||
// const imageBuffer = Buffer.concat(chunks)
|
|
||||||
|
|
||||||
// const image = await Jimp.read(Body)
|
|
||||||
|
|
||||||
// if (!checkResult) {
|
|
||||||
// processedImage = await image.toBuffer()
|
|
||||||
// }
|
|
||||||
|
|
||||||
//let processedImage
|
|
||||||
// if (!checkResult) {
|
|
||||||
// processedImage = await sharp(imageBuffer).toBuffer()
|
|
||||||
// } else {
|
|
||||||
// processedImage = await sharp(imageBuffer)
|
|
||||||
// .extract({
|
|
||||||
// width: parseInt(width),
|
|
||||||
// height: parseInt(height),
|
|
||||||
// left: parseInt(left),
|
|
||||||
// top: parseInt(top),
|
|
||||||
// })
|
|
||||||
// .png()
|
|
||||||
// .toBuffer()
|
|
||||||
// }
|
|
||||||
// return processedImage
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error processing image:', error)
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//크롭된 이미지를 배경 크기에 맞게 리사이즈
|
|
||||||
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) {
|
|
||||||
try {
|
|
||||||
const formData = await req.formData()
|
|
||||||
const file = formData.get('file')
|
|
||||||
const objectNo = formData.get('objectNo')
|
|
||||||
const planNo = formData.get('planNo')
|
|
||||||
const type = formData.get('type')
|
|
||||||
const width = formData.get('width')
|
|
||||||
const height = formData.get('height')
|
|
||||||
const left = formData.get('left')
|
|
||||||
const top = formData.get('top')
|
|
||||||
|
|
||||||
const OriginalKey = `Drawing/${uuidv4()}`
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 원본 이미지를 우선 저장한다.
|
|
||||||
* 이미지 이름이 겹지는 현상을 방지하기 위해 uuid 를 사용한다.
|
|
||||||
*/
|
|
||||||
await s3.send(
|
|
||||||
new PutObjectCommand({
|
|
||||||
Bucket,
|
|
||||||
Key: OriginalKey,
|
|
||||||
Body: Buffer.from(await file.arrayBuffer()),
|
|
||||||
ContentType: 'image/png',
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 저장된 원본 이미지를 기준으로 크롭여부를 결정하여 크롭 이미지를 저장한다.
|
|
||||||
*/
|
|
||||||
const bufferImage = await cropImage(OriginalKey, width, height, left, top)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 크롭 이미지 이름을 결정한다.
|
|
||||||
*/
|
|
||||||
const Key = `Drawing/${process.env.S3_PROFILE}/${objectNo}_${planNo}_${type}.png`
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 크롭이 완료된 이미지를 업로드한다.
|
|
||||||
*/
|
|
||||||
await s3.send(
|
|
||||||
new PutObjectCommand({
|
|
||||||
Bucket,
|
|
||||||
Key,
|
|
||||||
Body: bufferImage,
|
|
||||||
ContentType: 'image/png',
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 크롭이미지 저장이 완료되면 원본 이미지를 삭제한다.
|
|
||||||
*/
|
|
||||||
await s3.send(
|
|
||||||
new DeleteObjectCommand({
|
|
||||||
Bucket,
|
|
||||||
Key: OriginalKey,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
const result = {
|
|
||||||
filePath: `https://${process.env.AMPLIFY_BUCKET}.s3.${process.env.AWS_REGION}.amazonaws.com/${Key}`,
|
|
||||||
fileName: Key,
|
|
||||||
}
|
|
||||||
|
|
||||||
return NextResponse.json(result)
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error in POST:', error)
|
|
||||||
return NextResponse.json({ error: 'Failed to process and upload image' }, { status: 500 })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,59 +0,0 @@
|
|||||||
import { NextResponse } from 'next/server'
|
|
||||||
import { S3Client, CopyObjectCommand, GetObjectCommand } from '@aws-sdk/client-s3'
|
|
||||||
import sharp from 'sharp'
|
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
|
||||||
const Bucket = process.env.AMPLIFY_BUCKET
|
|
||||||
const s3 = new S3Client({
|
|
||||||
region: process.env.AWS_REGION,
|
|
||||||
credentials: {
|
|
||||||
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
|
|
||||||
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
export async function POST(req) {
|
|
||||||
const { objectNo, planNo, newObjectNo, newPlanNo } = await req.json()
|
|
||||||
|
|
||||||
const responseArray = []
|
|
||||||
|
|
||||||
//견적서1 번 이미지
|
|
||||||
const isExistImage1 = await s3.send(
|
|
||||||
new GetObjectCommand({
|
|
||||||
Bucket,
|
|
||||||
Key: `Drawing/${process.env.S3_PROFILE}/${objectNo}_${planNo}_1.png`,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
//견적서2 번 이미지
|
|
||||||
const isExistImage2 = await s3.send(
|
|
||||||
new GetObjectCommand({
|
|
||||||
Bucket,
|
|
||||||
Key: `Drawing/${process.env.S3_PROFILE}/${objectNo}_${planNo}_2.png`,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
//견적서1,2 번 이미지 둘다 있어야함
|
|
||||||
if (isExistImage1.$metadata.httpStatusCode === 200 && isExistImage2.$metadata.httpStatusCode === 200) {
|
|
||||||
//견적서1 번 이미지 복사
|
|
||||||
const copyCommand = new CopyObjectCommand({
|
|
||||||
Bucket,
|
|
||||||
CopySource: encodeURI(`${Bucket}/Drawing/${process.env.S3_PROFILE}/${objectNo}_${planNo}_1.png`),
|
|
||||||
Key: `Drawing/${process.env.S3_PROFILE}/${newObjectNo}_${newPlanNo}_1.png`,
|
|
||||||
})
|
|
||||||
|
|
||||||
const response = await s3.send(copyCommand)
|
|
||||||
|
|
||||||
const copyCommand2 = new CopyObjectCommand({
|
|
||||||
Bucket,
|
|
||||||
CopySource: encodeURI(`${Bucket}/Drawing/${process.env.S3_PROFILE}/${objectNo}_${planNo}_2.png`),
|
|
||||||
Key: `Drawing/${process.env.S3_PROFILE}/${newObjectNo}_${newPlanNo}_2.png`,
|
|
||||||
})
|
|
||||||
|
|
||||||
const response2 = await s3.send(copyCommand2)
|
|
||||||
|
|
||||||
responseArray.push(response, response2)
|
|
||||||
return NextResponse.json({ message: '견적서 이미지 복사 성공', responseArray }, { status: 200 })
|
|
||||||
} else {
|
|
||||||
return NextResponse.json({ message: '견적서 이미지 복사 실패(존재하지 않는 이미지)', responseArray }, { status: 400 })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,78 +0,0 @@
|
|||||||
import { NextResponse } from 'next/server'
|
|
||||||
import { S3Client, PutObjectCommand, DeleteObjectCommand } from '@aws-sdk/client-s3'
|
|
||||||
|
|
||||||
const Bucket = process.env.AMPLIFY_BUCKET
|
|
||||||
const s3 = new S3Client({
|
|
||||||
region: process.env.AWS_REGION,
|
|
||||||
credentials: {
|
|
||||||
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
|
|
||||||
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
export async function GET(req) {
|
|
||||||
try {
|
|
||||||
const searchParams = req.nextUrl.searchParams
|
|
||||||
const q = searchParams.get('q')
|
|
||||||
const fileNm = searchParams.get('fileNm')
|
|
||||||
const zoom = searchParams.get('zoom')
|
|
||||||
|
|
||||||
/** 구글 맵을 이미지로 변경하기 위한 API */
|
|
||||||
const API_KEY = 'AIzaSyDO7nVR1N_D2tKy60hgGFavpLaXkHpiHpc'
|
|
||||||
const targetUrl = `https://maps.googleapis.com/maps/api/staticmap?center=${q}&zoom=${zoom}&maptype=satellite&size=640x640&scale=1&key=${API_KEY}`
|
|
||||||
const decodeUrl = decodeURIComponent(targetUrl)
|
|
||||||
|
|
||||||
/** 구글 맵을 이미지로 변경하기 위한 API 호출 */
|
|
||||||
const response = await fetch(decodeUrl)
|
|
||||||
const data = await response.arrayBuffer()
|
|
||||||
// const buffer = Buffer.from(data)
|
|
||||||
|
|
||||||
/** 변경된 이미지를 S3에 업로드 */
|
|
||||||
const Body = Buffer.from(data)
|
|
||||||
const Key = `maps/${fileNm}`
|
|
||||||
const ContentType = 'image/png'
|
|
||||||
|
|
||||||
await s3.send(
|
|
||||||
new PutObjectCommand({
|
|
||||||
Bucket,
|
|
||||||
Key,
|
|
||||||
Body,
|
|
||||||
ContentType,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
const result = {
|
|
||||||
filePath: `https://${process.env.AMPLIFY_BUCKET}.s3.${process.env.AWS_REGION}.amazonaws.com/${Key}`,
|
|
||||||
fileName: Key,
|
|
||||||
}
|
|
||||||
|
|
||||||
return NextResponse.json(result)
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error)
|
|
||||||
return NextResponse.json({ error: 'Failed to upload image' }, { status: 500 })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function DELETE(req) {
|
|
||||||
try {
|
|
||||||
const searchParams = req.nextUrl.searchParams
|
|
||||||
const Key = `maps/${searchParams.get('fileName')}`
|
|
||||||
console.log('🚀 ~ DELETE ~ Key:', Key)
|
|
||||||
|
|
||||||
if (!Key) {
|
|
||||||
return NextResponse.json({ error: 'fileName parameter is required' }, { status: 400 })
|
|
||||||
}
|
|
||||||
|
|
||||||
await s3.send(
|
|
||||||
new DeleteObjectCommand({
|
|
||||||
Bucket,
|
|
||||||
Key,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
return NextResponse.json({ message: '이미지 삭제 성공' }, { status: 200 })
|
|
||||||
} catch (error) {
|
|
||||||
console.error('S3 Delete Error:', error)
|
|
||||||
return NextResponse.json({ error: 'Failed to delete image' }, { status: 500 })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,72 +0,0 @@
|
|||||||
import { NextResponse } from 'next/server'
|
|
||||||
import { S3Client, PutObjectCommand, DeleteObjectCommand } from '@aws-sdk/client-s3'
|
|
||||||
|
|
||||||
const Bucket = process.env.AMPLIFY_BUCKET
|
|
||||||
const s3 = new S3Client({
|
|
||||||
region: process.env.AWS_REGION,
|
|
||||||
credentials: {
|
|
||||||
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
|
|
||||||
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const uploadImage = async (file) => {
|
|
||||||
const Body = Buffer.from(await file.arrayBuffer())
|
|
||||||
const Key = `upload/${file.name}`
|
|
||||||
const ContentType = file.ContentType
|
|
||||||
|
|
||||||
await s3.send(
|
|
||||||
new PutObjectCommand({
|
|
||||||
Bucket,
|
|
||||||
Key,
|
|
||||||
Body,
|
|
||||||
ContentType,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
|
||||||
filePath: `https://${process.env.AMPLIFY_BUCKET}.s3.${process.env.AWS_REGION}.amazonaws.com/${Key}`,
|
|
||||||
fileName: Key,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function POST(req) {
|
|
||||||
try {
|
|
||||||
const formData = await req.formData()
|
|
||||||
const file = formData.get('file')
|
|
||||||
|
|
||||||
const result = await uploadImage(file)
|
|
||||||
result.message = '이미지 업로드 성공'
|
|
||||||
|
|
||||||
return NextResponse.json(result)
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error)
|
|
||||||
return NextResponse.json({ error: 'Failed to upload image' }, { status: 500 })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function DELETE(req) {
|
|
||||||
try {
|
|
||||||
const searchParams = req.nextUrl.searchParams
|
|
||||||
const fileName = searchParams.get('fileName')
|
|
||||||
|
|
||||||
if (!fileName) {
|
|
||||||
return NextResponse.json({ error: 'fileName parameter is required' }, { status: 400 })
|
|
||||||
}
|
|
||||||
|
|
||||||
const Key = `upload/${fileName}`
|
|
||||||
console.log('🚀 ~ DELETE ~ Key:', Key)
|
|
||||||
|
|
||||||
await s3.send(
|
|
||||||
new DeleteObjectCommand({
|
|
||||||
Bucket,
|
|
||||||
Key,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
return NextResponse.json({ message: '이미지 삭제 성공' }, { status: 200 })
|
|
||||||
} catch (error) {
|
|
||||||
console.error('S3 Delete Error:', error)
|
|
||||||
return NextResponse.json({ error: 'Failed to delete image' }, { status: 500 })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
import Qna from '@/components/community/Qna'
|
|
||||||
|
|
||||||
export default async function CommunityQnaPage() {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Qna />
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@ -21,8 +21,6 @@ const defaultEstimateData = {
|
|||||||
fileList: [],
|
fileList: [],
|
||||||
fileFlg: '0', //후일 자료 제출 (체크 1 노체크 0)
|
fileFlg: '0', //후일 자료 제출 (체크 1 노체크 0)
|
||||||
priceCd: '',
|
priceCd: '',
|
||||||
pricingFlag: false, // 가격 처리 플래그 추가
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -47,19 +45,8 @@ const FloorPlanProvider = ({ children }) => {
|
|||||||
// const pathname = usePathname()
|
// const pathname = usePathname()
|
||||||
// const setCorrentObjectNo = useSetRecoilState(correntObjectNoState)
|
// const setCorrentObjectNo = useSetRecoilState(correntObjectNoState)
|
||||||
const searchParams = useSearchParams()
|
const searchParams = useSearchParams()
|
||||||
const path = usePathname()
|
|
||||||
const objectNo = searchParams.get('objectNo')
|
const objectNo = searchParams.get('objectNo')
|
||||||
const pid = searchParams.get('pid')
|
const pid = searchParams.get('pid')
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setFloorPlanState((prev) => {
|
|
||||||
return {
|
|
||||||
...prev,
|
|
||||||
objectNo,
|
|
||||||
pid,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}, [path])
|
|
||||||
// useEffect(() => {
|
// useEffect(() => {
|
||||||
// console.log('🚀 ~ useEffect ~ objectNo:')
|
// console.log('🚀 ~ useEffect ~ objectNo:')
|
||||||
// if (pathname === '/floor-plan') {
|
// if (pathname === '/floor-plan') {
|
||||||
|
|||||||
@ -21,8 +21,8 @@ import GlobalLoadingProvider from './GlobalLoadingProvider'
|
|||||||
* 서버 컴포넌트에 한해서 개별로 설정할 수 있음
|
* 서버 컴포넌트에 한해서 개별로 설정할 수 있음
|
||||||
*/
|
*/
|
||||||
export const metadata = {
|
export const metadata = {
|
||||||
title: 'HANASYS DESIGN',
|
title: 'HANASYS設計',
|
||||||
description: 'HANASYS DESIGN',
|
description: 'HANASYS設計',
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -58,8 +58,6 @@ export default async function RootLayout({ children }) {
|
|||||||
pwdInitYn: session.pwdInitYn,
|
pwdInitYn: session.pwdInitYn,
|
||||||
custCd: session.custCd,
|
custCd: session.custCd,
|
||||||
isLoggedIn: session.isLoggedIn,
|
isLoggedIn: session.isLoggedIn,
|
||||||
builderNo: session.builderNo,
|
|
||||||
custNm: session.custNm
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!headerPathname.includes('/login') && !session.isLoggedIn) {
|
if (!headerPathname.includes('/login') && !session.isLoggedIn) {
|
||||||
|
|||||||
@ -13,7 +13,6 @@ 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: {
|
||||||
@ -61,7 +60,6 @@ export const LINE_TYPE = {
|
|||||||
*/
|
*/
|
||||||
DEFAULT: 'default',
|
DEFAULT: 'default',
|
||||||
EAVES: 'eaves',
|
EAVES: 'eaves',
|
||||||
EAVE_HELP_LINE: 'eaveHelpLine',
|
|
||||||
GABLE: 'gable',
|
GABLE: 'gable',
|
||||||
GABLE_LEFT: 'gableLeft', //케라바 왼쪽
|
GABLE_LEFT: 'gableLeft', //케라바 왼쪽
|
||||||
GABLE_RIGHT: 'gableRight', //케라바 오른쪽
|
GABLE_RIGHT: 'gableRight', //케라바 오른쪽
|
||||||
@ -127,11 +125,6 @@ export const TRESTLE_MATERIAL = {
|
|||||||
BRACKET: 'bracket',
|
BRACKET: 'bracket',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MODULE_SETUP_TYPE = {
|
|
||||||
LAYOUT: 'layout',
|
|
||||||
AUTO: 'auto',
|
|
||||||
}
|
|
||||||
|
|
||||||
export const SAVE_KEY = [
|
export const SAVE_KEY = [
|
||||||
'selectable',
|
'selectable',
|
||||||
'name',
|
'name',
|
||||||
@ -210,21 +203,6 @@ export const SAVE_KEY = [
|
|||||||
'fontWeight',
|
'fontWeight',
|
||||||
'dormerAttributes',
|
'dormerAttributes',
|
||||||
'toFixed',
|
'toFixed',
|
||||||
'startPoint',
|
|
||||||
'endPoint',
|
|
||||||
'editable',
|
|
||||||
'isSortedPoints',
|
|
||||||
'isMultipleOf45',
|
|
||||||
'from',
|
|
||||||
'originColor',
|
|
||||||
'originWidth',
|
|
||||||
'originHeight',
|
|
||||||
'skeletonLines',
|
|
||||||
'skeleton',
|
|
||||||
'viewportTransform',
|
|
||||||
'outerLineFix',
|
|
||||||
'adjustRoofLines',
|
|
||||||
'northModuleYn',
|
|
||||||
]
|
]
|
||||||
|
|
||||||
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]
|
||||||
|
|||||||
@ -14,13 +14,11 @@ import { sessionStore } from '@/store/commonAtom'
|
|||||||
import { isObjectNotEmpty } from '@/util/common-utils'
|
import { isObjectNotEmpty } from '@/util/common-utils'
|
||||||
|
|
||||||
import BoardDetailModal from './community/modal/BoardDetailModal'
|
import BoardDetailModal from './community/modal/BoardDetailModal'
|
||||||
import Config from '@/config/config.export'
|
|
||||||
|
|
||||||
export default function MainPage() {
|
export default function MainPage() {
|
||||||
const [sessionState, setSessionState] = useRecoilState(sessionStore)
|
const [sessionState, setSessionState] = useRecoilState(sessionStore)
|
||||||
const [changePasswordPopOpen, setChangePasswordPopOpen] = useState(false)
|
const [chagePasswordPopOpen, setChagePasswordPopOpen] = useState(false)
|
||||||
// 데이터 확인 완료 여부 상태 추가
|
|
||||||
const [isSessionLoaded, setIsSessionLoaded] = useState(false)
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
|
|
||||||
@ -53,14 +51,6 @@ export default function MainPage() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (isObjectNotEmpty(sessionState)) {
|
|
||||||
if (sessionState?.pwdInitYn !== 'Y') {
|
|
||||||
setChangePasswordPopOpen(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [sessionState])
|
|
||||||
|
|
||||||
// 라디오 변경 이벤트
|
// 라디오 변경 이벤트
|
||||||
const handleOnChangeRadio = (e) => {
|
const handleOnChangeRadio = (e) => {
|
||||||
setSearchRadioType(e.target.value)
|
setSearchRadioType(e.target.value)
|
||||||
@ -86,7 +76,7 @@ export default function MainPage() {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isObjectNotEmpty(sessionState)) {
|
if (isObjectNotEmpty(sessionState)) {
|
||||||
if (sessionState?.pwdInitYn !== 'Y') {
|
if (sessionState?.pwdInitYn !== 'Y') {
|
||||||
setChangePasswordPopOpen(true)
|
setChagePasswordPopOpen(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [sessionState])
|
}, [sessionState])
|
||||||
@ -95,25 +85,10 @@ export default function MainPage() {
|
|||||||
const [open, setOpen] = useState(false)
|
const [open, setOpen] = useState(false)
|
||||||
const [modalNoticeNo, setModalNoticeNo] = useState('')
|
const [modalNoticeNo, setModalNoticeNo] = useState('')
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (isObjectNotEmpty(sessionState)) {
|
|
||||||
if (sessionState?.pwdInitYn !== 'Y') {
|
|
||||||
setChangePasswordPopOpen(true)
|
|
||||||
} else {
|
|
||||||
// pwdInitYn이 'Y'라면 팝업을 닫음 (false)
|
|
||||||
setChangePasswordPopOpen(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [sessionState])
|
|
||||||
|
|
||||||
//if (!isSessionLoaded) return null
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{open && <BoardDetailModal noticeNo={modalNoticeNo} setOpen={setOpen} />}
|
{open && <BoardDetailModal noticeNo={modalNoticeNo} setOpen={setOpen} />}
|
||||||
{changePasswordPopOpen ? (
|
{(!chagePasswordPopOpen && (
|
||||||
<ChangePasswordPop setChangePasswordPopOpen={setChangePasswordPopOpen} />
|
|
||||||
) : (
|
|
||||||
<>
|
<>
|
||||||
<div className="background-bord"></div>
|
<div className="background-bord"></div>
|
||||||
<div className="main-contents">
|
<div className="main-contents">
|
||||||
@ -155,8 +130,11 @@ export default function MainPage() {
|
|||||||
<MainContents setFaqOpen={setOpen} setFaqModalNoticeNo={setModalNoticeNo} />
|
<MainContents setFaqOpen={setOpen} setFaqModalNoticeNo={setModalNoticeNo} />
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
)) || (
|
||||||
|
<>
|
||||||
|
<ChangePasswordPop setChagePasswordPopOpen={setChagePasswordPopOpen} />
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,51 +2,15 @@
|
|||||||
|
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { useMessage } from '@/hooks/useMessage'
|
import { useMessage } from '@/hooks/useMessage'
|
||||||
import { setSession, login } from '@/lib/authActions'
|
|
||||||
import { sessionStore } from '@/store/commonAtom'
|
|
||||||
import { useRecoilState } from 'recoil'
|
|
||||||
import { useAxios } from '@/hooks/useAxios'
|
|
||||||
import { globalLocaleStore } from '@/store/localeAtom'
|
|
||||||
import { useRouter } from 'next/navigation'
|
|
||||||
|
|
||||||
import GlobalSpinner from '@/components/common/spinner/GlobalSpinner'
|
import GlobalSpinner from '@/components/common/spinner/GlobalSpinner'
|
||||||
|
|
||||||
export default function AutoLoginPage({ autoLoginParam }) {
|
export default function AutoLoginPage() {
|
||||||
const router = useRouter()
|
const [isLoading, setIsLoading] = useState(true)
|
||||||
|
|
||||||
const [isLoading, setIsLoading] = useState(autoLoginParam === 'Y' ? false : true)
|
|
||||||
const [globalLocaleState, setGlbalLocaleState] = useRecoilState(globalLocaleStore)
|
|
||||||
|
|
||||||
const { promisePost } = useAxios(globalLocaleState)
|
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
|
|
||||||
const [userId, setUserId] = useState('')
|
|
||||||
const [sessionState, setSessionState] = useRecoilState(sessionStore)
|
|
||||||
|
|
||||||
const [idFocus, setIdFocus] = useState(false)
|
|
||||||
|
|
||||||
const loginProcess = async () => {
|
|
||||||
setIsLoading(true)
|
|
||||||
await promisePost({ url: '/api/login/v1.0/user', data: { loginId: userId } }).then((response) => {
|
|
||||||
setIsLoading(false)
|
|
||||||
if (response.data) {
|
|
||||||
const res = response.data
|
|
||||||
const result = { ...res, storeLvl: res.groupId === '60000' ? '1' : '2', pwdInitYn: 'Y' }
|
|
||||||
setSession(result)
|
|
||||||
setSessionState(result)
|
|
||||||
login()
|
|
||||||
} else {
|
|
||||||
alert(getMessage('login.fail'))
|
|
||||||
router.push('/login?autoLoginParam1=Y')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{isLoading && <GlobalSpinner />}
|
{isLoading && <GlobalSpinner />}
|
||||||
{autoLoginParam !== 'Y' ? (
|
|
||||||
<>
|
|
||||||
<div className="login-input-frame">
|
<div className="login-input-frame">
|
||||||
<div className="login-frame-tit ">
|
<div className="login-frame-tit ">
|
||||||
<span>{getMessage('site.name')}</span>
|
<span>{getMessage('site.name')}</span>
|
||||||
@ -59,54 +23,5 @@ export default function AutoLoginPage({ autoLoginParam }) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<div className="login-input-frame">
|
|
||||||
<form
|
|
||||||
onSubmit={(e) => {
|
|
||||||
e.preventDefault()
|
|
||||||
loginProcess()
|
|
||||||
}}
|
|
||||||
className="space-y-6"
|
|
||||||
>
|
|
||||||
<div className="login-frame-tit">
|
|
||||||
<span>{getMessage('site.name')}</span>
|
|
||||||
{getMessage('site.sub_name')}
|
|
||||||
</div>
|
|
||||||
<div className="login-input-wrap">
|
|
||||||
<div className={`login-area id ${idFocus ? 'focus' : ''}`}>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
className="login-input"
|
|
||||||
id="userId"
|
|
||||||
name="id"
|
|
||||||
required
|
|
||||||
value={userId}
|
|
||||||
placeholder={getMessage('login.id.placeholder')}
|
|
||||||
onChange={(e) => {
|
|
||||||
setUserId(e.target.value)
|
|
||||||
}}
|
|
||||||
onFocus={() => setIdFocus(true)}
|
|
||||||
onBlur={() => setIdFocus(false)}
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="id-delete"
|
|
||||||
onClick={(e) => {
|
|
||||||
setUserId('')
|
|
||||||
}}
|
|
||||||
></button>
|
|
||||||
</div>
|
|
||||||
<div className="login-btn-box">
|
|
||||||
<button type="submit" className="login-btn">
|
|
||||||
{getMessage('login')}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,10 +25,8 @@ export default function Login() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (autoLoginParam) {
|
if (autoLoginParam) {
|
||||||
if (autoLoginParam !== 'Y') {
|
|
||||||
autoLoginProcess(autoLoginParam)
|
autoLoginProcess(autoLoginParam)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// console.log('🚀 ~ checkSession ~ checkSession():', checkSession())
|
// console.log('🚀 ~ checkSession ~ checkSession():', checkSession())
|
||||||
// checkSession().then((res) => {
|
// checkSession().then((res) => {
|
||||||
@ -336,7 +334,7 @@ export default function Login() {
|
|||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{autoLoginParam && <AutoLogin autoLoginParam={autoLoginParam} />}
|
{autoLoginParam && <AutoLogin />}
|
||||||
</div>
|
</div>
|
||||||
<div className="login-copyright">COPYRIGHT©2024 Hanwha Japan All Rights Reserved.</div>
|
<div className="login-copyright">COPYRIGHT©2024 Hanwha Japan All Rights Reserved.</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -6,19 +6,29 @@ import { contextMenuListState, contextMenuState } from '@/store/contextMenu'
|
|||||||
import { useTempGrid } from '@/hooks/useTempGrid'
|
import { useTempGrid } from '@/hooks/useTempGrid'
|
||||||
import { useContextMenu } from '@/hooks/useContextMenu'
|
import { useContextMenu } from '@/hooks/useContextMenu'
|
||||||
import { useEvent } from '@/hooks/useEvent'
|
import { useEvent } from '@/hooks/useEvent'
|
||||||
import { canvasState, currentObjectState } from '@/store/canvasAtom'
|
import { canvasState } from '@/store/canvasAtom'
|
||||||
|
|
||||||
export default function QContextMenu(props) {
|
export default function QContextMenu(props) {
|
||||||
const canvas = useRecoilValue(canvasState)
|
const canvas = useRecoilValue(canvasState)
|
||||||
const { contextRef, canvasProps } = props
|
const { contextRef, canvasProps } = props
|
||||||
const [contextMenu, setContextMenu] = useRecoilState(contextMenuState)
|
const [contextMenu, setContextMenu] = useRecoilState(contextMenuState)
|
||||||
const contextMenuList = useRecoilValue(contextMenuListState)
|
const contextMenuList = useRecoilValue(contextMenuListState)
|
||||||
const currentObject = useRecoilValue(currentObjectState)
|
const activeObject = canvasProps?.getActiveObject() //액티브된 객체를 가져옴
|
||||||
const { tempGridMode, setTempGridMode } = useTempGrid()
|
const { tempGridMode, setTempGridMode } = useTempGrid()
|
||||||
const { handleKeyup } = useContextMenu()
|
const { handleKeyup } = useContextMenu()
|
||||||
const { addDocumentEventListener, removeDocumentEvent } = useEvent()
|
const { addDocumentEventListener, removeDocumentEvent } = useEvent()
|
||||||
// const { addDocumentEventListener, removeDocumentEvent } = useContext(EventContext)
|
// const { addDocumentEventListener, removeDocumentEvent } = useContext(EventContext)
|
||||||
|
|
||||||
|
let contextType = ''
|
||||||
|
|
||||||
|
if (activeObject) {
|
||||||
|
if (activeObject.initOptions && activeObject.initOptions.name) {
|
||||||
|
//이건 바뀔 가능성이 있음
|
||||||
|
if (activeObject.initOptions?.name?.indexOf('guide') > -1) {
|
||||||
|
contextType = 'surface' //면형상
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
const getYPosition = (e) => {
|
const getYPosition = (e) => {
|
||||||
const contextLength = contextMenuList.reduce((acc, cur, index) => {
|
const contextLength = contextMenuList.reduce((acc, cur, index) => {
|
||||||
return acc + cur.length
|
return acc + cur.length
|
||||||
@ -26,13 +36,11 @@ export default function QContextMenu(props) {
|
|||||||
return e?.clientY - (contextLength * 25 + contextMenuList.length * 2 * 17)
|
return e?.clientY - (contextLength * 25 + contextMenuList.length * 2 * 17)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!contextRef.current) return
|
||||||
|
|
||||||
const handleContextMenu = (e) => {
|
const handleContextMenu = (e) => {
|
||||||
// e.preventDefault() //기존 contextmenu 막고
|
e.preventDefault() //기존 contextmenu 막고
|
||||||
|
|
||||||
if (currentObject) {
|
|
||||||
const isArray = currentObject.hasOwnProperty('arrayData')
|
|
||||||
if (isArray && currentObject.arrayData.length === 0) return
|
|
||||||
|
|
||||||
if (tempGridMode) return
|
if (tempGridMode) return
|
||||||
const position = {
|
const position = {
|
||||||
x: window.innerWidth / 2 < e.pageX ? e.pageX - 240 : e.pageX,
|
x: window.innerWidth / 2 < e.pageX ? e.pageX - 240 : e.pageX,
|
||||||
@ -40,7 +48,7 @@ export default function QContextMenu(props) {
|
|||||||
}
|
}
|
||||||
setContextMenu({ visible: true, ...position, currentMousePos: canvasProps.getPointer(e) })
|
setContextMenu({ visible: true, ...position, currentMousePos: canvasProps.getPointer(e) })
|
||||||
addDocumentEventListener('keyup', document, handleKeyup)
|
addDocumentEventListener('keyup', document, handleKeyup)
|
||||||
}
|
canvasProps?.upperCanvasEl.removeEventListener('contextmenu', handleContextMenu) //한번 노출 후 이벤트 삭제
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleClick = (e) => {
|
const handleClick = (e) => {
|
||||||
@ -56,9 +64,6 @@ export default function QContextMenu(props) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!contextRef.current) return
|
|
||||||
|
|
||||||
canvasProps?.upperCanvasEl.addEventListener('contextmenu', handleContextMenu)
|
canvasProps?.upperCanvasEl.addEventListener('contextmenu', handleContextMenu)
|
||||||
document.addEventListener('click', handleClick)
|
document.addEventListener('click', handleClick)
|
||||||
document.addEventListener('click', handleOutsideClick)
|
document.addEventListener('click', handleOutsideClick)
|
||||||
@ -67,9 +72,43 @@ export default function QContextMenu(props) {
|
|||||||
removeDocumentEvent('keyup')
|
removeDocumentEvent('keyup')
|
||||||
document.removeEventListener('click', handleClick)
|
document.removeEventListener('click', handleClick)
|
||||||
document.removeEventListener('click', handleOutsideClick)
|
document.removeEventListener('click', handleOutsideClick)
|
||||||
canvasProps?.upperCanvasEl.removeEventListener('contextmenu', handleContextMenu) //한번 노출 후 이벤트 삭제
|
|
||||||
}
|
}
|
||||||
}, [contextRef, contextMenuList, currentObject])
|
}, [contextRef, contextMenuList])
|
||||||
|
|
||||||
|
const handleObjectMove = () => {
|
||||||
|
activeObject.set({
|
||||||
|
lockMovementX: false, // X 축 이동 잠금
|
||||||
|
lockMovementY: false, // Y 축 이동 잠금
|
||||||
|
})
|
||||||
|
|
||||||
|
canvasProps?.on('object:modified', function (e) {
|
||||||
|
activeObject.set({
|
||||||
|
lockMovementX: true, // X 축 이동 잠금
|
||||||
|
lockMovementY: true, // Y 축 이동 잠금
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleObjectDelete = () => {
|
||||||
|
if (confirm('삭제하실거?')) {
|
||||||
|
canvasProps.remove(activeObject)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleObjectCopy = () => {
|
||||||
|
activeObject.clone((cloned) => {
|
||||||
|
cloned.set({
|
||||||
|
left: activeObject.left + activeObject.width + 20,
|
||||||
|
initOptions: { ...activeObject.initOptions },
|
||||||
|
lockMovementX: true, // X 축 이동 잠금
|
||||||
|
lockMovementY: true, // Y 축 이동 잠금
|
||||||
|
lockRotation: true, // 회전 잠금
|
||||||
|
lockScalingX: true, // X 축 크기 조정 잠금
|
||||||
|
lockScalingY: true, // Y 축 크기 조정 잠금
|
||||||
|
})
|
||||||
|
canvasProps?.add(cloned)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@ -24,8 +24,7 @@ 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={handle === '' ? '.modal-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}
|
||||||
@ -38,19 +37,16 @@ export default function WithDraggable({ isShow, children, pos = { x: 0, y: 0 },
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function WithDraggableHeader({ title, onClose, children, isFold, onFold = null }) {
|
function WithDraggableHeader({ title, onClose, children }) {
|
||||||
return (
|
return (
|
||||||
<div className="modal-head modal-handle">
|
<div className="modal-head modal-handle">
|
||||||
<h1 className="title">{title}</h1>
|
<h1 className="title">{title}</h1>
|
||||||
<div className="modal-btn-wrap">
|
|
||||||
{onFold && <button className={`modal-fold ${isFold ? '' : 'act'}`} onClick={onFold}></button>}
|
|
||||||
{onClose && (
|
{onClose && (
|
||||||
<button className="modal-close" onClick={() => onClose()}>
|
<button className="modal-close" onClick={() => onClose()}>
|
||||||
닫기
|
닫기
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,526 +0,0 @@
|
|||||||
import React, { useState, useRef, useEffect, forwardRef, useImperativeHandle } 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, maxLength = 12 }, 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 newValue = value || '0'
|
|
||||||
setDisplayValue(newValue)
|
|
||||||
|
|
||||||
// 외부에서 value가 변경될 때 계산기 내부 상태도 동기화
|
|
||||||
const calculator = calculatorRef.current
|
|
||||||
if (calculator) {
|
|
||||||
// 연산 중이 아닐 때 외부에서 값이 들어오면 현재 피연산자로 설정
|
|
||||||
calculator.currentOperand = newValue.toString()
|
|
||||||
calculator.previousOperand = ''
|
|
||||||
calculator.operation = undefined
|
|
||||||
setHasOperation(false)
|
|
||||||
}
|
|
||||||
}, [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 = ''
|
|
||||||
|
|
||||||
// maxLength 체크
|
|
||||||
if (maxLength > 0) {
|
|
||||||
const currentLength = (calculator.currentOperand || '').length + (calculator.previousOperand || '').length + (calculator.operation || '').length
|
|
||||||
if (currentLength >= maxLength) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 소수점 이하 2자리 제한 로직 추가
|
|
||||||
const shouldPreventInput = (value) => {
|
|
||||||
if (!value) return false
|
|
||||||
const decimalParts = value.toString().split('.')
|
|
||||||
return decimalParts.length > 1 && decimalParts[1].length >= 2
|
|
||||||
}
|
|
||||||
|
|
||||||
// 숫자 추가 함수
|
|
||||||
const appendNumber = (current, num) => {
|
|
||||||
// maxLength 체크
|
|
||||||
if (maxLength > 0 && (current + num).length > maxLength) {
|
|
||||||
return current
|
|
||||||
}
|
|
||||||
// 현재 값이 0이고 소수점이 없을 때 0이 아닌 숫자를 입력하면 대체
|
|
||||||
if (current === '0' && num !== '.' && !current.includes('.')) {
|
|
||||||
return num.toString()
|
|
||||||
}
|
|
||||||
// 0. 다음에 0을 입력하는 경우 허용
|
|
||||||
if (current === '0' && num === '0') {
|
|
||||||
return '0.'
|
|
||||||
}
|
|
||||||
return current + num
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasOperation) {
|
|
||||||
// 연산자 이후 숫자 입력 시
|
|
||||||
if (calculator.shouldResetDisplay) {
|
|
||||||
calculator.currentOperand = num.toString()
|
|
||||||
calculator.shouldResetDisplay = false
|
|
||||||
} else if (num === '.') {
|
|
||||||
if (!calculator.currentOperand.includes('.')) {
|
|
||||||
calculator.currentOperand = calculator.currentOperand || '0' + '.'
|
|
||||||
}
|
|
||||||
} else if (!shouldPreventInput(calculator.currentOperand)) {
|
|
||||||
calculator.currentOperand = appendNumber(calculator.currentOperand || '0', num)
|
|
||||||
}
|
|
||||||
|
|
||||||
newDisplayValue = calculator.previousOperand + calculator.operation + calculator.currentOperand
|
|
||||||
setDisplayValue(newDisplayValue)
|
|
||||||
} else {
|
|
||||||
// 첫 번째 숫자 입력 시
|
|
||||||
if (calculator.shouldResetDisplay) {
|
|
||||||
calculator.currentOperand = num.toString()
|
|
||||||
calculator.shouldResetDisplay = false
|
|
||||||
newDisplayValue = calculator.currentOperand
|
|
||||||
setDisplayValue(newDisplayValue)
|
|
||||||
if (!hasOperation) {
|
|
||||||
onChange(calculator.currentOperand)
|
|
||||||
}
|
|
||||||
} else if (num === '.') {
|
|
||||||
if (!calculator.currentOperand.includes('.')) {
|
|
||||||
calculator.currentOperand = (calculator.currentOperand || '0') + '.'
|
|
||||||
newDisplayValue = calculator.currentOperand
|
|
||||||
setDisplayValue(newDisplayValue)
|
|
||||||
if (!hasOperation) {
|
|
||||||
onChange(newDisplayValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (!shouldPreventInput(calculator.currentOperand)) {
|
|
||||||
calculator.currentOperand = appendNumber(calculator.currentOperand || '0', num)
|
|
||||||
newDisplayValue = calculator.currentOperand
|
|
||||||
setDisplayValue(newDisplayValue)
|
|
||||||
if (!hasOperation) {
|
|
||||||
onChange(newDisplayValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 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 호출
|
|
||||||
onChange(filteredValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
//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') {
|
|
||||||
if (hasOperation) {
|
|
||||||
handleCompute(true) // 계산 수행
|
|
||||||
}
|
|
||||||
setShowKeypad(false)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 모든 방향키는 기본 동작 허용
|
|
||||||
if (e.key === 'ArrowLeft' || e.key === 'ArrowRight' || e.key === 'ArrowUp' || e.key === 'ArrowDown') {
|
|
||||||
if (hasOperation) {
|
|
||||||
handleCompute(true) // 계산 수행
|
|
||||||
}
|
|
||||||
setShowKeypad(false)
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- 여기서부터는 브라우저의 기본 입력을 막고 계산기 로직만 적용함 ---
|
|
||||||
if (e.key !== 'Process') { // 한글 입력 등 특수 상황 방지 (필요시)
|
|
||||||
// e.preventDefault() 호출 위치를 확인하세요.
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
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}
|
|
||||||
maxLength={maxLength}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{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,7 +14,6 @@ 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,8 +26,6 @@ export default function QSelectBox({
|
|||||||
targetKey = '',
|
targetKey = '',
|
||||||
showKey = '',
|
showKey = '',
|
||||||
params = {},
|
params = {},
|
||||||
tagTitle = '',
|
|
||||||
showFirstOptionWhenEmpty = false,
|
|
||||||
}) {
|
}) {
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
|
|
||||||
@ -41,10 +38,8 @@ 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가 있으면 우선 보여준다
|
||||||
if (showFirstOptionWhenEmpty && options.length > 0) {
|
// return options[0][showKey]
|
||||||
return options[0][showKey]
|
return title
|
||||||
}
|
|
||||||
return title !== '' ? title : getMessage('selectbox.title')
|
|
||||||
} else if (showKey !== '' && value) {
|
} else if (showKey !== '' && value) {
|
||||||
//value가 있으면 sourceKey와 targetKey를 비교하여 보여준다
|
//value가 있으면 sourceKey와 targetKey를 비교하여 보여준다
|
||||||
|
|
||||||
@ -52,18 +47,12 @@ 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')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -84,7 +73,7 @@ export default function QSelectBox({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// value && handleClickSelectOption(value)
|
// value && handleClickSelectOption(value)
|
||||||
setSelected(handleInitState())
|
setSelected(handleInitState())
|
||||||
}, [options, value, sourceKey, targetKey, showKey, showFirstOptionWhenEmpty])
|
}, [options, value, sourceKey, targetKey, showKey])
|
||||||
|
|
||||||
useOnClickOutside(ref, handleClose)
|
useOnClickOutside(ref, handleClose)
|
||||||
|
|
||||||
@ -93,13 +82,12 @@ export default function QSelectBox({
|
|||||||
className={`sort-select ${openSelect ? 'active' : ''} ${disabled ? 'disabled' : ''}`}
|
className={`sort-select ${openSelect ? 'active' : ''} ${disabled ? 'disabled' : ''}`}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
onClick={disabled ? () => {} : () => setOpenSelect(!openSelect)}
|
onClick={disabled ? () => {} : () => setOpenSelect(!openSelect)}
|
||||||
title={tagTitle}
|
|
||||||
>
|
>
|
||||||
<p>{selected}</p>
|
<p>{selected}</p>
|
||||||
<ul className="select-item-wrap" style={{ maxHeight: '200px' }}>
|
<ul className="select-item-wrap">
|
||||||
{options?.length > 0 &&
|
{options?.length > 0 &&
|
||||||
options?.map((option, index) => (
|
options?.map((option, index) => (
|
||||||
<li key={option.id + '_' + index} className="select-item" onClick={() => handleClickSelectOption(option)}>
|
<li key={option.id || index} className="select-item" onClick={() => handleClickSelectOption(option)}>
|
||||||
<button key={option.id + 'btn'}>{showKey !== '' ? option[showKey] : option.name}</button>
|
<button key={option.id + 'btn'}>{showKey !== '' ? option[showKey] : option.name}</button>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@ -1,213 +0,0 @@
|
|||||||
'use client'
|
|
||||||
|
|
||||||
import Link from 'next/link'
|
|
||||||
import Image from 'next/image'
|
|
||||||
|
|
||||||
import Search from '@/components/community/Search'
|
|
||||||
import Pagination from '@/components/community/Pagination'
|
|
||||||
|
|
||||||
import { useContext } from 'react'
|
|
||||||
|
|
||||||
import { useEffect, useState } from 'react'
|
|
||||||
import { useResetRecoilState, useRecoilValue, useRecoilState } from 'recoil'
|
|
||||||
import { useMessage } from '@/hooks/useMessage'
|
|
||||||
|
|
||||||
import { searchState } from '@/store/boardAtom'
|
|
||||||
|
|
||||||
import { QcastContext } from '@/app/QcastProvider'
|
|
||||||
import QnaBoardDetailModal from '@/components/community/modal/QnaDetailModal'
|
|
||||||
import { sessionStore } from '@/store/commonAtom'
|
|
||||||
import { useAxios } from '@/hooks/useAxios'
|
|
||||||
import { useCommonCode } from '@/hooks/common/useCommonCode'
|
|
||||||
|
|
||||||
export default function Qna() {
|
|
||||||
const { getMessage } = useMessage()
|
|
||||||
const resetSearch = useResetRecoilState(searchState)
|
|
||||||
const [isInitialized, setIsInitialized] = useState(false)
|
|
||||||
|
|
||||||
//const search = useRecoilValue(searchState)
|
|
||||||
const [searchForm, setSearchForm] = useRecoilState(searchState)
|
|
||||||
const { findCommonCode } = useCommonCode()
|
|
||||||
const { setIsGlobalLoading } = useContext(QcastContext)
|
|
||||||
const { get } = useAxios()
|
|
||||||
const [boardList, setBoardList] = useState([])
|
|
||||||
const [sessionState, setSessionState] = useRecoilState(sessionStore)
|
|
||||||
const [search, setSearch] = useRecoilState(searchState)
|
|
||||||
// 팝업 관련
|
|
||||||
const [open, setOpen] = useState(false)
|
|
||||||
const [modalQnaNo, setModalQnaNo] = useState('')
|
|
||||||
const [modalQnaType, setModalQnaType] = useState('')
|
|
||||||
|
|
||||||
// 목록 조회
|
|
||||||
useEffect(() => {
|
|
||||||
async function fetchData() {
|
|
||||||
setIsGlobalLoading(true)
|
|
||||||
const startRow = (search.currentPage - 1) * search.pageBlock > 0 ? (search.currentPage - 1) * search.pageBlock + 1 : 1
|
|
||||||
const endRow = search.currentPage * search.pageBlock
|
|
||||||
|
|
||||||
const url = `/api/board/list`
|
|
||||||
const params = new URLSearchParams({
|
|
||||||
schNoticeTpCd : 'QC',
|
|
||||||
schNoticeClsCd: 'QNA',
|
|
||||||
compCd : 5200,
|
|
||||||
storeId : sessionState.storeId,
|
|
||||||
loginId : sessionState.userId,
|
|
||||||
schTitle : search.searchValue ? search.searchValue : '',
|
|
||||||
startRow : startRow,
|
|
||||||
endRow : endRow,
|
|
||||||
schMainYn : 'N',
|
|
||||||
siteTpCd : 'QC',
|
|
||||||
langCd : 'JA',
|
|
||||||
})
|
|
||||||
const apiUrl = `${url}?${params.toString()}`
|
|
||||||
|
|
||||||
const resultData = await get({ url: apiUrl })
|
|
||||||
|
|
||||||
if (resultData) {
|
|
||||||
if (resultData.result.code === 200) {
|
|
||||||
if (resultData.data.length > 0) {
|
|
||||||
setBoardList(resultData.data)
|
|
||||||
setSearch({ ...search, totalCount: resultData.data[0].totCnt })
|
|
||||||
} else {
|
|
||||||
setBoardList([])
|
|
||||||
setSearch({ ...search, totalCount: 0 })
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
alert(resultData.result.message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setIsGlobalLoading(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
fetchData()
|
|
||||||
}, [search.currentPage, search.searchValue, search.pageBlock, search.searchFlag])
|
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (search.mainFlag === 'N') {
|
|
||||||
resetSearch()
|
|
||||||
} else {
|
|
||||||
//메인에서 FAQ 조회 왔을때 로딩바 해제
|
|
||||||
setIsGlobalLoading(false)
|
|
||||||
setSearchForm({ ...searchForm, mainFlag: 'N' })
|
|
||||||
}
|
|
||||||
setIsInitialized(true)
|
|
||||||
|
|
||||||
//문의구분코드
|
|
||||||
// const codeL = findCommonCode(204200)
|
|
||||||
// const codeM = findCommonCode(204300)
|
|
||||||
// const codeS = findCommonCode(204400)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
if (!isInitialized) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
const boardType = {
|
|
||||||
boardTitle: getMessage('qna.title'),
|
|
||||||
subTitle: getMessage('qna.sub.title'),
|
|
||||||
clsCode: 'QNA',
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className="sub-header">
|
|
||||||
<div className="sub-header-inner">
|
|
||||||
<ul className="sub-header-title-wrap">
|
|
||||||
<li className="title-item">
|
|
||||||
<Link className="sub-header-title" href={'#'}>
|
|
||||||
{getMessage('qna.title')}
|
|
||||||
</Link>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<ul className="sub-header-location">
|
|
||||||
<li className="location-item">
|
|
||||||
<span className="home">
|
|
||||||
<Image src="/static/images/main/home_icon.svg" alt="react" width={16} height={16} />
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
<li className="location-item">
|
|
||||||
<span>{getMessage('header.menus.community')}</span>
|
|
||||||
</li>
|
|
||||||
<li className="location-item">
|
|
||||||
<span>{getMessage('qna.title')}</span>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="sub-content">
|
|
||||||
<div className="sub-content-inner">
|
|
||||||
<div className="sub-table-box">
|
|
||||||
<Search title={boardType.boardTitle} subTitle={boardType.subTitle} isSelectUse={true} clsCode={boardType.clsCode} />
|
|
||||||
{/*<QnaTable clsCode={boardType.clsCode} />*/}
|
|
||||||
<div className="community-table">
|
|
||||||
<table>
|
|
||||||
<colgroup>
|
|
||||||
<col width={100}/>
|
|
||||||
<col width={150}/>
|
|
||||||
<col />
|
|
||||||
<col width={150}/>
|
|
||||||
<col width={150}/>
|
|
||||||
</colgroup>
|
|
||||||
<tbody>
|
|
||||||
{boardList.length > 0 ? (
|
|
||||||
boardList?.map((board) => (
|
|
||||||
<tr
|
|
||||||
key={board.qnaNo}
|
|
||||||
onClick={() => {
|
|
||||||
setOpen(true)
|
|
||||||
setModalQnaNo(board.qnaNo)
|
|
||||||
setModalQnaType("["+board?.qnaClsLrgCd+"/"+board?.qnaClsMidCd+"/"+board?.qnaClsSmlCd+"]")
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<td className="al-c">
|
|
||||||
{/* 번호 */}
|
|
||||||
{board.totCnt - board.rowNumber + 1}
|
|
||||||
</td>
|
|
||||||
{/* 답변 */}
|
|
||||||
{board?.answerYn === 'Y'? (<td className="al-c "> {getMessage('qna.list.header.answer.yes')}</td>) : (<td className="al-c org"> {getMessage('qna.list.header.answer.no')}</td>)}
|
|
||||||
<td>
|
|
||||||
<div className="mb5">[{board?.qnaClsLrgCd} / {board?.qnaClsMidCd} / {board?.qnaClsSmlCd}]</div>
|
|
||||||
{/* 제목 */}
|
|
||||||
<div className="text-frame">
|
|
||||||
<div className="text-overflow">{board.title}{board.qstTitle}</div>
|
|
||||||
{board.attachYn === 'Y' && <span className="clip"></span>}
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div className="renewal">
|
|
||||||
{/*{board.uptDt && (*/}
|
|
||||||
{/* <>*/}
|
|
||||||
{/* (<span>{getMessage('board.uptDt')}</span> : {board.uptDt})*/}
|
|
||||||
{/* </>*/}
|
|
||||||
{/*)}*/}
|
|
||||||
{board.regUserNm}
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td className="al-c">
|
|
||||||
{/* 등록일 */}
|
|
||||||
{board.regDt.split(' ')[0]}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
))
|
|
||||||
) : (
|
|
||||||
<tr>
|
|
||||||
<td className="al-c no-data" colSpan={5}>{getMessage('common.message.no.data')}</td>
|
|
||||||
</tr>
|
|
||||||
)}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Pagination />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{open && <QnaBoardDetailModal qnaNo={modalQnaNo} setOpen={setOpen} qnaType = {modalQnaType} />}
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@ -4,11 +4,10 @@ import { searchState } from '@/store/boardAtom'
|
|||||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { useMessage } from '@/hooks/useMessage'
|
import { useMessage } from '@/hooks/useMessage'
|
||||||
import QnaRegModal from '@/components/community/modal/QnaRegModal'
|
|
||||||
|
|
||||||
export default function Search({ title = '', subTitle = '', isSelectUse = false, clsCode = '' }) {
|
export default function Search({ title = '', subTitle = '', isSelectUse = false }) {
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
const [open, setOpen] = useState(false)
|
|
||||||
const search = useRecoilValue(searchState)
|
const search = useRecoilValue(searchState)
|
||||||
const [searchForm, setSearchForm] = useRecoilState(searchState)
|
const [searchForm, setSearchForm] = useRecoilState(searchState)
|
||||||
|
|
||||||
@ -33,13 +32,7 @@ export default function Search({ title = '', subTitle = '', isSelectUse = false,
|
|||||||
} else {
|
} else {
|
||||||
setSearchView(false)
|
setSearchView(false)
|
||||||
setSearchViewText('')
|
setSearchViewText('')
|
||||||
setSearchForm({
|
setSearchForm({ ...searchForm, currentPage: 1, searchValue: '', pageBlock: block, searchFlag: !searchForm.searchFlag })
|
||||||
...searchForm,
|
|
||||||
currentPage: 1,
|
|
||||||
searchValue: '',
|
|
||||||
pageBlock : block,
|
|
||||||
searchFlag : !searchForm.searchFlag,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
// 조회 후 값 비워주기
|
// 조회 후 값 비워주기
|
||||||
setSearchValue('')
|
setSearchValue('')
|
||||||
@ -64,10 +57,7 @@ export default function Search({ title = '', subTitle = '', isSelectUse = false,
|
|||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
value={searchValue}
|
value={searchValue}
|
||||||
/>
|
/>
|
||||||
|
<button type="button" className="community-search-ico" onClick={() => handleSearch(searchValue, selectPageBlock)}></button>
|
||||||
<button type="button" className="community-search-ico"
|
|
||||||
onClick={() => handleSearch(searchValue, selectPageBlock)}></button>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
{searchView && (
|
{searchView && (
|
||||||
<div className="community-search-keyword">
|
<div className="community-search-keyword">
|
||||||
@ -102,14 +92,6 @@ export default function Search({ title = '', subTitle = '', isSelectUse = false,
|
|||||||
</div>
|
</div>
|
||||||
{isSelectUse && (
|
{isSelectUse && (
|
||||||
<div className="left-unit-box">
|
<div className="left-unit-box">
|
||||||
{clsCode === 'QNA' &&
|
|
||||||
<div>
|
|
||||||
<button className="btn-origin navy mr10"
|
|
||||||
onClick={() => {
|
|
||||||
setOpen(true)
|
|
||||||
}}> {getMessage('qna.sub.btn.inquiry')}</button>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
<div className="select-box" style={{ width: '80px' }}>
|
<div className="select-box" style={{ width: '80px' }}>
|
||||||
<select
|
<select
|
||||||
className="select-light black"
|
className="select-light black"
|
||||||
@ -130,7 +112,6 @@ export default function Search({ title = '', subTitle = '', isSelectUse = false,
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{open && <QnaRegModal setOpen={setOpen} setReload={handleSearch} searchValue={searchValue ? searchValue : searchViewText} selectPageBlock = {selectPageBlock}/>}
|
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,128 +0,0 @@
|
|||||||
'use client'
|
|
||||||
|
|
||||||
import { useEffect, useState } from 'react'
|
|
||||||
import { useAxios } from '@/hooks/useAxios'
|
|
||||||
import { handleFileDown } from '@/util/board-utils'
|
|
||||||
import { useMessage } from '@/hooks/useMessage'
|
|
||||||
import { sessionStore } from '@/store/commonAtom'
|
|
||||||
import { useRecoilState } from 'recoil'
|
|
||||||
|
|
||||||
export default function QnaDetailModal({ qnaNo, setOpen, qnaType }) {
|
|
||||||
const { getMessage } = useMessage()
|
|
||||||
// api 조회 관련
|
|
||||||
const { get } = useAxios()
|
|
||||||
const [boardDetail, setBoardDetail] = useState({})
|
|
||||||
const [sessionState, setSessionState] = useRecoilState(sessionStore)
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// 상세 조회
|
|
||||||
const fetchDetail = async (qnaNo) => {
|
|
||||||
const url = `/api/board/detail`
|
|
||||||
const params = new URLSearchParams({
|
|
||||||
noticeNo : qnaNo,
|
|
||||||
qnaNo : qnaNo,
|
|
||||||
schNoticeClsCd: 'QNA',
|
|
||||||
compCd : 5200,
|
|
||||||
loginId : sessionState.userId,
|
|
||||||
langCd : 'JA',
|
|
||||||
siteTpCd : 'QC',
|
|
||||||
})
|
|
||||||
const apiUrl = `${url}?${params.toString()}`
|
|
||||||
|
|
||||||
const resultData = await get({ url: apiUrl })
|
|
||||||
|
|
||||||
if (resultData) {
|
|
||||||
if (resultData.result.code === 200) {
|
|
||||||
const boardDetail = resultData.data
|
|
||||||
setBoardDetail(boardDetail)
|
|
||||||
} else {
|
|
||||||
alert(resultData.result.message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fetchDetail(qnaNo)
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div key={qnaNo} className="modal-popup community">
|
|
||||||
<div className="modal-dialog">
|
|
||||||
<div className="modal-content">
|
|
||||||
<div className="modal-header">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="modal-close"
|
|
||||||
onClick={() => {
|
|
||||||
setOpen(false)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{getMessage('board.sub.btn.close')}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div className="modal-body">
|
|
||||||
<div className="oneonone-header-wrap">
|
|
||||||
<div className="oneonone-title"> {qnaType} {boardDetail.qstTitle}</div>
|
|
||||||
<div className="oneonone-infor">
|
|
||||||
<div className="profile">{boardDetail.regUserNm}</div>
|
|
||||||
<div className="date">{boardDetail.regDt}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="oneonone-detail">
|
|
||||||
{boardDetail.listFile && (
|
|
||||||
<dl className="community_detail-file-wrap">
|
|
||||||
<dt>{getMessage('qna.detail.sub.fileList')}</dt>
|
|
||||||
{boardDetail.listFile.map((boardFile) => (
|
|
||||||
<dd key={boardFile.encodeFileNo}>
|
|
||||||
<button type="button" className="down" onClick={() => handleFileDown(boardFile.fileNo, 'NO')}>
|
|
||||||
{boardFile.srcFileNm}
|
|
||||||
</button>
|
|
||||||
</dd>
|
|
||||||
))}
|
|
||||||
</dl>
|
|
||||||
)}
|
|
||||||
<div
|
|
||||||
className="community_detail-inner"
|
|
||||||
dangerouslySetInnerHTML={{
|
|
||||||
__html: boardDetail.qstContents ? boardDetail.qstContents.replaceAll('\n', '<br/>') : '',
|
|
||||||
}}
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
{boardDetail?.answerYn === 'Y' && (
|
|
||||||
<div className="oneonone-answer">
|
|
||||||
<div className="answer-title-wrap">
|
|
||||||
<div className="answer-title">Hanwha Japan {getMessage('qna.detail.sub.answer')}</div>
|
|
||||||
<div className="oneonone-infor">
|
|
||||||
<div className="profile">{boardDetail.ansRegNm}</div>
|
|
||||||
<div className="date">{boardDetail.ansRegDt}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className="community_detail-inner"
|
|
||||||
dangerouslySetInnerHTML={{
|
|
||||||
__html: boardDetail.ansContents ? boardDetail.ansContents.replaceAll('\n', '<br/>') : '',
|
|
||||||
}}
|
|
||||||
></div>
|
|
||||||
{boardDetail.ansListFile && (
|
|
||||||
<dl className="community_detail-file-wrap">
|
|
||||||
<dt>{getMessage('qna.detail.sub.fileList')}</dt>
|
|
||||||
{boardDetail.ansListFile.map((boardFile) => (
|
|
||||||
<dd key={boardFile.encodeFileNo}>
|
|
||||||
<button type="button" className="down" onClick={() => handleFileDown(boardFile.fileNo, 'N')}>
|
|
||||||
{boardFile.srcFileNm}
|
|
||||||
</button>
|
|
||||||
</dd>
|
|
||||||
))}
|
|
||||||
</dl>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@ -1,140 +0,0 @@
|
|||||||
'use client'
|
|
||||||
|
|
||||||
import { useRef } from 'react'
|
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
|
||||||
import { useMessage } from '@/hooks/useMessage'
|
|
||||||
import { useSwal } from '@/hooks/useSwal'
|
|
||||||
|
|
||||||
export default function QnaFileUploader({ uploadFiles, setUploadFiles, qnaData, setQnaData }) {
|
|
||||||
const fileInputRef = useRef(null)
|
|
||||||
const { getMessage } = useMessage()
|
|
||||||
|
|
||||||
const { swalFire } = useSwal()
|
|
||||||
|
|
||||||
const handleButtonClick = (e) => {
|
|
||||||
e.preventDefault()
|
|
||||||
fileInputRef.current.click()
|
|
||||||
}
|
|
||||||
|
|
||||||
const onChangeFiles = async (e) => {
|
|
||||||
if (e.target.files.length <= 0) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const fileList = []
|
|
||||||
let passFlag = true
|
|
||||||
const allowedFileTypes = [
|
|
||||||
'image/',
|
|
||||||
'application/pdf',
|
|
||||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
||||||
'application/vnd.ms-excel',
|
|
||||||
'application/vnd.openxmlformats-officedocument.presentationml.presentation', // PPTX 형식
|
|
||||||
'application/vnd.ms-powerpoint', // PPT 형식
|
|
||||||
]
|
|
||||||
Array.from(e.target.files).forEach((file) => {
|
|
||||||
//엑셀, pdf, 이미지
|
|
||||||
const fileType = file.type
|
|
||||||
if (!allowedFileTypes.some((type) => fileType.includes(type))) {
|
|
||||||
passFlag = false
|
|
||||||
} else {
|
|
||||||
fileList.push({ data: file, id: uuidv4() })
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!passFlag) {
|
|
||||||
swalFire({ text: getMessage('estimate.detail.fileList.extCheck'), type: 'alert', icon: 'error' })
|
|
||||||
}
|
|
||||||
|
|
||||||
setUploadFiles([...uploadFiles, ...fileList])
|
|
||||||
setQnaData({...qnaData, files:[...uploadFiles, ...fileList]})
|
|
||||||
e.target.value = ''
|
|
||||||
}
|
|
||||||
|
|
||||||
const deleteFile = (id) => {
|
|
||||||
setUploadFiles(uploadFiles.filter((file) => file.id !== id))
|
|
||||||
setQnaData({...qnaData, files:uploadFiles.filter((file) => file.id !== id)})
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleDrop = (e) => {
|
|
||||||
e.preventDefault()
|
|
||||||
e.stopPropagation()
|
|
||||||
const fileList = []
|
|
||||||
let passFlag = true
|
|
||||||
const allowedFileTypes = [
|
|
||||||
'image/',
|
|
||||||
'application/pdf',
|
|
||||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
||||||
'application/vnd.ms-excel',
|
|
||||||
'application/vnd.openxmlformats-officedocument.presentationml.presentation', // PPTX 형식
|
|
||||||
'application/vnd.ms-powerpoint', // PPT 형식
|
|
||||||
]
|
|
||||||
|
|
||||||
Array.from(e.dataTransfer.files).forEach((file) => {
|
|
||||||
//엑셀, pdf, 이미지
|
|
||||||
let fileType = file.type
|
|
||||||
if (!allowedFileTypes.some((type) => fileType.includes(type))) {
|
|
||||||
passFlag = false
|
|
||||||
} else {
|
|
||||||
fileList.push({ data: file, id: uuidv4() })
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!passFlag) {
|
|
||||||
swalFire({ text: getMessage('estimate.detail.fileList.extCheck'), type: 'alert', icon: 'error' })
|
|
||||||
}
|
|
||||||
|
|
||||||
setUploadFiles([...uploadFiles, ...fileList])
|
|
||||||
setQnaData({...qnaData, files:[...uploadFiles, ...fileList]})
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleDragOver = (e) => {
|
|
||||||
e.preventDefault()
|
|
||||||
e.stopPropagation()
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleDragEnd = (e) => {
|
|
||||||
e.preventDefault()
|
|
||||||
e.stopPropagation()
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleDragLeave = (e) => {
|
|
||||||
e.preventDefault()
|
|
||||||
e.stopPropagation()
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
|
|
||||||
<div className="design-request-grid mt15">
|
|
||||||
<div className="design-request-count">
|
|
||||||
<div className="design-request-grid-tit">{getMessage("qna.reg.header.fileList")}</div>
|
|
||||||
<div className="btn-area one-on-one">
|
|
||||||
<label className="file-upload" htmlFor="img" onClick={handleButtonClick}>
|
|
||||||
Attach File
|
|
||||||
</label>
|
|
||||||
<input type="file" multiple name="file" ref={fileInputRef} style={{ display: 'none' }} onChange={(e) => onChangeFiles(e)} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="drag-file-box one-on-one">
|
|
||||||
<div className="drag-file-area"
|
|
||||||
draggable
|
|
||||||
onDrop={(e) => handleDrop(e)}
|
|
||||||
onDragOver={(e) => handleDragOver(e)}
|
|
||||||
onDragEnd={(e) => handleDragEnd(e)}
|
|
||||||
onDragLeave={(e) => handleDragLeave(e)}>
|
|
||||||
<p>Drag file here</p>
|
|
||||||
<ul className="file-list">
|
|
||||||
{uploadFiles.length > 0 &&
|
|
||||||
uploadFiles.map((file) => (
|
|
||||||
<li className="file-item" key={file.id}>
|
|
||||||
<span>
|
|
||||||
{file.data.name} <button className="delete" onClick={() => deleteFile(file.id)}></button>
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@ -1,464 +0,0 @@
|
|||||||
'use client'
|
|
||||||
|
|
||||||
import { useMessage } from '@/hooks/useMessage'
|
|
||||||
import { sessionStore } from '@/store/commonAtom'
|
|
||||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
|
||||||
import QnaFileUploader from '@/components/community/modal/QnaFileUploader'
|
|
||||||
import { useContext, useEffect, useRef, useState } from 'react'
|
|
||||||
import { useCommonCode } from '@/hooks/common/useCommonCode'
|
|
||||||
import Select from 'react-select'
|
|
||||||
import dayjs from 'dayjs'
|
|
||||||
import { useSwal } from '@/hooks/useSwal'
|
|
||||||
import { QcastContext } from '@/app/QcastProvider'
|
|
||||||
import { useAxios } from '@/hooks/useAxios'
|
|
||||||
import { globalLocaleStore } from '@/store/localeAtom'
|
|
||||||
import { e } from 'mathjs'
|
|
||||||
import { set } from 'react-hook-form'
|
|
||||||
|
|
||||||
|
|
||||||
export default function QnaRegModal({ setOpen, setReload, searchValue, selectPageBlock }) {
|
|
||||||
const { getMessage } = useMessage()
|
|
||||||
const [fileList, setFileList] = useState([])
|
|
||||||
const [sessionState, setSessionState] = useRecoilState(sessionStore)
|
|
||||||
const globalLocaleState = useRecoilValue(globalLocaleStore)
|
|
||||||
const [files, setFiles] = useState([])
|
|
||||||
//const [qnaData, setQnaData] = useState([])
|
|
||||||
const [qnaData, setQnaData] = useState({})
|
|
||||||
const [closeMdFlg, setCloseMdFlg] = useState(true)
|
|
||||||
const [closeSmFlg, setCloseSmFlg] = useState(true)
|
|
||||||
const [hideSmFlg, setHideSmFlg] = useState(false)
|
|
||||||
const qnaTypeLgCodeRef = useRef(null)
|
|
||||||
const qnaTypeMdCodeRef = useRef(null)
|
|
||||||
const qnaTypeSmCodeRef = useRef(null)
|
|
||||||
const qstMail = useRef(null);
|
|
||||||
const regUserNmRef = useRef(null)
|
|
||||||
const regUserTelNoRef = useRef(null)
|
|
||||||
const titleRef = useRef(null)
|
|
||||||
const contentsRef = useRef(null)
|
|
||||||
const { findCommonCode } = useCommonCode()
|
|
||||||
const [qnaTypeLgCodeList, setQnaTypeLgCodeList] = useState([])
|
|
||||||
const [qnaTypeMdCodeList, setQnaTypeMdCodeList] = useState([])
|
|
||||||
const [qnaTypeSmCodeList, setQnaTypeSmCodeList] = useState([])
|
|
||||||
const [phoneNumber, setPhoneNumber] = useState("");
|
|
||||||
const { swalFire } = useSwal()
|
|
||||||
const { setIsGlobalLoading } = useContext(QcastContext)
|
|
||||||
const [isBtnDisable, setIsBtnDisable] = useState(false);
|
|
||||||
const { promiseGet, post, promisePost } = useAxios(globalLocaleState)
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
console.log('qnaData updated:', qnaData);
|
|
||||||
}, [qnaData]);
|
|
||||||
|
|
||||||
let fileCheck = false;
|
|
||||||
const regPhoneNumber = (e) => {
|
|
||||||
const result = e.target.value
|
|
||||||
.replace(/[^0-9.]/g, "")
|
|
||||||
//.replace(/^(\d{0,3})(\d{0,4})(\d{0,4})$/g, "$1-$2-$3")
|
|
||||||
//.replace(/(-{1,2})$/g, "");
|
|
||||||
//setPhoneNumber(result);
|
|
||||||
|
|
||||||
setQnaData({...qnaData, regUserTelNo: result })
|
|
||||||
}
|
|
||||||
|
|
||||||
const fileUploadProps = {
|
|
||||||
uploadFiles: files,
|
|
||||||
setUploadFiles: setFiles,
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// const fileSave = (qnaData, fileUploadProps) => {
|
|
||||||
// return qnaData.files.push(fileUploadProps.uploadFiles)
|
|
||||||
// }
|
|
||||||
|
|
||||||
const initQnaReg = async () => {
|
|
||||||
|
|
||||||
|
|
||||||
qstMail.current.value = ''
|
|
||||||
regUserNmRef.current.value = ''
|
|
||||||
regUserTelNoRef.current.value = ''
|
|
||||||
qnaTypeLgCodeRef.current.setValue();
|
|
||||||
qnaTypeMdCodeRef.current.setValue();
|
|
||||||
qnaTypeSmCodeRef.current?.setValue();
|
|
||||||
titleRef.current.value = ''
|
|
||||||
contentsRef.current.value = ''
|
|
||||||
|
|
||||||
//setQnaData([])
|
|
||||||
|
|
||||||
setQnaData({
|
|
||||||
compCd: "5200",
|
|
||||||
siteTpCd: "QC",
|
|
||||||
schNoticeClsCd: "QNA",
|
|
||||||
regId: sessionState?.userId || '',
|
|
||||||
storeId: sessionState?.storeId || '',
|
|
||||||
qstMail: sessionState?.email || '',
|
|
||||||
qnaClsLrgCd: '',
|
|
||||||
qnaClsMidCd: '',
|
|
||||||
qnaClsSmlCd: ''
|
|
||||||
});
|
|
||||||
|
|
||||||
const codeL = findCommonCode(204200)
|
|
||||||
if (codeL != null) {
|
|
||||||
setQnaTypeLgCodeList(codeL)
|
|
||||||
}
|
|
||||||
setIsGlobalLoading(false)
|
|
||||||
setIsBtnDisable(false);
|
|
||||||
}
|
|
||||||
const onChangeQnaTypeL = (e) => {
|
|
||||||
if(e === undefined || e === null) return;
|
|
||||||
const codeM = findCommonCode(204300)
|
|
||||||
if (codeM != null) {
|
|
||||||
|
|
||||||
let codeList = []
|
|
||||||
|
|
||||||
codeM.map((item) => {
|
|
||||||
|
|
||||||
if(item.clRefChr1 === e.clCode) {
|
|
||||||
codeList.push(item);
|
|
||||||
|
|
||||||
}
|
|
||||||
})
|
|
||||||
setQnaTypeMdCodeList(codeList)
|
|
||||||
setQnaData({ ...qnaData, qnaClsLrgCd:e.clCode})
|
|
||||||
setCloseMdFlg(false)
|
|
||||||
qnaTypeMdCodeRef.current.setValue();
|
|
||||||
qnaTypeSmCodeRef.current?.setValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
const onChangeQnaTypeM = (e) => {
|
|
||||||
if (!e?.clCode) return;
|
|
||||||
|
|
||||||
// 중분류 코드 업데이트
|
|
||||||
setQnaData(prevState => ({
|
|
||||||
...prevState,
|
|
||||||
qnaClsMidCd: e.clCode,
|
|
||||||
// 소분류는 초기화 (새로 선택하도록)
|
|
||||||
qnaClsSmlCd: ''
|
|
||||||
}));
|
|
||||||
|
|
||||||
// 소분류 코드 목록 설정
|
|
||||||
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) => {
|
|
||||||
if (!e?.clCode) return;
|
|
||||||
|
|
||||||
setQnaData(prevState => ({
|
|
||||||
...prevState,
|
|
||||||
qnaClsSmlCd: e.clCode
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
const onFileSave = () => {
|
|
||||||
const formData= []
|
|
||||||
if(fileUploadProps.uploadFiles.length === 0) return;
|
|
||||||
if(!fileCheck) return;
|
|
||||||
|
|
||||||
fileUploadProps.uploadFiles.forEach((file) => {
|
|
||||||
//console.log("file::::::::",file)
|
|
||||||
formData.push(file)
|
|
||||||
|
|
||||||
})
|
|
||||||
setQnaData({ ...qnaData, files:formData })
|
|
||||||
fileCheck = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const isValidEmail = (email) => {
|
|
||||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
||||||
return emailRegex.test(email);
|
|
||||||
};
|
|
||||||
|
|
||||||
const isEmpty = (value) => {
|
|
||||||
return value === null || value === undefined || value.trim() === "";
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
const handleQnaSubmit = async () => {
|
|
||||||
//필수 체크
|
|
||||||
|
|
||||||
//console.log("1::::",qnaData)
|
|
||||||
|
|
||||||
|
|
||||||
let regUserNm = qnaData?.regUserNm??'';
|
|
||||||
|
|
||||||
if (!isValidEmail(qnaData.qstMail)) {
|
|
||||||
qstMail.current.focus();
|
|
||||||
swalFire({
|
|
||||||
title: getMessage('qna.reg.alert.require.qstMail'),
|
|
||||||
icon: 'warning',
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (isEmpty(regUserNm)) {
|
|
||||||
regUserNmRef.current.value = '';
|
|
||||||
regUserNmRef.current.focus()
|
|
||||||
swalFire({
|
|
||||||
title: getMessage('qna.reg.alert.require.regUserNm'),
|
|
||||||
icon: 'warning',
|
|
||||||
})
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
let qnaClsLrgCd = qnaData?.qnaClsLrgCd??'';
|
|
||||||
let qnaClsMidCd = qnaData?.qnaClsMidCd??'';
|
|
||||||
|
|
||||||
if (isEmpty(qnaClsLrgCd) || isEmpty(qnaClsMidCd) ) {
|
|
||||||
(isEmpty(qnaClsLrgCd))?qnaTypeLgCodeRef.current.focus():qnaTypeMdCodeRef.current.focus()
|
|
||||||
swalFire({
|
|
||||||
title: getMessage('qna.reg.alert.select.type'),
|
|
||||||
icon: 'warning',
|
|
||||||
})
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
let title = qnaData?.title??'';
|
|
||||||
|
|
||||||
if (isEmpty(title)) {
|
|
||||||
titleRef.current.value = '';
|
|
||||||
titleRef.current.focus()
|
|
||||||
swalFire({
|
|
||||||
title: getMessage('qna.reg.alert.require.title'),
|
|
||||||
icon: 'warning',
|
|
||||||
})
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
//console.log("5::::",qnaData)
|
|
||||||
let contents = qnaData?.contents??'';
|
|
||||||
|
|
||||||
if (isEmpty(contents)) {
|
|
||||||
contentsRef.current.value = '';
|
|
||||||
contentsRef.current.focus()
|
|
||||||
swalFire({
|
|
||||||
title: getMessage('qna.reg.alert.require.contents'),
|
|
||||||
icon: 'warning',
|
|
||||||
})
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
const formData = new FormData()
|
|
||||||
if(qnaData?.files?.length > 0) {
|
|
||||||
qnaData?.files.forEach((file) => {
|
|
||||||
formData.append('files', file.data)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
formData.append("compCd", qnaData.compCd)
|
|
||||||
formData.append("siteTpCd", qnaData.siteTpCd)
|
|
||||||
formData.append("qnaClsLrgCd", qnaData.qnaClsLrgCd)
|
|
||||||
formData.append("qnaClsMidCd", qnaData.qnaClsMidCd)
|
|
||||||
formData.append("qnaClsSmlCd", qnaData.qnaClsSmlCd)
|
|
||||||
formData.append("title", qnaData.title)
|
|
||||||
formData.append("contents", qnaData.contents)
|
|
||||||
formData.append("regId", qnaData.regId)
|
|
||||||
formData.append("storeId", qnaData.storeId)
|
|
||||||
formData.append("regUserNm", qnaData.regUserNm)
|
|
||||||
formData.append("regUserTelNo", qnaData.regUserTelNo)
|
|
||||||
formData.append("qstMail", qnaData.qstMail)
|
|
||||||
formData.append("schNoticeClsCd", qnaData.schNoticeClsCd)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//console.log(Array.from(formData));
|
|
||||||
|
|
||||||
swalFire({
|
|
||||||
html: getMessage('qna.reg.confirm.save'),
|
|
||||||
type: 'confirm',
|
|
||||||
confirmFn: async () => {
|
|
||||||
|
|
||||||
setIsBtnDisable(true);
|
|
||||||
setIsGlobalLoading(true)
|
|
||||||
|
|
||||||
try {
|
|
||||||
|
|
||||||
const apiUrl = 'api/board'
|
|
||||||
//console.log("7::::",qnaData)
|
|
||||||
await post({ url: `${apiUrl}/saveQna`, data: formData }).then((res) => {
|
|
||||||
if (res?.result.code === 200) {
|
|
||||||
//qnaData.newFileList = []
|
|
||||||
setIsGlobalLoading(false)
|
|
||||||
swalFire({ text: getMessage('qna.reg.alert.save'), type: 'alert' })
|
|
||||||
setOpen(false)
|
|
||||||
setReload(searchValue, selectPageBlock);
|
|
||||||
}else{
|
|
||||||
setIsGlobalLoading(false)
|
|
||||||
swalFire({ text: getMessage('qna.reg.alert.saveFail'), type: 'alert', icon: 'error' })
|
|
||||||
console.error('error::::::::::::', res)
|
|
||||||
}
|
|
||||||
|
|
||||||
setIsBtnDisable(false)
|
|
||||||
})
|
|
||||||
} catch (e) {
|
|
||||||
setIsGlobalLoading(false)
|
|
||||||
setIsBtnDisable(false);
|
|
||||||
console.error('error::::::::::::', e.message)
|
|
||||||
swalFire({ text: e.message, type: 'alert' , icon: 'error'})
|
|
||||||
console.error('error::::::::::::', e.message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
initQnaReg()
|
|
||||||
},[])
|
|
||||||
|
|
||||||
// useEffect(() => {
|
|
||||||
// onFileSave()
|
|
||||||
//
|
|
||||||
// }, [onFileSave])
|
|
||||||
return (
|
|
||||||
<div className="modal-popup">
|
|
||||||
<div className="modal-dialog big">
|
|
||||||
<div className="modal-content">
|
|
||||||
<div className="modal-header">
|
|
||||||
<h1 className="title">{getMessage('qna.title')}</h1>
|
|
||||||
<button className="modal-close"
|
|
||||||
onClick={() => {
|
|
||||||
setOpen(false)
|
|
||||||
}}>{getMessage('board.sub.btn.close')}</button>
|
|
||||||
</div>
|
|
||||||
<div className="modal-body">
|
|
||||||
<div className="modal-body-inner">
|
|
||||||
<div className="design-request-table">
|
|
||||||
<div className="common-table">
|
|
||||||
<table>
|
|
||||||
<colgroup>
|
|
||||||
<col style={{ width: '100px' }} />
|
|
||||||
<col />
|
|
||||||
<col style={{ width: '120px' }} />
|
|
||||||
<col />
|
|
||||||
<col style={{ width: '150px' }} />
|
|
||||||
<col />
|
|
||||||
</colgroup>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<th>{getMessage('qna.list.header.regNm')}</th>
|
|
||||||
<td><input type="text" className="input-light" value={sessionState?.userNm || ''} readOnly /></td>
|
|
||||||
<th>E-Mail<span className="red">*</span></th>
|
|
||||||
<td ><input type="text" className="input-light" required
|
|
||||||
ref={qstMail}
|
|
||||||
value={qnaData?.qstMail || ''}
|
|
||||||
onChange={(e) => setQnaData({...qnaData, qstMail: e.target.value })}
|
|
||||||
onBlur={(e) => setQnaData({ ...qnaData, qstMail: e.target.value })} />
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<th>{getMessage('qna.reg.header.regDt')}</th>
|
|
||||||
<td>{dayjs(new Date()).format('YYYY-MM-DD')}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th>Customer</th>
|
|
||||||
<td><input type="text" className="input-light" value={sessionState?.custNm || ''} readOnly /></td>
|
|
||||||
<th>{getMessage('qna.reg.header.regUserNm')}<span className="red">*</span></th>
|
|
||||||
<td ><input type="text" className="input-light" required
|
|
||||||
ref={regUserNmRef}
|
|
||||||
value={qnaData?.regUserNm || '' }
|
|
||||||
onChange={(e) => setQnaData({...qnaData, regUserNm: e.target.value })}
|
|
||||||
onBlur={(e) => setQnaData({ ...qnaData, regUserNm: e.target.value })} /> </td>
|
|
||||||
<th>{getMessage('qna.reg.header.regUserTelNo')}</th>
|
|
||||||
<td ><input type="text" className="input-light"
|
|
||||||
ref={regUserTelNoRef}
|
|
||||||
maxLength={13}
|
|
||||||
value={qnaData?.regUserTelNo || '' }
|
|
||||||
onChange={regPhoneNumber}
|
|
||||||
/></td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="design-request-grid">
|
|
||||||
<div className="design-request-count">
|
|
||||||
<div className="design-request-grid-tit">{getMessage("qna.reg.header.type")}, {getMessage("qna.reg.header.title")} <span
|
|
||||||
className="red">*</span></div>
|
|
||||||
</div>
|
|
||||||
<div className="flx-box one-on-one">
|
|
||||||
<div className="select-wrap mr5" >
|
|
||||||
<Select name="" ref={qnaTypeLgCodeRef}
|
|
||||||
options={qnaTypeLgCodeList}
|
|
||||||
placeholder="Select"
|
|
||||||
onChange={(e) => onChangeQnaTypeL(e)}
|
|
||||||
getOptionLabel={(x) => x.clCodeNm}
|
|
||||||
getOptionValue={(x) => x.clCode}
|
|
||||||
isClearable={false}
|
|
||||||
isSearchable={false}
|
|
||||||
|
|
||||||
/>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div className="select-wrap mr5" >
|
|
||||||
<Select name="" ref={qnaTypeMdCodeRef}
|
|
||||||
options={qnaTypeMdCodeList}
|
|
||||||
placeholder="Select"
|
|
||||||
onChange={(e) => onChangeQnaTypeM(e)}
|
|
||||||
getOptionLabel={(x) => x.clCodeNm}
|
|
||||||
getOptionValue={(x) => x.clCode}
|
|
||||||
isClearable={false}
|
|
||||||
isSearchable={false}
|
|
||||||
isDisabled={closeMdFlg}
|
|
||||||
defaultValue={''}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="select-wrap" >
|
|
||||||
{!hideSmFlg && (
|
|
||||||
<Select name="" ref={qnaTypeSmCodeRef}
|
|
||||||
options={qnaTypeSmCodeList}
|
|
||||||
placeholder="Select"
|
|
||||||
onChange={(e) => onChangeQnaTypeS(e)}
|
|
||||||
getOptionLabel={(x) => x.clCodeNm}
|
|
||||||
getOptionValue={(x) => x.clCode}
|
|
||||||
isClearable={false}
|
|
||||||
isSearchable={false}
|
|
||||||
isDisabled={closeSmFlg}
|
|
||||||
/>)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="input-wrap mt5">
|
|
||||||
<input type="text" className="input-light" maxLength={200}
|
|
||||||
ref = {titleRef}
|
|
||||||
value={qnaData?.title || '' }
|
|
||||||
onChange={(e) => {setQnaData({ ...qnaData, title: e.target.value })}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="design-request-grid mt15">
|
|
||||||
<div className="design-request-count">
|
|
||||||
<div className="design-request-grid-tit">{getMessage("qna.reg.header.contents")} <span className="red">*</span></div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<textarea className="textarea-form" name="" id="" maxLength={4000}
|
|
||||||
ref={contentsRef}
|
|
||||||
value={qnaData?.contents || '' }
|
|
||||||
onChange={(e) => {setQnaData({ ...qnaData, contents: e.target.value })}} ></textarea>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<QnaFileUploader {...fileUploadProps} qnaData={qnaData} setQnaData={setQnaData} />
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div className="footer-btn-wrap">
|
|
||||||
{isBtnDisable === false && <button className="btn-origin navy mr5" onClick={handleQnaSubmit}>{getMessage("qna.reg.header.save")}</button>}
|
|
||||||
<button className="btn-origin grey" onClick={() => {
|
|
||||||
setOpen(false)
|
|
||||||
}}>{getMessage("board.sub.btn.close")}</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@ -13,7 +13,7 @@ import dayjs from 'dayjs'
|
|||||||
import { useCommonCode } from '@/hooks/common/useCommonCode'
|
import { useCommonCode } from '@/hooks/common/useCommonCode'
|
||||||
import { useEstimateController } from '@/hooks/floorPlan/estimate/useEstimateController'
|
import { useEstimateController } from '@/hooks/floorPlan/estimate/useEstimateController'
|
||||||
import { SessionContext } from '@/app/SessionProvider'
|
import { SessionContext } from '@/app/SessionProvider'
|
||||||
import Select, { components } from 'react-select'
|
import Select from 'react-select'
|
||||||
import { convertNumberToPriceDecimal, convertNumberToPriceDecimalToFixed } from '@/util/common-utils'
|
import { convertNumberToPriceDecimal, convertNumberToPriceDecimalToFixed } from '@/util/common-utils'
|
||||||
import ProductFeaturesPop from './popup/ProductFeaturesPop'
|
import ProductFeaturesPop from './popup/ProductFeaturesPop'
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
@ -23,8 +23,6 @@ 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'
|
|
||||||
import { CalculatorInput } from '@/components/common/input/CalcInput'
|
|
||||||
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)
|
||||||
@ -62,7 +60,7 @@ export default function Estimate({}) {
|
|||||||
|
|
||||||
const [cableItemList, setCableItemList] = useState([]) //케이블 리스트
|
const [cableItemList, setCableItemList] = useState([]) //케이블 리스트
|
||||||
const [cableItem, setCableItem] = useState('') //케이블 선택값
|
const [cableItem, setCableItem] = useState('') //케이블 선택값
|
||||||
const [cableDbItem, setCableDbItem] = useState('') //케이블 선택값
|
|
||||||
const [startDate, setStartDate] = useState(new Date())
|
const [startDate, setStartDate] = useState(new Date())
|
||||||
const singleDatePickerProps = {
|
const singleDatePickerProps = {
|
||||||
startDate,
|
startDate,
|
||||||
@ -100,7 +98,7 @@ export default function Estimate({}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const initEstimate = (currPid = currentPid) => {
|
const initEstimate = (currPid = currentPid) => {
|
||||||
// console.log('🚀 ~ initEstimate ~ currPid:', currPid)
|
console.log('🚀 ~ initEstimate ~ currPid:', currPid)
|
||||||
closeAll()
|
closeAll()
|
||||||
setObjectNo(objectRecoil.floorPlanObjectNo)
|
setObjectNo(objectRecoil.floorPlanObjectNo)
|
||||||
|
|
||||||
@ -119,7 +117,6 @@ export default function Estimate({}) {
|
|||||||
item.value = item.clRefChr1
|
item.value = item.clRefChr1
|
||||||
item.label = item.clRefChr2
|
item.label = item.clRefChr2
|
||||||
})
|
})
|
||||||
// console.log(code2)
|
|
||||||
setCableItemList(code2)
|
setCableItemList(code2)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,27 +136,7 @@ export default function Estimate({}) {
|
|||||||
updatedRes = [...res]
|
updatedRes = [...res]
|
||||||
}
|
}
|
||||||
|
|
||||||
const groupByItemGroup = (items) => {
|
setOriginDisplayItemList(res)
|
||||||
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)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -174,22 +151,9 @@ 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)
|
||||||
}, [selectedPlan])
|
}, [selectedPlan])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -211,10 +175,7 @@ export default function Estimate({}) {
|
|||||||
row.check = false
|
row.check = false
|
||||||
estimateOption.map((row2) => {
|
estimateOption.map((row2) => {
|
||||||
if (row.pkgYn === '0') {
|
if (row.pkgYn === '0') {
|
||||||
// if (row2 === row.code) {
|
if (row2 === row.code) {
|
||||||
// row.check = true
|
|
||||||
// }
|
|
||||||
if (row.code.split('、').includes(row2)) {
|
|
||||||
row.check = true
|
row.check = true
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -256,10 +217,7 @@ export default function Estimate({}) {
|
|||||||
row.check = false
|
row.check = false
|
||||||
estimateOption.map((row2) => {
|
estimateOption.map((row2) => {
|
||||||
if (row.pkgYn === '0') {
|
if (row.pkgYn === '0') {
|
||||||
// if (row2 === row.code) {
|
if (row2 === row.code) {
|
||||||
// row.check = true
|
|
||||||
// }
|
|
||||||
if (row.code.split('、').includes(row2)) {
|
|
||||||
row.check = true
|
row.check = true
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -282,6 +240,7 @@ export default function Estimate({}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
setSpecialNoteList(res)
|
setSpecialNoteList(res)
|
||||||
|
|
||||||
setSpecialNoteFirstFlg(true)
|
setSpecialNoteFirstFlg(true)
|
||||||
@ -418,8 +377,8 @@ export default function Estimate({}) {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (estimateContextState.estimateType !== '') {
|
if (estimateContextState.estimateType !== '') {
|
||||||
const param = {
|
const param = {
|
||||||
saleStoreId: estimateContextState.sapSaleStoreId,
|
saleStoreId: session.storeId,
|
||||||
sapSalesStoreCd: estimateContextState.sapSalesStoreCd,
|
sapSalesStoreCd: session.custCd,
|
||||||
docTpCd: estimateContextState?.estimateType,
|
docTpCd: estimateContextState?.estimateType,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -428,8 +387,6 @@ export default function Estimate({}) {
|
|||||||
if (isNotEmptyArray(res?.data)) {
|
if (isNotEmptyArray(res?.data)) {
|
||||||
setStorePriceList(res.data)
|
setStorePriceList(res.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
setItemChangeYn(true)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
if (estimateContextState.estimateType === 'YJSS') {
|
if (estimateContextState.estimateType === 'YJSS') {
|
||||||
@ -459,6 +416,8 @@ export default function Estimate({}) {
|
|||||||
handlePricing('UNIT_PRICE')
|
handlePricing('UNIT_PRICE')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setItemChangeYn(true)
|
||||||
}
|
}
|
||||||
}, [estimateContextState?.estimateType])
|
}, [estimateContextState?.estimateType])
|
||||||
|
|
||||||
@ -510,21 +469,6 @@ export default function Estimate({}) {
|
|||||||
} else {
|
} else {
|
||||||
item.check = false
|
item.check = false
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
let codes = item.code.split('、')
|
|
||||||
let flg = '0'
|
|
||||||
if (codes.length > 1) {
|
|
||||||
for (let i = 0; i < pushData.length; i++) {
|
|
||||||
if (codes.indexOf(pushData[i]) > -1) {
|
|
||||||
flg = '1'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (flg === '1') {
|
|
||||||
item.check = true
|
|
||||||
} else {
|
|
||||||
item.check = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -534,27 +478,12 @@ export default function Estimate({}) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
//Pricing 버튼클릭시 confirm 노출
|
|
||||||
const handlePricingBtn = (showPriceCd) => {
|
|
||||||
swalFire({
|
|
||||||
text: getMessage('estimate.detail.showPrice.pricingBtn.confirm'),
|
|
||||||
type: 'confirm',
|
|
||||||
icon: 'warning',
|
|
||||||
confirmFn: () => {
|
|
||||||
handlePricing(showPriceCd)
|
|
||||||
setEstimateContextState({ pricingFlag:true })
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
//Pricing 버튼
|
//Pricing 버튼
|
||||||
const handlePricing = async (showPriceCd) => {
|
const handlePricing = async (showPriceCd) => {
|
||||||
const param = {
|
const param = {
|
||||||
saleStoreId: estimateContextState.sapSaleStoreId,
|
saleStoreId: session.storeId,
|
||||||
sapSalesStoreCd: estimateContextState.sapSalesStoreCd,
|
sapSalesStoreCd: session.custCd,
|
||||||
docTpCd: estimateContextState.estimateType,
|
docTpCd: estimateContextState.estimateType,
|
||||||
secSapSalesStoreCd:
|
|
||||||
estimateContextState.secSapSalesStoreCd?.length > 0 && showPriceCd === 'QSP_PRICE' ? estimateContextState.secSapSalesStoreCd : '',
|
|
||||||
priceCd: showPriceCd,
|
priceCd: showPriceCd,
|
||||||
itemIdList: estimateContextState.itemList.filter((item) => item.delFlg === '0' && item.paDispOrder === null),
|
itemIdList: estimateContextState.itemList.filter((item) => item.delFlg === '0' && item.paDispOrder === null),
|
||||||
}
|
}
|
||||||
@ -577,6 +506,7 @@ export default function Estimate({}) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setIsGlobalLoading(true)
|
setIsGlobalLoading(true)
|
||||||
await promisePost({ url: '/api/estimate/price/item-price-list', data: param }).then((res) => {
|
await promisePost({ url: '/api/estimate/price/item-price-list', data: param }).then((res) => {
|
||||||
let updateList = []
|
let updateList = []
|
||||||
@ -601,7 +531,6 @@ export default function Estimate({}) {
|
|||||||
updateList.push({
|
updateList.push({
|
||||||
...item,
|
...item,
|
||||||
openFlg: data.data2[i].unitPrice === '0.0' ? '1' : '0',
|
openFlg: data.data2[i].unitPrice === '0.0' ? '1' : '0',
|
||||||
unitOpenFlg: (showPriceCd === 'QSP_PRICE' && item.openFlg === '1') ? '1' : '0',
|
|
||||||
salePrice: data.data2[i].unitPrice === null ? '0' : data.data2[i].unitPrice,
|
salePrice: data.data2[i].unitPrice === null ? '0' : data.data2[i].unitPrice,
|
||||||
saleTotPrice: (item.amount * data.data2[i].unitPrice).toString(),
|
saleTotPrice: (item.amount * data.data2[i].unitPrice).toString(),
|
||||||
})
|
})
|
||||||
@ -680,14 +609,11 @@ export default function Estimate({}) {
|
|||||||
newValue = parts[0] + '.' + parts[1].substring(0, 2)
|
newValue = parts[0] + '.' + parts[1].substring(0, 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
let pkgAsp = normalizeDecimal(newValue || '0')
|
let pkgAsp = 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,
|
||||||
@ -701,7 +627,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(normalizeDigits(value))
|
let amount = Number(value.replace(/[^0-9]/g, '').replaceAll(',', ''))
|
||||||
|
|
||||||
if (isNaN(amount)) {
|
if (isNaN(amount)) {
|
||||||
amount = '0'
|
amount = '0'
|
||||||
@ -739,8 +665,7 @@ export default function Estimate({}) {
|
|||||||
// 단가 변경
|
// 단가 변경
|
||||||
const onChangeSalePrice = (value, dispOrder, index) => {
|
const onChangeSalePrice = (value, dispOrder, index) => {
|
||||||
//itemChangeFlg, partAdd 받아온 그대로
|
//itemChangeFlg, partAdd 받아온 그대로
|
||||||
let salePrice = Number(normalizeDecimal(value))
|
let salePrice = Number(value.replace(/[^0-9]/g, '').replaceAll(',', ''))
|
||||||
|
|
||||||
if (isNaN(salePrice)) {
|
if (isNaN(salePrice)) {
|
||||||
salePrice = 0
|
salePrice = 0
|
||||||
} else {
|
} else {
|
||||||
@ -771,7 +696,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' && item.itemTpCd !== 'S13') {
|
if (item.dispCableFlg === '1') {
|
||||||
if (value !== '') {
|
if (value !== '') {
|
||||||
onChangeDisplayItem(value, item.dispOrder, index, true)
|
onChangeDisplayItem(value, item.dispOrder, index, true)
|
||||||
}
|
}
|
||||||
@ -780,18 +705,6 @@ export default function Estimate({}) {
|
|||||||
setCableItem(value)
|
setCableItem(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 케이블 select 변경시 */
|
|
||||||
const onChangeDisplayDoubleCableItem = (value, itemList) => {
|
|
||||||
itemList.map((item, index) => {
|
|
||||||
if (item.dispCableFlg === '1' && (item.itemTpCd === 'M12' || item.itemTpCd === 'S13')) {
|
|
||||||
if (value !== '') {
|
|
||||||
onChangeDisplayItem(value, item.dispOrder, index, true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
setCableDbItem(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 아이템 자동완성 검색시 아이템 추가/변경시
|
// 아이템 자동완성 검색시 아이템 추가/변경시
|
||||||
const onChangeDisplayItem = (itemId, dispOrder, index, flag) => {
|
const onChangeDisplayItem = (itemId, dispOrder, index, flag) => {
|
||||||
const param = {
|
const param = {
|
||||||
@ -979,7 +892,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(normalizeDigits(item.amount)) || 0
|
let amount = Number(item.amount?.replace(/[^0-9]/g, '').replaceAll(',', '')) || 0
|
||||||
let price
|
let price
|
||||||
if (amount === 0) {
|
if (amount === 0) {
|
||||||
price = 0
|
price = 0
|
||||||
@ -1014,7 +927,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(normalizeDigits(item.amount)) || 0
|
let amount = Number(item.amount?.replace(/[^0-9]/g, '').replaceAll(',', '')) || 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
|
||||||
@ -1048,8 +961,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
|
||||||
@ -1108,7 +1021,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(normalizeDigits(item.amount)) || 0
|
let amount = Number(item.amount?.replace(/[^0-9]/g, '').replaceAll(',', '')) || 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
|
||||||
@ -1143,24 +1056,18 @@ export default function Estimate({}) {
|
|||||||
|
|
||||||
if (item.dispCableFlg === '1') {
|
if (item.dispCableFlg === '1') {
|
||||||
dispCableFlgCnt++
|
dispCableFlgCnt++
|
||||||
if(item.itemTpCd === 'M12' || item.itemTpCd === 'S13') {
|
|
||||||
setCableDbItem(item.itemId)
|
|
||||||
}else{
|
|
||||||
setCableItem(item.itemId)
|
setCableItem(item.itemId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
if (dispCableFlgCnt === 0) {
|
if (dispCableFlgCnt === 0) {
|
||||||
setCableItem('100038')
|
setCableItem('100038')
|
||||||
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
|
||||||
@ -1190,7 +1097,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(normalizeDigits(item.amount)) || 0
|
let amount = Number(item.amount?.replace(/[^0-9]/g, '').replaceAll(',', '')) || 0
|
||||||
let price
|
let price
|
||||||
if (amount === 0) {
|
if (amount === 0) {
|
||||||
price = 0
|
price = 0
|
||||||
@ -1216,18 +1123,16 @@ export default function Estimate({}) {
|
|||||||
|
|
||||||
if (item.dispCableFlg === '1') {
|
if (item.dispCableFlg === '1') {
|
||||||
dispCableFlgCnt++
|
dispCableFlgCnt++
|
||||||
if(item.itemTpCd === 'M12' || item.itemTpCd === 'S13') {
|
|
||||||
setCableDbItem(item.itemId)
|
|
||||||
}else{
|
|
||||||
setCableItem(item.itemId)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (item.dispCableFlg === '1') {
|
||||||
|
setCableItem(item.itemId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
if (dispCableFlgCnt === 0) {
|
if (dispCableFlgCnt === 0) {
|
||||||
setCableItem('100038')
|
setCableItem('100038')
|
||||||
setCableDbItem('100037')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
totals.vatPrice = totals.supplyPrice * 0.1
|
totals.vatPrice = totals.supplyPrice * 0.1
|
||||||
@ -1288,26 +1193,10 @@ export default function Estimate({}) {
|
|||||||
|
|
||||||
if (dispCableFlgCnt === 0) {
|
if (dispCableFlgCnt === 0) {
|
||||||
setCableItem('100038')
|
setCableItem('100038')
|
||||||
setCableDbItem('100037')
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [estimateContextState?.itemList, cableItemList])
|
}, [estimateContextState?.itemList, cableItemList])
|
||||||
|
|
||||||
const [agencyCustList, setAgencyCustList] = useState([])
|
|
||||||
useEffect(() => {
|
|
||||||
// 952 - 2차점 특가 sapSalesStoreCd
|
|
||||||
if (estimateContextState?.sapSalesStoreCd && session?.storeLvl === '1') {
|
|
||||||
const param = {
|
|
||||||
sapSalesStoreCd: estimateContextState.sapSalesStoreCd,
|
|
||||||
}
|
|
||||||
const apiUrl = `api/estimate/agency-cust-list?${queryStringFormatter(param)}`
|
|
||||||
get({ url: apiUrl }).then((res) => {
|
|
||||||
if (isNotEmptyArray(res?.data)) {
|
|
||||||
setAgencyCustList(res?.data)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}, [estimateContextState?.sapSalesStoreCd])
|
|
||||||
return (
|
return (
|
||||||
<div className="sub-content estimate">
|
<div className="sub-content estimate">
|
||||||
<div className="sub-content-inner">
|
<div className="sub-content-inner">
|
||||||
@ -1318,7 +1207,7 @@ export default function Estimate({}) {
|
|||||||
<div className="estimate-box">
|
<div className="estimate-box">
|
||||||
<div className="estimate-tit">{getMessage('estimate.detail.objectNo')}</div>
|
<div className="estimate-tit">{getMessage('estimate.detail.objectNo')}</div>
|
||||||
<div className="estimate-name">
|
<div className="estimate-name">
|
||||||
{currentObjectNo} (Plan No: {currentPid})
|
{currentObjectNo} (Plan No: {planNo})
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="estimate-box">
|
<div className="estimate-box">
|
||||||
@ -1452,32 +1341,21 @@ export default function Estimate({}) {
|
|||||||
{getMessage('estimate.detail.estimateType')} <span className="important">*</span>
|
{getMessage('estimate.detail.estimateType')} <span className="important">*</span>
|
||||||
</th>
|
</th>
|
||||||
<td colSpan={3}>
|
<td colSpan={3}>
|
||||||
<div className="form-flex-wrap">
|
|
||||||
<div className="radio-wrap">
|
<div className="radio-wrap">
|
||||||
{/*pkgRank is null, empty 인 경우 : 사용불가, 이전에 등록된 경우 사용가능, style로 제어*/}
|
<div className="d-check-radio light mr10">
|
||||||
<div
|
<input
|
||||||
className="d-check-radio light mr10"
|
type="radio"
|
||||||
style={{
|
name="estimateType"
|
||||||
display:
|
id="YJSS"
|
||||||
(isNotEmptyArray(storePriceList) > 0 && storePriceList[0].pkgRank !== null && storePriceList[0].pkgRank !== '') ||
|
value={'YJSS'}
|
||||||
estimateContextState?.estimateType === 'YJSS'
|
checked={estimateContextState?.estimateType === 'YJSS' ? true : false}
|
||||||
? ''
|
onChange={(e) => {
|
||||||
: 'none',
|
//주문분류
|
||||||
|
setHandlePricingFlag(true)
|
||||||
|
setEstimateContextState({ estimateType: e.target.value })
|
||||||
}}
|
}}
|
||||||
>
|
/>
|
||||||
{/*<input*/}
|
<label htmlFor="YJSS">{getMessage('estimate.detail.estimateType.yjss')}</label>
|
||||||
{/* type="radio"*/}
|
|
||||||
{/* name="estimateType"*/}
|
|
||||||
{/* id="YJSS"*/}
|
|
||||||
{/* value={'YJSS'}*/}
|
|
||||||
{/* checked={estimateContextState?.estimateType === 'YJSS' ? true : false}*/}
|
|
||||||
{/* onChange={(e) => {*/}
|
|
||||||
{/* //주문분류*/}
|
|
||||||
{/* setHandlePricingFlag(true)*/}
|
|
||||||
{/* setEstimateContextState({ estimateType: e.target.value, setEstimateContextState })*/}
|
|
||||||
{/* }}*/}
|
|
||||||
{/*/>*/}
|
|
||||||
{/*<label htmlFor="YJSS">{getMessage('estimate.detail.estimateType.yjss')}</label>*/}
|
|
||||||
</div>
|
</div>
|
||||||
<div className="d-check-radio light">
|
<div className="d-check-radio light">
|
||||||
<input
|
<input
|
||||||
@ -1494,38 +1372,6 @@ 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 === '100000' && agencyCustList.length > 0 ? ( //일시적으로 1 => 100000로 정리
|
|
||||||
<div className="form-flex-select ml10">
|
|
||||||
<label htmlFor="">{getMessage('estimate.detail.agency')}</label>
|
|
||||||
<div className="select-wrap" style={{ width: '400px' }}>
|
|
||||||
<Select
|
|
||||||
id="agencyName"
|
|
||||||
instanceId="agencyName"
|
|
||||||
className="react-select-custom"
|
|
||||||
classNamePrefix="custom"
|
|
||||||
placeholder="Select"
|
|
||||||
options={agencyCustList}
|
|
||||||
onChange={(e) => {
|
|
||||||
if (isObjectNotEmpty(e)) {
|
|
||||||
setEstimateContextState({ secSapSalesStoreCd: e.sapSalesStoreCd })
|
|
||||||
} else {
|
|
||||||
setEstimateContextState({ secSapSalesStoreCd: '' })
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
getOptionLabel={(x) => x.sapSalesStoreNm}
|
|
||||||
getOptionValue={(x) => x.sapSalesStoreCd}
|
|
||||||
isClearable={true}
|
|
||||||
isSearchable={true}
|
|
||||||
value={agencyCustList.filter(function (option) {
|
|
||||||
return option.sapSalesStoreCd === estimateContextState.secSapSalesStoreCd
|
|
||||||
})}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
''
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
@ -1694,13 +1540,13 @@ export default function Estimate({}) {
|
|||||||
{/* 파일첨부 끝 */}
|
{/* 파일첨부 끝 */}
|
||||||
{/* 견적특이사항 시작 */}
|
{/* 견적특이사항 시작 */}
|
||||||
<div className="table-box-title-wrap">
|
<div className="table-box-title-wrap">
|
||||||
{/*<div className="title-wrap">*/}
|
<div className="title-wrap">
|
||||||
{/* <h3 className="product">{getMessage('estimate.detail.header.specialEstimate')}</h3>*/}
|
<h3 className="product">{getMessage('estimate.detail.header.specialEstimate')}</h3>
|
||||||
{/* <div className="estimate-check-btn">*/}
|
<div className="estimate-check-btn">
|
||||||
{/* <button className={`estimate-arr-btn down mr5 ${hidden ? '' : 'on'}`} onClick={() => setHidden(false)}></button>*/}
|
<button className={`estimate-arr-btn down mr5 ${hidden ? '' : 'on'}`} onClick={() => setHidden(false)}></button>
|
||||||
{/* <button className={`estimate-arr-btn up ${hidden ? 'on' : ''}`} onClick={() => setHidden(true)}></button>*/}
|
<button className={`estimate-arr-btn up ${hidden ? 'on' : ''}`} onClick={() => setHidden(true)}></button>
|
||||||
{/* </div>*/}
|
</div>
|
||||||
{/*</div>*/}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/* 견적 특이사항 코드영역시작 */}
|
{/* 견적 특이사항 코드영역시작 */}
|
||||||
<div className={`estimate-check-wrap ${hidden ? 'hide' : ''}`}>
|
<div className={`estimate-check-wrap ${hidden ? 'hide' : ''}`}>
|
||||||
@ -1883,17 +1729,15 @@ export default function Estimate({}) {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="btn-origin grey ml5"
|
className="btn-origin grey ml5"
|
||||||
onClick={(event) => {
|
onClick={() => {
|
||||||
|
|
||||||
setHandlePricingFlag(true)
|
setHandlePricingFlag(true)
|
||||||
handlePricingBtn(showPriceCd)
|
handlePricing(showPriceCd)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{getMessage('estimate.detail.showPrice.pricingBtn')}
|
{getMessage('estimate.detail.showPrice.pricingBtn')}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="product-price-wrap ml10">
|
<div className="product-price-wrap ml10">
|
||||||
<div className="product-price-tit">{getMessage('estimate.detail.header.singleCable')}</div>
|
|
||||||
<div className="select-wrap">
|
<div className="select-wrap">
|
||||||
<select
|
<select
|
||||||
className="select-light"
|
className="select-light"
|
||||||
@ -1903,34 +1747,11 @@ export default function Estimate({}) {
|
|||||||
value={cableItem}
|
value={cableItem}
|
||||||
>
|
>
|
||||||
{cableItemList.length > 0 &&
|
{cableItemList.length > 0 &&
|
||||||
cableItemList.map((row) => {
|
cableItemList.map((row) => (
|
||||||
if(!row.clRefChr2.includes('S')){
|
<option key={row.clRefChr1} value={row.clRefChr1}>
|
||||||
return <option key={row.clRefChr1} value={row.clRefChr1}>
|
|
||||||
{row.clRefChr2}
|
{row.clRefChr2}
|
||||||
</option>
|
</option>
|
||||||
}
|
))}
|
||||||
})}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="product-price-wrap ml10">
|
|
||||||
<div className="product-price-tit">{getMessage('estimate.detail.header.doubleCable')}</div>
|
|
||||||
<div className="select-wrap">
|
|
||||||
<select
|
|
||||||
className="select-light"
|
|
||||||
onChange={(e) => {
|
|
||||||
onChangeDisplayDoubleCableItem(e.target.value, estimateContextState.itemList)
|
|
||||||
}}
|
|
||||||
value={cableDbItem}
|
|
||||||
>
|
|
||||||
{cableItemList.length > 0 &&
|
|
||||||
cableItemList.map((row) => {
|
|
||||||
if(row.clRefChr2.includes('S')){
|
|
||||||
return <option key={row.clRefChr1} value={row.clRefChr1}>
|
|
||||||
{row.clRefChr2.replace('S','')}
|
|
||||||
</option>
|
|
||||||
}
|
|
||||||
})}
|
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -2013,7 +1834,7 @@ export default function Estimate({}) {
|
|||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
id={item?.dispOrder}
|
id={item?.dispOrder}
|
||||||
disabled={!!item?.paDispOrder || item.dispCableFlg === '1X'}
|
disabled={!!item?.paDispOrder || item.dispCableFlg === '1'}
|
||||||
onChange={() => onChangeSelect(item.dispOrder)}
|
onChange={() => onChangeSelect(item.dispOrder)}
|
||||||
checked={!!selection.has(item.dispOrder)}
|
checked={!!selection.has(item.dispOrder)}
|
||||||
/>
|
/>
|
||||||
@ -2032,20 +1853,14 @@ 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)
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
menuPlacement={'auto'}
|
menuPlacement={'auto'}
|
||||||
getOptionLabel={(x) => x.itemName + ' (' + x.itemNo + ')'}
|
getOptionLabel={(x) => x.itemName}
|
||||||
getOptionValue={(x) => x.itemNo}
|
getOptionValue={(x) => x.itemNo}
|
||||||
components={{
|
|
||||||
SingleValue: ({ children, ...props }) => {
|
|
||||||
return <components.SingleValue {...props}>{props.data.itemName}</components.SingleValue>
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
isClearable={false}
|
isClearable={false}
|
||||||
isDisabled={!!item?.paDispOrder}
|
isDisabled={!!item?.paDispOrder}
|
||||||
value={displayItemList.filter(function (option) {
|
value={displayItemList.filter(function (option) {
|
||||||
@ -2065,17 +1880,12 @@ export default function Estimate({}) {
|
|||||||
placeholder="Select"
|
placeholder="Select"
|
||||||
options={cableItemList}
|
options={cableItemList}
|
||||||
menuPlacement={'auto'}
|
menuPlacement={'auto'}
|
||||||
getOptionLabel={(x) => x.clRefChr3 + ' (' + x.clRefChr1 + ')'}
|
getOptionLabel={(x) => x.clRefChr3}
|
||||||
getOptionValue={(x) => x.clRefChr1}
|
getOptionValue={(x) => x.clRefChr1}
|
||||||
components={{
|
|
||||||
SingleValue: ({ children, ...props }) => {
|
|
||||||
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.itemTpCd === 'S13' )? item.itemId : option.clRefChr1 === item.itemId
|
return option.clRefChr1 === item.itemId
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@ -2107,27 +1917,15 @@ export default function Estimate({}) {
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div className="input-wrap" style={{ width: '100%' }}>
|
<div className="input-wrap" style={{ width: '100%' }}>
|
||||||
{/*<input*/}
|
<input
|
||||||
{/* type="text"*/}
|
type="text"
|
||||||
{/* className="input-light al-r"*/}
|
className="input-light al-r"
|
||||||
{/* value={convertNumberToPriceDecimal(item?.amount?.replaceAll(',', ''))}*/}
|
|
||||||
{/* disabled={item.itemId === '' || !!item?.paDispOrder}*/}
|
|
||||||
{/* onChange={(e) => {*/}
|
|
||||||
{/* onChangeAmount(e.target.value, item.dispOrder, index)*/}
|
|
||||||
{/* }}*/}
|
|
||||||
{/* maxLength={6}*/}
|
|
||||||
{/*/>*/}
|
|
||||||
<CalculatorInput
|
|
||||||
className={"input-light al-r"}
|
|
||||||
value={convertNumberToPriceDecimal(item?.amount?.replaceAll(',', ''))}
|
value={convertNumberToPriceDecimal(item?.amount?.replaceAll(',', ''))}
|
||||||
disabled={item.itemId === '' || !!item?.paDispOrder}
|
disabled={item.itemId === '' || !!item?.paDispOrder}
|
||||||
onChange={(value) =>{
|
onChange={(e) => {
|
||||||
onChangeAmount(value, item.dispOrder, index)
|
onChangeAmount(e.target.value, item.dispOrder, index)
|
||||||
}}
|
|
||||||
options={{
|
|
||||||
allowNegative: false,
|
|
||||||
allowDecimal: false
|
|
||||||
}}
|
}}
|
||||||
|
maxLength={6}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
@ -2135,37 +1933,10 @@ export default function Estimate({}) {
|
|||||||
<td>
|
<td>
|
||||||
<div className="form-flex-wrap">
|
<div className="form-flex-wrap">
|
||||||
<div className="input-wrap mr5">
|
<div className="input-wrap mr5">
|
||||||
{/*<input*/}
|
<input
|
||||||
{/* type="text"*/}
|
type="text"
|
||||||
{/* className="input-light al-r"*/}
|
className="input-light al-r"
|
||||||
{/* value={*/}
|
value={convertNumberToPriceDecimal(item?.showSalePrice === '0' ? null : item?.salePrice?.replaceAll(',', ''))}
|
||||||
{/* item.openFlg === '1'*/}
|
|
||||||
{/* ? 'OPEN'*/}
|
|
||||||
{/* : convertNumberToPriceDecimal(item?.showSalePrice === '0' ? null : item?.salePrice?.replaceAll(',', ''))*/}
|
|
||||||
{/* }*/}
|
|
||||||
{/* disabled={*/}
|
|
||||||
{/* item.openFlg === '1'*/}
|
|
||||||
{/* ? true*/}
|
|
||||||
{/* : estimateContextState?.estimateType === 'YJSS'*/}
|
|
||||||
{/* ? item?.paDispOrder*/}
|
|
||||||
{/* ? true*/}
|
|
||||||
{/* : item.pkgMaterialFlg !== '1'*/}
|
|
||||||
{/* : item.itemId === '' || !!item?.paDispOrder*/}
|
|
||||||
{/* ? true*/}
|
|
||||||
{/* : item.openFlg === '1'*/}
|
|
||||||
{/* }*/}
|
|
||||||
{/* onChange={(e) => {*/}
|
|
||||||
{/* onChangeSalePrice(e.target.value, item.dispOrder, index)*/}
|
|
||||||
{/* }}*/}
|
|
||||||
{/* maxLength={12}*/}
|
|
||||||
{/*/>*/}
|
|
||||||
<CalculatorInput
|
|
||||||
className={"input-light al-r"}
|
|
||||||
value={
|
|
||||||
item.openFlg === '1'
|
|
||||||
? 'OPEN'
|
|
||||||
: convertNumberToPriceDecimal(item?.showSalePrice === '0' ? null : item?.salePrice?.replaceAll(',', ''))
|
|
||||||
}
|
|
||||||
disabled={
|
disabled={
|
||||||
item.openFlg === '1'
|
item.openFlg === '1'
|
||||||
? true
|
? true
|
||||||
@ -2176,15 +1947,13 @@ export default function Estimate({}) {
|
|||||||
: item.itemId === '' || !!item?.paDispOrder
|
: item.itemId === '' || !!item?.paDispOrder
|
||||||
? true
|
? true
|
||||||
: item.openFlg === '1'
|
: item.openFlg === '1'
|
||||||
|
? true
|
||||||
|
: false
|
||||||
}
|
}
|
||||||
onChange={(value) =>{
|
onChange={(e) => {
|
||||||
onChangeSalePrice(value, item.dispOrder, index)
|
onChangeSalePrice(e.target.value, item.dispOrder, index)
|
||||||
}}
|
}}
|
||||||
maxLength={12}
|
maxLength={12}
|
||||||
options={{
|
|
||||||
allowNegative: false,
|
|
||||||
allowDecimal: false
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{item.openFlg === '1' && (
|
{item.openFlg === '1' && (
|
||||||
@ -2195,9 +1964,7 @@ export default function Estimate({}) {
|
|||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td className="al-r">
|
<td className="al-r">
|
||||||
{item?.openFlg === '1'
|
{convertNumberToPriceDecimal(
|
||||||
? 'OPEN'
|
|
||||||
: convertNumberToPriceDecimal(
|
|
||||||
item?.showSaleTotPrice === '0'
|
item?.showSaleTotPrice === '0'
|
||||||
? null
|
? null
|
||||||
: item?.amount === ''
|
: item?.amount === ''
|
||||||
|
|||||||
@ -6,7 +6,6 @@ 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)
|
||||||
@ -31,7 +30,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'
|
||||||
@ -68,8 +67,6 @@ 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' }
|
||||||
@ -133,7 +130,7 @@ export default function DocDownOptionPop({ planNo, setEstimatePopupOpen, docDown
|
|||||||
<div className="common-table">
|
<div className="common-table">
|
||||||
<table>
|
<table>
|
||||||
<colgroup>
|
<colgroup>
|
||||||
<col style={{ width: '220px' }} />
|
<col style={{ width: '260px' }} />
|
||||||
<col />
|
<col />
|
||||||
</colgroup>
|
</colgroup>
|
||||||
<tbody>
|
<tbody>
|
||||||
@ -186,7 +183,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 mr10">
|
<div className="d-check-radio light ">
|
||||||
<input
|
<input
|
||||||
type="radio"
|
type="radio"
|
||||||
id="schUnitPricePdfFlg1"
|
id="schUnitPricePdfFlg1"
|
||||||
@ -200,20 +197,6 @@ 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,8 +1,6 @@
|
|||||||
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',
|
||||||
@ -16,11 +14,10 @@ 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(Number(point)?.toFixed(1)))
|
points = points.map((point) => 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) {
|
||||||
@ -34,16 +31,14 @@ 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 () {
|
||||||
@ -71,29 +66,31 @@ export const QLine = fabric.util.createClass(fabric.Line, {
|
|||||||
},
|
},
|
||||||
|
|
||||||
setLength() {
|
setLength() {
|
||||||
// Ensure all required properties are valid numbers
|
if (this.attributes?.actualSize !== undefined && this.attributes?.planeSize !== undefined) {
|
||||||
const { x1, y1, x2, y2 } = this
|
if (this.textMode === 'plane') {
|
||||||
if (isNaN(x1) || isNaN(y1) || isNaN(x2) || isNaN(y2)) {
|
this.length = this.attributes.planeSize / 10
|
||||||
logger.error('Invalid coordinates in QLine:', { x1, y1, x2, y2 })
|
} else if (this.textMode === 'actual') {
|
||||||
this.length = 0
|
this.length = this.attributes.actualSize / 10
|
||||||
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
|
|
||||||
},
|
|
||||||
|
|
||||||
setLengthByValue(length) {
|
|
||||||
this.length = length / 10
|
|
||||||
},
|
},
|
||||||
|
|
||||||
addLengthText() {
|
addLengthText() {
|
||||||
const thisText = this.canvas.getObjects().find((obj) => obj.name === 'lengthText' && obj.parentId === this.id)
|
const thisText = this.canvas.getObjects().find((obj) => obj.name === 'lengthText' && obj.parentId === this.id)
|
||||||
|
|
||||||
|
if (this.textMode === 'none') {
|
||||||
if (thisText) {
|
if (thisText) {
|
||||||
if (this.attributes?.actualSize) {
|
this.canvas.remove(thisText)
|
||||||
thisText.set({ actualSize: this.attributes.actualSize })
|
|
||||||
}
|
|
||||||
if (this.attributes?.planeSize) {
|
|
||||||
thisText.set({ planeSize: this.attributes.planeSize })
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.setLength()
|
this.setLength()
|
||||||
@ -104,6 +101,11 @@ export const QLine = fabric.util.createClass(fabric.Line, {
|
|||||||
const x2 = this.left + this.width * scaleX
|
const x2 = this.left + this.width * scaleX
|
||||||
const y2 = this.top + this.height * scaleY
|
const y2 = this.top + this.height * scaleY
|
||||||
|
|
||||||
|
if (thisText) {
|
||||||
|
thisText.set({ text: this.getLength().toString(), left: (x1 + x2) / 2, top: (y1 + y2) / 2 })
|
||||||
|
this.text = thisText
|
||||||
|
return
|
||||||
|
}
|
||||||
let left, top
|
let left, top
|
||||||
if (this.direction === 'left' || this.direction === 'right') {
|
if (this.direction === 'left' || this.direction === 'right') {
|
||||||
left = (x1 + x2) / 2
|
left = (x1 + x2) / 2
|
||||||
@ -120,8 +122,6 @@ export const QLine = fabric.util.createClass(fabric.Line, {
|
|||||||
const degree = (Math.atan2(y2 - y1, x2 - x1) * 180) / Math.PI
|
const degree = (Math.atan2(y2 - y1, x2 - x1) * 180) / Math.PI
|
||||||
|
|
||||||
const text = new fabric.Textbox(this.getLength().toString(), {
|
const text = new fabric.Textbox(this.getLength().toString(), {
|
||||||
actualSize: this.attributes?.actualSize,
|
|
||||||
planeSize: this.attributes?.planeSize,
|
|
||||||
left: left,
|
left: left,
|
||||||
top: top,
|
top: top,
|
||||||
fontSize: this.fontSize,
|
fontSize: this.fontSize,
|
||||||
@ -182,78 +182,4 @@ 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,11 +2,10 @@ 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, drawGableRoof, drawRoofByAttribute, drawShedRoof, toGeoJSON } from '@/util/qpolygon-utils'
|
import { calculateAngle, drawGabledRoof, 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',
|
||||||
@ -30,14 +29,12 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
|||||||
this.texts = []
|
this.texts = []
|
||||||
this.hips = []
|
this.hips = []
|
||||||
this.ridges = []
|
this.ridges = []
|
||||||
|
this.connectRidges = []
|
||||||
this.cells = []
|
this.cells = []
|
||||||
this.innerLines = []
|
this.innerLines = []
|
||||||
this.children = []
|
this.children = []
|
||||||
this.separatePolygon = []
|
this.separatePolygon = []
|
||||||
this.toFixed = options.toFixed ?? 1
|
this.toFixed = options.toFixed ?? 1
|
||||||
this.baseLines = []
|
|
||||||
this.adjustRoofLines = []
|
|
||||||
// this.colorLines = []
|
|
||||||
|
|
||||||
// 소수점 전부 제거
|
// 소수점 전부 제거
|
||||||
points.forEach((point) => {
|
points.forEach((point) => {
|
||||||
@ -48,11 +45,8 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
|||||||
options.sort = options.sort ?? true
|
options.sort = options.sort ?? true
|
||||||
options.parentId = options.parentId ?? null
|
options.parentId = options.parentId ?? null
|
||||||
|
|
||||||
this.isSortedPoints = false
|
|
||||||
|
|
||||||
if (!options.sort && points.length <= 8) {
|
if (!options.sort && points.length <= 8) {
|
||||||
points = sortedPointLessEightPoint(points)
|
points = sortedPointLessEightPoint(points)
|
||||||
this.isSortedPoints = true
|
|
||||||
} else {
|
} else {
|
||||||
let isDiagonal = false
|
let isDiagonal = false
|
||||||
points.forEach((point, i) => {
|
points.forEach((point, i) => {
|
||||||
@ -68,7 +62,6 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
|||||||
|
|
||||||
if (!isDiagonal) {
|
if (!isDiagonal) {
|
||||||
points = sortedPoints(points)
|
points = sortedPoints(points)
|
||||||
this.isSortedPoints = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,10 +82,6 @@ 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() {
|
||||||
@ -130,15 +119,11 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
|||||||
this.addLengthText()
|
this.addLengthText()
|
||||||
|
|
||||||
this.on('moving', () => {
|
this.on('moving', () => {
|
||||||
this.initLines()
|
|
||||||
this.addLengthText()
|
this.addLengthText()
|
||||||
this.setCoords()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
this.on('modified', () => {
|
this.on('modified', (e) => {
|
||||||
this.initLines()
|
|
||||||
this.addLengthText()
|
this.addLengthText()
|
||||||
this.setCoords()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
this.on('selected', () => {
|
this.on('selected', () => {
|
||||||
@ -181,27 +166,8 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
|||||||
return fabric.util.transformPoint(p, matrix)
|
return fabric.util.transformPoint(p, matrix)
|
||||||
})
|
})
|
||||||
this.points = transformedPoints
|
this.points = transformedPoints
|
||||||
|
const { left, top } = this.calcOriginCoords()
|
||||||
// 바운딩 박스 재계산 (width, height 업데이트 - fill 영역 수정)
|
this.set('pathOffset', { x: left, y: top })
|
||||||
const calcDim = this._calcDimensions({})
|
|
||||||
this.width = calcDim.width
|
|
||||||
this.height = calcDim.height
|
|
||||||
|
|
||||||
const newPathOffset = {
|
|
||||||
x: calcDim.left + this.width / 2,
|
|
||||||
y: calcDim.top + this.height / 2,
|
|
||||||
}
|
|
||||||
this.set('pathOffset', newPathOffset)
|
|
||||||
|
|
||||||
// 변환을 points에 적용했으므로 left, top, angle, scale 모두 리셋 (이중 변환 방지)
|
|
||||||
this.set({
|
|
||||||
left: newPathOffset.x,
|
|
||||||
top: newPathOffset.y,
|
|
||||||
angle: 0,
|
|
||||||
scaleX: 1,
|
|
||||||
scaleY: 1,
|
|
||||||
})
|
|
||||||
|
|
||||||
this.setCoords()
|
this.setCoords()
|
||||||
this.initLines()
|
this.initLines()
|
||||||
})
|
})
|
||||||
@ -217,8 +183,8 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
|||||||
|
|
||||||
this.lines = []
|
this.lines = []
|
||||||
|
|
||||||
this.getCurrentPoints().forEach((point, i) => {
|
this.points.forEach((point, i) => {
|
||||||
const nextPoint = this.getCurrentPoints()[(i + 1) % this.points.length]
|
const nextPoint = this.points[(i + 1) % this.points.length]
|
||||||
const line = new QLine([point.x, point.y, nextPoint.x, nextPoint.y], {
|
const line = new QLine([point.x, point.y, nextPoint.x, nextPoint.y], {
|
||||||
stroke: this.stroke,
|
stroke: this.stroke,
|
||||||
strokeWidth: this.strokeWidth,
|
strokeWidth: this.strokeWidth,
|
||||||
@ -237,50 +203,14 @@ 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) => {
|
|
||||||
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))
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 보조선 그리기
|
* 보조선 그리기
|
||||||
* @param settingModalFirstOptions
|
* @param settingModalFirstOptions
|
||||||
*/
|
*/
|
||||||
drawHelpLine(settingModalFirstOptions) {
|
drawHelpLine(settingModalFirstOptions) {
|
||||||
/* innerLines 초기화 */
|
|
||||||
this.canvas
|
|
||||||
.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.adjustRoofLines = []
|
|
||||||
this.canvas.renderAll()
|
|
||||||
|
|
||||||
let textMode = 'plane'
|
let textMode = 'plane'
|
||||||
|
|
||||||
const dimensionDisplay = settingModalFirstOptions?.dimensionDisplay.find((opt) => opt.selected).id
|
const dimensionDisplay = settingModalFirstOptions?.dimensionDisplay.find((opt) => opt.selected).id
|
||||||
@ -298,80 +228,61 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
const types = this.lines.map((line) => line.attributes.type)
|
const types = []
|
||||||
|
this.lines.forEach((line) => types.push(line.attributes.type))
|
||||||
|
|
||||||
const isGableRoof = function (types) {
|
const eavesType = [LINE_TYPE.WALLLINE.EAVES, LINE_TYPE.WALLLINE.HIPANDGABLE]
|
||||||
if (!types.includes(LINE_TYPE.WALLLINE.GABLE)) {
|
const gableType = [LINE_TYPE.WALLLINE.GABLE, LINE_TYPE.WALLLINE.JERKINHEAD]
|
||||||
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 oddAllEaves = oddTypes.every((type) => type === LINE_TYPE.WALLLINE.EAVES)
|
// const isEaves = types.every((type) => eavesType.includes(type))
|
||||||
const evenAllGable = evenTypes.every((type) => gableTypes.includes(type))
|
const gableOdd = types.filter((type, i) => i % 2 === 0)
|
||||||
const evenAllEaves = evenTypes.every((type) => type === LINE_TYPE.WALLLINE.EAVES)
|
const gableEven = types.filter((type, i) => i % 2 === 1)
|
||||||
const oddAllGable = oddTypes.every((type) => gableTypes.includes(type))
|
const hasShed = types.includes(LINE_TYPE.WALLLINE.SHED)
|
||||||
|
|
||||||
return (oddAllEaves && evenAllGable) || (evenAllEaves && oddAllGable)
|
|
||||||
}
|
|
||||||
|
|
||||||
const isShedRoof = function (types, lines) {
|
|
||||||
const gableTypes = [LINE_TYPE.WALLLINE.GABLE, LINE_TYPE.WALLLINE.JERKINHEAD]
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
const getParallelEavesLines = function (shedLines, lines) {
|
|
||||||
const referenceAngle = calculateAngle(shedLines[0].startPoint, shedLines[0].endPoint)
|
|
||||||
const otherSideLines = lines.filter((line) => {
|
|
||||||
const lineAngle = calculateAngle(line.startPoint, line.endPoint)
|
|
||||||
return Math.abs(referenceAngle - lineAngle) === 180
|
|
||||||
})
|
|
||||||
const containNotEaves = otherSideLines.filter((line) => line.attributes?.type !== LINE_TYPE.WALLLINE.EAVES)
|
|
||||||
|
|
||||||
if (containNotEaves.length === 0) {
|
|
||||||
return otherSideLines
|
|
||||||
} else {
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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('용마루 지붕')
|
|
||||||
drawSkeletonRidgeRoof(this.id, this.canvas, textMode)
|
|
||||||
} else if (isGableRoof(types)) {
|
|
||||||
// A형, B형 박공 지붕
|
// A형, B형 박공 지붕
|
||||||
// console.log('패턴 지붕')
|
if (
|
||||||
drawGableRoof(this.id, this.canvas, textMode)
|
(gableOdd.every((type) => type === LINE_TYPE.WALLLINE.EAVES) && gableEven.every((type) => gableType.includes(type))) ||
|
||||||
} else if (isShedRoof(types, this.lines)) {
|
(gableEven.every((type) => type === LINE_TYPE.WALLLINE.EAVES) && gableOdd.every((type) => gableType.includes(type)))
|
||||||
// console.log('한쪽흐름 지붕')
|
) {
|
||||||
|
drawGabledRoof(this.id, this.canvas, textMode)
|
||||||
|
} else if (hasShed) {
|
||||||
|
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 angle2 = calculateAngle(line2.startPoint, line2.endPoint)
|
||||||
|
return angle1 === angle2
|
||||||
|
}
|
||||||
|
|
||||||
|
let isShedRoof = true
|
||||||
|
sheds.forEach((shed, i) => {
|
||||||
|
isShedRoof = areLinesParallel(shed, sheds[(i + 1) % sheds.length])
|
||||||
|
})
|
||||||
|
if (isShedRoof) {
|
||||||
|
const eaves = this.lines
|
||||||
|
.filter((line) => line.attributes !== undefined && line.attributes.type === LINE_TYPE.WALLLINE.EAVES)
|
||||||
|
.filter((line) => {
|
||||||
|
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)
|
drawShedRoof(this.id, this.canvas, textMode)
|
||||||
} else {
|
} else {
|
||||||
// console.log('변별로 설정')
|
drawRidgeRoof(this.id, this.canvas, textMode)
|
||||||
drawRoofByAttribute(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)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -400,31 +311,13 @@ 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(left, top)
|
midPoint = new fabric.Point((start.x + end.x) / 2, (start.y + end.y) / 2)
|
||||||
|
|
||||||
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()
|
||||||
|
|
||||||
// Create a new text object if it doesn't exist
|
// Create new text object if it doesn't exist
|
||||||
const text = new fabric.Text(length.toString(), {
|
const text = new fabric.Text(length.toString(), {
|
||||||
left: midPoint.x,
|
left: midPoint.x,
|
||||||
top: midPoint.y,
|
top: midPoint.y,
|
||||||
@ -473,15 +366,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
|||||||
this.canvas = canvas
|
this.canvas = canvas
|
||||||
},
|
},
|
||||||
fillCellABType(
|
fillCellABType(
|
||||||
cell = {
|
cell = { width: 50, height: 100, padding: 5, wallDirection: 'left', referenceDirection: 'none', startIndex: -1, isCellCenter: false },
|
||||||
width: 50,
|
|
||||||
height: 100,
|
|
||||||
padding: 5,
|
|
||||||
wallDirection: 'left',
|
|
||||||
referenceDirection: 'none',
|
|
||||||
startIndex: -1,
|
|
||||||
isCellCenter: false,
|
|
||||||
},
|
|
||||||
) {
|
) {
|
||||||
const points = this.points
|
const points = this.points
|
||||||
|
|
||||||
@ -783,115 +668,13 @@ 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만 역변환 (캔버스 줌/팬 보정)
|
|
||||||
// 결과는 WORLD 좌표 (캔버스 좌표계)
|
|
||||||
let canvasPoint = point
|
|
||||||
if (this.canvas) {
|
|
||||||
const vpt = this.canvas.viewportTransform
|
|
||||||
if (vpt) {
|
|
||||||
const inverted = fabric.util.invertTransform(vpt)
|
|
||||||
canvasPoint = fabric.util.transformPoint(point, inverted)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// canvasPoint는 WORLD 좌표
|
|
||||||
// inPolygonImproved에서 getCurrentPoints()도 WORLD 좌표를 반환
|
|
||||||
// 따라서 좌표 시스템이 일치함
|
|
||||||
const checkPoint = {
|
|
||||||
x: Number(canvasPoint.x.toFixed(this.toFixed)),
|
|
||||||
y: Number(canvasPoint.y.toFixed(this.toFixed)),
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.name === POLYGON_TYPE.ROOF && this.isFixed) {
|
if (this.name === POLYGON_TYPE.ROOF && this.isFixed) {
|
||||||
const isInside = this.inPolygonImproved(checkPoint)
|
const isInside = this.inPolygon(point)
|
||||||
if (!this.selectable) {
|
|
||||||
this.set('selectable', isInside)
|
this.set('selectable', isInside)
|
||||||
}
|
|
||||||
return isInside
|
return isInside
|
||||||
} else {
|
} else {
|
||||||
return this.inPolygonImproved(checkPoint)
|
return this.callSuper('containsPoint', point)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@ -11,9 +11,9 @@ import { useCanvas } from '@/hooks/useCanvas'
|
|||||||
import { usePlan } from '@/hooks/usePlan'
|
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 { canvasZoomState, currentMenuState } from '@/store/canvasAtom'
|
import { currentMenuState } from '@/store/canvasAtom'
|
||||||
import { totalDisplaySelector } from '@/store/settingAtom'
|
import { totalDisplaySelector } from '@/store/settingAtom'
|
||||||
import { POLYGON_TYPE } from '@/common/common'
|
import { MENU, 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,15 +30,11 @@ import { useCanvasSetting } from '@/hooks/option/useCanvasSetting'
|
|||||||
import { useCanvasMenu } from '@/hooks/common/useCanvasMenu'
|
import { useCanvasMenu } from '@/hooks/common/useCanvasMenu'
|
||||||
import { useEvent } from '@/hooks/useEvent'
|
import { useEvent } from '@/hooks/useEvent'
|
||||||
import { compasDegAtom } from '@/store/orientationAtom'
|
import { compasDegAtom } from '@/store/orientationAtom'
|
||||||
import { hotkeyStore } from '@/store/hotkeyAtom'
|
|
||||||
import { usePopup } from '@/hooks/usePopup'
|
|
||||||
import { outerLinePointsState } from '@/store/outerLineAtom'
|
|
||||||
|
|
||||||
export default function CanvasFrame() {
|
export default function CanvasFrame() {
|
||||||
const canvasRef = useRef(null)
|
const canvasRef = useRef(null)
|
||||||
const { canvas } = useCanvas('canvas')
|
const { canvas } = useCanvas('canvas')
|
||||||
const { canvasLoadInit, gridInit } = useCanvasConfigInitialize()
|
const { canvasLoadInit, gridInit } = useCanvasConfigInitialize()
|
||||||
const { closeAll } = usePopup()
|
|
||||||
const currentMenu = useRecoilValue(currentMenuState)
|
const currentMenu = useRecoilValue(currentMenuState)
|
||||||
const { floorPlanState } = useContext(FloorPlanContext)
|
const { floorPlanState } = useContext(FloorPlanContext)
|
||||||
const { contextMenu, handleClick } = useContextMenu()
|
const { contextMenu, handleClick } = useContextMenu()
|
||||||
@ -46,13 +42,11 @@ export default function CanvasFrame() {
|
|||||||
const totalDisplay = useRecoilValue(totalDisplaySelector) // 집계표 표시 여부
|
const totalDisplay = useRecoilValue(totalDisplaySelector) // 집계표 표시 여부
|
||||||
const { setIsGlobalLoading } = useContext(QcastContext)
|
const { setIsGlobalLoading } = useContext(QcastContext)
|
||||||
const resetModuleStatisticsState = useResetRecoilState(moduleStatisticsState)
|
const resetModuleStatisticsState = useResetRecoilState(moduleStatisticsState)
|
||||||
const resetOuterLinePoints = useResetRecoilState(outerLinePointsState)
|
|
||||||
const resetMakersState = useResetRecoilState(makersState)
|
const resetMakersState = useResetRecoilState(makersState)
|
||||||
const resetSelectedMakerState = useResetRecoilState(selectedMakerState)
|
const resetSelectedMakerState = useResetRecoilState(selectedMakerState)
|
||||||
const resetSeriesState = useResetRecoilState(seriesState)
|
const resetSeriesState = useResetRecoilState(seriesState)
|
||||||
const resetModelsState = useResetRecoilState(modelsState)
|
const resetModelsState = useResetRecoilState(modelsState)
|
||||||
const resetCompasDeg = useResetRecoilState(compasDegAtom)
|
const resetCompasDeg = useResetRecoilState(compasDegAtom)
|
||||||
const [zoom, setCanvasZoom] = useRecoilState(canvasZoomState)
|
|
||||||
const resetSelectedModelsState = useResetRecoilState(selectedModelsState)
|
const resetSelectedModelsState = useResetRecoilState(selectedModelsState)
|
||||||
const resetPcsCheckState = useResetRecoilState(pcsCheckState)
|
const resetPcsCheckState = useResetRecoilState(pcsCheckState)
|
||||||
const { handleModuleSelectionTotal } = useCanvasPopupStatusController()
|
const { handleModuleSelectionTotal } = useCanvasPopupStatusController()
|
||||||
@ -70,45 +64,15 @@ export default function CanvasFrame() {
|
|||||||
canvasLoadInit() //config된 상태로 캔버스 객체를 그린다
|
canvasLoadInit() //config된 상태로 캔버스 객체를 그린다
|
||||||
canvas?.renderAll() // 캔버스를 다시 그립니다.
|
canvas?.renderAll() // 캔버스를 다시 그립니다.
|
||||||
|
|
||||||
if (canvas.viewportTransform) {
|
|
||||||
if (canvas.viewportTransform[0] !== 1) {
|
|
||||||
setCanvasZoom(Number((canvas.viewportTransform[0] * 100).toFixed(0)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
canvas.originViewPortTransform = canvas.viewportTransform
|
|
||||||
|
|
||||||
if (canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE).length > 0) {
|
if (canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE).length > 0) {
|
||||||
setTimeout(() => {
|
|
||||||
setSelectedMenu('module')
|
setSelectedMenu('module')
|
||||||
}, 500)
|
|
||||||
} else if (canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.WALL).length > 0) {
|
|
||||||
setSelectedMenu('outline')
|
|
||||||
} else {
|
} else {
|
||||||
setTimeout(() => {
|
|
||||||
setSelectedMenu('surface')
|
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 {
|
|
||||||
setSelectedMenu(null)
|
|
||||||
}
|
}
|
||||||
Object.keys(currentCanvasPlan).length > 0 && canvas && handleModuleSelectionTotal()
|
Object.keys(currentCanvasPlan).length > 0 && canvas && handleModuleSelectionTotal()
|
||||||
} else {
|
|
||||||
setSelectedMenu(null)
|
|
||||||
}
|
}
|
||||||
gridInit()
|
gridInit()
|
||||||
}
|
}
|
||||||
@ -128,8 +92,6 @@ export default function CanvasFrame() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setIsGlobalLoading(false)
|
setIsGlobalLoading(false)
|
||||||
// 혹시 모를 팝업이 떠있는 경우 닫고 시작한다.
|
|
||||||
closeAll()
|
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
canvas?.clear()
|
canvas?.clear()
|
||||||
@ -139,7 +101,6 @@ export default function CanvasFrame() {
|
|||||||
|
|
||||||
const resetRecoilData = () => {
|
const resetRecoilData = () => {
|
||||||
// resetModuleStatisticsState()
|
// resetModuleStatisticsState()
|
||||||
resetOuterLinePoints()
|
|
||||||
resetMakersState()
|
resetMakersState()
|
||||||
resetSelectedMakerState()
|
resetSelectedMakerState()
|
||||||
resetSeriesState()
|
resetSeriesState()
|
||||||
@ -149,38 +110,6 @@ export default function CanvasFrame() {
|
|||||||
resetPcsCheckState()
|
resetPcsCheckState()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 캔버스가 있을 경우 핫키 이벤트 처리
|
|
||||||
* hotkeyStore에 핫키 이벤트 리스너 추가
|
|
||||||
*
|
|
||||||
* const hotkeys = [
|
|
||||||
{ key: 'c', fn: () => asdf() },
|
|
||||||
{ key: 'v', fn: () => qwer() },
|
|
||||||
]
|
|
||||||
setHotkeyStore(hotkeys)
|
|
||||||
*/
|
|
||||||
const hotkeyState = useRecoilValue(hotkeyStore)
|
|
||||||
const hotkeyHandlerRef = useRef(null)
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
hotkeyHandlerRef.current = (e) => {
|
|
||||||
hotkeyState.forEach((hotkey) => {
|
|
||||||
if (e.key === hotkey.key) {
|
|
||||||
hotkey.fn()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
document.addEventListener('keyup', hotkeyHandlerRef.current)
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
if (hotkeyHandlerRef.current) {
|
|
||||||
document.removeEventListener('keyup', hotkeyHandlerRef.current)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [hotkeyState])
|
|
||||||
/** 핫키 이벤트 처리 끝 */
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="canvas-frame">
|
<div className="canvas-frame">
|
||||||
<canvas ref={canvasRef} id="canvas" style={{ position: 'relative' }}></canvas>
|
<canvas ref={canvasRef} id="canvas" style={{ position: 'relative' }}></canvas>
|
||||||
|
|||||||
@ -31,13 +31,12 @@ export default function CanvasLayout({ children }) {
|
|||||||
return (
|
return (
|
||||||
<div className="canvas-layout">
|
<div className="canvas-layout">
|
||||||
<div className={`canvas-page-list ${['outline', 'surface', 'module'].includes(selectedMenu) ? 'active' : ''}`}>
|
<div className={`canvas-page-list ${['outline', 'surface', 'module'].includes(selectedMenu) ? 'active' : ''}`}>
|
||||||
<div className="canvas-id">{objectNo}</div>
|
|
||||||
<div className="canvas-plane-wrap">
|
<div className="canvas-plane-wrap">
|
||||||
{plans.map((plan, index) => (
|
{plans.map((plan, index) => (
|
||||||
<button
|
<button
|
||||||
key={`plan-${plan.id}`}
|
key={`plan-${plan.id}`}
|
||||||
className={`canvas-page-box ${plan.isCurrent === true ? 'on' : ''}`}
|
className={`canvas-page-box ${plan.isCurrent === true ? 'on' : ''}`}
|
||||||
onClick={() => (plan.isCurrent ? '' : handleCurrentPlan(plan.id))}
|
onClick={() => handleCurrentPlan(plan.id)}
|
||||||
>
|
>
|
||||||
<span>{`Plan ${plan.planNo}`}</span>
|
<span>{`Plan ${plan.planNo}`}</span>
|
||||||
{plans.length > 1 && !pathname.includes('/estimate') && !pathname.includes('/simulator') && (
|
{plans.length > 1 && !pathname.includes('/estimate') && !pathname.includes('/simulator') && (
|
||||||
|
|||||||
@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
import { useContext, useEffect, useState } from 'react'
|
import { useContext, useEffect, useState } from 'react'
|
||||||
|
|
||||||
import { usePathname, useRouter } from 'next/navigation'
|
import { usePathname, useRouter, useSearchParams } from 'next/navigation'
|
||||||
|
|
||||||
import { useRecoilState, useRecoilValue, useResetRecoilState, useSetRecoilState } from 'recoil'
|
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
|
||||||
|
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
|
|
||||||
@ -25,18 +25,17 @@ import { useCommonUtils } from '@/hooks/common/useCommonUtils'
|
|||||||
import useMenu from '@/hooks/common/useMenu'
|
import useMenu from '@/hooks/common/useMenu'
|
||||||
import { useEstimateController } from '@/hooks/floorPlan/estimate/useEstimateController'
|
import { useEstimateController } from '@/hooks/floorPlan/estimate/useEstimateController'
|
||||||
import { useAxios } from '@/hooks/useAxios'
|
import { useAxios } from '@/hooks/useAxios'
|
||||||
import {
|
import { canvasSettingState, canvasState, canvasZoomState, currentMenuState, verticalHorizontalModeState } from '@/store/canvasAtom'
|
||||||
canvasSettingState,
|
|
||||||
canvasState,
|
|
||||||
canvasZoomState,
|
|
||||||
currentCanvasPlanState,
|
|
||||||
currentMenuState,
|
|
||||||
verticalHorizontalModeState,
|
|
||||||
} from '@/store/canvasAtom'
|
|
||||||
import { sessionStore } from '@/store/commonAtom'
|
import { sessionStore } from '@/store/commonAtom'
|
||||||
import { outerLinePointsState } from '@/store/outerLineAtom'
|
import { outerLinePointsState } from '@/store/outerLineAtom'
|
||||||
import { appMessageStore, globalLocaleStore } from '@/store/localeAtom'
|
import { appMessageStore, globalLocaleStore } from '@/store/localeAtom'
|
||||||
import { addedRoofsState, basicSettingState, selectedRoofMaterialSelector, settingModalFirstOptionsState } from '@/store/settingAtom'
|
import {
|
||||||
|
addedRoofsState,
|
||||||
|
basicSettingState,
|
||||||
|
corridorDimensionSelector,
|
||||||
|
selectedRoofMaterialSelector,
|
||||||
|
settingModalFirstOptionsState,
|
||||||
|
} from '@/store/settingAtom'
|
||||||
import { placementShapeDrawingPointsState } from '@/store/placementShapeDrawingAtom'
|
import { placementShapeDrawingPointsState } from '@/store/placementShapeDrawingAtom'
|
||||||
import { commonUtilsState } from '@/store/commonUtilsAtom'
|
import { commonUtilsState } from '@/store/commonUtilsAtom'
|
||||||
import { menusState } from '@/store/menuAtom'
|
import { menusState } from '@/store/menuAtom'
|
||||||
@ -51,10 +50,7 @@ import JA from '@/locales/ja.json'
|
|||||||
import { QcastContext } from '@/app/QcastProvider'
|
import { QcastContext } from '@/app/QcastProvider'
|
||||||
import { useRoofFn } from '@/hooks/common/useRoofFn'
|
import { useRoofFn } from '@/hooks/common/useRoofFn'
|
||||||
import { usePolygon } from '@/hooks/usePolygon'
|
import { usePolygon } from '@/hooks/usePolygon'
|
||||||
import { useTrestle } from '@/hooks/module/useTrestle'
|
|
||||||
|
|
||||||
export default function CanvasMenu(props) {
|
export default function CanvasMenu(props) {
|
||||||
const [currentCanvasPlan, setCurrentCanvasPlan] = useRecoilState(currentCanvasPlanState)
|
|
||||||
const { selectedMenu, setSelectedMenu } = props
|
const { selectedMenu, setSelectedMenu } = props
|
||||||
const pathname = usePathname()
|
const pathname = usePathname()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@ -71,7 +67,6 @@ export default function CanvasMenu(props) {
|
|||||||
const globalLocale = useRecoilValue(globalLocaleStore)
|
const globalLocale = useRecoilValue(globalLocaleStore)
|
||||||
const canvas = useRecoilValue(canvasState)
|
const canvas = useRecoilValue(canvasState)
|
||||||
const { handleZoomClear, handleZoom } = useCanvasEvent()
|
const { handleZoomClear, handleZoom } = useCanvasEvent()
|
||||||
const { setAllModuleSurfaceIsComplete, isAllComplete } = useTrestle()
|
|
||||||
const { handleMenu } = useMenu()
|
const { handleMenu } = useMenu()
|
||||||
// const urlParams = useSearchParams()
|
// const urlParams = useSearchParams()
|
||||||
const { handleEstimateSubmit, fetchSetting, estimateContextState, setEstimateContextState } = useEstimateController()
|
const { handleEstimateSubmit, fetchSetting, estimateContextState, setEstimateContextState } = useEstimateController()
|
||||||
@ -101,7 +96,7 @@ export default function CanvasMenu(props) {
|
|||||||
const [lockButtonStyle, setLockButtonStyle] = useState('') //잠금 버튼
|
const [lockButtonStyle, setLockButtonStyle] = useState('') //잠금 버튼
|
||||||
|
|
||||||
const setFloorPlanObjectNo = useSetRecoilState(floorPlanObjectState) //견적서 화면용 물건번호리코일
|
const setFloorPlanObjectNo = useSetRecoilState(floorPlanObjectState) //견적서 화면용 물건번호리코일
|
||||||
const resetCommonUtils = useResetRecoilState(commonUtilsState)
|
|
||||||
// 발전시뮬레이션 메뉴 이동
|
// 발전시뮬레이션 메뉴 이동
|
||||||
const { objectNo, pid } = floorPlanState
|
const { objectNo, pid } = floorPlanState
|
||||||
|
|
||||||
@ -120,7 +115,7 @@ export default function CanvasMenu(props) {
|
|||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
objectNo: objectNo,
|
objectNo: objectNo,
|
||||||
planNo: selectedPlan?.planNo ? selectedPlan.planNo : pid,
|
planNo: selectedPlan.planNo,
|
||||||
schDownload: donwloadType,
|
schDownload: donwloadType,
|
||||||
schDrawingFlg: drawingFlg,
|
schDrawingFlg: drawingFlg,
|
||||||
pwrGnrSimType: pwrGnrSimTypeRecoil.type,
|
pwrGnrSimType: pwrGnrSimTypeRecoil.type,
|
||||||
@ -169,7 +164,6 @@ export default function CanvasMenu(props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const onClickNav = async (menu) => {
|
const onClickNav = async (menu) => {
|
||||||
resetCommonUtils()
|
|
||||||
switch (menu.type) {
|
switch (menu.type) {
|
||||||
case 'drawing':
|
case 'drawing':
|
||||||
swalFire({
|
swalFire({
|
||||||
@ -198,17 +192,8 @@ 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)
|
|
||||||
const moduleSurfacesArray = canvas
|
const moduleSurfacesArray = canvas
|
||||||
.getObjects()
|
.getObjects()
|
||||||
.filter((obj) => [POLYGON_TYPE.MODULE_SETUP_SURFACE, POLYGON_TYPE.MODULE, POLYGON_TYPE.OBJECT_SURFACE].includes(obj.name))
|
.filter((obj) => [POLYGON_TYPE.MODULE_SETUP_SURFACE, POLYGON_TYPE.MODULE, POLYGON_TYPE.OBJECT_SURFACE].includes(obj.name))
|
||||||
@ -245,18 +230,11 @@ export default function CanvasMenu(props) {
|
|||||||
router.push(`/floor-plan?pid=${pid}&objectNo=${objectNo}`)
|
router.push(`/floor-plan?pid=${pid}&objectNo=${objectNo}`)
|
||||||
setSelectedMenu('module')
|
setSelectedMenu('module')
|
||||||
}
|
}
|
||||||
await reloadCanvasStatus(objectNo, currentCanvasPlan?.planNo ?? pid)
|
await reloadCanvasStatus(objectNo, pid)
|
||||||
break
|
break
|
||||||
case 'estimate':
|
case 'estimate':
|
||||||
if (selectedMenu !== 'simulation') {
|
|
||||||
if (!isAllComplete()) {
|
|
||||||
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}/detail` }).then((res) => {
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
const estimateDetail = res.data
|
const estimateDetail = res.data
|
||||||
if (estimateDetail.estimateDate !== null) {
|
if (estimateDetail.estimateDate !== null) {
|
||||||
@ -264,7 +242,7 @@ export default function CanvasMenu(props) {
|
|||||||
setCurrentMenu(menu.title)
|
setCurrentMenu(menu.title)
|
||||||
setFloorPlanObjectNo({ floorPlanObjectNo: objectNo })
|
setFloorPlanObjectNo({ floorPlanObjectNo: objectNo })
|
||||||
setIsGlobalLoading(false)
|
setIsGlobalLoading(false)
|
||||||
router.push(`/floor-plan/estimate/5?pid=${selectedPlan?.planNo ?? pid}&objectNo=${objectNo}`)
|
router.push(`/floor-plan/estimate/5?pid=${selectedPlan.planNo}&objectNo=${objectNo}`)
|
||||||
if (pathname === '/floor-plan/estimate/5') {
|
if (pathname === '/floor-plan/estimate/5') {
|
||||||
setIsGlobalLoading(false)
|
setIsGlobalLoading(false)
|
||||||
}
|
}
|
||||||
@ -277,13 +255,13 @@ export default function CanvasMenu(props) {
|
|||||||
break
|
break
|
||||||
case 'simulation':
|
case 'simulation':
|
||||||
setIsGlobalLoading(true)
|
setIsGlobalLoading(true)
|
||||||
promiseGet({ url: `/api/estimate/${objectNo}/${selectedPlan?.planNo ?? pid}/detail` }).then((res) => {
|
promiseGet({ url: `/api/estimate/${objectNo}/${selectedPlan.planNo}/detail` }).then((res) => {
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
const estimateDetail = res.data
|
const estimateDetail = res.data
|
||||||
if (estimateDetail.estimateDate !== null && estimateDetail.docNo) {
|
if (estimateDetail.estimateDate !== null && estimateDetail.docNo) {
|
||||||
setSelectedMenu(menu.type)
|
setSelectedMenu(menu.type)
|
||||||
setCurrentMenu(menu.title)
|
setCurrentMenu(menu.title)
|
||||||
router.push(`/floor-plan/simulator/6?pid=${selectedPlan?.planNo ?? pid}&objectNo=${objectNo}`)
|
router.push(`/floor-plan/simulator/6?pid=${selectedPlan.planNo}&objectNo=${objectNo}`)
|
||||||
if (pathname === '/floor-plan/simulator/6') {
|
if (pathname === '/floor-plan/simulator/6') {
|
||||||
setIsGlobalLoading(false)
|
setIsGlobalLoading(false)
|
||||||
}
|
}
|
||||||
@ -325,6 +303,7 @@ 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()
|
||||||
}
|
}
|
||||||
@ -517,10 +496,7 @@ export default function CanvasMenu(props) {
|
|||||||
if (createUser === 'T01' && sessionState.storeId !== 'T01') {
|
if (createUser === 'T01' && sessionState.storeId !== 'T01') {
|
||||||
setAllButtonStyles('none')
|
setAllButtonStyles('none')
|
||||||
} else {
|
} else {
|
||||||
setEstimateContextState({
|
setEstimateContextState({ tempFlg: estimateRecoilState.tempFlg, lockFlg: estimateRecoilState.lockFlg })
|
||||||
tempFlg: estimateRecoilState.tempFlg,
|
|
||||||
lockFlg: estimateRecoilState.lockFlg,
|
|
||||||
})
|
|
||||||
handleButtonStyles(estimateRecoilState.tempFlg, estimateRecoilState.lockFlg, estimateContextState.docNo)
|
handleButtonStyles(estimateRecoilState.tempFlg, estimateRecoilState.lockFlg, estimateContextState.docNo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -572,26 +548,13 @@ export default function CanvasMenu(props) {
|
|||||||
{
|
{
|
||||||
<div className={`vertical-horizontal ${verticalHorizontalMode ? 'on' : ''}`}>
|
<div className={`vertical-horizontal ${verticalHorizontalMode ? 'on' : ''}`}>
|
||||||
<span>{getMessage('plan.mode.vertical.horizontal')}</span>
|
<span>{getMessage('plan.mode.vertical.horizontal')}</span>
|
||||||
<button
|
<button onClick={() => setVerticalHorizontalMode(!verticalHorizontalMode)}>{verticalHorizontalMode ? 'ON' : 'OFF'}</button>
|
||||||
title={`${getMessage('plan.mode.vertical.horizontal')} ${verticalHorizontalMode ? 'ON' : 'OFF'}`}
|
|
||||||
onClick={() => setVerticalHorizontalMode(!verticalHorizontalMode)}
|
|
||||||
>
|
|
||||||
{verticalHorizontalMode ? 'ON' : 'OFF'}
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
<div className="btn-from">
|
<div className="btn-from">
|
||||||
<button className={`btn01 ${commonUtils.text ? 'active' : ''}`} onClick={() => commonFunctions('text')} title="文字作成"></button>
|
<button className={`btn01 ${commonUtils.text ? 'active' : ''}`} onClick={() => commonFunctions('text')}></button>
|
||||||
<button
|
<button className={`btn02 ${commonUtils.dimension ? 'active' : ''} `} onClick={() => commonFunctions('dimension')}></button>
|
||||||
className={`btn02 ${commonUtils.dimension ? 'active' : ''} `}
|
<button className={`btn03 ${commonUtils.distance ? 'active' : ''} `} onClick={() => commonFunctions('distance')}></button>
|
||||||
onClick={() => commonFunctions('dimension')}
|
|
||||||
title="寸法作成"
|
|
||||||
></button>
|
|
||||||
<button
|
|
||||||
className={`btn03 ${commonUtils.distance ? 'active' : ''} `}
|
|
||||||
onClick={() => commonFunctions('distance')}
|
|
||||||
title="定規"
|
|
||||||
></button>
|
|
||||||
</div>
|
</div>
|
||||||
{isObjectNotEmpty(selectedRoofMaterial) && addedRoofs.length > 0 && (
|
{isObjectNotEmpty(selectedRoofMaterial) && addedRoofs.length > 0 && (
|
||||||
<div className="select-box">
|
<div className="select-box">
|
||||||
@ -617,7 +580,6 @@ export default function CanvasMenu(props) {
|
|||||||
sourceKey={'index'}
|
sourceKey={'index'}
|
||||||
targetKey={'index'}
|
targetKey={'index'}
|
||||||
disabled={+basicSetting.roofSizeSet === 3}
|
disabled={+basicSetting.roofSizeSet === 3}
|
||||||
tagTitle={'屋根材変更'}
|
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
@ -626,10 +588,9 @@ export default function CanvasMenu(props) {
|
|||||||
<button
|
<button
|
||||||
className={`btn10 ${floorPlanState.refFileModalOpen && 'active'}`}
|
className={`btn10 ${floorPlanState.refFileModalOpen && 'active'}`}
|
||||||
onClick={() => setFloorPlanState({ ...floorPlanState, refFileModalOpen: true })}
|
onClick={() => setFloorPlanState({ ...floorPlanState, refFileModalOpen: true })}
|
||||||
title="読込"
|
|
||||||
></button>
|
></button>
|
||||||
{/*<button className="btn04" onClick={() => setShowCanvasSettingModal(true)}></button>*/}
|
{/*<button className="btn04" onClick={() => setShowCanvasSettingModal(true)}></button>*/}
|
||||||
<button className="btn04" onClick={handlePopup} title="設定"></button>
|
<button className="btn04" onClick={handlePopup}></button>
|
||||||
</div>
|
</div>
|
||||||
<div className="size-control">
|
<div className="size-control">
|
||||||
<button
|
<button
|
||||||
@ -638,9 +599,7 @@ export default function CanvasMenu(props) {
|
|||||||
handleZoom(false)
|
handleZoom(false)
|
||||||
}}
|
}}
|
||||||
></button>
|
></button>
|
||||||
<span onClick={handleZoomClear} title="拡大・縮小">
|
<span onClick={handleZoomClear}>{canvasZoom}%</span>
|
||||||
{canvasZoom}%
|
|
||||||
</span>
|
|
||||||
<button
|
<button
|
||||||
className="control-btn plus"
|
className="control-btn plus"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@ -649,8 +608,8 @@ export default function CanvasMenu(props) {
|
|||||||
></button>
|
></button>
|
||||||
</div>
|
</div>
|
||||||
<div className="btn-from">
|
<div className="btn-from">
|
||||||
<button className="btn08" onClick={handleSaveCanvas} title="保存"></button>
|
<button className="btn08" onClick={handleSaveCanvas}></button>
|
||||||
<button className="btn09" onClick={handleLeaveCanvas} title="物件検索画面へ移動"></button>
|
<button className="btn09" onClick={handleLeaveCanvas}></button>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
@ -675,7 +634,7 @@ export default function CanvasMenu(props) {
|
|||||||
onClick={() => setEstimatePopupOpen(true)}
|
onClick={() => setEstimatePopupOpen(true)}
|
||||||
>
|
>
|
||||||
<span className="ico ico01"></span>
|
<span className="ico ico01"></span>
|
||||||
<span className="name">{getMessage('plan.menu.estimate.docDownload')}</span>
|
<span className="name">{getMessage('plan.menu.estimate.docDown')}</span>
|
||||||
</button>
|
</button>
|
||||||
<button type="button" style={{ display: saveButtonStyle }} className="btn-frame gray ico-flx" onClick={handleEstimateSubmit}>
|
<button type="button" style={{ display: saveButtonStyle }} className="btn-frame gray ico-flx" onClick={handleEstimateSubmit}>
|
||||||
<span className="ico ico02"></span>
|
<span className="ico ico02"></span>
|
||||||
|
|||||||
@ -1,19 +1,15 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { useContext, useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import CanvasMenu from '@/components/floor-plan/CanvasMenu'
|
import CanvasMenu from '@/components/floor-plan/CanvasMenu'
|
||||||
import { useCanvasMenu } from '@/hooks/common/useCanvasMenu'
|
import { useCanvasMenu } from '@/hooks/common/useCanvasMenu'
|
||||||
import { useCanvasSetting } from '@/hooks/option/useCanvasSetting'
|
import { useCanvasSetting } from '@/hooks/option/useCanvasSetting'
|
||||||
import { usePopup } from '@/hooks/usePopup'
|
import { usePopup } from '@/hooks/usePopup'
|
||||||
import '@/styles/contents.scss'
|
import '@/styles/contents.scss'
|
||||||
import { notFound, useSearchParams } from 'next/navigation'
|
import { notFound, useSearchParams } from 'next/navigation'
|
||||||
import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil'
|
import { useRecoilState, useResetRecoilState } from 'recoil'
|
||||||
import { correntObjectNoState } from '@/store/settingAtom'
|
import { correntObjectNoState } from '@/store/settingAtom'
|
||||||
import { currentMenuState } from '@/store/canvasAtom'
|
import { currentMenuState } from '@/store/canvasAtom'
|
||||||
import { globalLocaleStore } from '@/store/localeAtom'
|
|
||||||
import { useAxios } from '@/hooks/useAxios'
|
|
||||||
import { GlobalDataContext } from '@/app/GlobalDataProvider'
|
|
||||||
import { sessionStore } from '@/store/commonAtom'
|
|
||||||
|
|
||||||
export default function FloorPlan({ children }) {
|
export default function FloorPlan({ children }) {
|
||||||
const [correntObjectNo, setCurrentObjectNo] = useRecoilState(correntObjectNoState)
|
const [correntObjectNo, setCurrentObjectNo] = useRecoilState(correntObjectNoState)
|
||||||
@ -24,46 +20,12 @@ export default function FloorPlan({ children }) {
|
|||||||
const { selectedMenu, setSelectedMenu } = useCanvasMenu()
|
const { selectedMenu, setSelectedMenu } = useCanvasMenu()
|
||||||
const { fetchSettings } = useCanvasSetting()
|
const { fetchSettings } = useCanvasSetting()
|
||||||
const resetCurrentMenu = useResetRecoilState(currentMenuState)
|
const resetCurrentMenu = useResetRecoilState(currentMenuState)
|
||||||
const globalLocaleState = useRecoilValue(globalLocaleStore)
|
|
||||||
const { promiseGet } = useAxios(globalLocaleState)
|
|
||||||
const { setManagementState } = useContext(GlobalDataContext)
|
|
||||||
const [sessionState, setSessionState] = useRecoilState(sessionStore)
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getStuffDetailInfo()
|
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
resetCurrentMenu()
|
resetCurrentMenu()
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const getStuffDetailInfo = () => {
|
|
||||||
promiseGet({ url: `/api/object/${objectNo}/detail` }).then((res) => {
|
|
||||||
if (res.status === 200) {
|
|
||||||
const { data } = res
|
|
||||||
console.log(data)
|
|
||||||
|
|
||||||
let surfaceTypeValue
|
|
||||||
if (res.data.surfaceType === 'Ⅲ・Ⅳ') {
|
|
||||||
surfaceTypeValue = '3'
|
|
||||||
} else if (res.data.surfaceType === 'Ⅱ') {
|
|
||||||
surfaceTypeValue = '2'
|
|
||||||
}
|
|
||||||
//설치높이 0이면 빈값으로로 셋팅
|
|
||||||
if (res.data.installHeight === '0') {
|
|
||||||
res.data.installHeight = ''
|
|
||||||
}
|
|
||||||
|
|
||||||
setSessionState((prev) => ({
|
|
||||||
...prev,
|
|
||||||
oneTwoStoreId: res.data.saleStoreId
|
|
||||||
}));
|
|
||||||
|
|
||||||
setManagementState({ ...res.data, surfaceTypeValue: surfaceTypeValue })
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* URL 파라미터에서 objectNo 설정
|
* URL 파라미터에서 objectNo 설정
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -5,10 +5,9 @@ import { useEffect } from 'react'
|
|||||||
import { useMessage } from '@/hooks/useMessage'
|
import { useMessage } from '@/hooks/useMessage'
|
||||||
import useMenu from '@/hooks/common/useMenu'
|
import useMenu from '@/hooks/common/useMenu'
|
||||||
import { canvasState, currentMenuState } from '@/store/canvasAtom'
|
import { canvasState, currentMenuState } from '@/store/canvasAtom'
|
||||||
import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil'
|
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||||
import { subMenusState } from '@/store/menuAtom'
|
import { subMenusState } from '@/store/menuAtom'
|
||||||
import { useCanvasMenu } from '@/hooks/common/useCanvasMenu'
|
import { useCanvasMenu } from '@/hooks/common/useCanvasMenu'
|
||||||
import { commonUtilsState } from '@/store/commonUtilsAtom'
|
|
||||||
|
|
||||||
export default function MenuDepth01() {
|
export default function MenuDepth01() {
|
||||||
const canvas = useRecoilValue(canvasState)
|
const canvas = useRecoilValue(canvasState)
|
||||||
@ -17,10 +16,8 @@ export default function MenuDepth01() {
|
|||||||
const { selectedMenu, setSelectedMenu } = useCanvasMenu()
|
const { selectedMenu, setSelectedMenu } = useCanvasMenu()
|
||||||
const [currentMenu, setCurrentMenu] = useRecoilState(currentMenuState)
|
const [currentMenu, setCurrentMenu] = useRecoilState(currentMenuState)
|
||||||
const subMenus = useRecoilValue(subMenusState)
|
const subMenus = useRecoilValue(subMenusState)
|
||||||
const resetCommonUtils = useResetRecoilState(commonUtilsState)
|
|
||||||
|
|
||||||
const onClickMenu = ({ id, menu }) => {
|
const onClickMenu = ({ id, menu }) => {
|
||||||
resetCommonUtils()
|
|
||||||
if (menu === currentMenu) {
|
if (menu === currentMenu) {
|
||||||
handleMenu(selectedMenu)
|
handleMenu(selectedMenu)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -120,7 +120,7 @@ export default function ImgLoad() {
|
|||||||
value={refImage ? (refImage?.name ?? '') : (currentCanvasPlan?.bgImageName ?? '')}
|
value={refImage ? (refImage?.name ?? '') : (currentCanvasPlan?.bgImageName ?? '')}
|
||||||
readOnly
|
readOnly
|
||||||
/>
|
/>
|
||||||
{currentCanvasPlan?.bgImageName && <button className="img-check" onClick={handleFileDelete}></button>}
|
{refImage && <button className="img-check" onClick={handleFileDelete}></button>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -4,7 +4,6 @@ import { globalPitchState, pitchSelector, pitchTextSelector } from '@/store/canv
|
|||||||
import { useRecoilState } from 'recoil'
|
import { useRecoilState } from 'recoil'
|
||||||
import { useRef } from 'react'
|
import { useRef } from 'react'
|
||||||
import { usePopup } from '@/hooks/usePopup'
|
import { usePopup } from '@/hooks/usePopup'
|
||||||
import { CalculatorInput } from '@/components/common/input/CalcInput'
|
|
||||||
|
|
||||||
export default function Slope({ id, pos = { x: 50, y: 230 } }) {
|
export default function Slope({ id, pos = { x: 50, y: 230 } }) {
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
@ -23,19 +22,7 @@ export default function Slope({ id, pos = { x: 50, y: 230 } }) {
|
|||||||
{getMessage('slope')}
|
{getMessage('slope')}
|
||||||
</span>
|
</span>
|
||||||
<div className="input-grid mr5">
|
<div className="input-grid mr5">
|
||||||
{/*<input type="text" className="input-origin block" defaultValue={globalPitch} ref={inputRef} />*/}
|
<input type="text" className="input-origin block" defaultValue={globalPitch} ref={inputRef} />
|
||||||
<CalculatorInput
|
|
||||||
id=""
|
|
||||||
name=""
|
|
||||||
label=""
|
|
||||||
className="input-origin block"
|
|
||||||
ref={inputRef}
|
|
||||||
value={globalPitch}
|
|
||||||
options={{
|
|
||||||
allowNegative: false,
|
|
||||||
allowDecimal: true //(index !== 0),
|
|
||||||
}}
|
|
||||||
></CalculatorInput>
|
|
||||||
</div>
|
</div>
|
||||||
<span className="thin">{pitchText}</span>
|
<span className="thin">{pitchText}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -8,12 +8,8 @@ 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()
|
||||||
|
|
||||||
@ -56,15 +52,6 @@ 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,8 +7,6 @@ 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'
|
|
||||||
import { CalculatorInput } from '@/components/common/input/CalcInput'
|
|
||||||
|
|
||||||
export default function AuxiliaryEdit(props) {
|
export default function AuxiliaryEdit(props) {
|
||||||
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
||||||
@ -42,15 +40,15 @@ export default function AuxiliaryEdit(props) {
|
|||||||
if (currentObject) {
|
if (currentObject) {
|
||||||
copy(
|
copy(
|
||||||
currentObject,
|
currentObject,
|
||||||
arrow2 ? (arrow2 === '←' ? (Number(normalizeDigits(horizonSize)) / 10) * -1 : Number(normalizeDigits(horizonSize)) / 10) : 0,
|
arrow2 ? (arrow2 === '←' ? Number(horizonSize) * -1 : Number(horizonSize)) : 0,
|
||||||
arrow1 ? (arrow1 === '↑' ? (Number(normalizeDigits(verticalSize)) / 10) * -1 : Number(normalizeDigits(verticalSize)) / 10) : 0,
|
arrow1 ? (arrow1 === '↑' ? Number(verticalSize) * -1 : Number(verticalSize)) : 0,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
move(
|
move(
|
||||||
currentObject,
|
currentObject,
|
||||||
arrow2 ? (arrow2 === '←' ? (Number(normalizeDigits(horizonSize)) / 10) * -1 : Number(normalizeDigits(horizonSize)) / 10) : 0,
|
arrow2 ? (arrow2 === '←' ? Number(horizonSize) * -1 : Number(horizonSize)) : 0,
|
||||||
arrow1 ? (arrow1 === '↑' ? (Number(normalizeDigits(verticalSize)) / 10) * -1 : Number(normalizeDigits(verticalSize)) / 10) : 0,
|
arrow1 ? (arrow1 === '↑' ? Number(verticalSize) * -1 : Number(verticalSize)) : 0,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,19 +65,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(normalizeDigits(e.target.value))} />*/}
|
<input type="text" className="input-origin" value={verticalSize} onChange={(e) => setVerticalSize(e.target.value)} />
|
||||||
<CalculatorInput
|
|
||||||
id=""
|
|
||||||
name=""
|
|
||||||
label=""
|
|
||||||
className="input-origin block"
|
|
||||||
value={verticalSize}
|
|
||||||
onChange={(value) => setVerticalSize(value)}
|
|
||||||
options={{
|
|
||||||
allowNegative: false,
|
|
||||||
allowDecimal: false
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<span>mm</span>
|
<span>mm</span>
|
||||||
<div className="direction-move-wrap">
|
<div className="direction-move-wrap">
|
||||||
@ -101,19 +87,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(normalizeDigits(e.target.value))} />*/}
|
<input type="text" className="input-origin" value={horizonSize} onChange={(e) => setHorizonSize(e.target.value)} />
|
||||||
<CalculatorInput
|
|
||||||
id=""
|
|
||||||
name=""
|
|
||||||
label=""
|
|
||||||
className="input-origin block"
|
|
||||||
value={horizonSize}
|
|
||||||
onChange={(value) => setHorizonSize(value)}
|
|
||||||
options={{
|
|
||||||
allowNegative: false,
|
|
||||||
allowDecimal: false
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<span>mm</span>
|
<span>mm</span>
|
||||||
<div className="direction-move-wrap">
|
<div className="direction-move-wrap">
|
||||||
|
|||||||
@ -7,22 +7,19 @@ 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'
|
|
||||||
import { CalculatorInput } from '@/components/common/input/CalcInput'
|
|
||||||
|
|
||||||
export default function AuxiliarySize(props) {
|
export default function AuxiliarySize(props) {
|
||||||
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
||||||
const { id, pos = contextPopupPosition } = props
|
const { id, pos = contextPopupPosition } = props
|
||||||
const [checkedRadio, setCheckedRadio] = useState(null)
|
const [checkedRadio, setCheckedRadio] = useState(null)
|
||||||
const [value1, setValue1] = useState('')
|
const [value1, setValue1] = useState(null)
|
||||||
const [value2, setValue2] = useState('')
|
const [value2, setValue2] = useState(null)
|
||||||
const [size, setSize] = useState(0)
|
const [size, setSize] = useState(0)
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
const { closePopup } = usePopup()
|
const { closePopup } = usePopup()
|
||||||
const currentObject = useRecoilValue(currentObjectState)
|
const currentObject = useRecoilValue(currentObjectState)
|
||||||
const canvas = useRecoilValue(canvasState)
|
const canvas = useRecoilValue(canvasState)
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return () => {
|
return () => {
|
||||||
canvas?.discardActiveObject()
|
canvas?.discardActiveObject()
|
||||||
@ -39,14 +36,13 @@ export default function AuxiliarySize(props) {
|
|||||||
}, [currentObject])
|
}, [currentObject])
|
||||||
|
|
||||||
const handleInput = (e) => {
|
const handleInput = (e) => {
|
||||||
let value = e.replace(/^0+/, '')
|
let value = e.target.value.replace(/^0+/, '')
|
||||||
if (value === '') {
|
if (value === '') {
|
||||||
if (checkedRadio === 1) setValue1(value)
|
if (checkedRadio === 1) setValue1(value)
|
||||||
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())
|
||||||
@ -132,20 +128,7 @@ export default function AuxiliarySize(props) {
|
|||||||
<div className="outline-form">
|
<div className="outline-form">
|
||||||
<span style={{ width: 'auto' }}>{getMessage('length')}</span>
|
<span style={{ width: 'auto' }}>{getMessage('length')}</span>
|
||||||
<div className="input-grid mr5">
|
<div className="input-grid mr5">
|
||||||
{/*<input type="text" className="input-origin block" value={value1} readOnly={checkedRadio !== 1} onChange={handleInput} />*/}
|
<input type="text" className="input-origin block" value={value1} readOnly={checkedRadio !== 1} onChange={handleInput} />
|
||||||
<CalculatorInput
|
|
||||||
id=""
|
|
||||||
name=""
|
|
||||||
label=""
|
|
||||||
className="input-origin block"
|
|
||||||
value={value1}
|
|
||||||
onChange={handleInput}
|
|
||||||
readOnly={checkedRadio !== 1}
|
|
||||||
options={{
|
|
||||||
allowNegative: false,
|
|
||||||
allowDecimal: false
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<span className="thin">mm</span>
|
<span className="thin">mm</span>
|
||||||
</div>
|
</div>
|
||||||
@ -164,20 +147,7 @@ export default function AuxiliarySize(props) {
|
|||||||
<div className="outline-form">
|
<div className="outline-form">
|
||||||
<span style={{ width: 'auto' }}>{getMessage('length')}</span>
|
<span style={{ width: 'auto' }}>{getMessage('length')}</span>
|
||||||
<div className="input-grid mr5">
|
<div className="input-grid mr5">
|
||||||
{/*<input type="text" className="input-origin block" value={value2} readOnly={checkedRadio !== 2} onChange={handleInput} />*/}
|
<input type="text" className="input-origin block" value={value2} readOnly={checkedRadio !== 2} onChange={handleInput} />
|
||||||
<CalculatorInput
|
|
||||||
id=""
|
|
||||||
name=""
|
|
||||||
label=""
|
|
||||||
className="input-origin block"
|
|
||||||
value={value2}
|
|
||||||
onChange={handleInput}
|
|
||||||
readOnly={checkedRadio !== 2}
|
|
||||||
options={{
|
|
||||||
allowNegative: false,
|
|
||||||
allowDecimal: false
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<span className="thin">mm</span>
|
<span className="thin">mm</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,89 +1,109 @@
|
|||||||
import { MODULE_SETUP_TYPE, POLYGON_TYPE } from '@/common/common'
|
import { useMessage } from '@/hooks/useMessage'
|
||||||
import WithDraggable from '@/components/common/draggable/WithDraggable'
|
import WithDraggable from '@/components/common/draggable/WithDraggable'
|
||||||
import { Orientation } from '@/components/floor-plan/modal/basic/step/Orientation'
|
import { useContext, useEffect, useRef, useState } from 'react'
|
||||||
|
import Module from '@/components/floor-plan/modal/basic/step/Module'
|
||||||
|
import PitchModule from '@/components/floor-plan/modal/basic/step/pitch/PitchModule'
|
||||||
import PitchPlacement from '@/components/floor-plan/modal/basic/step/pitch/PitchPlacement'
|
import PitchPlacement from '@/components/floor-plan/modal/basic/step/pitch/PitchPlacement'
|
||||||
import Placement from '@/components/floor-plan/modal/basic/step/Placement'
|
import Placement from '@/components/floor-plan/modal/basic/step/Placement'
|
||||||
|
import { useRecoilValue, useRecoilState } from 'recoil'
|
||||||
|
import { canvasSettingState, canvasState, checkedModuleState, isManualModuleSetupState } from '@/store/canvasAtom'
|
||||||
|
import { usePopup } from '@/hooks/usePopup'
|
||||||
|
import { Orientation } from '@/components/floor-plan/modal/basic/step/Orientation'
|
||||||
|
import { useModuleBasicSetting } from '@/hooks/module/useModuleBasicSetting'
|
||||||
|
import { useEvent } from '@/hooks/useEvent'
|
||||||
|
import { moduleSelectionDataState, selectedModuleState } from '@/store/selectedModuleOptions'
|
||||||
|
import { addedRoofsState, corridorDimensionSelector, basicSettingState } from '@/store/settingAtom'
|
||||||
|
import { isObjectNotEmpty } from '@/util/common-utils'
|
||||||
|
import Swal from 'sweetalert2'
|
||||||
import { useCanvasPopupStatusController } from '@/hooks/common/useCanvasPopupStatusController'
|
import { useCanvasPopupStatusController } from '@/hooks/common/useCanvasPopupStatusController'
|
||||||
import { useMasterController } from '@/hooks/common/useMasterController'
|
import { useMasterController } from '@/hooks/common/useMasterController'
|
||||||
import { useModuleBasicSetting } from '@/hooks/module/useModuleBasicSetting'
|
|
||||||
import { useModuleSelection } from '@/hooks/module/useModuleSelection'
|
|
||||||
import { useOrientation } from '@/hooks/module/useOrientation'
|
|
||||||
import { useMessage } from '@/hooks/useMessage'
|
|
||||||
import { usePopup } from '@/hooks/usePopup'
|
|
||||||
import {
|
|
||||||
canvasState,
|
|
||||||
checkedModuleState,
|
|
||||||
currentCanvasPlanState,
|
|
||||||
isManualModuleLayoutSetupState,
|
|
||||||
isManualModuleSetupState,
|
|
||||||
toggleManualSetupModeState,
|
|
||||||
} from '@/store/canvasAtom'
|
|
||||||
import { loginUserStore } from '@/store/commonAtom'
|
import { loginUserStore } from '@/store/commonAtom'
|
||||||
import { roofsState } from '@/store/roofAtom'
|
import { currentCanvasPlanState } from '@/store/canvasAtom'
|
||||||
import { moduleSelectionDataState } from '@/store/selectedModuleOptions'
|
import { POLYGON_TYPE } from '@/common/common'
|
||||||
import { addedRoofsState, basicSettingState } from '@/store/settingAtom'
|
|
||||||
import { isObjectNotEmpty } from '@/util/common-utils'
|
|
||||||
import { useEffect, useRef, useState } from 'react'
|
|
||||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
|
||||||
import Swal from 'sweetalert2'
|
|
||||||
import Trestle from './step/Trestle'
|
|
||||||
|
|
||||||
export default function BasicSetting({ id, pos = { x: 50, y: 230 } }) {
|
export default function BasicSetting({ id, pos = { x: 50, y: 230 } }) {
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
const { closePopup } = usePopup()
|
const { closePopup } = usePopup()
|
||||||
const [tabNum, setTabNum] = useState(1)
|
const [tabNum, setTabNum] = useState(1)
|
||||||
|
const canvasSetting = useRecoilValue(canvasSettingState)
|
||||||
const orientationRef = useRef(null)
|
const orientationRef = useRef(null)
|
||||||
|
const { initEvent } = useEvent()
|
||||||
const [isManualModuleSetup, setIsManualModuleSetup] = useRecoilState(isManualModuleSetupState)
|
const [isManualModuleSetup, setIsManualModuleSetup] = useRecoilState(isManualModuleSetupState)
|
||||||
const [isManualModuleLayoutSetup, setIsManualModuleLayoutSetup] = useRecoilState(isManualModuleLayoutSetupState)
|
const moduleSelectionData = useRecoilValue(moduleSelectionDataState)
|
||||||
const trestleRef = useRef(null)
|
const addedRoofs = useRecoilValue(addedRoofsState)
|
||||||
const [moduleSelectionData, setModuleSelectionData] = useRecoilState(moduleSelectionDataState)
|
|
||||||
const [addedRoofs, setAddedRoofs] = useRecoilState(addedRoofsState)
|
|
||||||
const loginUserState = useRecoilValue(loginUserStore)
|
const loginUserState = useRecoilValue(loginUserStore)
|
||||||
const currentCanvasPlan = useRecoilValue(currentCanvasPlanState)
|
const currentCanvasPlan = useRecoilValue(currentCanvasPlanState)
|
||||||
const canvas = useRecoilValue(canvasState)
|
const canvas = useRecoilValue(canvasState)
|
||||||
const [basicSetting, setBasicSettings] = useRecoilState(basicSettingState)
|
const [basicSetting, setBasicSettings] = useRecoilState(basicSettingState)
|
||||||
const [isClosePopup, setIsClosePopup] = useState({ close: false, id: 0 })
|
const [isClosePopup, setIsClosePopup] = useState({ close: false, id: 0 })
|
||||||
const [checkedModules, setCheckedModules] = useRecoilState(checkedModuleState)
|
const [checkedModules, setCheckedModules] = useRecoilState(checkedModuleState)
|
||||||
const [roofs, setRoofs] = useState(addedRoofs)
|
|
||||||
const [manualSetupMode, setManualSetupMode] = useRecoilState(toggleManualSetupModeState)
|
|
||||||
const [layoutSetup, setLayoutSetup] = useState([{}])
|
|
||||||
const {
|
|
||||||
selectedModules,
|
|
||||||
roughnessCodes,
|
|
||||||
windSpeedCodes,
|
|
||||||
managementState,
|
|
||||||
setManagementState,
|
|
||||||
moduleList,
|
|
||||||
setSelectedModules,
|
|
||||||
selectedSurfaceType,
|
|
||||||
setSelectedSurfaceType,
|
|
||||||
installHeight,
|
|
||||||
setInstallHeight,
|
|
||||||
standardWindSpeed,
|
|
||||||
setStandardWindSpeed,
|
|
||||||
verticalSnowCover,
|
|
||||||
setVerticalSnowCover,
|
|
||||||
handleChangeModule,
|
|
||||||
handleChangeSurfaceType,
|
|
||||||
handleChangeWindSpeed,
|
|
||||||
handleChangeInstallHeight,
|
|
||||||
handleChangeVerticalSnowCover,
|
|
||||||
} = useModuleSelection({ addedRoofs })
|
|
||||||
const { nextStep, compasDeg, setCompasDeg } = useOrientation()
|
|
||||||
const { trigger: orientationTrigger } = useCanvasPopupStatusController(1)
|
|
||||||
const { trigger: trestleTrigger } = useCanvasPopupStatusController(2)
|
|
||||||
const { trigger: placementTrigger } = useCanvasPopupStatusController(3)
|
|
||||||
const [roofsStore, setRoofsStore] = useRecoilState(roofsState)
|
|
||||||
const [isFold, setIsFold] = useState(false)
|
|
||||||
|
|
||||||
// const { initEvent } = useContext(EventContext)
|
// const { initEvent } = useContext(EventContext)
|
||||||
const { manualModuleSetup, autoModuleSetup, manualFlatroofModuleSetup, autoFlatroofModuleSetup, manualModuleLayoutSetup, restoreModuleInstArea } =
|
const { manualModuleSetup, autoModuleSetup, manualFlatroofModuleSetup, autoFlatroofModuleSetup } = useModuleBasicSetting(tabNum)
|
||||||
useModuleBasicSetting(tabNum)
|
|
||||||
const { updateObjectDate } = useMasterController()
|
const { updateObjectDate } = useMasterController()
|
||||||
|
|
||||||
useEffect(() => {
|
const handleBtnNextStep = () => {
|
||||||
const moduleTabNum = basicSetting.roofSizeSet != 3 ? 3 : 2
|
if (tabNum === 1) {
|
||||||
|
orientationRef.current.handleNextStep()
|
||||||
|
} else if (tabNum === 2) {
|
||||||
|
if (basicSetting.roofSizeSet !== '3') {
|
||||||
|
if (!isObjectNotEmpty(moduleSelectionData.module)) {
|
||||||
|
Swal.fire({
|
||||||
|
title: getMessage('module.not.found'),
|
||||||
|
icon: 'warning',
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addedRoofs.length !== moduleSelectionData.roofConstructions.length) {
|
||||||
|
Swal.fire({
|
||||||
|
title: getMessage('construction.length.difference'),
|
||||||
|
icon: 'warning',
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//물건정보 갱신일 수정
|
||||||
|
updateObjectDataApi({
|
||||||
|
objectNo: currentCanvasPlan.objectNo, //오브젝트_no
|
||||||
|
standardWindSpeedId: moduleSelectionData.common.stdWindSpeed, //기준풍속코드
|
||||||
|
verticalSnowCover: moduleSelectionData.common.stdSnowLd, //적설량
|
||||||
|
surfaceType: moduleSelectionData.common.illuminationTpNm, //면조도구분
|
||||||
|
installHeight: moduleSelectionData.common.instHt, //설치높이
|
||||||
|
userId: loginUserState.userId, //작성자아아디
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
if (!isObjectNotEmpty(moduleSelectionData.module)) {
|
||||||
|
Swal.fire({
|
||||||
|
title: getMessage('module.not.found'),
|
||||||
|
icon: 'warning',
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setTabNum(tabNum + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
const placementRef = {
|
||||||
|
isChidori: useRef('false'),
|
||||||
|
setupLocation: useRef('eaves'),
|
||||||
|
isMaxSetup: useRef('false'),
|
||||||
|
}
|
||||||
|
|
||||||
|
const placementFlatRef = {
|
||||||
|
setupLocation: useRef('south'),
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleManualModuleSetup = () => {
|
||||||
|
setIsManualModuleSetup(!isManualModuleSetup)
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateObjectDataApi = async (params) => {
|
||||||
|
const res = await updateObjectDate(params)
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
let hasModules = canvas
|
let hasModules = canvas
|
||||||
.getObjects()
|
.getObjects()
|
||||||
.filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE)
|
.filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE)
|
||||||
@ -91,42 +111,23 @@ export default function BasicSetting({ id, pos = { x: 50, y: 230 } }) {
|
|||||||
|
|
||||||
if (hasModules) {
|
if (hasModules) {
|
||||||
orientationRef.current.handleNextStep()
|
orientationRef.current.handleNextStep()
|
||||||
setTabNum(moduleTabNum)
|
setTabNum(3)
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
useEffect(() => {
|
//팝업 닫기 버튼 이벤트
|
||||||
if (roofsStore && addedRoofs) {
|
const handleClosePopup = (id) => {
|
||||||
setRoofs(
|
if (tabNum == 3) {
|
||||||
addedRoofs.map((roof, index) => {
|
if (isManualModuleSetup) {
|
||||||
return {
|
setIsManualModuleSetup(false)
|
||||||
...roof,
|
|
||||||
...roofsStore[index]?.addRoof,
|
|
||||||
construction: roofsStore[index]?.construction,
|
|
||||||
trestle: roofsStore[index]?.trestle,
|
|
||||||
trestleDetail: roofsStore[index]?.trestleDetail,
|
|
||||||
}
|
}
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
setModuleSelectionData({
|
|
||||||
...moduleSelectionData,
|
|
||||||
roofConstructions: roofsStore.map((roof) => {
|
|
||||||
return {
|
|
||||||
roofIndex: roof.roofIndex,
|
|
||||||
addRoof: roof.addRoof,
|
|
||||||
construction: roof.construction,
|
|
||||||
trestle: roof.trestle,
|
|
||||||
trestleDetail: roof.trestleDetail,
|
|
||||||
}
|
}
|
||||||
}),
|
setIsClosePopup({ close: true, id: id })
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}, [roofsStore, addedRoofs])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (basicSetting.roofSizeSet !== '3') {
|
if (basicSetting.roofSizeSet !== '3') {
|
||||||
manualModuleSetup()
|
manualModuleSetup(placementRef)
|
||||||
} else {
|
} else {
|
||||||
manualFlatroofModuleSetup(placementFlatRef)
|
manualFlatroofModuleSetup(placementFlatRef)
|
||||||
}
|
}
|
||||||
@ -139,231 +140,55 @@ export default function BasicSetting({ id, pos = { x: 50, y: 230 } }) {
|
|||||||
setIsManualModuleSetup(false)
|
setIsManualModuleSetup(false)
|
||||||
}, [checkedModules])
|
}, [checkedModules])
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (basicSetting.roofSizeSet !== '3') {
|
|
||||||
if (manualSetupMode.indexOf('manualSetup') > -1) {
|
|
||||||
manualModuleSetup()
|
|
||||||
} else if (manualSetupMode.indexOf('manualLayoutSetup') > -1) {
|
|
||||||
manualModuleLayoutSetup(layoutSetup)
|
|
||||||
} else if (manualSetupMode.indexOf('off') > -1) {
|
|
||||||
manualModuleSetup()
|
|
||||||
manualModuleLayoutSetup(layoutSetup)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
manualFlatroofModuleSetup(placementFlatRef)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isClosePopup.close) {
|
|
||||||
closePopup(isClosePopup.id)
|
|
||||||
}
|
|
||||||
}, [manualSetupMode, isClosePopup])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (isManualModuleLayoutSetup) {
|
|
||||||
manualModuleLayoutSetup(layoutSetup)
|
|
||||||
}
|
|
||||||
}, [layoutSetup])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setIsManualModuleSetup(false)
|
|
||||||
setIsManualModuleLayoutSetup(false)
|
|
||||||
setManualSetupMode(`off`)
|
|
||||||
}, [checkedModules])
|
|
||||||
|
|
||||||
const handleBtnNextStep = () => {
|
|
||||||
if (tabNum === 1) {
|
|
||||||
orientationRef.current.handleNextStep()
|
|
||||||
setAddedRoofs(roofs)
|
|
||||||
// setTabNum(tabNum + 1)
|
|
||||||
return
|
|
||||||
} else if (tabNum === 2) {
|
|
||||||
if (basicSetting.roofSizeSet !== '3') {
|
|
||||||
// if (addedRoofs.length !== moduleSelectionData.roofConstructions.length) {
|
|
||||||
// Swal.fire({
|
|
||||||
// title: getMessage('construction.length.difference'),
|
|
||||||
// icon: 'warning',
|
|
||||||
// })
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
trestleRef.current.isComplete().then((res) => {
|
|
||||||
if (!res) return
|
|
||||||
})
|
|
||||||
//물건정보 갱신일 수정
|
|
||||||
} else {
|
|
||||||
if (!isObjectNotEmpty(moduleSelectionData.module)) {
|
|
||||||
Swal.fire({
|
|
||||||
title: getMessage('module.not.found'),
|
|
||||||
icon: 'warning',
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
setTabNum(tabNum + 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const placementFlatRef = {
|
|
||||||
setupLocation: useRef('south'),
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleManualModuleSetup = () => {
|
|
||||||
setManualSetupMode(`manualSetup_${!isManualModuleSetup}`)
|
|
||||||
setIsManualModuleSetup(!isManualModuleSetup)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleManualModuleLayoutSetup = () => {
|
|
||||||
setManualSetupMode(`manualLayoutSetup_${!isManualModuleLayoutSetup}`)
|
|
||||||
setIsManualModuleLayoutSetup(!isManualModuleLayoutSetup)
|
|
||||||
}
|
|
||||||
|
|
||||||
const updateObjectDataApi = async (params) => {
|
|
||||||
const res = await updateObjectDate(params)
|
|
||||||
}
|
|
||||||
|
|
||||||
//팝업 닫기 버튼 이벤트
|
|
||||||
const handleClosePopup = (id) => {
|
|
||||||
if (tabNum == 3) {
|
|
||||||
if (isManualModuleSetup) {
|
|
||||||
setIsManualModuleSetup(false)
|
|
||||||
}
|
|
||||||
if (isManualModuleLayoutSetup) {
|
|
||||||
setIsManualModuleLayoutSetup(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setIsClosePopup({ close: true, id: id })
|
|
||||||
}
|
|
||||||
|
|
||||||
const orientationProps = {
|
|
||||||
roofs,
|
|
||||||
setRoofs,
|
|
||||||
tabNum,
|
|
||||||
setTabNum,
|
|
||||||
compasDeg, // 방위각
|
|
||||||
setCompasDeg,
|
|
||||||
selectedModules,
|
|
||||||
moduleSelectionData,
|
|
||||||
setModuleSelectionData,
|
|
||||||
roughnessCodes, // 면조도 목록
|
|
||||||
windSpeedCodes, // 기준풍속 목록
|
|
||||||
managementState,
|
|
||||||
setManagementState,
|
|
||||||
moduleList, // 모듈 리스트
|
|
||||||
setSelectedModules,
|
|
||||||
selectedSurfaceType,
|
|
||||||
setSelectedSurfaceType,
|
|
||||||
installHeight, // 설치높이
|
|
||||||
setInstallHeight,
|
|
||||||
standardWindSpeed, // 기준풍속
|
|
||||||
setStandardWindSpeed,
|
|
||||||
verticalSnowCover, // 적설량
|
|
||||||
setVerticalSnowCover,
|
|
||||||
currentCanvasPlan,
|
|
||||||
loginUserState,
|
|
||||||
handleChangeModule,
|
|
||||||
handleChangeSurfaceType,
|
|
||||||
handleChangeWindSpeed,
|
|
||||||
handleChangeInstallHeight,
|
|
||||||
handleChangeVerticalSnowCover,
|
|
||||||
orientationTrigger,
|
|
||||||
nextStep,
|
|
||||||
updateObjectDataApi,
|
|
||||||
}
|
|
||||||
const trestleProps = {
|
|
||||||
roofs,
|
|
||||||
setRoofs,
|
|
||||||
setRoofsStore,
|
|
||||||
tabNum,
|
|
||||||
setTabNum,
|
|
||||||
moduleSelectionData,
|
|
||||||
setModuleSelectionData,
|
|
||||||
trestleTrigger,
|
|
||||||
}
|
|
||||||
const placementProps = {}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<WithDraggable isShow={true} pos={pos} className={basicSetting.roofSizeSet && basicSetting.roofSizeSet != '3' ? 'll' : 'lx-2'}>
|
<WithDraggable isShow={true} pos={pos} className="lx-2">
|
||||||
<WithDraggable.Header
|
<WithDraggable.Header title={getMessage('plan.menu.module.circuit.setting.default')} onClose={() => handleClosePopup(id)} />
|
||||||
title={getMessage('plan.menu.module.circuit.setting.default')}
|
|
||||||
isFold={isFold}
|
|
||||||
onClose={() => handleClosePopup(id)}
|
|
||||||
onFold={() => setIsFold(!isFold)}
|
|
||||||
/>
|
|
||||||
<WithDraggable.Body>
|
<WithDraggable.Body>
|
||||||
<div style={{ display: isFold ? 'none' : 'block' }}>
|
|
||||||
<div className="roof-module-tab">
|
<div className="roof-module-tab">
|
||||||
<div className={`module-tab-bx act`}>{getMessage('modal.module.basic.setting.orientation.setting')}</div>
|
<div className={`module-tab-bx act`}>{getMessage('modal.module.basic.setting.orientation.setting')}</div>
|
||||||
<span className={`tab-arr ${tabNum !== 1 ? 'act' : ''}`}></span>
|
<span className={`tab-arr ${tabNum !== 1 ? 'act' : ''}`}></span>
|
||||||
{basicSetting.roofSizeSet && basicSetting.roofSizeSet != '3' && (
|
|
||||||
<>
|
|
||||||
<div className={`module-tab-bx ${tabNum !== 1 ? 'act' : ''}`}>{getMessage('modal.module.basic.setting.module.setting')}</div>
|
<div className={`module-tab-bx ${tabNum !== 1 ? 'act' : ''}`}>{getMessage('modal.module.basic.setting.module.setting')}</div>
|
||||||
<span className={`tab-arr ${tabNum === 3 ? 'act' : ''}`}></span>
|
<span className={`tab-arr ${tabNum === 3 ? 'act' : ''}`}></span>
|
||||||
<div className={`module-tab-bx ${tabNum === 3 ? 'act' : ''}`}>{getMessage('modal.module.basic.setting.module.placement')}</div>
|
<div className={`module-tab-bx ${tabNum === 3 ? 'act' : ''}`}>{getMessage('modal.module.basic.setting.module.placement')}</div>
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{basicSetting.roofSizeSet && basicSetting.roofSizeSet == '3' && (
|
|
||||||
<>
|
|
||||||
<div className={`module-tab-bx ${tabNum === 2 ? 'act' : ''}`}>{getMessage('modal.module.basic.setting.module.placement')}</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
{tabNum === 1 && <Orientation ref={orientationRef} {...orientationProps} />}
|
{tabNum === 1 && <Orientation ref={orientationRef} tabNum={tabNum} setTabNum={setTabNum} />}
|
||||||
{/*배치면 초기설정 - 입력방법: 복시도 입력 || 실측값 입력*/}
|
{/*배치면 초기설정 - 입력방법: 복시도 입력 || 실측값 입력*/}
|
||||||
{basicSetting.roofSizeSet && basicSetting.roofSizeSet != '3' && tabNum === 2 && <Trestle ref={trestleRef} {...trestleProps} />}
|
{basicSetting.roofSizeSet && basicSetting.roofSizeSet != '3' && tabNum === 2 && <Module setTabNum={setTabNum} />}
|
||||||
{basicSetting.roofSizeSet && basicSetting.roofSizeSet != '3' && tabNum === 3 && (
|
{basicSetting.roofSizeSet && basicSetting.roofSizeSet != '3' && tabNum === 3 && <Placement setTabNum={setTabNum} ref={placementRef} />}
|
||||||
<Placement setTabNum={setTabNum} layoutSetup={layoutSetup} setLayoutSetup={setLayoutSetup} />
|
|
||||||
)}
|
|
||||||
{/*배치면 초기설정 - 입력방법: 육지붕*/}
|
{/*배치면 초기설정 - 입력방법: 육지붕*/}
|
||||||
{/* {basicSetting.roofSizeSet && basicSetting.roofSizeSet == '3' && tabNum === 3 && <PitchModule setTabNum={setTabNum} />} */}
|
{basicSetting.roofSizeSet && basicSetting.roofSizeSet == '3' && tabNum === 2 && <PitchModule setTabNum={setTabNum} />}
|
||||||
{basicSetting.roofSizeSet && basicSetting.roofSizeSet == '3' && tabNum === 2 && (
|
{basicSetting.roofSizeSet && basicSetting.roofSizeSet == '3' && tabNum === 3 && (
|
||||||
<PitchPlacement setTabNum={setTabNum} ref={placementFlatRef} />
|
<PitchPlacement setTabNum={setTabNum} ref={placementFlatRef} />
|
||||||
)}
|
)}
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="grid-btn-wrap">
|
<div className="grid-btn-wrap">
|
||||||
{/* {tabNum === 1 && <button className="btn-frame modal mr5">{getMessage('modal.common.save')}</button>} */}
|
|
||||||
{basicSetting.roofSizeSet && basicSetting.roofSizeSet != '3' && (
|
|
||||||
<>
|
|
||||||
{tabNum !== 1 && (
|
{tabNum !== 1 && (
|
||||||
<button className="btn-frame modal mr5" onClick={() => setTabNum(tabNum - 1)}>
|
<button className="btn-frame modal mr5" onClick={() => setTabNum(tabNum - 1)}>
|
||||||
{getMessage('modal.module.basic.setting.prev')}
|
{getMessage('modal.module.basic.setting.prev')}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
{/*{tabNum !== 3 && <button className="btn-frame modal act mr5">{getMessage('modal.common.save')}</button>}*/}
|
||||||
{tabNum !== 3 && (
|
{tabNum !== 3 && (
|
||||||
<button className="btn-frame modal" onClick={handleBtnNextStep}>
|
<button className="btn-frame modal" onClick={handleBtnNextStep}>
|
||||||
Next
|
Next
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{tabNum === 3 && (
|
{tabNum === 3 && (
|
||||||
<>
|
<>
|
||||||
<button className={`btn-frame modal mr5 ${isManualModuleLayoutSetup ? 'act' : ''}`} onClick={handleManualModuleLayoutSetup}>
|
{basicSetting.roofSizeSet && basicSetting.roofSizeSet != '3' && (
|
||||||
{getMessage('modal.module.basic.setting.row.batch')}
|
<>
|
||||||
</button>
|
|
||||||
<button className="btn-frame modal mr5" onClick={() => autoModuleSetup(MODULE_SETUP_TYPE.LAYOUT, layoutSetup)}>
|
|
||||||
{getMessage('modal.module.basic.setting.auto.row.batch')}
|
|
||||||
</button>
|
|
||||||
<button className={`btn-frame modal mr5 ${isManualModuleSetup ? 'act' : ''}`} onClick={handleManualModuleSetup}>
|
<button className={`btn-frame modal mr5 ${isManualModuleSetup ? 'act' : ''}`} onClick={handleManualModuleSetup}>
|
||||||
{getMessage('modal.module.basic.setting.passivity.placement')}
|
{getMessage('modal.module.basic.setting.passivity.placement')}
|
||||||
</button>
|
</button>
|
||||||
<button className="btn-frame modal act mr5" onClick={() => autoModuleSetup(MODULE_SETUP_TYPE.AUTO)}>
|
<button className="btn-frame modal act" onClick={() => autoModuleSetup(placementRef)}>
|
||||||
{getMessage('modal.module.basic.setting.auto.placement')}
|
{getMessage('modal.module.basic.setting.auto.placement')}
|
||||||
</button>
|
</button>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{basicSetting.roofSizeSet && basicSetting.roofSizeSet == '3' && (
|
{basicSetting.roofSizeSet && basicSetting.roofSizeSet == '3' && (
|
||||||
<>
|
<>
|
||||||
{tabNum === 1 && (
|
|
||||||
<button className="btn-frame modal" onClick={handleBtnNextStep}>
|
|
||||||
Next
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
{tabNum === 2 && (
|
|
||||||
<>
|
|
||||||
<button className="btn-frame modal mr5" onClick={() => setTabNum(tabNum - 1)}>
|
|
||||||
{getMessage('modal.module.basic.setting.prev')}
|
|
||||||
</button>
|
|
||||||
<button className={`btn-frame modal mr5 ${isManualModuleSetup ? 'act' : ''}`} onClick={handleManualModuleSetup}>
|
<button className={`btn-frame modal mr5 ${isManualModuleSetup ? 'act' : ''}`} onClick={handleManualModuleSetup}>
|
||||||
{getMessage('modal.module.basic.setting.passivity.placement')}
|
{getMessage('modal.module.basic.setting.passivity.placement')}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@ -10,8 +10,6 @@ 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'
|
|
||||||
import { CalculatorInput } from '@/components/common/input/CalcInput'
|
|
||||||
|
|
||||||
export default function Module({ setTabNum }) {
|
export default function Module({ setTabNum }) {
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
@ -186,23 +184,11 @@ export default function Module({ setTabNum }) {
|
|||||||
<div className="eaves-keraba-td">
|
<div className="eaves-keraba-td">
|
||||||
<div className="outline-form">
|
<div className="outline-form">
|
||||||
<div className="grid-select mr10">
|
<div className="grid-select mr10">
|
||||||
{/*<input*/}
|
<input
|
||||||
{/* type="text"*/}
|
type="text"
|
||||||
{/* className="input-origin block"*/}
|
|
||||||
{/* value={inputInstallHeight}*/}
|
|
||||||
{/* onChange={(e) => setInputInstallHeight(normalizeDecimal(e.target.value))}*/}
|
|
||||||
{/*/>*/}
|
|
||||||
<CalculatorInput
|
|
||||||
id=""
|
|
||||||
name=""
|
|
||||||
label=""
|
|
||||||
className="input-origin block"
|
className="input-origin block"
|
||||||
value={inputInstallHeight}
|
value={inputInstallHeight}
|
||||||
onChange={(value) => setInputInstallHeight(value)}
|
onChange={(e) => setInputInstallHeight(e.target.value)}
|
||||||
options={{
|
|
||||||
allowNegative: false,
|
|
||||||
allowDecimal: false
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<span className="thin">m</span>
|
<span className="thin">m</span>
|
||||||
@ -235,23 +221,11 @@ export default function Module({ setTabNum }) {
|
|||||||
<div className="eaves-keraba-td">
|
<div className="eaves-keraba-td">
|
||||||
<div className="outline-form">
|
<div className="outline-form">
|
||||||
<div className="grid-select mr10">
|
<div className="grid-select mr10">
|
||||||
{/*<input*/}
|
<input
|
||||||
{/* type="text"*/}
|
type="text"
|
||||||
{/* className="input-origin block"*/}
|
|
||||||
{/* value={inputVerticalSnowCover}*/}
|
|
||||||
{/* onChange={(e) => setInputVerticalSnowCover(normalizeDecimal(e.target.value))}*/}
|
|
||||||
{/*/>*/}
|
|
||||||
<CalculatorInput
|
|
||||||
id=""
|
|
||||||
name=""
|
|
||||||
label=""
|
|
||||||
className="input-origin block"
|
className="input-origin block"
|
||||||
value={inputVerticalSnowCover}
|
value={inputVerticalSnowCover}
|
||||||
onChange={(value) => setInputVerticalSnowCover(value)}
|
onChange={(e) => setInputVerticalSnowCover(e.target.value)}
|
||||||
options={{
|
|
||||||
allowNegative: false,
|
|
||||||
allowDecimal: false
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<span className="thin">cm</span>
|
<span className="thin">cm</span>
|
||||||
|
|||||||
@ -1,399 +1,54 @@
|
|||||||
import { forwardRef, use, useContext, useEffect, useImperativeHandle, useState } from 'react'
|
import { forwardRef, useContext, useEffect, useImperativeHandle, useState } from 'react'
|
||||||
import { useMessage } from '@/hooks/useMessage'
|
import { useMessage } from '@/hooks/useMessage'
|
||||||
|
import { useOrientation } from '@/hooks/module/useOrientation'
|
||||||
import { getDegreeInOrientation } from '@/util/canvas-util'
|
import { getDegreeInOrientation } from '@/util/canvas-util'
|
||||||
import { numberCheck } from '@/util/common-utils'
|
import { numberCheck } from '@/util/common-utils'
|
||||||
import { addedRoofsState, basicSettingState } from '@/store/settingAtom'
|
import { useCanvasPopupStatusController } from '@/hooks/common/useCanvasPopupStatusController'
|
||||||
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
|
|
||||||
import QSelectBox from '@/components/common/select/QSelectBox'
|
|
||||||
import { roofsState } from '@/store/roofAtom'
|
|
||||||
import { useModuleBasicSetting } from '@/hooks/module/useModuleBasicSetting'
|
|
||||||
import { useCommonCode } from '@/hooks/common/useCommonCode'
|
|
||||||
import Swal from 'sweetalert2'
|
|
||||||
import { normalizeDecimal} from '@/util/input-utils'
|
|
||||||
import { CalculatorInput } from '@/components/common/input/CalcInput'
|
|
||||||
|
|
||||||
export const Orientation = forwardRef((props, ref) => {
|
export const Orientation = forwardRef(({ tabNum }, ref) => {
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
const { findCommonCode } = useCommonCode()
|
|
||||||
|
const { trigger: canvasPopupStatusTrigger } = useCanvasPopupStatusController(1)
|
||||||
|
|
||||||
|
const { nextStep, compasDeg, setCompasDeg } = useOrientation()
|
||||||
|
|
||||||
const [hasAnglePassivity, setHasAnglePassivity] = useState(false)
|
const [hasAnglePassivity, setHasAnglePassivity] = useState(false)
|
||||||
const basicSetting = useRecoilValue(basicSettingState)
|
|
||||||
const [addedRoofs, setAddedRoofs] = useRecoilState(addedRoofsState) //지붕재 선택
|
|
||||||
const [roofsStore, setRoofsStore] = useRecoilState(roofsState)
|
|
||||||
const [roofTab, setRoofTab] = useState(0) //지붕재 탭
|
|
||||||
const [selectedModuleSeries, setSelectedModuleSeries] = useState(null)
|
|
||||||
const [moduleSeriesList, setModuleSeriesList] = useState([])
|
|
||||||
const [filteredModuleList, setFilteredModuleList] = useState([])
|
|
||||||
const {
|
|
||||||
roofs,
|
|
||||||
setRoofs,
|
|
||||||
tabNum,
|
|
||||||
setTabNum,
|
|
||||||
compasDeg,
|
|
||||||
setCompasDeg,
|
|
||||||
selectedModules,
|
|
||||||
roughnessCodes,
|
|
||||||
windSpeedCodes,
|
|
||||||
managementState,
|
|
||||||
setManagementState,
|
|
||||||
moduleList,
|
|
||||||
moduleSelectionData,
|
|
||||||
setModuleSelectionData,
|
|
||||||
setSelectedModules,
|
|
||||||
selectedSurfaceType,
|
|
||||||
setSelectedSurfaceType,
|
|
||||||
installHeight,
|
|
||||||
setInstallHeight,
|
|
||||||
standardWindSpeed,
|
|
||||||
setStandardWindSpeed,
|
|
||||||
verticalSnowCover,
|
|
||||||
setVerticalSnowCover,
|
|
||||||
orientationTrigger,
|
|
||||||
nextStep,
|
|
||||||
currentCanvasPlan,
|
|
||||||
loginUserState,
|
|
||||||
updateObjectDataApi,
|
|
||||||
} = props
|
|
||||||
const [inputCompasDeg, setInputCompasDeg] = useState(compasDeg ?? 0)
|
|
||||||
const [inputInstallHeight, setInputInstallHeight] = useState('0')
|
|
||||||
const [inputMargin, setInputMargin] = useState('0')
|
|
||||||
const [inputVerticalSnowCover, setInputVerticalSnowCover] = useState('0')
|
|
||||||
const [inputRoughness, setInputRoughness] = useState(selectedSurfaceType)
|
|
||||||
const [inputStandardWindSpeed, setInputStandardWindSpeed] = useState(standardWindSpeed)
|
|
||||||
const { restoreModuleInstArea } = useModuleBasicSetting()
|
|
||||||
const moduleData = {
|
|
||||||
header: [
|
|
||||||
{ name: getMessage('module'), width: 150, prop: 'module', type: 'color-box' },
|
|
||||||
{
|
|
||||||
name: `${getMessage('height')} (mm)`,
|
|
||||||
prop: 'height',
|
|
||||||
},
|
|
||||||
{ name: `${getMessage('width')} (mm)`, prop: 'width' },
|
|
||||||
{ name: `${getMessage('output')} (W)`, prop: 'output' },
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
||||||
const allOption = {
|
|
||||||
moduleSerCd: 'ALL',
|
|
||||||
moduleSerNm: getMessage("board.sub.total") || 'ALL'
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (basicSetting.roofSizeSet === '3') {
|
|
||||||
restoreModuleInstArea()
|
|
||||||
}
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (moduleSelectionData?.common) {
|
|
||||||
setInputMargin(moduleSelectionData?.common?.margin)
|
|
||||||
}
|
|
||||||
}, [moduleSelectionData])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (selectedModules) {
|
|
||||||
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, moduleList, moduleSeriesList])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (selectedSurfaceType) {
|
|
||||||
setInputRoughness(roughnessCodes.find((code) => code.clCode === managementState?.surfaceTypeValue))
|
|
||||||
}
|
|
||||||
}, [selectedSurfaceType])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (standardWindSpeed) setInputStandardWindSpeed(windSpeedCodes.find((code) => code.clCode === managementState?.standardWindSpeedId))
|
|
||||||
}, [standardWindSpeed])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (managementState?.installHeight && managementState?.installHeight) {
|
|
||||||
setSelectedSurfaceType(roughnessCodes.find((code) => code.clCode === managementState?.surfaceTypeValue))
|
|
||||||
setInputInstallHeight(managementState?.installHeight)
|
|
||||||
setStandardWindSpeed(windSpeedCodes.find((code) => code.clCode === managementState?.standardWindSpeedId))
|
|
||||||
setInputVerticalSnowCover(managementState?.verticalSnowCover)
|
|
||||||
}
|
|
||||||
}, [managementState])
|
|
||||||
|
|
||||||
useImperativeHandle(ref, () => ({
|
useImperativeHandle(ref, () => ({
|
||||||
handleNextStep,
|
handleNextStep,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const handleNextStep = () => {
|
const handleNextStep = () => {
|
||||||
if (isComplete()) {
|
nextStep()
|
||||||
const common = {
|
canvasPopupStatusTrigger(compasDeg)
|
||||||
illuminationTp: inputRoughness.clCode,
|
|
||||||
illuminationTpNm: inputRoughness.clCodeNm,
|
|
||||||
instHt: inputInstallHeight,
|
|
||||||
stdWindSpeed: inputStandardWindSpeed?.clCode,
|
|
||||||
stdSnowLd: inputVerticalSnowCover,
|
|
||||||
saleStoreNorthFlg: managementState?.saleStoreNorthFlg,
|
|
||||||
moduleTpCd: selectedModules.itemTp,
|
|
||||||
moduleItemId: selectedModules.itemId,
|
|
||||||
margin: inputMargin,
|
|
||||||
}
|
}
|
||||||
setCompasDeg(inputCompasDeg)
|
|
||||||
setInstallHeight(inputInstallHeight)
|
|
||||||
setVerticalSnowCover(inputVerticalSnowCover)
|
|
||||||
setSelectedSurfaceType(inputRoughness)
|
|
||||||
setStandardWindSpeed(inputStandardWindSpeed)
|
|
||||||
nextStep(inputCompasDeg)
|
|
||||||
setManagementState({
|
|
||||||
...managementState,
|
|
||||||
installHeight: inputInstallHeight,
|
|
||||||
verticalSnowCover: inputVerticalSnowCover,
|
|
||||||
standardWindSpeedId: inputStandardWindSpeed?.clCode,
|
|
||||||
surfaceType: inputRoughness.clCodeNm,
|
|
||||||
surfaceTypeValue: inputRoughness.clCode,
|
|
||||||
})
|
|
||||||
setModuleSelectionData({
|
|
||||||
...moduleSelectionData,
|
|
||||||
module: {
|
|
||||||
...selectedModules,
|
|
||||||
},
|
|
||||||
common,
|
|
||||||
})
|
|
||||||
orientationTrigger({
|
|
||||||
compasDeg: inputCompasDeg,
|
|
||||||
common: common,
|
|
||||||
module: {
|
|
||||||
...selectedModules,
|
|
||||||
},
|
|
||||||
margin: inputMargin,
|
|
||||||
})
|
|
||||||
updateObjectDataApi({
|
|
||||||
objectNo: currentCanvasPlan.objectNo, //오브젝트_no
|
|
||||||
standardWindSpeedId: inputStandardWindSpeed?.clCode, //기준풍속코드
|
|
||||||
verticalSnowCover: inputVerticalSnowCover, //적설량
|
|
||||||
surfaceType: inputRoughness.clCodeNm, //면조도구분
|
|
||||||
installHeight: inputInstallHeight, //설치높이
|
|
||||||
userId: loginUserState.userId, //작성자아아디
|
|
||||||
})
|
|
||||||
setTabNum(2)
|
|
||||||
} else {
|
|
||||||
if (!selectedModules || !selectedModules.itemId) {
|
|
||||||
Swal.fire({
|
|
||||||
title: getMessage('module.not.found'),
|
|
||||||
icon: 'warning',
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
useEffect(() => {
|
||||||
}
|
checkDegree(compasDeg)
|
||||||
}
|
}, [compasDeg])
|
||||||
|
|
||||||
const checkDegree = (e) => {
|
const checkDegree = (e) => {
|
||||||
if (e === '-0' || e === '-') {
|
if (e === '-0' || e === '-') {
|
||||||
setInputCompasDeg('-')
|
setCompasDeg('-')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (e === '0-') {
|
if (e === '0-') {
|
||||||
setInputCompasDeg('-0')
|
setCompasDeg('-0')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const n = Number(normalizeDecimal(e))
|
if (Number(e) >= -180 && Number(e) <= 180) {
|
||||||
if (n >= -180 && n <= 180) {
|
if (numberCheck(Number(e))) {
|
||||||
if (numberCheck(n)) {
|
setCompasDeg(Number(e))
|
||||||
setInputCompasDeg(n)
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setInputCompasDeg(compasDeg)
|
setCompasDeg(compasDeg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const isComplete = () => {
|
|
||||||
if (!selectedModules || !selectedModules.itemId) return false
|
|
||||||
if (basicSetting && basicSetting.roofSizeSet !== '3') {
|
|
||||||
if (inputInstallHeight <= 0) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (+inputVerticalSnowCover <= 0) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!inputStandardWindSpeed) return false
|
|
||||||
if (!inputRoughness) return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
const 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) => {
|
|
||||||
resetRoofs()
|
|
||||||
setSelectedModules(e)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleChangeRoughness = (e) => {
|
|
||||||
resetRoofs()
|
|
||||||
setInputRoughness(e)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleChangeInstallHeight = (e) => {
|
|
||||||
resetRoofs()
|
|
||||||
setInputInstallHeight(e)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleChangeStandardWindSpeed = (e) => {
|
|
||||||
resetRoofs()
|
|
||||||
setInputStandardWindSpeed(e)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleChangeVerticalSnowCover = (e) => {
|
|
||||||
resetRoofs()
|
|
||||||
setInputVerticalSnowCover(e)
|
|
||||||
}
|
|
||||||
|
|
||||||
const resetRoofs = () => {
|
|
||||||
const newRoofs = addedRoofs.map((roof) => {
|
|
||||||
return {
|
|
||||||
...roof,
|
|
||||||
trestle: {
|
|
||||||
lengthBase: null,
|
|
||||||
trestleMkrCd: null,
|
|
||||||
constMthdCd: null,
|
|
||||||
constTp: null,
|
|
||||||
roofBaseCd: null,
|
|
||||||
roofPchBase: null,
|
|
||||||
},
|
|
||||||
addRoof: {
|
|
||||||
...roof.addRoof,
|
|
||||||
lengthBase: null,
|
|
||||||
eavesMargin: null,
|
|
||||||
kerabaMargin: null,
|
|
||||||
ridgeMargin: null,
|
|
||||||
},
|
|
||||||
construction: {
|
|
||||||
constTp: null,
|
|
||||||
cvrYn: 'N',
|
|
||||||
snowGdPossYn: 'N',
|
|
||||||
cvrChecked: false,
|
|
||||||
snowGdChecked: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
})
|
|
||||||
// setRoofs(newRoofs)
|
|
||||||
// setAddedRoofs(newRoofs)
|
|
||||||
setRoofsStore(newRoofs)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 모듈시리즈 목록 생성 및 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">
|
||||||
<div className="outline-wrap">
|
<div className="outline-wrap">
|
||||||
<div className="roof-module-inner">
|
|
||||||
<div className="compas-wrapper">
|
|
||||||
<div className="guide">{getMessage('modal.module.basic.setting.orientation.setting.info')}</div>
|
<div className="guide">{getMessage('modal.module.basic.setting.orientation.setting.info')}</div>
|
||||||
<div className="roof-module-compas">
|
<div className="roof-module-compas">
|
||||||
<div className="compas-box">
|
<div className="compas-box">
|
||||||
@ -401,265 +56,60 @@ 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) === -15 * index + 180 || (index === 0 && inputCompasDeg >= 172 && index === 0 && inputCompasDeg <= 180) || (inputCompasDeg === -180 && index === 0) ? 'act' : ''}`}
|
className={`circle ${getDegreeInOrientation(compasDeg) === -1 * (-15 * index + 180) || (index === 0 && compasDeg >= 172 && index === 0 && compasDeg <= 180) || (compasDeg === -180 && index === 0) ? 'act' : ''}`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (index === 0) {
|
if (index === 0) {
|
||||||
setInputCompasDeg(180)
|
setCompasDeg(180)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
setInputCompasDeg(-15 * index + 180)
|
setCompasDeg(-1 * (-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) === -1 * 15 * index ? 'act' : ''}`}
|
className={`circle ${compasDeg !== 180 && getDegreeInOrientation(compasDeg) === 15 * index ? 'act' : ''}`}
|
||||||
onClick={() => setInputCompasDeg(15 * index * -1)}
|
onClick={() => setCompasDeg(15 * index)}
|
||||||
>
|
>
|
||||||
{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(${-1 * getDegreeInOrientation(inputCompasDeg)}deg)` }}></div>
|
<div className="compas-arr" style={{ transform: `rotate(${getDegreeInOrientation(compasDeg)}deg)` }}></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="center-wrap">
|
<div className="center-wrap">
|
||||||
<div className="outline-form">
|
<div className="d-check-box pop">
|
||||||
<div className="d-check-box pop mr10">
|
|
||||||
<input type="checkbox" id="ch99" checked={hasAnglePassivity} onChange={() => setHasAnglePassivity(!hasAnglePassivity)} />
|
<input type="checkbox" id="ch99" checked={hasAnglePassivity} onChange={() => setHasAnglePassivity(!hasAnglePassivity)} />
|
||||||
<label htmlFor="ch99">{getMessage('modal.module.basic.setting.orientation.setting.angle.passivity')}</label>
|
<label htmlFor="ch99">{getMessage('modal.module.basic.setting.orientation.setting.angle.passivity')}(-180 〜 180)</label>
|
||||||
</div>
|
</div>
|
||||||
<div className="input-grid mr10" style={{ width: '60px' }}>
|
<div className="outline-form">
|
||||||
{/*<input*/}
|
<div className="input-grid mr10" style={{ width: '160px' }}>
|
||||||
{/* type="text"*/}
|
<input
|
||||||
{/* className="input-origin block"*/}
|
type="text"
|
||||||
{/* value={inputCompasDeg}*/}
|
|
||||||
{/* readOnly={!hasAnglePassivity}*/}
|
|
||||||
{/* placeholder={0}*/}
|
|
||||||
{/* onChange={(e) => checkDegree(e.target.value)}*/}
|
|
||||||
{/*/>*/}
|
|
||||||
<CalculatorInput
|
|
||||||
id=""
|
|
||||||
name=""
|
|
||||||
label=""
|
|
||||||
className="input-origin block"
|
className="input-origin block"
|
||||||
value={inputCompasDeg}
|
value={compasDeg}
|
||||||
readOnly={!hasAnglePassivity}
|
readOnly={!hasAnglePassivity}
|
||||||
onChange={(value) => {
|
placeholder={0}
|
||||||
// Convert to number and ensure it's within -180 to 180 range
|
onChange={
|
||||||
const numValue = parseInt(value, 10);
|
(e) => checkDegree(e.target.value)
|
||||||
if (!isNaN(numValue)) {
|
// setCompasDeg(
|
||||||
const clampedValue = Math.min(180, Math.max(-180, numValue));
|
|
||||||
setInputCompasDeg(String(clampedValue));
|
// e.target.value === '-' || (e.target.value !== '' && parseInt(e.target.value) <= 180 && parseInt(e.target.value) >= -180)
|
||||||
} else {
|
// ? e.target.value
|
||||||
setInputCompasDeg(value);
|
// : 0,
|
||||||
|
// )
|
||||||
}
|
}
|
||||||
}}
|
|
||||||
options={{
|
|
||||||
allowNegative: true,
|
|
||||||
allowDecimal: false
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<span className="thin">°</span>
|
<span className="thin">°</span>
|
||||||
<span className="thin">( -180 〜 180 )</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="compas-table-wrap">
|
|
||||||
<div className="compas-table-box mb10">
|
|
||||||
<div className="outline-form mb10">
|
|
||||||
<span>{getMessage('modal.module.basic.setting.module.series.setting')}</span>
|
|
||||||
<div className="grid-select">
|
|
||||||
<div className="grid-select">
|
|
||||||
<QSelectBox
|
|
||||||
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}
|
|
||||||
targetKey={'itemId'}
|
|
||||||
sourceKey={'itemId'}
|
|
||||||
showKey={'itemNm'}
|
|
||||||
onChange={(e) => handleChangeModule(e)}
|
|
||||||
showFirstOptionWhenEmpty = {true}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="roof-module-table">
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
{moduleData.header.map((header) => {
|
|
||||||
return (
|
|
||||||
<th key={header.prop} style={{ width: header.width ? header.width + 'px' : '' }}>
|
|
||||||
{header.name}
|
|
||||||
</th>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{Array.from({ length: 3 }).map((_, index) => {
|
|
||||||
return selectedModules && selectedModules?.itemList && selectedModules?.itemList?.length >= index + 1 ? (
|
|
||||||
<tr key={index}>
|
|
||||||
<td>
|
|
||||||
<div className="color-wrap">
|
|
||||||
<span
|
|
||||||
className="color-box"
|
|
||||||
style={{
|
|
||||||
backgroundColor: selectedModules.itemList[index].color,
|
|
||||||
}}
|
|
||||||
></span>
|
|
||||||
<span className="name">{selectedModules.itemList[index].itemNm}</span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td className="al-r">{Number(selectedModules.itemList[index].shortAxis).toFixed(0)}</td>
|
|
||||||
<td className="al-r">{Number(selectedModules.itemList[index].longAxis).toFixed(0)}</td>
|
|
||||||
<td className="al-r">{Number(selectedModules.itemList[index].wpOut).toFixed(0)}</td>
|
|
||||||
</tr>
|
|
||||||
) : (
|
|
||||||
<tr key={index}>
|
|
||||||
<td>
|
|
||||||
<div className="color-wrap"></div>
|
|
||||||
</td>
|
|
||||||
<td className="al-r"></td>
|
|
||||||
<td className="al-r"></td>
|
|
||||||
<td className="al-r"></td>
|
|
||||||
</tr>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
{basicSetting && basicSetting.roofSizeSet === '3' && (
|
|
||||||
<div className="outline-form mt15">
|
|
||||||
<span>{getMessage('modal.module.basic.setting.module.placement.area')}</span>
|
|
||||||
<div className="input-grid mr10" style={{ width: '60px' }}>
|
|
||||||
{/*<input type="text" className="input-origin block" value={inputMargin} onChange={(e) => setInputMargin(normalizeDecimal(e.target.value))} />*/}
|
|
||||||
<CalculatorInput
|
|
||||||
id=""
|
|
||||||
name=""
|
|
||||||
label=""
|
|
||||||
className="input-origin block"
|
|
||||||
value={inputMargin}
|
|
||||||
onChange={(value) => setInputMargin(value)}
|
|
||||||
options={{
|
|
||||||
allowNegative: false,
|
|
||||||
allowDecimal: false
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<span className="thin">m</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{basicSetting && basicSetting.roofSizeSet !== '3' && (
|
|
||||||
<div className="compas-table-box">
|
|
||||||
<div className="compas-grid-table">
|
|
||||||
<div className="outline-form">
|
|
||||||
<span>{getMessage('modal.module.basic.setting.module.surface.type')}</span>
|
|
||||||
<div className="grid-select">
|
|
||||||
{roughnessCodes.length > 0 && managementState && (
|
|
||||||
<QSelectBox
|
|
||||||
options={roughnessCodes}
|
|
||||||
value={inputRoughness}
|
|
||||||
targetKey={'clCode'}
|
|
||||||
sourceKey={'clCode'}
|
|
||||||
showKey={'clCodeNm'}
|
|
||||||
onChange={(e) => handleChangeRoughness(e)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="outline-form">
|
|
||||||
<span>{getMessage('modal.module.basic.setting.module.fitting.height')}</span>
|
|
||||||
<div className="input-grid mr10">
|
|
||||||
{/*<input*/}
|
|
||||||
{/* type="text"*/}
|
|
||||||
{/* className="input-origin block"*/}
|
|
||||||
{/* value={inputInstallHeight}*/}
|
|
||||||
{/* onChange={(e) => handleChangeInstallHeight(normalizeDecimal(e.target.value))}*/}
|
|
||||||
{/*/>*/}
|
|
||||||
<CalculatorInput
|
|
||||||
id=""
|
|
||||||
name=""
|
|
||||||
label=""
|
|
||||||
className="input-origin block"
|
|
||||||
value={inputInstallHeight}
|
|
||||||
onChange={(value) => handleChangeInstallHeight(value)}
|
|
||||||
options={{
|
|
||||||
allowNegative: false,
|
|
||||||
allowDecimal: false
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<span className="thin">m</span>
|
|
||||||
</div>
|
|
||||||
<div className="outline-form">
|
|
||||||
<span>{getMessage('modal.module.basic.setting.module.standard.wind.speed')}</span>
|
|
||||||
<div className="grid-select">
|
|
||||||
{windSpeedCodes.length > 0 && managementState && (
|
|
||||||
<QSelectBox
|
|
||||||
title={''}
|
|
||||||
options={windSpeedCodes}
|
|
||||||
value={inputStandardWindSpeed}
|
|
||||||
targetKey={'clCode'}
|
|
||||||
sourceKey={'clCode'}
|
|
||||||
showKey={'clCodeNm'}
|
|
||||||
onChange={(e) => handleChangeStandardWindSpeed(e)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="outline-form">
|
|
||||||
<span>{getMessage('modal.module.basic.setting.module.standard.snowfall.amount')}</span>
|
|
||||||
<div className="input-grid mr10">
|
|
||||||
{/*<input*/}
|
|
||||||
{/* type="text"*/}
|
|
||||||
{/* className="input-origin block"*/}
|
|
||||||
{/* value={inputVerticalSnowCover}*/}
|
|
||||||
{/* onChange={(e) => handleChangeVerticalSnowCover(normalizeDecimal(e.target.value))}*/}
|
|
||||||
{/*/>*/}
|
|
||||||
<CalculatorInput
|
|
||||||
id=""
|
|
||||||
name=""
|
|
||||||
label=""
|
|
||||||
className="input-origin block"
|
|
||||||
value={inputVerticalSnowCover}
|
|
||||||
onChange={(value) => handleChangeVerticalSnowCover(value)}
|
|
||||||
options={{
|
|
||||||
allowNegative: false,
|
|
||||||
allowDecimal: false
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<span className="thin">cm</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,46 +1,29 @@
|
|||||||
import { forwardRef, useEffect, useState } from 'react'
|
import { forwardRef, useEffect, useState } from 'react'
|
||||||
import { useMessage } from '@/hooks/useMessage'
|
import { useMessage } from '@/hooks/useMessage'
|
||||||
import { useModuleBasicSetting } from '@/hooks/module/useModuleBasicSetting'
|
import { useModuleBasicSetting } from '@/hooks/module/useModuleBasicSetting'
|
||||||
import {
|
import { checkedModuleState, currentCanvasPlanState, isManualModuleSetupState } from '@/store/canvasAtom'
|
||||||
checkedModuleState,
|
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
|
||||||
isManualModuleLayoutSetupState,
|
|
||||||
isManualModuleSetupState,
|
|
||||||
moduleRowColArrayState,
|
|
||||||
moduleSetupOptionState,
|
|
||||||
toggleManualSetupModeState,
|
|
||||||
} from '@/store/canvasAtom'
|
|
||||||
import { useRecoilState, useRecoilValue, useResetRecoilState, useSetRecoilState } from 'recoil'
|
|
||||||
import { moduleSelectionDataState, selectedModuleState } from '@/store/selectedModuleOptions'
|
import { moduleSelectionDataState, selectedModuleState } from '@/store/selectedModuleOptions'
|
||||||
import { isObjectNotEmpty } from '@/util/common-utils'
|
import { isObjectNotEmpty } from '@/util/common-utils'
|
||||||
import { normalizeDigits } from '@/util/input-utils'
|
|
||||||
import Image from 'next/image'
|
|
||||||
|
|
||||||
const Placement = forwardRef((props, refs) => {
|
const Placement = forwardRef((props, refs) => {
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
const [useTab, setUseTab] = useState(true)
|
const [isChidori, setIsChidori] = useState(false)
|
||||||
const [guideType, setGuideType] = useState('batch')
|
|
||||||
|
|
||||||
const [isChidoriNotAble, setIsChidoriNotAble] = useState(false)
|
const [isChidoriNotAble, setIsChidoriNotAble] = useState(false)
|
||||||
|
|
||||||
|
const [setupLocation, setSetupLocation] = useState('eaves')
|
||||||
|
const [isMaxSetup, setIsMaxSetup] = useState('false')
|
||||||
const [selectedItems, setSelectedItems] = useState({})
|
const [selectedItems, setSelectedItems] = useState({})
|
||||||
|
|
||||||
const [selectedModules, setSelectedModules] = useRecoilState(selectedModuleState)
|
const [selectedModules, setSelectedModules] = useRecoilState(selectedModuleState)
|
||||||
|
|
||||||
const setCheckedModules = useSetRecoilState(checkedModuleState)
|
const setCheckedModules = useSetRecoilState(checkedModuleState)
|
||||||
const moduleSelectionData = useRecoilValue(moduleSelectionDataState)
|
const moduleSelectionData = useRecoilValue(moduleSelectionDataState)
|
||||||
const { makeModuleInitArea, roofOutlineColor } = useModuleBasicSetting(3)
|
const { makeModuleInitArea } = useModuleBasicSetting(3)
|
||||||
|
|
||||||
const [isMultiModule, setIsMultiModule] = useState(false)
|
const [isMultiModule, setIsMultiModule] = useState(false)
|
||||||
|
|
||||||
//언마운트시 버튼 초기화
|
const [isManualModuleSetup, setIsManualModuleSetup] = useRecoilState(isManualModuleSetupState)
|
||||||
const setIsManualModuleSetup = useSetRecoilState(isManualModuleSetupState)
|
|
||||||
const setIsManualModuleLayoutSetup = useSetRecoilState(isManualModuleLayoutSetupState)
|
|
||||||
const setManualSetupMode = useSetRecoilState(toggleManualSetupModeState)
|
|
||||||
|
|
||||||
const [moduleSetupOption, setModuleSetupOption] = useRecoilState(moduleSetupOptionState) //모듈 설치 옵션
|
|
||||||
const resetModuleSetupOption = useResetRecoilState(moduleSetupOptionState)
|
|
||||||
|
|
||||||
const [colspan, setColspan] = useState(1)
|
|
||||||
const moduleRowColArray = useRecoilValue(moduleRowColArrayState)
|
|
||||||
|
|
||||||
//모듈 배치면 생성
|
//모듈 배치면 생성
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -53,24 +36,11 @@ const Placement = forwardRef((props, refs) => {
|
|||||||
makeModuleInitArea(moduleSelectionData)
|
makeModuleInitArea(moduleSelectionData)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (moduleSelectionData.module.itemList.length > 1) {
|
|
||||||
setColspan(2)
|
|
||||||
}
|
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
// refs.isChidori.current = 'false'
|
|
||||||
// refs.setupLocation.current = 'eaves'
|
|
||||||
setIsManualModuleSetup(false)
|
setIsManualModuleSetup(false)
|
||||||
setIsManualModuleLayoutSetup(false)
|
|
||||||
setManualSetupMode('off')
|
|
||||||
resetModuleSetupOption()
|
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
// useEffect(() => {
|
|
||||||
// console.log('moduleRowColArray', moduleRowColArray)
|
|
||||||
// }, [moduleRowColArray])
|
|
||||||
|
|
||||||
//최초 지입시 체크
|
//최초 지입시 체크
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isObjectNotEmpty(moduleSelectionData)) {
|
if (isObjectNotEmpty(moduleSelectionData)) {
|
||||||
@ -84,10 +54,8 @@ const Placement = forwardRef((props, refs) => {
|
|||||||
initCheckedModule = { ...initCheckedModule, [obj.itemId]: true }
|
initCheckedModule = { ...initCheckedModule, [obj.itemId]: true }
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
setSelectedItems(initCheckedModule)
|
setSelectedItems(initCheckedModule)
|
||||||
setSelectedModules(moduleSelectionData.module)
|
setSelectedModules(moduleSelectionData.module)
|
||||||
props.setLayoutSetup(moduleSelectionData.module.itemList.map((item) => ({ moduleId: item.itemId, col: 0, row: 0, checked: true })))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//모듈 배치면 생성
|
//모듈 배치면 생성
|
||||||
@ -112,58 +80,40 @@ const Placement = forwardRef((props, refs) => {
|
|||||||
header: [
|
header: [
|
||||||
{ type: 'check', name: '', prop: 'check', width: 70 },
|
{ type: 'check', name: '', prop: 'check', width: 70 },
|
||||||
{ type: 'color-box', name: getMessage('module'), prop: 'module' },
|
{ type: 'color-box', name: getMessage('module'), prop: 'module' },
|
||||||
{ type: 'text', name: getMessage('modal.module.basic.setting.module.placement.mix.asg.yn'), prop: 'mixAsgYn', width: 50 },
|
{ type: 'text', name: `${getMessage('output')} (W)`, prop: 'output', width: 70 },
|
||||||
{ type: 'text', name: `段数`, prop: 'rows', width: 60 },
|
|
||||||
{ type: 'text', name: `列数`, prop: 'cols', width: 60 },
|
|
||||||
],
|
],
|
||||||
rows: [],
|
rows: [],
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleChangeChidori = (e) => {
|
const handleChangeChidori = (e) => {
|
||||||
const bool = e.target.value === 'true' ? true : false
|
const bool = e.target.value === 'true' ? true : false
|
||||||
setModuleSetupOption({ ...moduleSetupOption, isChidori: bool })
|
setIsChidori(bool)
|
||||||
|
refs.isChidori.current = e.target.value
|
||||||
//변경하면 수동 다 꺼짐
|
|
||||||
setIsManualModuleSetup(false)
|
|
||||||
setIsManualModuleLayoutSetup(false)
|
|
||||||
setManualSetupMode('off')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSetupLocation = (e) => {
|
const handleSetupLocation = (e) => {
|
||||||
setModuleSetupOption({ ...moduleSetupOption, setupLocation: e.target.value })
|
setSetupLocation(e.target.value)
|
||||||
|
refs.setupLocation.current = e.target.value
|
||||||
|
}
|
||||||
|
|
||||||
//변경하면 수동 다 꺼짐
|
const handleMaxSetup = (e) => {
|
||||||
setIsManualModuleSetup(false)
|
if (e.target.checked) {
|
||||||
setIsManualModuleLayoutSetup(false)
|
setIsMaxSetup('true')
|
||||||
setManualSetupMode('off')
|
refs.isMaxSetup.current = 'true'
|
||||||
|
} else {
|
||||||
|
setIsMaxSetup('false')
|
||||||
|
refs.isMaxSetup.current = 'false'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//체크된 모듈 아이디 추출
|
//체크된 모듈 아이디 추출
|
||||||
const handleSelectedItem = (e, itemId) => {
|
const handleSelectedItem = (e) => {
|
||||||
setSelectedItems({ ...selectedItems, [e.target.name]: e.target.checked })
|
setSelectedItems({ ...selectedItems, [e.target.name]: e.target.checked })
|
||||||
|
|
||||||
const newLayoutSetup = [...props.layoutSetup]
|
|
||||||
props.layoutSetup.forEach((item, index) => {
|
|
||||||
if (item.moduleId === itemId) {
|
|
||||||
newLayoutSetup[index] = { ...props.layoutSetup[index], checked: e.target.checked }
|
|
||||||
}
|
|
||||||
})
|
|
||||||
props.setLayoutSetup(newLayoutSetup)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleLayoutSetup = (e, itemId, index) => {
|
|
||||||
const newLayoutSetup = [...props.layoutSetup]
|
|
||||||
newLayoutSetup[index] = {
|
|
||||||
...newLayoutSetup[index],
|
|
||||||
moduleId: itemId,
|
|
||||||
[e.target.name]: Number(normalizeDigits(e.target.value)),
|
|
||||||
}
|
|
||||||
props.setLayoutSetup(newLayoutSetup)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="module-table-flex-wrap">
|
<div className="module-table-flex-wrap mb10">
|
||||||
<div className="module-table-box">
|
<div className="module-table-box">
|
||||||
<div className="module-table-inner">
|
<div className="module-table-inner">
|
||||||
<div className="roof-module-table">
|
<div className="roof-module-table">
|
||||||
@ -185,8 +135,8 @@ const Placement = forwardRef((props, refs) => {
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{selectedModules?.itemList &&
|
{selectedModules.itemList &&
|
||||||
selectedModules?.itemList?.map((item, index) => (
|
selectedModules.itemList.map((item, index) => (
|
||||||
<tr key={index}>
|
<tr key={index}>
|
||||||
<td className="al-c">
|
<td className="al-c">
|
||||||
<div className="d-check-box no-text pop">
|
<div className="d-check-box no-text pop">
|
||||||
@ -195,7 +145,7 @@ const Placement = forwardRef((props, refs) => {
|
|||||||
id={item.itemId}
|
id={item.itemId}
|
||||||
name={item.itemId}
|
name={item.itemId}
|
||||||
checked={selectedItems[item.itemId]}
|
checked={selectedItems[item.itemId]}
|
||||||
onChange={(e) => handleSelectedItem(e, item.itemId)}
|
onChange={handleSelectedItem}
|
||||||
/>
|
/>
|
||||||
<label htmlFor={item.itemId}></label>
|
<label htmlFor={item.itemId}></label>
|
||||||
</div>
|
</div>
|
||||||
@ -206,208 +156,92 @@ const Placement = forwardRef((props, refs) => {
|
|||||||
<span className="name">{item.itemNm}</span>
|
<span className="name">{item.itemNm}</span>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td className="al-c">
|
<td className="al-r">{item.wpOut}</td>
|
||||||
<div className="color-wrap">
|
|
||||||
<span className="name">{item.mixAsgYn}</span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td className="al-r">
|
|
||||||
<div className="input-grid">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
className="input-origin block"
|
|
||||||
name="row"
|
|
||||||
value={props.layoutSetup[index]?.row ?? 1}
|
|
||||||
//defaultValue={0}
|
|
||||||
onChange={(e) => handleLayoutSetup(e, item.itemId, index)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td className="al-r">
|
|
||||||
<div className="input-grid">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
className="input-origin block"
|
|
||||||
name="col"
|
|
||||||
value={props.layoutSetup[index]?.col ?? 1}
|
|
||||||
//defaultValue={0}
|
|
||||||
onChange={(e) => handleLayoutSetup(e, item.itemId, index)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
))}
|
||||||
<tr>
|
|
||||||
<td></td>
|
|
||||||
<td></td>
|
|
||||||
<td></td>
|
|
||||||
<td></td>
|
|
||||||
<td></td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="module-table-box non-flex">
|
<div className="module-table-box">
|
||||||
<div className="module-table-inner">
|
<div className="module-table-inner">
|
||||||
<div className="roof-module-table">
|
<div className="self-table-tit">{getMessage('modal.module.basic.setting.module.placement.select.fitting.type')}</div>
|
||||||
<table>
|
<div className="module-self-table">
|
||||||
<thead>
|
<div className="self-table-item">
|
||||||
<tr>
|
<div className="self-item-th">{getMessage('modal.module.basic.setting.module.placement.waterfowl.arrangement')}</div>
|
||||||
<th>{getMessage('modal.module.basic.setting.module.placement.waterfowl.arrangement')}</th>
|
<div className="self-item-td">
|
||||||
<th>{getMessage('modal.module.basic.setting.module.placement.arrangement.standard')}</th>
|
<div className="pop-form-radio">
|
||||||
</tr>
|
<div className="d-check-radio pop">
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<div className="hexagonal-radio-wrap">
|
|
||||||
<div className="d-check-radio pop mb10">
|
|
||||||
<input
|
<input
|
||||||
type="radio"
|
type="radio"
|
||||||
name="radio02"
|
name="radio01"
|
||||||
id="ra03"
|
id="ra01"
|
||||||
checked={moduleSetupOption.isChidori}
|
checked={isChidori}
|
||||||
disabled={isChidoriNotAble}
|
disabled={isChidoriNotAble}
|
||||||
value={'true'}
|
value={'true'}
|
||||||
onChange={(e) => handleChangeChidori(e)}
|
onChange={(e) => handleChangeChidori(e)}
|
||||||
/>
|
/>
|
||||||
<label htmlFor="ra03">{getMessage('modal.module.basic.setting.module.placement.do')}</label>
|
<label htmlFor="ra01">{getMessage('modal.module.basic.setting.module.placement.do')}</label>
|
||||||
|
</div>
|
||||||
|
<div className="d-check-radio pop">
|
||||||
|
<input type="radio" name="radio02" id="ra02" checked={!isChidori} value={'false'} onChange={(e) => handleChangeChidori(e)} />
|
||||||
|
<label htmlFor="ra02">{getMessage('modal.module.basic.setting.module.placement.do.not')}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="self-table-item">
|
||||||
|
<div className="self-item-th">{getMessage('modal.module.basic.setting.module.placement.arrangement.standard')}</div>
|
||||||
|
<div className="self-item-td">
|
||||||
|
<div className="pop-form-radio">
|
||||||
|
<div className="d-check-radio pop">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="radio03"
|
||||||
|
id="ra03"
|
||||||
|
checked={setupLocation === 'center'}
|
||||||
|
value={'center'}
|
||||||
|
onChange={handleSetupLocation}
|
||||||
|
disabled={isMultiModule}
|
||||||
|
/>
|
||||||
|
<label htmlFor="ra03">{getMessage('modal.module.basic.setting.module.placement.arrangement.standard.center')}</label>
|
||||||
</div>
|
</div>
|
||||||
<div className="d-check-radio pop">
|
<div className="d-check-radio pop">
|
||||||
<input
|
<input
|
||||||
type="radio"
|
type="radio"
|
||||||
name="radio02"
|
name="radio04"
|
||||||
id="ra04"
|
id="ra04"
|
||||||
checked={!moduleSetupOption.isChidori}
|
checked={setupLocation === 'eaves'}
|
||||||
value={'false'}
|
|
||||||
onChange={(e) => handleChangeChidori(e)}
|
|
||||||
/>
|
|
||||||
<label htmlFor="ra04">{getMessage('modal.module.basic.setting.module.placement.do.not')}</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div className="hexagonal-radio-wrap">
|
|
||||||
<div className="d-check-radio pop mb10">
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
name="radio03"
|
|
||||||
id="ra05"
|
|
||||||
checked={moduleSetupOption.setupLocation === 'eaves'}
|
|
||||||
value={'eaves'}
|
value={'eaves'}
|
||||||
onChange={handleSetupLocation}
|
onChange={handleSetupLocation}
|
||||||
/>
|
/>
|
||||||
<label htmlFor="ra05">{getMessage('modal.module.basic.setting.module.placement.arrangement.standard.eaves')}</label>
|
<label htmlFor="ra04">{getMessage('modal.module.basic.setting.module.placement.arrangement.standard.eaves')}</label>
|
||||||
</div>
|
</div>
|
||||||
<div className="d-check-radio pop">
|
<div className="d-check-radio pop">
|
||||||
<input
|
<input
|
||||||
type="radio"
|
type="radio"
|
||||||
name="radio03"
|
name="radio05"
|
||||||
id="ra06"
|
id="ra05"
|
||||||
checked={moduleSetupOption.setupLocation === 'ridge'}
|
checked={setupLocation === 'ridge'}
|
||||||
value={'ridge'}
|
value={'ridge'}
|
||||||
onChange={handleSetupLocation}
|
onChange={handleSetupLocation}
|
||||||
disabled={isMultiModule}
|
disabled={isMultiModule}
|
||||||
/>
|
/>
|
||||||
<label htmlFor="ra06">{getMessage('modal.module.basic.setting.module.placement.arrangement.standard.ridge')}</label>
|
<label htmlFor="ra05">{getMessage('modal.module.basic.setting.module.placement.arrangement.standard.ridge')}</label>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="hide-tab-wrap">
|
|
||||||
<div className="hide-check-guide">
|
|
||||||
{getMessage('modal.module.basic.setting.module.placement.info')}
|
|
||||||
<button className={`arr ${useTab ? 'act' : ''}`} onClick={() => setUseTab(!useTab)}></button>
|
|
||||||
</div>
|
</div>
|
||||||
<div className={`hide-tab-contents ${!useTab ? 'hide' : ''}`}>
|
<div className="self-table-flx">
|
||||||
<div className="roof-content-tab-wrap">
|
{/* <div className="d-check-box pop">
|
||||||
<button className={`btn-frame block modal mr5 ${guideType === 'batch' ? 'act' : ''} `} onClick={() => setGuideType('batch')}>
|
<input type="checkbox" id="ch04" checked={isMaxSetup === 'true'} value={'true'} onChange={handleMaxSetup} />
|
||||||
{getMessage('modal.module.basic.setting.module.placement.info.batch')}
|
<label htmlFor="ch04">{getMessage('modal.module.basic.setting.module.placement.maximum')}</label>
|
||||||
</button>
|
</div> */}
|
||||||
<button className={`btn-frame block modal mr5 ${guideType === 'module' ? 'act' : ''}`} onClick={() => setGuideType('module')}>
|
|
||||||
{getMessage('modal.module.basic.setting.module.placement.info.module')}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
{guideType === 'batch' && (
|
|
||||||
<div className={`roof-warning-wrap mt10`}>
|
|
||||||
<div className="guide">
|
|
||||||
{getMessage('modal.module.basic.setting.module.placement.info.batch.content1')}
|
|
||||||
<br />
|
|
||||||
{getMessage('modal.module.basic.setting.module.placement.info.batch.content2')}
|
|
||||||
</div>
|
|
||||||
<div className="roof-warning-img-wrap">
|
|
||||||
<div className="roof-warning-img">
|
|
||||||
<Image src={'/static/images/canvas/roof_warning_correct.png'} width={350} height={198} alt="" />
|
|
||||||
</div>
|
|
||||||
<div className="roof-warning-img">
|
|
||||||
<Image src={'/static/images/canvas/roof_warning_wrong.png'} width={350} height={198} alt="" />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
{guideType === 'module' && (
|
|
||||||
<div className={`module-table-box mt10 ${!useTab ? 'hide' : ''}`}>
|
|
||||||
<div className="module-table-inner">
|
|
||||||
<div className="roof-module-table">
|
|
||||||
<table className="">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th rowSpan={2} style={{ width: '22%' }}></th>
|
|
||||||
{selectedModules &&
|
|
||||||
selectedModules.itemList?.map((item) => (
|
|
||||||
// <th colSpan={colspan}>
|
|
||||||
<th>
|
|
||||||
<div className="color-wrap">
|
|
||||||
<span className="color-box" style={{ backgroundColor: item.color }}></span>
|
|
||||||
<span className="name">{item.itemNm}</span>
|
|
||||||
</div>
|
|
||||||
</th>
|
|
||||||
))}
|
|
||||||
{colspan > 1 && <th rowSpan={2}>{getMessage('modal.module.basic.setting.module.placement.max.rows.multiple')}</th>}
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
{selectedModules &&
|
|
||||||
selectedModules.itemList?.map((item) => (
|
|
||||||
<>
|
|
||||||
<th>{getMessage('modal.module.basic.setting.module.placement.max.row')}</th>
|
|
||||||
{/* {colspan > 1 && <th>{getMessage('modal.module.basic.setting.module.placement.max.rows.multiple')}</th>} */}
|
|
||||||
</>
|
|
||||||
))}
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{moduleSelectionData.roofConstructions.map((item, index) => (
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<div className="color-wrap">
|
|
||||||
<span className="color-box" style={{ backgroundColor: roofOutlineColor(item.addRoof?.index) }}></span>
|
|
||||||
<span className="name">{item.addRoof?.roofMatlNmJp}</span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
{moduleRowColArray[index]?.map((item, index2) => (
|
|
||||||
<>
|
|
||||||
<td className="al-c">{item.moduleMaxRows}</td>
|
|
||||||
{/* {colspan > 1 && <td className="al-c">{item.mixModuleMaxRows}</td>} */}
|
|
||||||
{colspan > 1 && index2 === moduleRowColArray[index].length - 1 && <td className="al-c">{item.maxRow}</td>}
|
|
||||||
</>
|
|
||||||
))}
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1,999 +0,0 @@
|
|||||||
import { GlobalDataContext } from '@/app/GlobalDataProvider'
|
|
||||||
import QSelectBox from '@/components/common/select/QSelectBox'
|
|
||||||
import { useModuleBasicSetting } from '@/hooks/module/useModuleBasicSetting'
|
|
||||||
import { useModuleTrestle } from '@/hooks/module/useModuleTrestle'
|
|
||||||
import { useMessage } from '@/hooks/useMessage'
|
|
||||||
import { currentAngleTypeSelector, pitchTextSelector } from '@/store/canvasAtom'
|
|
||||||
import { roofsState } from '@/store/roofAtom'
|
|
||||||
import { moduleSelectionDataState, selectedModuleState } from '@/store/selectedModuleOptions'
|
|
||||||
import { forwardRef, useContext, useEffect, useImperativeHandle, useRef, useState } from 'react'
|
|
||||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
|
||||||
import Swal from 'sweetalert2'
|
|
||||||
import { normalizeDigits } from '@/util/input-utils'
|
|
||||||
import { CalculatorInput } from '@/components/common/input/CalcInput'
|
|
||||||
|
|
||||||
const Trestle = forwardRef((props, ref) => {
|
|
||||||
const { tabNum, setTabNum, trestleTrigger, roofs, setRoofs, moduleSelectionData, setModuleSelectionData, setRoofsStore } = props
|
|
||||||
const { getMessage } = useMessage()
|
|
||||||
// const [selectedTrestle, setSelectedTrestle] = useState()
|
|
||||||
const currentAngleType = useRecoilValue(currentAngleTypeSelector)
|
|
||||||
const pitchText = useRecoilValue(pitchTextSelector)
|
|
||||||
const [selectedRoof, setSelectedRoof] = useState(null)
|
|
||||||
const [isAutoSelecting, setIsAutoSelecting] = useState(false) // 자동 선택 중인지 상태
|
|
||||||
const [autoSelectTimeout, setAutoSelectTimeout] = useState(null) // 타임아웃 참조
|
|
||||||
const autoSelectTimeoutRef = useRef(null)
|
|
||||||
|
|
||||||
// 공통 타임아웃 설정 (밀리초)
|
|
||||||
const AUTO_SELECT_TIMEOUT = 500 // API 호출 완료 대기 시간
|
|
||||||
const {
|
|
||||||
trestleState,
|
|
||||||
trestleDetail,
|
|
||||||
dispatch,
|
|
||||||
raftBaseList,
|
|
||||||
trestleList,
|
|
||||||
constMthdList,
|
|
||||||
roofBaseList,
|
|
||||||
constructionList,
|
|
||||||
eavesMargin,
|
|
||||||
ridgeMargin,
|
|
||||||
kerabaMargin,
|
|
||||||
setEavesMargin,
|
|
||||||
setRidgeMargin,
|
|
||||||
setKerabaMargin,
|
|
||||||
lengthBase,
|
|
||||||
setLengthBase,
|
|
||||||
hajebichi,
|
|
||||||
setHajebichi,
|
|
||||||
cvrYn,
|
|
||||||
cvrChecked,
|
|
||||||
snowGdPossYn,
|
|
||||||
snowGdChecked,
|
|
||||||
setCvrYn,
|
|
||||||
setCvrChecked,
|
|
||||||
setSnowGdPossYn,
|
|
||||||
setSnowGdChecked,
|
|
||||||
} = useModuleTrestle({
|
|
||||||
selectedRoof,
|
|
||||||
})
|
|
||||||
const selectedModules = useRecoilValue(selectedModuleState) //선택된 모듈
|
|
||||||
// const [moduleSelectionData, setModuleSelectionData] = useRecoilState(moduleSelectionDataState)
|
|
||||||
const [selectedRaftBase, setSelectedRaftBase] = useState(null)
|
|
||||||
const [selectedTrestle, setSelectedTrestle] = useState(null)
|
|
||||||
const [selectedConstMthd, setSelectedConstMthd] = useState(null)
|
|
||||||
const [selectedConstruction, setSelectedConstruction] = useState(null)
|
|
||||||
const [selectedRoofBase, setSelectedRoofBase] = useState(null)
|
|
||||||
const { managementState } = useContext(GlobalDataContext)
|
|
||||||
const { restoreModuleInstArea } = useModuleBasicSetting()
|
|
||||||
const [flag, setFlag] = useState(false)
|
|
||||||
const tempModuleSelectionData = useRef(null)
|
|
||||||
const [autoSelectStep, setAutoSelectStep] = useState(null) // 'raftBase', 'trestle', 'constMthd', 'roofBase', 'construction'
|
|
||||||
const prevHajebichiRef = useRef();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (roofs && roofs.length > 0 && !selectedRoof) {
|
|
||||||
console.log("roofs:::::", roofs.length)
|
|
||||||
setLengthBase(roofs[0].length);
|
|
||||||
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()
|
|
||||||
}, [roofs, selectedRoof]) // selectedRoof 추가
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (flag && moduleSelectionData) {
|
|
||||||
if (JSON.stringify(tempModuleSelectionData.current) === JSON.stringify(moduleSelectionData)) {
|
|
||||||
setTabNum(tabNum + 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [flag, moduleSelectionData])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (selectedRoof) {
|
|
||||||
if (moduleSelectionData?.roofConstructions?.length >= selectedRoof.index + 1) {
|
|
||||||
const { construction, trestle, trestleDetail } = moduleSelectionData?.roofConstructions[selectedRoof.index]
|
|
||||||
dispatch({
|
|
||||||
type: 'SET_INITIALIZE',
|
|
||||||
roof: { common: moduleSelectionData.common, module: moduleSelectionData.module, construction, trestle, trestleDetail, ...selectedRoof },
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
dispatch({ type: 'SET_INITIALIZE', roof: { ...selectedRoof, common: moduleSelectionData.common, module: moduleSelectionData.module } })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [selectedRoof])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (raftBaseList.length > 0) {
|
|
||||||
setSelectedRaftBase(raftBaseList.find((raft) => raft.clCode === selectedRoof?.raft) ?? null)
|
|
||||||
} else {
|
|
||||||
setSelectedRaftBase(null)
|
|
||||||
}
|
|
||||||
}, [raftBaseList])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (trestleList.length > 0) {
|
|
||||||
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 {
|
|
||||||
setSelectedTrestle(null)
|
|
||||||
}
|
|
||||||
}, [trestleList, autoSelectStep])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (constMthdList.length > 0) {
|
|
||||||
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 {
|
|
||||||
setSelectedConstMthd(null)
|
|
||||||
}
|
|
||||||
}, [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(() => {
|
|
||||||
if (constructionList.length > 0) {
|
|
||||||
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) {
|
|
||||||
Swal.fire({
|
|
||||||
title: getMessage('modal.module.basic.settting.module.error4', [selectedRoof?.nameJp]),
|
|
||||||
icon: 'warning',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
setSelectedConstruction(null)
|
|
||||||
}
|
|
||||||
}, [constructionList, autoSelectStep])
|
|
||||||
|
|
||||||
const getConstructionState = (index) => {
|
|
||||||
if (constructionList && constructionList.length > 0) {
|
|
||||||
if (constructionList[index].constPossYn === 'Y') {
|
|
||||||
if (trestleState && trestleState.constTp === constructionList[index].constTp) {
|
|
||||||
return 'blue'
|
|
||||||
}
|
|
||||||
return 'white'
|
|
||||||
}
|
|
||||||
return 'no-click'
|
|
||||||
}
|
|
||||||
return 'no-click'
|
|
||||||
}
|
|
||||||
|
|
||||||
const onChangeLength = (e) => {
|
|
||||||
setLengthBase(e)
|
|
||||||
// 다음 단계들 초기화
|
|
||||||
setSelectedRaftBase(null)
|
|
||||||
setSelectedTrestle(null)
|
|
||||||
setSelectedConstMthd(null)
|
|
||||||
setSelectedRoofBase(null)
|
|
||||||
setSelectedConstruction(null)
|
|
||||||
|
|
||||||
dispatch({
|
|
||||||
type: 'SET_LENGTH',
|
|
||||||
roof: {
|
|
||||||
length: e,
|
|
||||||
moduleTpCd: selectedModules.itemTp ?? '',
|
|
||||||
roofMatlCd: selectedRoof?.roofMatlCd ?? '',
|
|
||||||
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) => {
|
|
||||||
setSelectedRaftBase(e)
|
|
||||||
// 다음 단계들 초기화
|
|
||||||
setSelectedTrestle(null)
|
|
||||||
setSelectedConstMthd(null)
|
|
||||||
setSelectedRoofBase(null)
|
|
||||||
setSelectedConstruction(null)
|
|
||||||
|
|
||||||
dispatch({
|
|
||||||
type: 'SET_RAFT_BASE',
|
|
||||||
roof: {
|
|
||||||
moduleTpCd: selectedModules.itemTp ?? '',
|
|
||||||
roofMatlCd: selectedRoof?.roofMatlCd ?? '',
|
|
||||||
raft: e.clCode,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
// 다음 단계(가대메이커) 자동 선택 설정 - 지연 실행
|
|
||||||
setTimeout(() => {
|
|
||||||
setAutoSelectStep('trestle')
|
|
||||||
}, AUTO_SELECT_TIMEOUT) // API 호출 완료를 위한 더 긴 지연
|
|
||||||
}
|
|
||||||
|
|
||||||
const onChangeHajebichi = (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({
|
|
||||||
type: 'SET_HAJEBICHI',
|
|
||||||
roof: {
|
|
||||||
moduleTpCd: selectedModules.itemTp ?? '',
|
|
||||||
roofMatlCd: selectedRoof?.roofMatlCd ?? '',
|
|
||||||
raft: selectedRaftBase?.clCode ?? selectedRoof?.roofBaseCd,
|
|
||||||
hajebichi: e,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
// 다음 단계(가대메이커) 자동 선택 설정 - 지연 실행
|
|
||||||
setTimeout(() => {
|
|
||||||
setAutoSelectStep('trestle')
|
|
||||||
}, AUTO_SELECT_TIMEOUT)
|
|
||||||
}
|
|
||||||
|
|
||||||
const onChangeTrestleMaker = (e) => {
|
|
||||||
setSelectedTrestle(e)
|
|
||||||
// 다음 단계들 초기화
|
|
||||||
setSelectedConstMthd(null)
|
|
||||||
setSelectedRoofBase(null)
|
|
||||||
setSelectedConstruction(null)
|
|
||||||
|
|
||||||
dispatch({
|
|
||||||
type: 'SET_TRESTLE_MAKER',
|
|
||||||
roof: {
|
|
||||||
moduleTpCd: selectedModules.itemTp ?? '',
|
|
||||||
roofMatlCd: selectedRoof?.roofMatlCd ?? '',
|
|
||||||
raft: selectedRaftBase?.clCode ?? selectedRoof?.roofBaseCd,
|
|
||||||
//hajebichi: selectedRaftBase?.hajebichi ?? selectedRoof?.hajebichi,
|
|
||||||
trestleMkrCd: e.trestleMkrCd,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
// API 호출 완료 후 다음 단계(공법) 자동 선택 설정
|
|
||||||
setTimeout(() => {
|
|
||||||
setAutoSelectStep('constMthd')
|
|
||||||
}, AUTO_SELECT_TIMEOUT)
|
|
||||||
}
|
|
||||||
|
|
||||||
const onChangeConstMthd = (e) => {
|
|
||||||
setSelectedConstMthd(e)
|
|
||||||
// 다음 단계 초기화
|
|
||||||
setSelectedRoofBase(null)
|
|
||||||
setSelectedConstruction(null)
|
|
||||||
|
|
||||||
dispatch({
|
|
||||||
type: 'SET_CONST_MTHD',
|
|
||||||
roof: {
|
|
||||||
moduleTpCd: selectedModules.itemTp ?? '',
|
|
||||||
roofMatlCd: selectedRoof?.roofMatlCd ?? '',
|
|
||||||
raft: selectedRaftBase?.clCode ?? selectedRoof?.roofBaseCd,
|
|
||||||
//hajebichi: selectedRaftBase?.hajebichi ?? selectedRoof?.hajebichi,
|
|
||||||
trestleMkrCd: selectedTrestle?.trestleMkrCd,
|
|
||||||
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) => {
|
|
||||||
setSelectedRoofBase(e)
|
|
||||||
setSelectedConstruction(null)
|
|
||||||
|
|
||||||
dispatch({
|
|
||||||
type: 'SET_ROOF_BASE',
|
|
||||||
roof: {
|
|
||||||
moduleTpCd: selectedModules.itemTp ?? '',
|
|
||||||
roofMatlCd: selectedRoof?.roofMatlCd ?? '',
|
|
||||||
raft: selectedRaftBase?.clCode ?? selectedRoof?.roofBaseCd,
|
|
||||||
//hajebichi: selectedRaftBase?.hajebichi ?? selectedRoof?.hajebichi,
|
|
||||||
trestleMkrCd: selectedTrestle?.trestleMkrCd,
|
|
||||||
constMthdCd: selectedConstMthd?.constMthdCd,
|
|
||||||
roofBaseCd: e.roofBaseCd,
|
|
||||||
illuminationTp: managementState?.surfaceTypeValue ?? '',
|
|
||||||
instHt: managementState?.installHeight ?? '',
|
|
||||||
stdWindSpeed: managementState?.standardWindSpeedId ?? '',
|
|
||||||
stdSnowLd: managementState?.verticalSnowCover ?? '',
|
|
||||||
inclCd: selectedRoof?.pitch ?? 0,
|
|
||||||
roofPitch: Math.round(hajebichi ?? 0),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
// API 호출 완료 후 다음 단계(construction) 자동 선택 설정
|
|
||||||
setTimeout(() => {
|
|
||||||
setAutoSelectStep('construction')
|
|
||||||
}, AUTO_SELECT_TIMEOUT)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleConstruction = (index) => {
|
|
||||||
if (constructionList[index]?.constPossYn === 'Y') {
|
|
||||||
dispatch({
|
|
||||||
type: 'SET_CONSTRUCTION',
|
|
||||||
roof: {
|
|
||||||
moduleTpCd: selectedModules.itemTp ?? '',
|
|
||||||
roofMatlCd: selectedRoof?.roofMatlCd ?? '',
|
|
||||||
raft: selectedRaftBase?.clCode ?? selectedRoof?.roofBaseCd,
|
|
||||||
//hajebichi: selectedRaftBase?.hajebichi ?? selectedRoof?.hajebichi,
|
|
||||||
trestleMkrCd: selectedTrestle.trestleMkrCd,
|
|
||||||
constMthdCd: selectedConstMthd.constMthdCd,
|
|
||||||
roofBaseCd: selectedRoofBase.roofBaseCd,
|
|
||||||
illuminationTp: managementState?.surfaceTypeValue ?? '',
|
|
||||||
instHt: managementState?.installHeight ?? '',
|
|
||||||
stdWindSpeed: managementState?.standardWindSpeedId ?? '',
|
|
||||||
stdSnowLd: managementState?.verticalSnowCover ?? '',
|
|
||||||
inclCd: selectedRoof?.pitch ?? 0,
|
|
||||||
roofPitch: Math.round(hajebichi ?? 0),
|
|
||||||
constTp: constructionList[index].constTp,
|
|
||||||
snowGdPossYn: constructionList[index].snowGdPossYn,
|
|
||||||
cvrYn: constructionList[index].cvrYn,
|
|
||||||
mixMatlNo: selectedModules.mixMatlNo,
|
|
||||||
// workingWidth: selectedRoof?.length?.toString() ?? '',
|
|
||||||
workingWidth: lengthBase,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
setCvrYn(constructionList[index].cvrYn)
|
|
||||||
setSnowGdPossYn(constructionList[index].snowGdPossYn)
|
|
||||||
setCvrChecked(false)
|
|
||||||
setSnowGdChecked(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleChangeRoofMaterial = (index) => {
|
|
||||||
const newAddedRoofs = roofs.map((roof, i) => {
|
|
||||||
if (i === selectedRoof.index) {
|
|
||||||
return {
|
|
||||||
...selectedRoof,
|
|
||||||
hajebichi,
|
|
||||||
length: lengthBase,
|
|
||||||
eavesMargin,
|
|
||||||
ridgeMargin,
|
|
||||||
kerabaMargin,
|
|
||||||
roofIndex: selectedRoof.index,
|
|
||||||
raft: selectedRaftBase?.clCode ?? selectedRoof?.roofBaseCd,
|
|
||||||
trestle: {
|
|
||||||
hajebichi: hajebichi,
|
|
||||||
length: lengthBase,
|
|
||||||
...selectedRaftBase,
|
|
||||||
...selectedTrestle,
|
|
||||||
...selectedConstMthd,
|
|
||||||
...selectedRoofBase,
|
|
||||||
},
|
|
||||||
construction: {
|
|
||||||
...constructionList.find((data) => data.constTp === trestleState.constTp),
|
|
||||||
cvrYn: cvrYn,
|
|
||||||
snowGdPossYn: snowGdPossYn,
|
|
||||||
cvrChecked: cvrChecked,
|
|
||||||
snowGdChecked: snowGdChecked,
|
|
||||||
setupCover: cvrChecked ?? false,
|
|
||||||
setupSnowCover: snowGdChecked ?? false,
|
|
||||||
},
|
|
||||||
trestleDetail: trestleDetail,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return roof
|
|
||||||
})
|
|
||||||
setRoofs(newAddedRoofs)
|
|
||||||
setSelectedRoof(newAddedRoofs[index])
|
|
||||||
}
|
|
||||||
|
|
||||||
const isComplete = async () => {
|
|
||||||
const newAddedRoofs = roofs.map((roof, i) => {
|
|
||||||
if (i === selectedRoof?.index) {
|
|
||||||
return {
|
|
||||||
...selectedRoof,
|
|
||||||
length: lengthBase,
|
|
||||||
eavesMargin,
|
|
||||||
ridgeMargin,
|
|
||||||
kerabaMargin,
|
|
||||||
roofIndex: roof.index,
|
|
||||||
raft: selectedRaftBase?.clCode ?? selectedRoof?.raft ?? '',
|
|
||||||
//hajebichi: selectedRaftBase?.hajebichi ?? selectedRoof?.hajebichi ?? 0,
|
|
||||||
trestle: {
|
|
||||||
length: lengthBase,
|
|
||||||
hajebichi: hajebichi,
|
|
||||||
...selectedRaftBase,
|
|
||||||
...selectedTrestle,
|
|
||||||
...selectedConstMthd,
|
|
||||||
...selectedRoofBase,
|
|
||||||
},
|
|
||||||
construction: {
|
|
||||||
//...constructionList.find((data) => newAddedRoofs[index].construction.constTp === data.constTp),
|
|
||||||
...constructionList.find((data) => trestleState.constTp === data.constTp),
|
|
||||||
cvrYn,
|
|
||||||
snowGdPossYn,
|
|
||||||
cvrChecked,
|
|
||||||
snowGdChecked,
|
|
||||||
setupCover: cvrChecked ?? false,
|
|
||||||
setupSnowCover: snowGdChecked ?? false,
|
|
||||||
},
|
|
||||||
trestleDetail: trestleDetail,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return roof
|
|
||||||
})
|
|
||||||
|
|
||||||
let result = true
|
|
||||||
for (let i = 0; i < newAddedRoofs.length; i++) {
|
|
||||||
const roof = newAddedRoofs[i]
|
|
||||||
|
|
||||||
if (!roof.trestle?.trestleMkrCd) {
|
|
||||||
Swal.fire({
|
|
||||||
title: getMessage('modal.module.basic.settting.module.error1', [roof.nameJp]), // 가대메이커를 선택해주세요.
|
|
||||||
icon: 'warning',
|
|
||||||
})
|
|
||||||
result = false
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!roof.trestle?.constMthdCd) {
|
|
||||||
Swal.fire({
|
|
||||||
title: getMessage('modal.module.basic.settting.module.error2', [roof.nameJp]), // 공법을 선택해주세요.
|
|
||||||
icon: 'warning',
|
|
||||||
})
|
|
||||||
result = false
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if (!roof.trestle?.roofBaseCd) {
|
|
||||||
Swal.fire({
|
|
||||||
title: getMessage('modal.module.basic.settting.module.error3', [roof.nameJp]), // 지붕밑바탕을 선택해주세요.
|
|
||||||
icon: 'warning',
|
|
||||||
})
|
|
||||||
result = false
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if (!roof.construction?.constTp) {
|
|
||||||
Swal.fire({
|
|
||||||
title: getMessage('modal.module.basic.settting.module.error12', [roof.nameJp]), // 시공법법을 선택해주세요.
|
|
||||||
icon: 'warning',
|
|
||||||
})
|
|
||||||
result = false
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (roof.lenAuth === 'C') {
|
|
||||||
if (!roof.trestle?.length) {
|
|
||||||
Swal.fire({
|
|
||||||
title: getMessage('modal.module.basic.settting.module.error5', [roof.nameJp]), // L 값을 입력해주세요.
|
|
||||||
icon: 'warning',
|
|
||||||
})
|
|
||||||
result = false
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (['C', 'R'].includes(roof.raftAuth)) {
|
|
||||||
if (!roof?.raft) {
|
|
||||||
Swal.fire({
|
|
||||||
title: getMessage('modal.module.basic.settting.module.error6', [roof.nameJp]), // 서까래 간격을 입력해주세요.
|
|
||||||
icon: 'warning',
|
|
||||||
})
|
|
||||||
result = false
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (['C', 'R'].includes(roof.roofPchAuth)) {
|
|
||||||
if (!roof?.hajebichi) {
|
|
||||||
Swal.fire({
|
|
||||||
title: getMessage('modal.module.basic.settting.module.error7', [roof.nameJp]), // 하제비치를 입력해주세요.
|
|
||||||
icon: 'warning',
|
|
||||||
})
|
|
||||||
result = false
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!roof?.eavesMargin || !roof?.ridgeMargin || !roof?.kerabaMargin) {
|
|
||||||
Swal.fire({
|
|
||||||
title: getMessage('modal.module.basic.settting.module.error8', [roof.nameJp]), // 모듈 배치 영영 값을 입력해주세요.
|
|
||||||
icon: 'warning',
|
|
||||||
})
|
|
||||||
result = false
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (roof.trestle.trestleMkrCd !== 'NO_DATA') {
|
|
||||||
// 가매 없음이 아닐때는 가대 정보보다 작을 수 없음
|
|
||||||
if (roof.trestleDetail?.eaveIntvl > roof.eavesMargin) {
|
|
||||||
Swal.fire({
|
|
||||||
title: getMessage('modal.module.basic.settting.module.error9', [roof.trestleDetail?.eaveIntvl, roof.nameJp]), // 모듈 배치 영역은 {0}mm 이상이어야 합니다.
|
|
||||||
icon: 'warning',
|
|
||||||
})
|
|
||||||
result = false
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (roof.trestleDetail?.ridgeIntvl > roof.ridgeMargin) {
|
|
||||||
Swal.fire({
|
|
||||||
title: getMessage('modal.module.basic.settting.module.error10', [roof.trestleDetail?.ridgeIntvl, roof.nameJp]), // 모듈 배치 영역은 {0}mm 이상이어야 합니다.
|
|
||||||
icon: 'warning',
|
|
||||||
})
|
|
||||||
result = false
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (roof.trestleDetail?.kerabaIntvl > roof.kerabaMargin) {
|
|
||||||
Swal.fire({
|
|
||||||
title: getMessage('modal.module.basic.settting.module.error11', [roof.trestleDetail?.kerabaIntvl, roof.nameJp]), // 모듈 배치 영역은 {0}mm 이상이어야 합니다.
|
|
||||||
icon: 'warning',
|
|
||||||
})
|
|
||||||
result = false
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result) {
|
|
||||||
const newRoofs = newAddedRoofs.map((roof) => {
|
|
||||||
const { addRoof, construction, trestle, trestleDetail, roofConstructions, ...rest } = roof
|
|
||||||
return rest
|
|
||||||
})
|
|
||||||
|
|
||||||
setModuleSelectionData({
|
|
||||||
...moduleSelectionData,
|
|
||||||
roofConstructions: newAddedRoofs.map((roof, index) => ({
|
|
||||||
roofIndex: newRoofs[index].index,
|
|
||||||
trestle: roof.trestle,
|
|
||||||
addRoof: newRoofs[index],
|
|
||||||
construction: roof.construction,
|
|
||||||
trestleDetail: roof.trestleDetail,
|
|
||||||
})),
|
|
||||||
})
|
|
||||||
setFlag(true)
|
|
||||||
tempModuleSelectionData.current = {
|
|
||||||
...moduleSelectionData,
|
|
||||||
roofConstructions: newAddedRoofs.map((roof, index) => ({
|
|
||||||
roofIndex: newRoofs[index].index,
|
|
||||||
trestle: roof.trestle,
|
|
||||||
addRoof: newRoofs[index],
|
|
||||||
construction: roof.construction,
|
|
||||||
trestleDetail: roof.trestleDetail,
|
|
||||||
})),
|
|
||||||
}
|
|
||||||
const updatePromises = [
|
|
||||||
// new Promise((resolve) => {
|
|
||||||
// resolve()
|
|
||||||
// }),
|
|
||||||
new Promise((resolve) => {
|
|
||||||
setRoofs(newRoofs)
|
|
||||||
resolve()
|
|
||||||
}),
|
|
||||||
|
|
||||||
new Promise((resolve) => {
|
|
||||||
const roofConstructions = newAddedRoofs.map((roof, index) => ({
|
|
||||||
roofIndex: newRoofs[index].index,
|
|
||||||
addRoof: newRoofs[index],
|
|
||||||
trestle: {
|
|
||||||
...roof.trestle,
|
|
||||||
raft: roof.raftBaseCd,
|
|
||||||
},
|
|
||||||
construction: {
|
|
||||||
// ...constructionList.find((construction) => newAddedRoofs[index].construction.constTp === construction.constTp),
|
|
||||||
...roof.construction,
|
|
||||||
roofIndex: roof.index,
|
|
||||||
selectedIndex: roof.index,
|
|
||||||
},
|
|
||||||
trestleDetail: roof.trestleDetail,
|
|
||||||
}))
|
|
||||||
trestleTrigger({
|
|
||||||
roofConstructions,
|
|
||||||
})
|
|
||||||
setRoofsStore(roofConstructions)
|
|
||||||
resolve()
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
|
|
||||||
await Promise.all(updatePromises)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
useImperativeHandle(ref, () => ({
|
|
||||||
isComplete,
|
|
||||||
}))
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="roof-module-tab2-overflow">
|
|
||||||
<div className="module-table-box mb10">
|
|
||||||
<div className="module-box-tab">
|
|
||||||
{roofs &&
|
|
||||||
roofs.map((roof, index) => (
|
|
||||||
<button
|
|
||||||
key={index}
|
|
||||||
className={`module-btn ${selectedRoof?.index === index ? 'act' : ''}`}
|
|
||||||
onClick={() => (roof ? handleChangeRoofMaterial(index) : null)}
|
|
||||||
>
|
|
||||||
{roof !== undefined ? `${roof.nameJp} (${currentAngleType === 'slope' ? roof.pitch : roof.angle}${pitchText})` : '-'}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<div className="module-table-inner">
|
|
||||||
<div className="module-table-flex-wrap tab2">
|
|
||||||
<div className="module-flex-item">
|
|
||||||
<div className="eaves-keraba-table">
|
|
||||||
{selectedRoof && selectedRoof.lenAuth === 'C' && (
|
|
||||||
<>
|
|
||||||
<div className="eaves-keraba-item">
|
|
||||||
<div className="eaves-keraba-th">L</div>
|
|
||||||
<div className="eaves-keraba-td">
|
|
||||||
<div className="grid-select">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
className="input-origin block"
|
|
||||||
value={lengthBase}
|
|
||||||
onChange={(e) => {
|
|
||||||
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'}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{selectedRoof && ['C', 'R'].includes(selectedRoof.raftAuth) && (
|
|
||||||
<>
|
|
||||||
<div className="eaves-keraba-item">
|
|
||||||
<div className="eaves-keraba-th">{getMessage('modal.module.basic.setting.module.rafter.margin')}</div>
|
|
||||||
<div className="eaves-keraba-td">
|
|
||||||
<div className="grid-select">
|
|
||||||
{raftBaseList.length > 0 && (
|
|
||||||
<QSelectBox
|
|
||||||
options={raftBaseList}
|
|
||||||
value={selectedRaftBase}
|
|
||||||
sourceKey={'clCode'}
|
|
||||||
targetKey={'clCode'}
|
|
||||||
showKey={'clCodeNm'}
|
|
||||||
disabled={selectedRoof.raftAuth === 'R'}
|
|
||||||
onChange={(e) => onChangeRaftBase(e)}
|
|
||||||
showFirstOptionWhenEmpty={true}
|
|
||||||
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{selectedRoof && ['C', 'R'].includes(selectedRoof.roofPchAuth) && (
|
|
||||||
<>
|
|
||||||
<div className="eaves-keraba-item">
|
|
||||||
<div className="eaves-keraba-th">{getMessage('modal.module.basic.setting.module.hajebichi')}</div>
|
|
||||||
<div className="eaves-keraba-td">
|
|
||||||
<div className="grid-select">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
className="input-origin block"
|
|
||||||
disabled={selectedRoof.roofPchAuth === 'R'}
|
|
||||||
onChange={(e) => {
|
|
||||||
const v = e.target.value
|
|
||||||
if (v === '') {
|
|
||||||
onChangeHajebichi('')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const n = Number(normalizeDigits(v))
|
|
||||||
if (Number.isNaN(n)) {
|
|
||||||
onChangeHajebichi('')
|
|
||||||
} else {
|
|
||||||
onChangeHajebichi(n)
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
value={hajebichi}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
<div className="eaves-keraba-item">
|
|
||||||
<div className="eaves-keraba-th">{getMessage('modal.module.basic.setting.module.trestle.maker')}</div>
|
|
||||||
<div className="eaves-keraba-td">
|
|
||||||
<div className="grid-select">
|
|
||||||
{trestleList && (
|
|
||||||
<QSelectBox
|
|
||||||
title={getMessage('selectbox.title')}
|
|
||||||
options={trestleList}
|
|
||||||
value={selectedTrestle}
|
|
||||||
sourceKey={'trestleMkrCd'}
|
|
||||||
targetKey={'trestleMkrCd'}
|
|
||||||
showKey={'trestleMkrCdJp'}
|
|
||||||
onChange={(e) => onChangeTrestleMaker(e)}
|
|
||||||
showFirstOptionWhenEmpty={true}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="eaves-keraba-item">
|
|
||||||
<div className="eaves-keraba-th">{getMessage('modal.module.basic.setting.module.construction.method')}</div>
|
|
||||||
<div className="eaves-keraba-td">
|
|
||||||
<div className="grid-select">
|
|
||||||
{constMthdList && (
|
|
||||||
<QSelectBox
|
|
||||||
title={getMessage('selectbox.title')}
|
|
||||||
options={constMthdList}
|
|
||||||
value={selectedConstMthd}
|
|
||||||
sourceKey={'constMthdCd'}
|
|
||||||
targetKey={'constMthdCd'}
|
|
||||||
showKey={'constMthdCdJp'}
|
|
||||||
onChange={(e) => onChangeConstMthd(e)}
|
|
||||||
showFirstOptionWhenEmpty={true}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="eaves-keraba-item">
|
|
||||||
<div className="eaves-keraba-th">{getMessage('modal.module.basic.setting.module.under.roof')}</div>
|
|
||||||
<div className="eaves-keraba-td">
|
|
||||||
<div className="grid-select">
|
|
||||||
{roofBaseList && (
|
|
||||||
<QSelectBox
|
|
||||||
title={getMessage('selectbox.title')}
|
|
||||||
options={roofBaseList}
|
|
||||||
sourceKey={'roofBaseCd'}
|
|
||||||
targetKey={'roofBaseCd'}
|
|
||||||
showKey={'roofBaseCdJp'}
|
|
||||||
value={selectedRoofBase}
|
|
||||||
onChange={(e) => onChangeRoofBase(e)}
|
|
||||||
showFirstOptionWhenEmpty={true}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="module-flex-item non-flex">
|
|
||||||
<div className="flex-item-btn-wrap">
|
|
||||||
<button className={`btn-frame roof ${getConstructionState(0)}`} onClick={() => handleConstruction(0)}>
|
|
||||||
{getMessage('modal.module.basic.setting.module.standard.construction')}(I)
|
|
||||||
</button>
|
|
||||||
<button className={`btn-frame roof ${getConstructionState(3)}`} onClick={() => handleConstruction(3)}>
|
|
||||||
{getMessage('modal.module.basic.setting.module.multiple.construction')}
|
|
||||||
</button>
|
|
||||||
<button className={`btn-frame roof ${getConstructionState(1)}`} onClick={() => handleConstruction(1)}>
|
|
||||||
{getMessage('modal.module.basic.setting.module.standard.construction')}
|
|
||||||
</button>
|
|
||||||
<button className={`btn-frame roof ${getConstructionState(4)}`} onClick={() => handleConstruction(4)}>
|
|
||||||
{getMessage('modal.module.basic.setting.module.multiple.construction')}(II)
|
|
||||||
</button>
|
|
||||||
<button className={`btn-frame roof ${getConstructionState(2)}`} onClick={() => handleConstruction(2)}>
|
|
||||||
{getMessage('modal.module.basic.setting.module.enforce.construction')}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div className="grid-check-form-flex">
|
|
||||||
<div className="d-check-box pop">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
id={`ch01`}
|
|
||||||
disabled={!cvrYn || cvrYn === 'N'}
|
|
||||||
checked={cvrChecked || false}
|
|
||||||
// onChange={() => dispatch({ type: 'SET_TRESTLE_DETAIL', roof: { ...trestleState, cvrChecked: !trestleState.cvrChecked } })}
|
|
||||||
onChange={() => setCvrChecked(!cvrChecked)}
|
|
||||||
/>
|
|
||||||
<label htmlFor={`ch01`}>{getMessage('modal.module.basic.setting.module.eaves.bar.fitting')}</label>
|
|
||||||
</div>
|
|
||||||
<div className="d-check-box pop">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
id={`ch02`}
|
|
||||||
disabled={!snowGdPossYn || snowGdPossYn === 'N'}
|
|
||||||
checked={snowGdChecked || false}
|
|
||||||
// onChange={() => dispatch({ type: 'SET_TRESTLE_DETAIL', roof: { ...trestleState, snowGdChecked: !trestleState.snowGdChecked } })}
|
|
||||||
onChange={() => setSnowGdChecked(!snowGdChecked)}
|
|
||||||
/>
|
|
||||||
<label htmlFor={`ch02`}>{getMessage('modal.module.basic.setting.module.blind.metal.fitting')}</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="module-input-area">
|
|
||||||
<div className="module-area-title">{getMessage('modal.module.basic.setting.module.placement.area')}</div>
|
|
||||||
<div className="module-input-wrap">
|
|
||||||
<div className="outline-form mr15">
|
|
||||||
<span>{getMessage('modal.module.basic.setting.module.placement.area.eaves')}</span>
|
|
||||||
<div className="input-grid mr10">
|
|
||||||
{/*<input*/}
|
|
||||||
{/* type="number"*/}
|
|
||||||
{/* className="input-origin block"*/}
|
|
||||||
{/* value={eavesMargin ?? 0}*/}
|
|
||||||
{/* // onChange={(e) => dispatch({ type: 'SET_TRESTLE_DETAIL', roof: { ...trestleState, eavesMargin: e.target.value } })}*/}
|
|
||||||
{/* onChange={(e) => setEavesMargin(+e.target.value)}*/}
|
|
||||||
{/*/>*/}
|
|
||||||
<CalculatorInput
|
|
||||||
id=""
|
|
||||||
name=""
|
|
||||||
label=""
|
|
||||||
className="input-origin block"
|
|
||||||
value={eavesMargin ?? 0}
|
|
||||||
onChange={(value) => setEavesMargin(value)}
|
|
||||||
options={{
|
|
||||||
allowNegative: false,
|
|
||||||
allowDecimal: false
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<span className="thin">mm</span>
|
|
||||||
</div>
|
|
||||||
<div className="outline-form mr15">
|
|
||||||
<span>{getMessage('modal.module.basic.setting.module.placement.area.ridge')}</span>
|
|
||||||
<div className="input-grid mr10">
|
|
||||||
{/*<input*/}
|
|
||||||
{/* type="number"*/}
|
|
||||||
{/* className="input-origin block"*/}
|
|
||||||
{/* value={ridgeMargin ?? 0}*/}
|
|
||||||
{/* // onChange={(e) => dispatch({ type: 'SET_TRESTLE_DETAIL', roof: { ...trestleState, ridgeMargin: e.target.value } })}*/}
|
|
||||||
{/* onChange={(e) => setRidgeMargin(+e.target.value)}*/}
|
|
||||||
{/*/>*/}
|
|
||||||
<CalculatorInput
|
|
||||||
id=""
|
|
||||||
name=""
|
|
||||||
label=""
|
|
||||||
className="input-origin block"
|
|
||||||
value={ridgeMargin ?? 0}
|
|
||||||
onChange={(value) => setRidgeMargin(value)}
|
|
||||||
options={{
|
|
||||||
allowNegative: false,
|
|
||||||
allowDecimal: false
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<span className="thin">mm</span>
|
|
||||||
</div>
|
|
||||||
<div className="outline-form ">
|
|
||||||
<span>{getMessage('modal.module.basic.setting.module.placement.area.keraba')}</span>
|
|
||||||
<div className="input-grid mr10">
|
|
||||||
{/*<input*/}
|
|
||||||
{/* type="number"*/}
|
|
||||||
{/* className="input-origin block"*/}
|
|
||||||
{/* value={kerabaMargin ?? 0}*/}
|
|
||||||
{/* // onChange={(e) => dispatch({ type: 'SET_TRESTLE_DETAIL', roof: { ...trestleState, kerabaMargin: e.target.value } })}*/}
|
|
||||||
{/* onChange={(e) => setKerabaMargin(+e.target.value)}*/}
|
|
||||||
{/*/>*/}
|
|
||||||
<CalculatorInput
|
|
||||||
id=""
|
|
||||||
name=""
|
|
||||||
label=""
|
|
||||||
className="input-origin block"
|
|
||||||
value={kerabaMargin ?? 0}
|
|
||||||
onChange={(value) => setKerabaMargin(value)}
|
|
||||||
options={{
|
|
||||||
allowNegative: false,
|
|
||||||
allowDecimal: false
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<span className="thin">mm</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="module-input-area">
|
|
||||||
<div className="module-area-title">{getMessage('modal.module.basic.setting.module.placement.margin')}</div>
|
|
||||||
<div className="module-input-wrap">
|
|
||||||
<div className="outline-form">
|
|
||||||
<span>{getMessage('modal.module.basic.setting.module.placement.margin.horizontal')}</span>
|
|
||||||
<div className="input-grid mr10">
|
|
||||||
<input type="text" className="input-origin block" defaultValue={trestleDetail?.moduleIntvlHor} readOnly />
|
|
||||||
</div>
|
|
||||||
<span className="thin">mm</span>
|
|
||||||
</div>
|
|
||||||
<div className="outline-form">
|
|
||||||
<span>{getMessage('modal.module.basic.setting.module.placement.margin.vertical')}</span>
|
|
||||||
<div className="input-grid mr10">
|
|
||||||
<input type="text" className="input-origin block" defaultValue={trestleDetail?.moduleIntvlVer} readOnly />
|
|
||||||
</div>
|
|
||||||
<span className="thin">mm</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="module-bottom">
|
|
||||||
<div className="module-table-box ">
|
|
||||||
<div className="warning-guide">
|
|
||||||
<div className="warning">
|
|
||||||
{getMessage('modal.module.basic.setting.module.setting.info1')}
|
|
||||||
<br />
|
|
||||||
{getMessage('modal.module.basic.setting.module.setting.info2')}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
export default Trestle
|
|
||||||
@ -33,6 +33,14 @@ const PitchPlacement = forwardRef((props, refs) => {
|
|||||||
setSelectedItems({ ...selectedItems, [e.target.name]: e.target.checked })
|
setSelectedItems({ ...selectedItems, [e.target.name]: e.target.checked })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const moduleData = {
|
||||||
|
header: [
|
||||||
|
{ type: 'check', name: '', prop: 'check', width: 70 },
|
||||||
|
{ type: 'color-box', name: getMessage('module'), prop: 'module' },
|
||||||
|
{ type: 'text', name: `${getMessage('output')} (W)`, prop: 'output', width: 70 },
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
//체크된 모듈 데이터
|
//체크된 모듈 데이터
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const checkedModuleIds = Object.keys(selectedItems).filter((key) => selectedItems[key])
|
const checkedModuleIds = Object.keys(selectedItems).filter((key) => selectedItems[key])
|
||||||
@ -97,20 +105,24 @@ const PitchPlacement = forwardRef((props, refs) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="hexagonal-flex-wrap">
|
<div className="module-table-box mb10">
|
||||||
<div className="module-table-box ">
|
|
||||||
<div className="module-table-inner">
|
<div className="module-table-inner">
|
||||||
<div className="roof-module-table">
|
<div className="roof-module-table">
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th style={{ width: '70px' }}>
|
{moduleData.header.map((data) => (
|
||||||
|
<th key={data.prop} style={{ width: data.width ? data.width : '' }}>
|
||||||
|
{data.type === 'check' ? (
|
||||||
<div className="d-check-box no-text pop">
|
<div className="d-check-box no-text pop">
|
||||||
<input type="checkbox" id="ch01" disabled />
|
<input type="checkbox" id="ch01" disabled />
|
||||||
<label htmlFor="ch01"></label>
|
<label htmlFor="ch01"></label>
|
||||||
</div>
|
</div>
|
||||||
|
) : (
|
||||||
|
data.name
|
||||||
|
)}
|
||||||
</th>
|
</th>
|
||||||
<th>{getMessage('module')}</th>
|
))}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@ -135,6 +147,7 @@ const PitchPlacement = forwardRef((props, refs) => {
|
|||||||
<span className="name">{item.itemNm}</span>
|
<span className="name">{item.itemNm}</span>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
<td className="al-r">{item.wpOut}</td>
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
))}
|
||||||
</tbody>
|
</tbody>
|
||||||
@ -142,20 +155,15 @@ const PitchPlacement = forwardRef((props, refs) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="module-table-box non-flex">
|
<div className="module-table-box mb10">
|
||||||
<div className="module-table-inner">
|
<div className="module-table-inner">
|
||||||
<div className="roof-module-table">
|
<div className="hexagonal-wrap">
|
||||||
<table>
|
<div className="hexagonal-item">
|
||||||
<thead>
|
<div className="bold-font">{getMessage('modal.module.basic.setting.pitch.module.placement.standard.setting')}</div>
|
||||||
<tr>
|
</div>
|
||||||
<th>{getMessage('modal.module.basic.setting.pitch.module.placement.standard.setting')}</th>
|
<div className="hexagonal-item">
|
||||||
</tr>
|
<div className="pop-form-radio">
|
||||||
</thead>
|
<div className="d-check-radio pop">
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<div className="hexagonal-radio-wrap">
|
|
||||||
<div className="d-check-radio pop mb10">
|
|
||||||
<input
|
<input
|
||||||
type="radio"
|
type="radio"
|
||||||
name="radio01"
|
name="radio01"
|
||||||
@ -178,10 +186,6 @@ const PitchPlacement = forwardRef((props, refs) => {
|
|||||||
<label htmlFor="ra02">{getMessage('modal.module.basic.setting.pitch.module.placement.standard.setting.select')}</label>
|
<label htmlFor="ra02">{getMessage('modal.module.basic.setting.pitch.module.placement.standard.setting.select')}</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,28 +1,30 @@
|
|||||||
import WithDraggable from '@/components/common/draggable/WithDraggable'
|
import WithDraggable from '@/components/common/draggable/WithDraggable'
|
||||||
import { useContext, useEffect, useRef, useState } from 'react'
|
import { useState, useEffect, useContext, useRef } 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 BasicSetting from '@/components/floor-plan/modal/basic/BasicSetting'
|
|
||||||
import { useMasterController } from '@/hooks/common/useMasterController'
|
import { useMasterController } from '@/hooks/common/useMasterController'
|
||||||
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
|
import { correntObjectNoState } from '@/store/settingAtom'
|
||||||
|
import { useRecoilValue } from 'recoil'
|
||||||
import { GlobalDataContext } from '@/app/GlobalDataProvider'
|
import { GlobalDataContext } from '@/app/GlobalDataProvider'
|
||||||
import { POLYGON_TYPE, MENU } from '@/common/common'
|
import { useRecoilState } from 'recoil'
|
||||||
|
import { makersState, modelsState, modelState, pcsCheckState, selectedMakerState, selectedModelsState, seriesState } from '@/store/circuitTrestleAtom'
|
||||||
|
import { POLYGON_TYPE } from '@/common/common'
|
||||||
import { useSwal } from '@/hooks/useSwal'
|
import { useSwal } from '@/hooks/useSwal'
|
||||||
import { canvasState, canvasZoomState, currentMenuState } from '@/store/canvasAtom'
|
import { canvasState } from '@/store/canvasAtom'
|
||||||
|
|
||||||
import { useTrestle } from '@/hooks/module/useTrestle'
|
import { useTrestle } from '@/hooks/module/useTrestle'
|
||||||
import { moduleSelectionDataState, selectedModuleState } from '@/store/selectedModuleOptions'
|
import { selectedModuleState } from '@/store/selectedModuleOptions'
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
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 { fontSelector } from '@/store/fontAtom'
|
|
||||||
import { fabric } from 'fabric'
|
|
||||||
|
|
||||||
const ALLOCATION_TYPE = {
|
const ALLOCATION_TYPE = {
|
||||||
AUTO: 'auto',
|
AUTO: 'auto',
|
||||||
@ -30,22 +32,18 @@ const ALLOCATION_TYPE = {
|
|||||||
}
|
}
|
||||||
export default function CircuitTrestleSetting({ id }) {
|
export default function CircuitTrestleSetting({ id }) {
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
const { closePopup, addPopup } = usePopup()
|
const { closePopup } = usePopup()
|
||||||
const { apply, setViewCircuitNumberTexts, getEstimateData, clear: clearTrestle, setAllModuleSurfaceIsComplete } = useTrestle()
|
const { apply, setViewCircuitNumberTexts, getEstimateData, clear: clearTrestle, setAllModuleSurfaceIsComplete } = useTrestle()
|
||||||
const { swalFire } = useSwal()
|
const { swalFire } = useSwal()
|
||||||
const { saveEstimate } = useEstimate()
|
const { saveEstimate } = useEstimate()
|
||||||
const canvas = useRecoilValue(canvasState)
|
const canvas = useRecoilValue(canvasState)
|
||||||
const setCurrentMenu = useSetRecoilState(currentMenuState)
|
|
||||||
const [canvasZoom, setCanvasZoom] = useRecoilState(canvasZoomState)
|
|
||||||
const [tabNum, setTabNum] = useState(1)
|
const [tabNum, setTabNum] = useState(1)
|
||||||
const [allocationType, setAllocationType] = useState(ALLOCATION_TYPE.AUTO)
|
const [allocationType, setAllocationType] = useState(ALLOCATION_TYPE.AUTO)
|
||||||
const [circuitAllocationType, setCircuitAllocationType] = useState(1)
|
const [circuitAllocationType, setCircuitAllocationType] = useState(1)
|
||||||
const { managementState, setManagementState } = useContext(GlobalDataContext)
|
const { managementState, setManagementState, managementStateLoaded } = useContext(GlobalDataContext)
|
||||||
const selectedModules = useRecoilValue(selectedModuleState)
|
const selectedModules = useRecoilValue(selectedModuleState)
|
||||||
const { getPcsAutoRecommendList, getPcsVoltageChk, getPcsVoltageStepUpList, getPcsManualConfChk } = useMasterController()
|
const { getPcsAutoRecommendList, getPcsVoltageChk, getPcsVoltageStepUpList, getPcsManualConfChk } = useMasterController()
|
||||||
const flowText = useRecoilValue(fontSelector('flowText'))
|
|
||||||
const lengthText = useRecoilValue(fontSelector('lengthText'))
|
|
||||||
const circuitNumberText = useRecoilValue(fontSelector('circuitNumberText'))
|
|
||||||
|
|
||||||
// 회로할당(승합설정)에서 선택된 값들을 저장할 상태 추가
|
// 회로할당(승합설정)에서 선택된 값들을 저장할 상태 추가
|
||||||
const [selectedStepUpValues, setSelectedStepUpValues] = useState({})
|
const [selectedStepUpValues, setSelectedStepUpValues] = useState({})
|
||||||
@ -57,13 +55,10 @@ export default function CircuitTrestleSetting({ id }) {
|
|||||||
const [seletedSubOption, setSeletedSubOption] = useState(null)
|
const [seletedSubOption, setSeletedSubOption] = useState(null)
|
||||||
const { setModuleStatisticsData } = useCircuitTrestle()
|
const { setModuleStatisticsData } = useCircuitTrestle()
|
||||||
const { handleCanvasToPng } = useImgLoader()
|
const { handleCanvasToPng } = useImgLoader()
|
||||||
const moduleSelectionData = useRecoilValue(moduleSelectionDataState)
|
|
||||||
const passivityCircuitAllocationRef = useRef()
|
const passivityCircuitAllocationRef = useRef()
|
||||||
const { setIsGlobalLoading } = useContext(QcastContext)
|
const { setIsGlobalLoading } = useContext(QcastContext)
|
||||||
|
|
||||||
const originCanvasViewPortTransform = useRef([])
|
|
||||||
const [isFold, setIsFold] = useState(false)
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
makers,
|
makers,
|
||||||
setMakers,
|
setMakers,
|
||||||
@ -88,8 +83,8 @@ export default function CircuitTrestleSetting({ id }) {
|
|||||||
} = useCircuitTrestle()
|
} = useCircuitTrestle()
|
||||||
// const { trigger: moduleSelectedDataTrigger } = useCanvasPopupStatusController(2)
|
// const { trigger: moduleSelectedDataTrigger } = useCanvasPopupStatusController(2)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
originCanvasViewPortTransform.current = [...canvas.viewportTransform]
|
|
||||||
if (!managementState) {
|
if (!managementState) {
|
||||||
|
setManagementState(managementStateLoaded)
|
||||||
}
|
}
|
||||||
// setCircuitData({
|
// setCircuitData({
|
||||||
// makers,
|
// makers,
|
||||||
@ -108,276 +103,6 @@ export default function CircuitTrestleSetting({ id }) {
|
|||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
// 모듈이 설치된 경우 rack설치 여부에 따라 설치 된 모듈 아래에 작은 모듈이 설치되어 있을 경우는 모듈 설치 메뉴 reopen
|
|
||||||
useEffect(() => {
|
|
||||||
const modules = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE)
|
|
||||||
if (modules.length === 0) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 랙 설치 시 모듈 크기 검증
|
|
||||||
* - 남쪽: 아래쪽 모듈이 위쪽 모듈보다 커야 함
|
|
||||||
* - 북쪽: 위쪽 모듈이 아래쪽 모듈보다 커야 함
|
|
||||||
* - 동쪽: 오른쪽 모듈이 왼쪽 모듈보다 커야 함
|
|
||||||
* - 서쪽: 왼쪽 모듈이 오른쪽 모듈보다 커야 함
|
|
||||||
*/
|
|
||||||
const validateModuleSizeForRack = (surface) => {
|
|
||||||
const { modules, direction, trestleDetail } = surface
|
|
||||||
const { rackYn, moduleIntvlHor, moduleIntvlVer } = trestleDetail
|
|
||||||
|
|
||||||
if (rackYn === 'N' || !modules || modules.length < 2) {
|
|
||||||
return true // 검증 통과
|
|
||||||
}
|
|
||||||
|
|
||||||
// 모듈 중심점 및 크기 정보 계산
|
|
||||||
const centerPoints = modules.map((module) => {
|
|
||||||
const { x, y } = module.getCenterPoint()
|
|
||||||
const { width, height } = module
|
|
||||||
return {
|
|
||||||
x,
|
|
||||||
y,
|
|
||||||
width: Math.floor(width),
|
|
||||||
height: Math.floor(height),
|
|
||||||
area: Math.floor(width) * Math.floor(height),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// 방향별 설정
|
|
||||||
const isVertical = direction === 'south' || direction === 'north'
|
|
||||||
const primaryInterval = isVertical ? moduleIntvlVer : moduleIntvlHor
|
|
||||||
const secondaryInterval = isVertical ? moduleIntvlHor : moduleIntvlVer
|
|
||||||
|
|
||||||
// 정렬 함수: 큰 모듈이 있어야 할 위치부터 시작
|
|
||||||
const getSortFn = () => {
|
|
||||||
switch (direction) {
|
|
||||||
case 'south': return (a, b) => b.y - a.y // 아래쪽(y 큼)부터
|
|
||||||
case 'north': return (a, b) => a.y - b.y // 위쪽(y 작음)부터
|
|
||||||
case 'east': return (a, b) => b.x - a.x // 오른쪽(x 큼)부터
|
|
||||||
case 'west': return (a, b) => a.x - b.x // 왼쪽(x 작음)부터
|
|
||||||
default: return () => 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 타겟 모듈 찾기 (현재 모듈보다 작아야 할 위치의 모듈)
|
|
||||||
const findTargetModules = (current, margin) => {
|
|
||||||
return centerPoints.filter(cp => {
|
|
||||||
const sameAxis = isVertical
|
|
||||||
? Math.abs(cp.x - current.x) < margin
|
|
||||||
: Math.abs(cp.y - current.y) < margin
|
|
||||||
const targetDirection = direction === 'south' ? cp.y < current.y
|
|
||||||
: direction === 'north' ? cp.y > current.y
|
|
||||||
: direction === 'east' ? cp.x < current.x
|
|
||||||
: cp.x > current.x
|
|
||||||
return sameAxis && targetDirection
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 가장 가까운 타겟 모듈 찾기
|
|
||||||
const getClosestTarget = (filtered) => {
|
|
||||||
if (filtered.length === 0) return null
|
|
||||||
return filtered.reduce((closest, cp) => {
|
|
||||||
switch (direction) {
|
|
||||||
case 'south': return cp.y > closest.y ? cp : closest
|
|
||||||
case 'north': return cp.y < closest.y ? cp : closest
|
|
||||||
case 'east': return cp.x > closest.x ? cp : closest
|
|
||||||
case 'west': return cp.x < closest.x ? cp : closest
|
|
||||||
default: return closest
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 두 모듈 간 간격 계산
|
|
||||||
const getGap = (current, target) => {
|
|
||||||
if (isVertical) {
|
|
||||||
return direction === 'south'
|
|
||||||
? (current.y - current.height / 2) - (target.y + target.height / 2)
|
|
||||||
: (target.y - target.height / 2) - (current.y + current.height / 2)
|
|
||||||
} else {
|
|
||||||
return direction === 'east'
|
|
||||||
? (current.x - current.width / 2) - (target.x + target.width / 2)
|
|
||||||
: (target.x - target.width / 2) - (current.x + current.width / 2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 인접 모듈 여부 확인
|
|
||||||
const isAdjacent = (current, target) => {
|
|
||||||
const gap = getGap(current, target)
|
|
||||||
return gap >= 0 && gap <= primaryInterval + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// 정렬된 모듈 순회
|
|
||||||
const sortedPoints = [...centerPoints].sort(getSortFn())
|
|
||||||
|
|
||||||
for (const current of sortedPoints) {
|
|
||||||
// 1. 일반 배치: 같은 라인에서 인접 모듈 검사
|
|
||||||
const directTargets = findTargetModules(current, secondaryInterval)
|
|
||||||
const closestTarget = getClosestTarget(directTargets)
|
|
||||||
|
|
||||||
if (closestTarget && isAdjacent(current, closestTarget) && closestTarget.area > current.area) {
|
|
||||||
return false // 검증 실패
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. 물떼새 배치: 반 오프셋 위치의 인접 모듈 검사
|
|
||||||
const size = isVertical ? current.width : current.height
|
|
||||||
const halfOffset = (size + secondaryInterval) / 2
|
|
||||||
|
|
||||||
for (const sign of [-1, 1]) {
|
|
||||||
const offsetValue = sign * halfOffset
|
|
||||||
const findHalfTarget = centerPoints.filter((cp) => {
|
|
||||||
const offsetAxis = isVertical
|
|
||||||
? Math.abs(cp.x - (current.x + offsetValue)) <= primaryInterval
|
|
||||||
: Math.abs(cp.y - (current.y + offsetValue)) <= primaryInterval
|
|
||||||
const targetDirection = direction === 'south' ? cp.y < current.y
|
|
||||||
: direction === 'north' ? cp.y > current.y
|
|
||||||
: direction === 'east' ? cp.x < current.x
|
|
||||||
: cp.x > current.x
|
|
||||||
return offsetAxis && targetDirection
|
|
||||||
})
|
|
||||||
|
|
||||||
const closestHalf = getClosestTarget(findHalfTarget)
|
|
||||||
if (closestHalf && isAdjacent(current, closestHalf) && closestHalf.area > current.area) {
|
|
||||||
return false // 검증 실패
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true // 검증 통과
|
|
||||||
}
|
|
||||||
|
|
||||||
// 모든 설치면에 대해 검증 수행
|
|
||||||
const moduleSetupSurfaces = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE)
|
|
||||||
for (const surface of moduleSetupSurfaces) {
|
|
||||||
if (!validateModuleSizeForRack(surface)) {
|
|
||||||
swalFire({
|
|
||||||
text: getMessage('module.size.validation.rack.error'),
|
|
||||||
icon: 'error',
|
|
||||||
confirmFn: () => {
|
|
||||||
// 현재 팝업 닫기
|
|
||||||
closePopup(id)
|
|
||||||
// 메뉴 하이라이트 변경
|
|
||||||
setCurrentMenu(MENU.MODULE_CIRCUIT_SETTING.BASIC_SETTING)
|
|
||||||
// 모듈/가대설정 팝업 열기
|
|
||||||
const newPopupId = uuidv4()
|
|
||||||
clearTrestle()
|
|
||||||
setAllModuleSurfaceIsComplete(false)
|
|
||||||
addPopup(newPopupId, 1, <BasicSetting id={newPopupId} />)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
const capture = async (type) => {
|
|
||||||
beforeCapture(type)
|
|
||||||
|
|
||||||
await handleCanvasToPng(type)
|
|
||||||
|
|
||||||
afterCapture(type)
|
|
||||||
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
setTimeout(() => {
|
|
||||||
resolve(true)
|
|
||||||
}, 1000)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 캡쳐 전 처리
|
|
||||||
const beforeCapture = (type) => {
|
|
||||||
setCanvasZoom(100)
|
|
||||||
canvas.set({ zoom: 1 })
|
|
||||||
|
|
||||||
// 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 })
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
let x, y
|
|
||||||
x = canvas.width / 2
|
|
||||||
y = canvas.height / 2
|
|
||||||
|
|
||||||
canvas.zoomToPoint(new fabric.Point(x, y), 0.4)
|
|
||||||
|
|
||||||
changeFontSize('lengthText', '28')
|
|
||||||
changeFontSize('circuitNumber', '28')
|
|
||||||
changeFontSize('flowText', '28')
|
|
||||||
canvas.renderAll()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 캡쳐 후 처리
|
|
||||||
const afterCapture = (type) => {
|
|
||||||
if (originCanvasViewPortTransform.current[0] !== 1) {
|
|
||||||
setCanvasZoom(Number((originCanvasViewPortTransform.current[0] * 100).toFixed(0)))
|
|
||||||
}
|
|
||||||
canvas.viewportTransform = [...originCanvasViewPortTransform.current]
|
|
||||||
canvas.renderAll()
|
|
||||||
|
|
||||||
changeFontSize('lengthText', lengthText.fontSize.value)
|
|
||||||
changeFontSize('circuitNumber', circuitNumberText.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()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 수동할당 시 모듈 삭제
|
// 수동할당 시 모듈 삭제
|
||||||
|
|
||||||
// 시리즈중 자동으로 추천 PCS 정보 조회
|
// 시리즈중 자동으로 추천 PCS 정보 조회
|
||||||
@ -390,33 +115,11 @@ export default function CircuitTrestleSetting({ id }) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const isMultiModule = selectedModules.itemList.length > 1
|
|
||||||
|
|
||||||
let isAllIndfcs = false
|
|
||||||
|
|
||||||
if (isMultiModule) {
|
|
||||||
//INDFCS 실내집중, OUTDMULTI 옥외멀티
|
|
||||||
// 1. 모듈이 혼합형일 경우 선택한 pcs가 실내집중인 경우 alert
|
|
||||||
if (selectedModels.length > 0) {
|
|
||||||
isAllIndfcs = selectedModels.every((model) => model.pcsTpCd === 'INDFCS')
|
|
||||||
} else {
|
|
||||||
isAllIndfcs = models.every((model) => model.pcsTpCd === 'INDFCS')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isAllIndfcs) {
|
|
||||||
swalFire({
|
|
||||||
title: getMessage('module.circuit.indoor.focused.error'),
|
|
||||||
type: 'alert',
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
...getOptYn(),
|
...getOptYn(),
|
||||||
useModuleItemList: getUseModuleItemList(),
|
useModuleItemList: getUseModuleItemList(),
|
||||||
roofSurfaceList: getRoofSurfaceList(),
|
roofSurfaceList: getRoofSurfaceList(),
|
||||||
pcsItemList: getPcsItemList(isMultiModule),
|
pcsItemList: getPcsItemList(),
|
||||||
}
|
}
|
||||||
|
|
||||||
// 파워컨디셔너 추천 목록 조회
|
// 파워컨디셔너 추천 목록 조회
|
||||||
@ -452,7 +155,6 @@ export default function CircuitTrestleSetting({ id }) {
|
|||||||
getPcsVoltageChk(pcsVoltageChkParams).then((res) => {
|
getPcsVoltageChk(pcsVoltageChkParams).then((res) => {
|
||||||
if (res.resultCode === 'S') {
|
if (res.resultCode === 'S') {
|
||||||
setTabNum(2)
|
setTabNum(2)
|
||||||
setAllModuleSurfaceIsComplete(false)
|
|
||||||
} else {
|
} else {
|
||||||
swalFire({
|
swalFire({
|
||||||
title: res.resultMsg,
|
title: res.resultMsg,
|
||||||
@ -477,16 +179,15 @@ export default function CircuitTrestleSetting({ id }) {
|
|||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
// 회로 구성 가능 여부 체크
|
// 회로 구성 가능 여부 체크
|
||||||
getPcsVoltageChk({ ...params, pcsItemList: getSelectedPcsItemList(isMultiModule) }).then((res) => {
|
getPcsVoltageChk({ ...params, pcsItemList: getSelectedPcsItemList() }).then((res) => {
|
||||||
if (res.resultCode === 'S') {
|
if (res.resultCode === 'S') {
|
||||||
// 회로 구성 가능 여부 체크 통과 시 승압설정 정보 조회
|
// 회로 구성 가능 여부 체크 통과 시 승압설정 정보 조회
|
||||||
getPcsVoltageStepUpList({
|
getPcsVoltageStepUpList({
|
||||||
...params,
|
...params,
|
||||||
pcsItemList: getSelectedPcsItemList(isMultiModule),
|
pcsItemList: getSelectedPcsItemList(),
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
if (res?.result.resultCode === 'S' && res?.data) {
|
if (res?.result.resultCode === 'S' && res?.data) {
|
||||||
setTabNum(2)
|
setTabNum(2)
|
||||||
setAllModuleSurfaceIsComplete(false)
|
|
||||||
} else {
|
} else {
|
||||||
swalFire({ text: getMessage('common.message.send.error') })
|
swalFire({ text: getMessage('common.message.send.error') })
|
||||||
}
|
}
|
||||||
@ -586,8 +287,6 @@ export default function CircuitTrestleSetting({ id }) {
|
|||||||
setSelectedModels(pcsItemList)
|
setSelectedModels(pcsItemList)
|
||||||
getPcsVoltageChk(pcsVoltageChkParams).then((res) => {
|
getPcsVoltageChk(pcsVoltageChkParams).then((res) => {
|
||||||
setAllocationType(ALLOCATION_TYPE.PASSIVITY)
|
setAllocationType(ALLOCATION_TYPE.PASSIVITY)
|
||||||
setAllModuleSurfaceIsComplete(false)
|
|
||||||
clearTrestle()
|
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
swalFire({
|
swalFire({
|
||||||
@ -610,15 +309,8 @@ export default function CircuitTrestleSetting({ id }) {
|
|||||||
|
|
||||||
const target = pcsCheck.max ? moduleMaxQty : moduleStdQty
|
const target = pcsCheck.max ? moduleMaxQty : moduleStdQty
|
||||||
const placementModules = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE)
|
const placementModules = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE)
|
||||||
let moduleAmount = placementModules.reduce((acc, module) => {
|
|
||||||
if (moduleSelectionData.module.itemList.length === 1 || module.moduleInfo.itemId === moduleSelectionData.module.itemList[0].itemId) {
|
|
||||||
return acc + 1
|
|
||||||
} else {
|
|
||||||
return acc + 0.66
|
|
||||||
}
|
|
||||||
}, 0)
|
|
||||||
|
|
||||||
if (moduleAmount > target) {
|
if (placementModules.length > target) {
|
||||||
swalFire({
|
swalFire({
|
||||||
title: getMessage('modal.circuit.trestle.setting.circuit.allocation.passivity.all.power.conditional.validation.error01'),
|
title: getMessage('modal.circuit.trestle.setting.circuit.allocation.passivity.all.power.conditional.validation.error01'),
|
||||||
type: 'alert',
|
type: 'alert',
|
||||||
@ -627,7 +319,6 @@ export default function CircuitTrestleSetting({ id }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setAllocationType(ALLOCATION_TYPE.PASSIVITY)
|
setAllocationType(ALLOCATION_TYPE.PASSIVITY)
|
||||||
clearTrestle()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -657,9 +348,13 @@ export default function CircuitTrestleSetting({ id }) {
|
|||||||
.map((obj) => {
|
.map((obj) => {
|
||||||
obj.pcses = getStepUpListData()
|
obj.pcses = getStepUpListData()
|
||||||
})
|
})
|
||||||
await capture(1)
|
|
||||||
|
setViewCircuitNumberTexts(false)
|
||||||
|
|
||||||
|
handleCanvasToPng(1)
|
||||||
|
|
||||||
//회로할당 저장 시 result=null인 경우에도 회로번호 텍스트 표시 유지 처리
|
//회로할당 저장 시 result=null인 경우에도 회로번호 텍스트 표시 유지 처리
|
||||||
|
setViewCircuitNumberTexts(true)
|
||||||
|
|
||||||
// 회로할당 저장 클릭 시
|
// 회로할당 저장 클릭 시
|
||||||
// 가대 및 지지금구 설치
|
// 가대 및 지지금구 설치
|
||||||
@ -674,7 +369,7 @@ export default function CircuitTrestleSetting({ id }) {
|
|||||||
const result = await getEstimateData()
|
const result = await getEstimateData()
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
await capture(2)
|
handleCanvasToPng(2)
|
||||||
// 견적서 저장
|
// 견적서 저장
|
||||||
await saveEstimate(result)
|
await saveEstimate(result)
|
||||||
} else {
|
} else {
|
||||||
@ -684,16 +379,6 @@ export default function CircuitTrestleSetting({ id }) {
|
|||||||
// removeNotAllocationModules()
|
// removeNotAllocationModules()
|
||||||
}
|
}
|
||||||
|
|
||||||
const changeFontSize = (name, size) => {
|
|
||||||
const textObjs = canvas?.getObjects().filter((obj) => obj.name === name)
|
|
||||||
textObjs.forEach((obj) => {
|
|
||||||
obj.set({
|
|
||||||
fontSize: size,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
canvas.renderAll()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 이전 버튼 클릭 시
|
// 이전 버튼 클릭 시
|
||||||
const onClickPrev = () => {
|
const onClickPrev = () => {
|
||||||
// setAllocationType(ALLOCATION_TYPE.AUTO)
|
// setAllocationType(ALLOCATION_TYPE.AUTO)
|
||||||
@ -708,7 +393,6 @@ export default function CircuitTrestleSetting({ id }) {
|
|||||||
obj.circuit = null
|
obj.circuit = null
|
||||||
obj.pcsItemId = null
|
obj.pcsItemId = null
|
||||||
obj.circuitNumber = null
|
obj.circuitNumber = null
|
||||||
obj.pcs = null
|
|
||||||
})
|
})
|
||||||
setSelectedModels(
|
setSelectedModels(
|
||||||
JSON.parse(JSON.stringify(selectedModels)).map((model) => {
|
JSON.parse(JSON.stringify(selectedModels)).map((model) => {
|
||||||
@ -795,7 +479,7 @@ export default function CircuitTrestleSetting({ id }) {
|
|||||||
console.log(stepUpListData)
|
console.log(stepUpListData)
|
||||||
stepUpListData[0].pcsItemList.map((item, index) => {
|
stepUpListData[0].pcsItemList.map((item, index) => {
|
||||||
return item.serQtyList
|
return item.serQtyList
|
||||||
.filter((serQty) => serQty.selected && serQty.paralQty > 0)
|
.filter((serQty) => serQty.selected)
|
||||||
.forEach((serQty) => {
|
.forEach((serQty) => {
|
||||||
pcs.push({
|
pcs.push({
|
||||||
pcsMkrCd: item.pcsMkrCd,
|
pcsMkrCd: item.pcsMkrCd,
|
||||||
@ -971,22 +655,14 @@ export default function CircuitTrestleSetting({ id }) {
|
|||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
setTabNum(2)
|
setTabNum(2)
|
||||||
setAllModuleSurfaceIsComplete(false)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<WithDraggable isShow={true} pos={{ x: 50, y: 230 }} className="l-2">
|
<WithDraggable isShow={true} pos={{ x: 50, y: 230 }} className="l-2">
|
||||||
<WithDraggable.Header
|
<WithDraggable.Header title={getMessage('modal.circuit.trestle.setting')} onClose={() => handleClose()} />
|
||||||
title={getMessage('modal.circuit.trestle.setting')}
|
|
||||||
onClose={() => handleClose()}
|
|
||||||
isFold={isFold}
|
|
||||||
onFold={() => setIsFold(!isFold)}
|
|
||||||
/>
|
|
||||||
<WithDraggable.Body>
|
<WithDraggable.Body>
|
||||||
<div style={{ display: !(tabNum === 1 && allocationType === ALLOCATION_TYPE.PASSIVITY) && isFold ? 'none' : 'block' }}>
|
|
||||||
<div style={{ display: tabNum === 1 && allocationType === ALLOCATION_TYPE.PASSIVITY && isFold ? 'none' : 'block' }}>
|
|
||||||
<div className="roof-module-tab">
|
<div className="roof-module-tab">
|
||||||
<div className={`module-tab-bx act`}>{getMessage('modal.circuit.trestle.setting.power.conditional.select')}</div>
|
<div className={`module-tab-bx act`}>{getMessage('modal.circuit.trestle.setting.power.conditional.select')}</div>
|
||||||
<span className={`tab-arr ${tabNum === 2 ? 'act' : ''}`}></span>
|
<span className={`tab-arr ${tabNum === 2 ? 'act' : ''}`}></span>
|
||||||
@ -994,14 +670,11 @@ export default function CircuitTrestleSetting({ id }) {
|
|||||||
{getMessage('modal.circuit.trestle.setting.circuit.allocation')}({getMessage('modal.circuit.trestle.setting.step.up.allocation')})
|
{getMessage('modal.circuit.trestle.setting.circuit.allocation')}({getMessage('modal.circuit.trestle.setting.step.up.allocation')})
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
{tabNum === 1 && allocationType === ALLOCATION_TYPE.AUTO && <PowerConditionalSelect {...powerConditionalSelectProps} />}
|
{tabNum === 1 && allocationType === ALLOCATION_TYPE.AUTO && <PowerConditionalSelect {...powerConditionalSelectProps} />}
|
||||||
{tabNum === 1 && allocationType === ALLOCATION_TYPE.PASSIVITY && (
|
{tabNum === 1 && allocationType === ALLOCATION_TYPE.PASSIVITY && (
|
||||||
<PassivityCircuitAllocation {...passivityProps} ref={passivityCircuitAllocationRef} isFold={isFold} />
|
<PassivityCircuitAllocation {...passivityProps} ref={passivityCircuitAllocationRef} />
|
||||||
)}
|
)}
|
||||||
{tabNum === 2 && <StepUp {...stepUpProps} onInitialize={handleStepUpInitialize} />}
|
{tabNum === 2 && <StepUp {...stepUpProps} onInitialize={handleStepUpInitialize} />}
|
||||||
</div>
|
|
||||||
|
|
||||||
{tabNum === 1 && allocationType === ALLOCATION_TYPE.AUTO && (
|
{tabNum === 1 && allocationType === ALLOCATION_TYPE.AUTO && (
|
||||||
<div className="grid-btn-wrap">
|
<div className="grid-btn-wrap">
|
||||||
<button className="btn-frame modal mr5 act" onClick={() => onAutoRecommend()}>
|
<button className="btn-frame modal mr5 act" onClick={() => onAutoRecommend()}>
|
||||||
|
|||||||
@ -5,7 +5,6 @@ import { useMasterController } from '@/hooks/common/useMasterController'
|
|||||||
import { useMessage } from '@/hooks/useMessage'
|
import { useMessage } from '@/hooks/useMessage'
|
||||||
import { useSwal } from '@/hooks/useSwal'
|
import { useSwal } from '@/hooks/useSwal'
|
||||||
import { pcsCheckState } from '@/store/circuitTrestleAtom'
|
import { pcsCheckState } from '@/store/circuitTrestleAtom'
|
||||||
import { sessionStore } from '@/store/commonAtom'
|
|
||||||
import { globalLocaleStore } from '@/store/localeAtom'
|
import { globalLocaleStore } from '@/store/localeAtom'
|
||||||
import { moduleSelectionDataState, selectedModuleState } from '@/store/selectedModuleOptions'
|
import { moduleSelectionDataState, selectedModuleState } from '@/store/selectedModuleOptions'
|
||||||
import { isNullOrUndefined } from '@/util/common-utils'
|
import { isNullOrUndefined } from '@/util/common-utils'
|
||||||
@ -39,14 +38,13 @@ export default function PowerConditionalSelect(props) {
|
|||||||
} = props
|
} = props
|
||||||
const [pcsCheck, setPcsCheck] = useRecoilState(pcsCheckState)
|
const [pcsCheck, setPcsCheck] = useRecoilState(pcsCheckState)
|
||||||
|
|
||||||
const sessionState = useRecoilValue(sessionStore)
|
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
const [selectedRow, setSelectedRow] = useState(null)
|
const [selectedRow, setSelectedRow] = useState(null)
|
||||||
const globalLocale = useRecoilValue(globalLocaleStore)
|
const globalLocale = useRecoilValue(globalLocaleStore)
|
||||||
const { getPcsMakerList, getPcsModelList } = useMasterController()
|
const { getPcsMakerList, getPcsModelList } = useMasterController()
|
||||||
const selectedModules = useRecoilValue(selectedModuleState)
|
const selectedModules = useRecoilValue(selectedModuleState)
|
||||||
const { swalFire } = useSwal()
|
const { swalFire } = useSwal()
|
||||||
// const { trigger: moduleSelectedDataTrigger } = useCanvasPopupStatusController(2)
|
const { trigger: moduleSelectedDataTrigger } = useCanvasPopupStatusController(2)
|
||||||
const [moduleSelectionData, setModuleSelectionData] = useRecoilState(moduleSelectionDataState)
|
const [moduleSelectionData, setModuleSelectionData] = useRecoilState(moduleSelectionDataState)
|
||||||
const modelHeader = [
|
const modelHeader = [
|
||||||
{ name: getMessage('modal.circuit.trestle.setting.circuit.allocation.passivity.series'), width: '15%', prop: 'pcsSerNm', type: 'color-box' },
|
{ name: getMessage('modal.circuit.trestle.setting.circuit.allocation.passivity.series'), width: '15%', prop: 'pcsSerNm', type: 'color-box' },
|
||||||
@ -74,9 +72,11 @@ 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) => {
|
||||||
@ -110,7 +110,6 @@ export default function PowerConditionalSelect(props) {
|
|||||||
selected: s.pcsSerCd === data.pcsSerCd ? !s.selected : false,
|
selected: s.pcsSerCd === data.pcsSerCd ? !s.selected : false,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
setSelectedModels([])
|
|
||||||
}
|
}
|
||||||
setSeries(copySeries)
|
setSeries(copySeries)
|
||||||
handleSetmodels(copySeries.filter((s) => s.selected))
|
handleSetmodels(copySeries.filter((s) => s.selected))
|
||||||
@ -132,7 +131,7 @@ export default function PowerConditionalSelect(props) {
|
|||||||
mixMatlNo: item.mixMatlNo,
|
mixMatlNo: item.mixMatlNo,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
getPcsModelList({ pcsMkrCd, pcsSerList, moduleItemList, storeId: sessionState.oneTwoStoreId }).then((res) => {
|
getPcsModelList({ pcsMkrCd, pcsSerList, moduleItemList }).then((res) => {
|
||||||
if (res?.result.code === 200 && res?.data) {
|
if (res?.result.code === 200 && res?.data) {
|
||||||
setModels(
|
setModels(
|
||||||
res.data.map((model) => {
|
res.data.map((model) => {
|
||||||
@ -177,7 +176,7 @@ export default function PowerConditionalSelect(props) {
|
|||||||
|
|
||||||
if (selectedMaker.pcsMkrMultiType === PCS_MKR_MULTI_TYPE.MULTI) {
|
if (selectedMaker.pcsMkrMultiType === PCS_MKR_MULTI_TYPE.MULTI) {
|
||||||
setSelectedModels([...selectedModels, { ...selectedRow, id: uuidv4() }])
|
setSelectedModels([...selectedModels, { ...selectedRow, id: uuidv4() }])
|
||||||
} else if (!selectedModels.find((m) => m.itemId === selectedRow.itemId && m.pcsSerCd === selectedRow.pcsSerCd)) {
|
} else if (!selectedModels.find((m) => m.itemId === selectedRow.itemId)) {
|
||||||
setSelectedModels([...selectedModels, { ...selectedRow, id: uuidv4() }])
|
setSelectedModels([...selectedModels, { ...selectedRow, id: uuidv4() }])
|
||||||
}
|
}
|
||||||
setSelectedRow(null)
|
setSelectedRow(null)
|
||||||
@ -200,8 +199,6 @@ 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(','),
|
|
||||||
storeId: sessionState.oneTwoStoreId,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getPcsMakerList(param).then((res) => {
|
getPcsMakerList(param).then((res) => {
|
||||||
|
|||||||
@ -1,10 +1,13 @@
|
|||||||
|
import { GlobalDataContext } from '@/app/GlobalDataProvider'
|
||||||
import QSelectBox from '@/components/common/select/QSelectBox'
|
import QSelectBox from '@/components/common/select/QSelectBox'
|
||||||
import { useMessage } from '@/hooks/useMessage'
|
import { useMessage } from '@/hooks/useMessage'
|
||||||
import { canvasState } from '@/store/canvasAtom'
|
import { canvasState } from '@/store/canvasAtom'
|
||||||
import { pcsCheckState } from '@/store/circuitTrestleAtom'
|
import { modelState, pcsCheckState } from '@/store/circuitTrestleAtom'
|
||||||
import { selectedModuleState } from '@/store/selectedModuleOptions'
|
import { selectedModuleState } from '@/store/selectedModuleOptions'
|
||||||
import { useEffect, useState } from 'react'
|
import { useContext, useEffect, useState } from 'react'
|
||||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||||
|
import { useCanvasPopupStatusController } from '@/hooks/common/useCanvasPopupStatusController'
|
||||||
|
import { canvasPopupStatusStore } from '@/store/canvasPopupStatusAtom'
|
||||||
import { useMasterController } from '@/hooks/common/useMasterController'
|
import { useMasterController } from '@/hooks/common/useMasterController'
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
import { globalLocaleStore } from '@/store/localeAtom'
|
import { globalLocaleStore } from '@/store/localeAtom'
|
||||||
@ -12,6 +15,7 @@ import { POLYGON_TYPE } from '@/common/common'
|
|||||||
import { useSwal } from '@/hooks/useSwal'
|
import { useSwal } from '@/hooks/useSwal'
|
||||||
import { circuitNumDisplaySelector } from '@/store/settingAtom'
|
import { circuitNumDisplaySelector } from '@/store/settingAtom'
|
||||||
import { fontSelector } from '@/store/fontAtom'
|
import { fontSelector } from '@/store/fontAtom'
|
||||||
|
import { PCS_MKR_MULTI_TYPE } from './PowerConditionalSelect'
|
||||||
|
|
||||||
export default function StepUp(props) {
|
export default function StepUp(props) {
|
||||||
const {
|
const {
|
||||||
@ -38,7 +42,7 @@ export default function StepUp(props) {
|
|||||||
const [arrayLength, setArrayLength] = useState(3) //module-table-inner의 반복 개수
|
const [arrayLength, setArrayLength] = useState(3) //module-table-inner의 반복 개수
|
||||||
const [pcsCheck, setPcsCheck] = useRecoilState(pcsCheckState)
|
const [pcsCheck, setPcsCheck] = useRecoilState(pcsCheckState)
|
||||||
const { getPcsVoltageStepUpList, getPcsAutoRecommendList, getPcsVoltageChk, getPcsConnOptionItemList } = useMasterController()
|
const { getPcsVoltageStepUpList, getPcsAutoRecommendList, getPcsVoltageChk, getPcsConnOptionItemList } = useMasterController()
|
||||||
const [originPcsVoltageStepUpList, setOriginPcsVoltageStepUpList] = useState([])
|
const { managementState, setManagementState, managementStateLoaded } = useContext(GlobalDataContext)
|
||||||
const canvas = useRecoilValue(canvasState)
|
const canvas = useRecoilValue(canvasState)
|
||||||
const selectedModules = useRecoilValue(selectedModuleState)
|
const selectedModules = useRecoilValue(selectedModuleState)
|
||||||
const [optCodes, setOptCodes] = useState([])
|
const [optCodes, setOptCodes] = useState([])
|
||||||
@ -97,9 +101,6 @@ export default function StepUp(props) {
|
|||||||
|
|
||||||
/** PCS 승압설정 정보 SET */
|
/** PCS 승압설정 정보 SET */
|
||||||
setStepUpListData(stepUpListData)
|
setStepUpListData(stepUpListData)
|
||||||
if (originPcsVoltageStepUpList.length === 0) {
|
|
||||||
setOriginPcsVoltageStepUpList(stepUpListData)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** PCS 옵션 조회 */
|
/** PCS 옵션 조회 */
|
||||||
// const formattedOptCodes = formatOptionCodes(res.data.optionList)
|
// const formattedOptCodes = formatOptionCodes(res.data.optionList)
|
||||||
@ -108,7 +109,7 @@ export default function StepUp(props) {
|
|||||||
|
|
||||||
/** 캔버스에 회로 정보 적용 */
|
/** 캔버스에 회로 정보 적용 */
|
||||||
// 병설일때 pcs 있으면 setSubOpsions, 없으면 setMainOptions
|
// 병설일때 pcs 있으면 setSubOpsions, 없으면 setMainOptions
|
||||||
let mChk = 0
|
console.log('stepUpListData', stepUpListData)
|
||||||
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,8 +165,6 @@ 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++
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -174,10 +173,6 @@ 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') })
|
||||||
}
|
}
|
||||||
@ -398,14 +393,10 @@ export default function StepUp(props) {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleChangeApplyParalQty = (mainIdx, subIdx, applyParalQty) => {
|
|
||||||
handleRowClick(mainIdx, subIdx, applyParalQty)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 행 선택 핸들러 함수 추가
|
* 행 선택 핸들러 함수 추가
|
||||||
*/
|
*/
|
||||||
const handleRowClick = (mainIdx, subIdx, applyParalQty = null) => {
|
const handleRowClick = (mainIdx, subIdx) => {
|
||||||
/** 자동 승압 설정인 경우만 실행 */
|
/** 자동 승압 설정인 경우만 실행 */
|
||||||
if (allocationType !== 'auto') return
|
if (allocationType !== 'auto') return
|
||||||
|
|
||||||
@ -437,13 +428,7 @@ export default function StepUp(props) {
|
|||||||
|
|
||||||
/** 선택된 serQty 찾기 */
|
/** 선택된 serQty 찾기 */
|
||||||
const selectedSerQty = matchingPcsItem?.serQtyList.find((serQty) => serQty.selected)?.serQty || 0
|
const selectedSerQty = matchingPcsItem?.serQtyList.find((serQty) => serQty.selected)?.serQty || 0
|
||||||
if (index === 0) {
|
|
||||||
return {
|
|
||||||
...pcsItem,
|
|
||||||
applySerQty: selectedSerQty,
|
|
||||||
applyParalQty: +applyParalQty,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {
|
return {
|
||||||
...pcsItem,
|
...pcsItem,
|
||||||
applySerQty: selectedSerQty,
|
applySerQty: selectedSerQty,
|
||||||
@ -588,7 +573,7 @@ export default function StepUp(props) {
|
|||||||
value={seletedMainOption}
|
value={seletedMainOption}
|
||||||
sourceKey="code"
|
sourceKey="code"
|
||||||
targetKey="code"
|
targetKey="code"
|
||||||
showKey={`${globalLocale === 'ja' ? 'nameJp' : 'name'}`}
|
showKey="name"
|
||||||
onChange={(e) => setSeletedMainOption(e)}
|
onChange={(e) => setSeletedMainOption(e)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@ -601,7 +586,7 @@ export default function StepUp(props) {
|
|||||||
value={seletedSubOption}
|
value={seletedSubOption}
|
||||||
sourceKey="code"
|
sourceKey="code"
|
||||||
targetKey="code"
|
targetKey="code"
|
||||||
showKey={`${globalLocale === 'ja' ? 'nameJp' : 'name'}`}
|
showKey="name"
|
||||||
onChange={(e) => setSeletedSubOption(e)}
|
onChange={(e) => setSeletedSubOption(e)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@ -654,51 +639,11 @@ export default function StepUp(props) {
|
|||||||
<tr
|
<tr
|
||||||
key={`row-${serQtyIdx}`}
|
key={`row-${serQtyIdx}`}
|
||||||
className={`${item.selected ? 'on' : ''}`}
|
className={`${item.selected ? 'on' : ''}`}
|
||||||
|
onClick={() => handleRowClick(idx, serQtyIdx)}
|
||||||
style={{ cursor: allocationType === 'auto' ? 'pointer' : 'default' }}
|
style={{ cursor: allocationType === 'auto' ? 'pointer' : 'default' }}
|
||||||
>
|
>
|
||||||
<td
|
<td className="al-r">{item.serQty}</td>
|
||||||
className="al-r"
|
<td className="al-r">{item.paralQty}</td>
|
||||||
onClick={() => {
|
|
||||||
handleRowClick(idx, serQtyIdx, item.paralQty)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{item.serQty}
|
|
||||||
</td>
|
|
||||||
<td className="al-r">
|
|
||||||
{/* 2025.12.04 select 추가 */}
|
|
||||||
{idx === 0 ? (
|
|
||||||
<select
|
|
||||||
className="select-light dark table-select"
|
|
||||||
defaultValue={item.paralQty}
|
|
||||||
name=""
|
|
||||||
id=""
|
|
||||||
onChange={(e) => {
|
|
||||||
handleChangeApplyParalQty(idx, serQtyIdx, e.target.value)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{item.paralQty === 0 && (
|
|
||||||
<option key="0" value="0">
|
|
||||||
0
|
|
||||||
</option>
|
|
||||||
)}
|
|
||||||
{Array.from(
|
|
||||||
{
|
|
||||||
length: originPcsVoltageStepUpList[index]
|
|
||||||
? originPcsVoltageStepUpList[index]?.pcsItemList[idx].serQtyList[serQtyIdx].paralQty
|
|
||||||
: item.paralQty,
|
|
||||||
},
|
|
||||||
(_, i) => i + 1,
|
|
||||||
).map((num) => (
|
|
||||||
<option key={num} value={num}>
|
|
||||||
{num}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
) : (
|
|
||||||
<>{item.paralQty}</>
|
|
||||||
)}
|
|
||||||
</td>
|
|
||||||
{/* <td className="al-r">{item.paralQty}</td> */}
|
|
||||||
</tr>
|
</tr>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { GlobalDataContext } from '@/app/GlobalDataProvider'
|
import { GlobalDataContext } from '@/app/GlobalDataProvider'
|
||||||
import { POLYGON_TYPE } from '@/common/common'
|
import { POLYGON_TYPE } from '@/common/common'
|
||||||
import { useMasterController } from '@/hooks/common/useMasterController'
|
import { useMasterController } from '@/hooks/common/useMasterController'
|
||||||
|
import { useModule } from '@/hooks/module/useModule'
|
||||||
import { useCircuitTrestle } from '@/hooks/useCirCuitTrestle'
|
import { useCircuitTrestle } from '@/hooks/useCirCuitTrestle'
|
||||||
import { useMessage } from '@/hooks/useMessage'
|
import { useMessage } from '@/hooks/useMessage'
|
||||||
import { useSwal } from '@/hooks/useSwal'
|
import { useSwal } from '@/hooks/useSwal'
|
||||||
@ -9,9 +10,8 @@ import { moduleStatisticsState } from '@/store/circuitTrestleAtom'
|
|||||||
import { fontSelector } from '@/store/fontAtom'
|
import { fontSelector } from '@/store/fontAtom'
|
||||||
import { selectedModuleState } from '@/store/selectedModuleOptions'
|
import { selectedModuleState } from '@/store/selectedModuleOptions'
|
||||||
import { circuitNumDisplaySelector } from '@/store/settingAtom'
|
import { circuitNumDisplaySelector } from '@/store/settingAtom'
|
||||||
import { useContext, useEffect, useRef, useState } from 'react'
|
import { useContext, useEffect, useState } from 'react'
|
||||||
import { 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 {
|
||||||
@ -21,18 +21,16 @@ export default function PassivityCircuitAllocation(props) {
|
|||||||
getOptYn: getApiProps,
|
getOptYn: getApiProps,
|
||||||
getUseModuleItemList: getSelectedModuleList,
|
getUseModuleItemList: getSelectedModuleList,
|
||||||
getSelectModelList: getSelectModelList,
|
getSelectModelList: getSelectModelList,
|
||||||
isFold,
|
|
||||||
} = props
|
} = props
|
||||||
const { swalFire } = useSwal()
|
const { swalFire } = useSwal()
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
const canvas = useRecoilValue(canvasState)
|
const canvas = useRecoilValue(canvasState)
|
||||||
const { managementState } = useContext(GlobalDataContext)
|
const { managementState, setManagementState, managementStateLoaded } = useContext(GlobalDataContext)
|
||||||
const selectedModules = useRecoilValue(selectedModuleState)
|
const selectedModules = useRecoilValue(selectedModuleState)
|
||||||
const [selectedPcs, setSelectedPcs] = useState(selectedModels[0])
|
const [selectedPcs, setSelectedPcs] = useState(selectedModels[0])
|
||||||
const { header, rows, footer } = useRecoilValue(moduleStatisticsState)
|
const { header, rows, footer } = useRecoilValue(moduleStatisticsState)
|
||||||
const [circuitNumber, setCircuitNumber] = useState(1)
|
const [circuitNumber, setCircuitNumber] = useState(1)
|
||||||
const [targetModules, setTargetModules] = useState([])
|
const [targetModules, setTargetModules] = useState([])
|
||||||
const targetModulesRef = useRef([])
|
|
||||||
const { getPcsManualConfChk } = useMasterController()
|
const { getPcsManualConfChk } = useMasterController()
|
||||||
const isDisplayCircuitNumber = useRecoilValue(circuitNumDisplaySelector)
|
const isDisplayCircuitNumber = useRecoilValue(circuitNumDisplaySelector)
|
||||||
const { setModuleStatisticsData } = useCircuitTrestle()
|
const { setModuleStatisticsData } = useCircuitTrestle()
|
||||||
@ -40,12 +38,12 @@ export default function PassivityCircuitAllocation(props) {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setModuleStatisticsData()
|
setModuleStatisticsData()
|
||||||
if (!managementState) {
|
if (!managementState) {
|
||||||
|
setManagementState(managementStateLoaded)
|
||||||
}
|
}
|
||||||
canvas
|
canvas
|
||||||
.getObjects()
|
.getObjects()
|
||||||
.filter((obj) => obj.name === POLYGON_TYPE.MODULE)
|
.filter((obj) => obj.name === POLYGON_TYPE.MODULE)
|
||||||
.forEach((obj) => {
|
.forEach((obj) => {
|
||||||
obj.set({ pcs: null })
|
|
||||||
obj.on('mousedown', (e) => handleTargetModules(obj))
|
obj.on('mousedown', (e) => handleTargetModules(obj))
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -61,10 +59,6 @@ export default function PassivityCircuitAllocation(props) {
|
|||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
targetModulesRef.current = targetModules
|
|
||||||
}, [targetModules])
|
|
||||||
|
|
||||||
const handleTargetModules = (obj) => {
|
const handleTargetModules = (obj) => {
|
||||||
if (!Array.isArray(targetModules)) {
|
if (!Array.isArray(targetModules)) {
|
||||||
setTargetModules([])
|
setTargetModules([])
|
||||||
@ -85,7 +79,6 @@ export default function PassivityCircuitAllocation(props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleCircuitNumberFix = () => {
|
const handleCircuitNumberFix = () => {
|
||||||
const pcsTpCd = selectedPcs.pcsTpCd // 실내집중형, 옥외멀티형
|
|
||||||
let uniqueCircuitNumbers = [
|
let uniqueCircuitNumbers = [
|
||||||
...new Set(
|
...new Set(
|
||||||
canvas
|
canvas
|
||||||
@ -94,16 +87,6 @@ export default function PassivityCircuitAllocation(props) {
|
|||||||
.map((obj) => obj.circuitNumber),
|
.map((obj) => obj.circuitNumber),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
const surfaceList = targetModules.map((module) => {
|
|
||||||
return canvas.getObjects().filter((obj) => obj.id === canvas.getObjects().filter((obj) => obj.id === module)[0].surfaceId)[0]
|
|
||||||
})
|
|
||||||
let surfaceType = {}
|
|
||||||
|
|
||||||
surfaceList.forEach((surface) => {
|
|
||||||
surfaceType[`${surface.direction}-${surface.roofMaterial.pitch}`] = surface
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!circuitNumber || circuitNumber === 0) {
|
if (!circuitNumber || circuitNumber === 0) {
|
||||||
swalFire({
|
swalFire({
|
||||||
text: getMessage('module.circuit.minimun.error'),
|
text: getMessage('module.circuit.minimun.error'),
|
||||||
@ -111,69 +94,14 @@ export default function PassivityCircuitAllocation(props) {
|
|||||||
icon: 'warning',
|
icon: 'warning',
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
} else if (targetModules.length === 0) {
|
||||||
|
|
||||||
if (targetModules.length === 0) {
|
|
||||||
swalFire({
|
swalFire({
|
||||||
text: getMessage('module.not.found'),
|
text: getMessage('module.not.found'),
|
||||||
type: 'alert',
|
type: 'alert',
|
||||||
icon: 'warning',
|
icon: 'warning',
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
} else if (selectedModels.length > 1) {
|
||||||
|
|
||||||
// targetModule중 북면 설치 여부가 Y인 것과 N인 것이 혼합이면 안됨.
|
|
||||||
const targetModuleGroup = [
|
|
||||||
...new Set(
|
|
||||||
canvas
|
|
||||||
.getObjects()
|
|
||||||
.filter((obj) => obj.name === POLYGON_TYPE.MODULE && targetModules.includes(obj.id))
|
|
||||||
.map((obj) => obj.moduleInfo.northModuleYn),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
|
|
||||||
if (targetModuleGroup.length > 1) {
|
|
||||||
swalFire({
|
|
||||||
text: getMessage('module.circuit.fix.not.same.roof.error'),
|
|
||||||
type: 'alert',
|
|
||||||
icon: 'warning',
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (pcsTpCd) {
|
|
||||||
case 'INDFCS': {
|
|
||||||
const originHaveThisPcsModules = canvas
|
|
||||||
.getObjects()
|
|
||||||
.filter((obj) => obj.name === POLYGON_TYPE.MODULE && obj.pcs && obj.pcs.id === selectedPcs.id)
|
|
||||||
// 1. 북면모듈, 북면외모듈 혼합 여부 체크
|
|
||||||
const targetModuleInfos = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE && targetModules.includes(obj.id))
|
|
||||||
debugger
|
|
||||||
const newTargetModuleGroup = [...new Set(targetModuleInfos.concat(originHaveThisPcsModules).map((obj) => obj.moduleInfo.northModuleYn))]
|
|
||||||
|
|
||||||
if (newTargetModuleGroup.length > 1) {
|
|
||||||
swalFire({
|
|
||||||
text: getMessage('module.circuit.fix.not.same.roof.error'),
|
|
||||||
type: 'alert',
|
|
||||||
icon: 'warning',
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case 'OUTDMULTI': {
|
|
||||||
if (surfaceList.length > 1) {
|
|
||||||
if (Object.keys(surfaceType).length > 1) {
|
|
||||||
swalFire({
|
|
||||||
text: getMessage('module.circuit.fix.not.same.roof.error'),
|
|
||||||
type: 'alert',
|
|
||||||
icon: 'warning',
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (selectedModels.length > 1) {
|
|
||||||
let result = false
|
let result = false
|
||||||
|
|
||||||
uniqueCircuitNumbers.forEach((number) => {
|
uniqueCircuitNumbers.forEach((number) => {
|
||||||
@ -193,8 +121,6 @@ export default function PassivityCircuitAllocation(props) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let tempSelectedPcs = { ...selectedPcs }
|
let tempSelectedPcs = { ...selectedPcs }
|
||||||
canvas.discardActiveObject()
|
canvas.discardActiveObject()
|
||||||
@ -243,8 +169,6 @@ export default function PassivityCircuitAllocation(props) {
|
|||||||
roofSurfaceId: surface.id,
|
roofSurfaceId: surface.id,
|
||||||
roofSurface: surface.direction,
|
roofSurface: surface.direction,
|
||||||
roofSurfaceIncl: +canvas.getObjects().filter((obj) => obj.id === surface.parentId)[0].pitch,
|
roofSurfaceIncl: +canvas.getObjects().filter((obj) => obj.id === surface.parentId)[0].pitch,
|
||||||
roofSurfaceNorthYn: surface.direction === 'north' ? 'Y' : 'N',
|
|
||||||
roofSurfaceNorthModuleYn: surface.northModuleYn,
|
|
||||||
moduleList: surface.modules.map((module) => {
|
moduleList: surface.modules.map((module) => {
|
||||||
return {
|
return {
|
||||||
itemId: module.moduleInfo.itemId,
|
itemId: module.moduleInfo.itemId,
|
||||||
@ -326,12 +250,6 @@ export default function PassivityCircuitAllocation(props) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
targetModules.forEach((module) => {
|
|
||||||
const modules = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE)
|
|
||||||
const targetModule = modules.find((obj) => obj.id === module)
|
|
||||||
targetModule.pcs = selectedPcs
|
|
||||||
})
|
|
||||||
|
|
||||||
setTargetModules([])
|
setTargetModules([])
|
||||||
setCircuitNumber(+circuitNumber + 1)
|
setCircuitNumber(+circuitNumber + 1)
|
||||||
setModuleStatisticsData()
|
setModuleStatisticsData()
|
||||||
@ -366,7 +284,6 @@ export default function PassivityCircuitAllocation(props) {
|
|||||||
obj.circuit = null
|
obj.circuit = null
|
||||||
obj.circuitNumber = null
|
obj.circuitNumber = null
|
||||||
obj.pcsItemId = null
|
obj.pcsItemId = null
|
||||||
obj.pcs = null
|
|
||||||
})
|
})
|
||||||
setCircuitNumber(minCircuitNumber)
|
setCircuitNumber(minCircuitNumber)
|
||||||
setTargetModules([])
|
setTargetModules([])
|
||||||
@ -393,7 +310,6 @@ export default function PassivityCircuitAllocation(props) {
|
|||||||
obj.circuit = null
|
obj.circuit = null
|
||||||
obj.circuitNumber = null
|
obj.circuitNumber = null
|
||||||
obj.pcsItemId = null
|
obj.pcsItemId = null
|
||||||
obj.pcs = null
|
|
||||||
})
|
})
|
||||||
canvas.renderAll()
|
canvas.renderAll()
|
||||||
setCircuitNumber(1)
|
setCircuitNumber(1)
|
||||||
@ -561,7 +477,6 @@ export default function PassivityCircuitAllocation(props) {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="properties-setting-wrap outer">
|
<div className="properties-setting-wrap outer">
|
||||||
<div style={{ display: isFold ? 'none' : 'block' }}>
|
|
||||||
<div className="setting-tit">{getMessage('modal.circuit.trestle.setting.circuit.allocation')}</div>
|
<div className="setting-tit">{getMessage('modal.circuit.trestle.setting.circuit.allocation')}</div>
|
||||||
<div className="module-table-box mb10">
|
<div className="module-table-box mb10">
|
||||||
<div className="module-table-inner">
|
<div className="module-table-inner">
|
||||||
@ -606,9 +521,7 @@ export default function PassivityCircuitAllocation(props) {
|
|||||||
<div className="module-table-inner">
|
<div className="module-table-inner">
|
||||||
<div className="hexagonal-wrap">
|
<div className="hexagonal-wrap">
|
||||||
<div className="hexagonal-item">
|
<div className="hexagonal-item">
|
||||||
<div className="bold-font">
|
<div className="bold-font">{getMessage('modal.circuit.trestle.setting.circuit.allocation.passivity.selected.power.conditional')}</div>
|
||||||
{getMessage('modal.circuit.trestle.setting.circuit.allocation.passivity.selected.power.conditional')}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="hexagonal-item">
|
<div className="hexagonal-item">
|
||||||
{selectedModels.map((model, index) => (
|
{selectedModels.map((model, index) => (
|
||||||
@ -635,7 +548,6 @@ export default function PassivityCircuitAllocation(props) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div className="slope-wrap">
|
<div className="slope-wrap">
|
||||||
<div className="circuit-right-wrap mb15">
|
<div className="circuit-right-wrap mb15">
|
||||||
<div className="outline-form">
|
<div className="outline-form">
|
||||||
@ -649,20 +561,7 @@ export default function PassivityCircuitAllocation(props) {
|
|||||||
value={circuitNumber}
|
value={circuitNumber}
|
||||||
min={1}
|
min={1}
|
||||||
max={99}
|
max={99}
|
||||||
onChange={(e) => {
|
onChange={(e) => setCircuitNumber(e.target.value)}
|
||||||
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,7 +10,6 @@ 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)
|
||||||
@ -145,13 +144,8 @@ export default function DimensionLineSetting(props) {
|
|||||||
defaultValue={''}
|
defaultValue={''}
|
||||||
value={changeLength}
|
value={changeLength}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const v = e.target.value
|
console.log(e.target)
|
||||||
if (v === '') {
|
setChangeLength(e.target.value)
|
||||||
setChangeLength('')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const n = Number(normalizeDigits(v))
|
|
||||||
setChangeLength(Number.isNaN(n) ? '' : n)
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -3,8 +3,6 @@ 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'
|
|
||||||
import { CalculatorInput } from '@/components/common/input/CalcInput'
|
|
||||||
|
|
||||||
export default function Eaves({ pitchRef, offsetRef, widthRef, radioTypeRef, pitchText }) {
|
export default function Eaves({ pitchRef, offsetRef, widthRef, radioTypeRef, pitchText }) {
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
@ -22,32 +20,7 @@ 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*/}
|
<input type="text" className="input-origin block" defaultValue={currentAngleType === ANGLE_TYPE.SLOPE ? 4 : 21.8} ref={pitchRef} />
|
||||||
{/* 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*/}
|
|
||||||
{/* }}*/}
|
|
||||||
{/*/>*/}
|
|
||||||
<CalculatorInput
|
|
||||||
id=""
|
|
||||||
name=""
|
|
||||||
label=""
|
|
||||||
className="input-origin block"
|
|
||||||
ref={pitchRef}
|
|
||||||
value={currentAngleType === ANGLE_TYPE.SLOPE ? 4 : 21.8}
|
|
||||||
onChange={(value) => {
|
|
||||||
if (pitchRef?.current) pitchRef.current.value = value
|
|
||||||
}}
|
|
||||||
options={{
|
|
||||||
allowNegative: false,
|
|
||||||
allowDecimal: true //(index !== 0),
|
|
||||||
}}
|
|
||||||
></CalculatorInput>
|
|
||||||
</div>
|
</div>
|
||||||
<span className="thin">{pitchText}</span>
|
<span className="thin">{pitchText}</span>
|
||||||
</div>
|
</div>
|
||||||
@ -56,32 +29,7 @@ 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*/}
|
<input type="text" className="input-origin block" defaultValue={500} ref={offsetRef} />
|
||||||
{/* 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*/}
|
|
||||||
{/* }}*/}
|
|
||||||
{/*/>*/}
|
|
||||||
<CalculatorInput
|
|
||||||
id=""
|
|
||||||
name=""
|
|
||||||
label=""
|
|
||||||
className="input-origin block"
|
|
||||||
ref={offsetRef}
|
|
||||||
value={500}
|
|
||||||
onChange={(value) => {
|
|
||||||
if (offsetRef?.current) offsetRef.current.value = value
|
|
||||||
}}
|
|
||||||
options={{
|
|
||||||
allowNegative: false,
|
|
||||||
allowDecimal: false //(index !== 0),
|
|
||||||
}}
|
|
||||||
></CalculatorInput>
|
|
||||||
</div>
|
</div>
|
||||||
<span className="thin">mm</span>
|
<span className="thin">mm</span>
|
||||||
</div>
|
</div>
|
||||||
@ -97,9 +45,7 @@ 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' : ''}`}>
|
||||||
<div style={{ width: 30, height: 30, position: 'relative' }}>
|
<Image src="/static/images/canvas/eaves_icon01.svg" alt="react" width={30} height={30} />
|
||||||
<Image src="/static/images/canvas/eaves_icon01.svg" alt="react" fill style={{ objectFit: 'contain' }} />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -112,9 +58,7 @@ 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' : ''}`}>
|
||||||
<div style={{ width: 30, height: 30, position: 'relative' }}>
|
<Image src="/static/images/canvas/eaves_icon02.svg" alt="react" width={30} height={30} />
|
||||||
<Image src="/static/images/canvas/eaves_icon02.svg" alt="react" fill style={{ objectFit: 'contain' }} />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -122,42 +66,14 @@ 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*/}
|
<input type="number" min={0} className="input-origin block" defaultValue={500} ref={widthRef} readOnly={type === '1'} />
|
||||||
{/* 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*/}
|
|
||||||
{/* }}*/}
|
|
||||||
{/*/>*/}
|
|
||||||
<CalculatorInput
|
|
||||||
id=""
|
|
||||||
name=""
|
|
||||||
label=""
|
|
||||||
className="input-origin block"
|
|
||||||
ref={widthRef}
|
|
||||||
value={500}
|
|
||||||
onChange={(value) => {
|
|
||||||
if (widthRef?.current) widthRef.current.value = value
|
|
||||||
}}
|
|
||||||
options={{
|
|
||||||
allowNegative: false,
|
|
||||||
allowDecimal: false //(index !== 0),
|
|
||||||
}}
|
|
||||||
></CalculatorInput>
|
|
||||||
</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 ">
|
||||||
<div style={{ width: 30, height: 30, position: 'relative' }}>
|
<Image src="/static/images/canvas/eaves_icon03.svg" alt="react" width={30} height={30} />
|
||||||
<Image src="/static/images/canvas/eaves_icon03.svg" alt="react" fill style={{ objectFit: 'contain' }} />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -3,7 +3,6 @@ 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 { CalculatorInput } from '@/components/common/input/CalcInput'
|
|
||||||
|
|
||||||
export default function Gable({ pitchRef, offsetRef, widthRef, radioTypeRef, pitchText }) {
|
export default function Gable({ pitchRef, offsetRef, widthRef, radioTypeRef, pitchText }) {
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
@ -22,19 +21,7 @@ export default function Gable({ 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={300} ref={offsetRef} />*/}
|
<input type="text" className="input-origin block" defaultValue={300} ref={offsetRef} />
|
||||||
<CalculatorInput
|
|
||||||
id=""
|
|
||||||
name=""
|
|
||||||
label=""
|
|
||||||
className="input-origin block"
|
|
||||||
ref={offsetRef}
|
|
||||||
value={300}
|
|
||||||
options={{
|
|
||||||
allowNegative: false,
|
|
||||||
allowDecimal: false //(index !== 0),
|
|
||||||
}}
|
|
||||||
></CalculatorInput>
|
|
||||||
</div>
|
</div>
|
||||||
<span className="thin">mm</span>
|
<span className="thin">mm</span>
|
||||||
</div>
|
</div>
|
||||||
@ -50,9 +37,7 @@ 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' : ''}`}>
|
||||||
<div style={{ width: 30, height: 30, position: 'relative' }}>
|
<Image src="/static/images/canvas/eaves_icon04.svg" alt="react" width={30} height={30} />
|
||||||
<Image src="/static/images/canvas/eaves_icon04.svg" alt="react" fill style={{ objectFit: 'contain' }} />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -65,9 +50,7 @@ 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' : ''}`}>
|
||||||
<div style={{ width: 30, height: 30, position: 'relative' }}>
|
<Image src="/static/images/canvas/eaves_icon09.svg" alt="react" width={30} height={30} />
|
||||||
<Image src="/static/images/canvas/eaves_icon09.svg" alt="react" fill style={{ objectFit: 'contain' }} />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -78,38 +61,20 @@ export default function Gable({ 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*/}
|
<input
|
||||||
{/* type="text"*/}
|
type="text"
|
||||||
{/* className="input-origin block"*/}
|
|
||||||
{/* defaultValue={currentAngleType === ANGLE_TYPE.SLOPE ? 4.5 : 20}*/}
|
|
||||||
{/* ref={pitchRef}*/}
|
|
||||||
{/* readOnly={type === '1'}*/}
|
|
||||||
{/*/>*/}
|
|
||||||
<CalculatorInput
|
|
||||||
id=""
|
|
||||||
name=""
|
|
||||||
label=""
|
|
||||||
className="input-origin block"
|
className="input-origin block"
|
||||||
|
defaultValue={currentAngleType === ANGLE_TYPE.SLOPE ? 4.5 : 20}
|
||||||
ref={pitchRef}
|
ref={pitchRef}
|
||||||
value={currentAngleType === ANGLE_TYPE.SLOPE ? 4.5 : 20}
|
|
||||||
readOnly={type === '1'}
|
readOnly={type === '1'}
|
||||||
onChange={(value) => {
|
/>
|
||||||
if (pitchRef?.current) pitchRef.current.value = value
|
|
||||||
}}
|
|
||||||
options={{
|
|
||||||
allowNegative: false,
|
|
||||||
allowDecimal: true //(index !== 0),
|
|
||||||
}}
|
|
||||||
></CalculatorInput>
|
|
||||||
</div>
|
</div>
|
||||||
<span className="thin">{pitchText}</span>
|
<span className="thin">{pitchText}</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 ">
|
||||||
<div style={{ width: 30, height: 30, position: 'relative' }}>
|
<Image src="/static/images/canvas/eaves_icon05.svg" alt="react" width={30} height={30} />
|
||||||
<Image src="/static/images/canvas/eaves_icon05.svg" alt="react" fill style={{ objectFit: 'contain' }} />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -120,29 +85,14 @@ export default function Gable({ 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={800} ref={widthRef} readOnly={type === '1'} />*/}
|
<input type="text" className="input-origin block" defaultValue={800} ref={widthRef} readOnly={type === '1'} />
|
||||||
<CalculatorInput
|
|
||||||
id=""
|
|
||||||
name=""
|
|
||||||
label=""
|
|
||||||
className="input-origin block"
|
|
||||||
ref={widthRef}
|
|
||||||
value={800}
|
|
||||||
readOnly={type === '1'}
|
|
||||||
options={{
|
|
||||||
allowNegative: false,
|
|
||||||
allowDecimal: false //(index !== 0),
|
|
||||||
}}
|
|
||||||
></CalculatorInput>
|
|
||||||
</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 ">
|
||||||
<div style={{ width: 30, height: 30, position: 'relative' }}>
|
<Image src="/static/images/canvas/eaves_icon10.svg" alt="react" width={30} height={30} />
|
||||||
<Image src="/static/images/canvas/eaves_icon10.svg" alt="react" fill style={{ objectFit: 'contain' }} />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import { useMessage } from '@/hooks/useMessage'
|
import { useMessage } from '@/hooks/useMessage'
|
||||||
import { CalculatorInput } from '@/components/common/input/CalcInput'
|
|
||||||
|
|
||||||
export default function Shed({ offsetRef }) {
|
export default function Shed({ offsetRef }) {
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
@ -11,19 +10,7 @@ export default function Shed({ offsetRef }) {
|
|||||||
{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" ref={offsetRef} defaultValue={300} />*/}
|
<input type="text" className="input-origin block" ref={offsetRef} defaultValue={300} />
|
||||||
<CalculatorInput
|
|
||||||
id=""
|
|
||||||
name=""
|
|
||||||
label=""
|
|
||||||
className="input-origin block"
|
|
||||||
ref={offsetRef}
|
|
||||||
value={300}
|
|
||||||
options={{
|
|
||||||
allowNegative: false,
|
|
||||||
allowDecimal: false //(index !== 0),
|
|
||||||
}}
|
|
||||||
></CalculatorInput>
|
|
||||||
</div>
|
</div>
|
||||||
<span className="thin">mm</span>
|
<span className="thin">mm</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import { useMessage } from '@/hooks/useMessage'
|
import { useMessage } from '@/hooks/useMessage'
|
||||||
import Image from 'next/image'
|
import Image from 'next/image'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { CalculatorInput } from '@/components/common/input/CalcInput'
|
|
||||||
|
|
||||||
export default function WallMerge({ offsetRef, radioTypeRef }) {
|
export default function WallMerge({ offsetRef, radioTypeRef }) {
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
@ -19,14 +18,12 @@ export default function WallMerge({ offsetRef, radioTypeRef }) {
|
|||||||
<div className="eaves-keraba-th">
|
<div className="eaves-keraba-th">
|
||||||
<div className="d-check-radio pop">
|
<div className="d-check-radio pop">
|
||||||
<input type="radio" name="radio01" id="ra01" value="1" checked={type === '1'} onChange={(e) => onChange(e)} />
|
<input type="radio" name="radio01" id="ra01" value="1" checked={type === '1'} onChange={(e) => onChange(e)} />
|
||||||
<label htmlFor="ra01">{getMessage('has.not.sleeve')}</label>
|
<label htmlFor="ra01">{getMessage('has.sleeve')}</label>
|
||||||
</div>
|
</div>
|
||||||
</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' : ''}`}>
|
||||||
<div style={{ width: 30, height: 30, position: 'relative' }}>
|
<Image src="/static/images/canvas/eaves_icon06.svg" alt="react" width={30} height={30} />
|
||||||
<Image src="/static/images/canvas/eaves_icon06.svg" alt="react" fill style={{ objectFit: 'contain' }} />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -34,14 +31,12 @@ export default function WallMerge({ offsetRef, radioTypeRef }) {
|
|||||||
<div className="eaves-keraba-th">
|
<div className="eaves-keraba-th">
|
||||||
<div className="d-check-radio pop">
|
<div className="d-check-radio pop">
|
||||||
<input type="radio" name="radio01" id="ra02" value="2" checked={type === '2'} onChange={(e) => onChange(e)} />
|
<input type="radio" name="radio01" id="ra02" value="2" checked={type === '2'} onChange={(e) => onChange(e)} />
|
||||||
<label htmlFor="ra02">{getMessage('has.sleeve')}</label>
|
<label htmlFor="ra02">{getMessage('has.not.sleeve')}</label>
|
||||||
</div>
|
</div>
|
||||||
</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' : ''}`}>
|
||||||
<div style={{ width: 30, height: 30, position: 'relative' }}>
|
<Image src="/static/images/canvas/eaves_icon07.svg" alt="react" width={30} height={30} />
|
||||||
<Image src="/static/images/canvas/eaves_icon07.svg" alt="react" fill style={{ objectFit: 'contain' }} />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -52,29 +47,14 @@ export default function WallMerge({ offsetRef, radioTypeRef }) {
|
|||||||
{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={300} ref={offsetRef} readOnly={type === '1'} />*/}
|
<input type="text" className="input-origin block" defaultValue={300} ref={offsetRef} readOnly={type === '1'} />
|
||||||
<CalculatorInput
|
|
||||||
id=""
|
|
||||||
name=""
|
|
||||||
label=""
|
|
||||||
className="input-origin block"
|
|
||||||
ref={offsetRef}
|
|
||||||
value={300}
|
|
||||||
readOnly={type === '1'}
|
|
||||||
options={{
|
|
||||||
allowNegative: false,
|
|
||||||
allowDecimal: false //(index !== 0),
|
|
||||||
}}
|
|
||||||
></CalculatorInput>
|
|
||||||
</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 ">
|
||||||
<div style={{ width: 30, height: 30, position: 'relative' }}>
|
<Image src="/static/images/canvas/eaves_icon08.svg" alt="react" width={30} height={30} />
|
||||||
<Image src="/static/images/canvas/eaves_icon08.svg" alt="react" fill style={{ objectFit: 'contain' }} />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -7,8 +7,6 @@ import { useMessage } from '@/hooks/useMessage'
|
|||||||
import { usePopup } from '@/hooks/usePopup'
|
import { usePopup } from '@/hooks/usePopup'
|
||||||
import { canvasState } from '@/store/canvasAtom'
|
import { canvasState } from '@/store/canvasAtom'
|
||||||
import { usePolygon } from '@/hooks/usePolygon'
|
import { usePolygon } from '@/hooks/usePolygon'
|
||||||
import { useSurfaceShapeBatch } from '@/hooks/surface/useSurfaceShapeBatch'
|
|
||||||
import { useRoofFn } from '@/hooks/common/useRoofFn'
|
|
||||||
|
|
||||||
const FLOW_DIRECTION_TYPE = {
|
const FLOW_DIRECTION_TYPE = {
|
||||||
EIGHT_AZIMUTH: 'eightAzimuth',
|
EIGHT_AZIMUTH: 'eightAzimuth',
|
||||||
@ -20,9 +18,6 @@ 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({})
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return () => {
|
return () => {
|
||||||
@ -34,40 +29,15 @@ 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: '' },
|
{ name: `${getMessage('commons.none')}`, value: 0 },
|
||||||
{ name: `${getMessage('commons.south')}`, value: 0 },
|
{ name: `${getMessage('commons.south')}`, value: 360 },
|
||||||
{ name: `${getMessage('commons.south')}${getMessage('commons.east')}`, value: 45 },
|
{ name: `${getMessage('commons.south')}${getMessage('commons.east')}`, value: 315 },
|
||||||
{ name: `${getMessage('commons.south')}${getMessage('commons.west')}`, value: -45 },
|
{ name: `${getMessage('commons.south')}${getMessage('commons.west')}`, value: 45 },
|
||||||
{ name: `${getMessage('commons.east')}`, value: 90 },
|
{ name: `${getMessage('commons.east')}`, value: 270 },
|
||||||
{ name: `${getMessage('commons.west')}`, value: -90 },
|
{ name: `${getMessage('commons.west')}`, value: 90 },
|
||||||
{ name: `${getMessage('commons.north')}${getMessage('commons.east')}`, value: 135 },
|
{ name: `${getMessage('commons.north')}${getMessage('commons.east')}`, value: 225 },
|
||||||
{ 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 },
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -81,10 +51,8 @@ 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)
|
|
||||||
closePopup(id)
|
closePopup(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,11 +105,6 @@ 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)
|
||||||
@ -170,32 +133,31 @@ 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 }).map((dot, index) => (
|
{Array.from({ length: 180 / 15 + 1 }).map((dot, index) => (
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={index}
|
||||||
className={`circle ${compasDeg === -15 * index + 180 ? 'act' : ''}`}
|
className={`circle ${compasDeg === 15 * (12 + index) && type === FLOW_DIRECTION_TYPE.TWENTY_FOUR_AZIMUTH ? '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 * index + 180)
|
setCompasDeg(15 * (12 + index))
|
||||||
}}
|
}}
|
||||||
></div>
|
></div>
|
||||||
))}
|
))}
|
||||||
{Array.from({ length: 180 / 15 }).map((dot, index) => (
|
{Array.from({ length: 180 / 15 - 1 }).map((dot, index) => (
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={index}
|
||||||
className={`circle ${compasDeg === -1 * 15 * index ? 'act' : ''}`}
|
className={`circle ${compasDeg === 15 * (index + 1) && type === FLOW_DIRECTION_TYPE.TWENTY_FOUR_AZIMUTH ? '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 className="compas-arr" style={{ transform: `${`rotate(${-1 * compasDeg}deg)`}` }}></div>
|
<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) => changeInput(normalizeDigits(e.target.value), e)}
|
onChange={(e) => onlyNumberInputChange(e, changeInput)}
|
||||||
/>
|
/>
|
||||||
</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) => changeInput(normalizeDigits(e.target.value), e)}
|
onChange={(e) => onlyNumberInputChange(e, changeInput)}
|
||||||
/>
|
/>
|
||||||
</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) => changeInput(normalizeDigits(e.target.value), e)}
|
onChange={(e) => onlyNumberInputChange(e, changeInput)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<span>mm</span>
|
<span>mm</span>
|
||||||
|
|||||||
@ -4,11 +4,9 @@ import { usePopup } from '@/hooks/usePopup'
|
|||||||
import { useRecoilValue } from 'recoil'
|
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 { currentObjectState } from '@/store/canvasAtom'
|
||||||
import { gridColorState } from '@/store/gridAtom'
|
import { useGrid } from '@/hooks/common/useGrid'
|
||||||
import { gridDisplaySelector } from '@/store/settingAtom'
|
|
||||||
|
|
||||||
const GRID_PADDING = 5
|
|
||||||
export default function GridCopy(props) {
|
export default function GridCopy(props) {
|
||||||
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
||||||
const { id, pos = contextPopupPosition } = props
|
const { id, pos = contextPopupPosition } = props
|
||||||
@ -17,39 +15,9 @@ export default function GridCopy(props) {
|
|||||||
const [length, setLength] = useState('0')
|
const [length, setLength] = useState('0')
|
||||||
const [arrow, setArrow] = useState(null)
|
const [arrow, setArrow] = useState(null)
|
||||||
const currentObject = useRecoilValue(currentObjectState)
|
const currentObject = useRecoilValue(currentObjectState)
|
||||||
const canvas = useRecoilValue(canvasState)
|
const { copy } = useGrid()
|
||||||
const gridColor = useRecoilValue(gridColorState)
|
|
||||||
const isGridDisplay = useRecoilValue(gridDisplaySelector)
|
|
||||||
const handleApply = () => {
|
const handleApply = () => {
|
||||||
copy(currentObject, ['↑', '←'].includes(arrow) ? (+length * -1) / 10 : +length / 10)
|
copy(currentObject, ['↑', '←'].includes(arrow) ? +length * -1 : +length)
|
||||||
}
|
|
||||||
|
|
||||||
const copy = (object, length) => {
|
|
||||||
const lineStartX = object.direction === 'vertical' ? object.x1 + length : object.x1
|
|
||||||
const lineEndX = object.direction === 'vertical' ? object.x2 + length : object.x2
|
|
||||||
const lineStartY = object.direction === 'vertical' ? object.y1 : object.y1 + length
|
|
||||||
const lineEndY = object.direction === 'vertical' ? object.y2 : object.y1 + length
|
|
||||||
|
|
||||||
const line = new fabric.Line([lineStartX, lineStartY, lineEndX, lineEndY], {
|
|
||||||
stroke: gridColor,
|
|
||||||
strokeWidth: 1,
|
|
||||||
selectable: true,
|
|
||||||
lockMovementX: true,
|
|
||||||
lockMovementY: true,
|
|
||||||
lockRotation: true,
|
|
||||||
lockScalingX: true,
|
|
||||||
lockScalingY: true,
|
|
||||||
strokeDashArray: [5, 2],
|
|
||||||
opacity: 0.3,
|
|
||||||
padding: GRID_PADDING,
|
|
||||||
direction: object.direction,
|
|
||||||
visible: isGridDisplay,
|
|
||||||
name: object.name,
|
|
||||||
})
|
|
||||||
|
|
||||||
canvas.add(line)
|
|
||||||
canvas.setActiveObject(line)
|
|
||||||
canvas.renderAll()
|
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<WithDraggable isShow={true} pos={pos} className="xm">
|
<WithDraggable isShow={true} pos={pos} className="xm">
|
||||||
|
|||||||
@ -3,10 +3,12 @@ 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 { useGrid } from '@/hooks/common/useGrid'
|
||||||
import { useSwal } from '@/hooks/useSwal'
|
import { useSwal } from '@/hooks/useSwal'
|
||||||
import { normalizeDigits } from '@/util/input-utils'
|
import { set } from 'react-hook-form'
|
||||||
|
|
||||||
export default function GridMove(props) {
|
export default function GridMove(props) {
|
||||||
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
||||||
@ -15,6 +17,7 @@ export default function GridMove(props) {
|
|||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
const { closePopup } = usePopup()
|
const { closePopup } = usePopup()
|
||||||
const { swalFire } = useSwal()
|
const { swalFire } = useSwal()
|
||||||
|
const { move } = useGrid()
|
||||||
const [currentObject, setCurrentObject] = useRecoilState(currentObjectState)
|
const [currentObject, setCurrentObject] = useRecoilState(currentObjectState)
|
||||||
const [isAll, setIsAll] = useState(false)
|
const [isAll, setIsAll] = useState(false)
|
||||||
const [verticalSize, setVerticalSize] = useState('0')
|
const [verticalSize, setVerticalSize] = useState('0')
|
||||||
@ -51,31 +54,21 @@ export default function GridMove(props) {
|
|||||||
.forEach((grid) => {
|
.forEach((grid) => {
|
||||||
move(
|
move(
|
||||||
grid,
|
grid,
|
||||||
arrow2 === '←' ? (Number(normalizeDigits(horizonSize)) * -1) / 10 : Number(normalizeDigits(horizonSize)) / 10,
|
arrow2 === '←' ? Number(horizonSize) * -1 : Number(horizonSize),
|
||||||
arrow1 === '↑' ? (Number(normalizeDigits(verticalSize)) * -1) / 10 : Number(normalizeDigits(verticalSize)) / 10,
|
arrow1 === '↑' ? Number(verticalSize) * -1 : Number(verticalSize),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
move(
|
move(
|
||||||
currentObject,
|
currentObject,
|
||||||
arrow2 === '←' ? (Number(normalizeDigits(horizonSize)) * -1) / 10 : Number(normalizeDigits(horizonSize)) / 10,
|
arrow2 === '←' ? Number(horizonSize) * -1 : Number(horizonSize),
|
||||||
arrow1 === '↑' ? (Number(normalizeDigits(verticalSize)) * -1) / 10 : Number(normalizeDigits(verticalSize)) / 10,
|
arrow1 === '↑' ? Number(verticalSize) * -1 : Number(verticalSize),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
canvas.renderAll()
|
canvas.renderAll()
|
||||||
handleClose()
|
handleClose()
|
||||||
}
|
}
|
||||||
|
|
||||||
const move = (object, x, y) => {
|
|
||||||
object.set({
|
|
||||||
...object,
|
|
||||||
x1: object.direction === 'vertical' ? object.x1 + x : object.x1,
|
|
||||||
x2: object.direction === 'vertical' ? object.x1 + x : object.x2,
|
|
||||||
y1: object.direction === 'vertical' ? object.y1 : object.y1 + y,
|
|
||||||
y2: object.direction === 'vertical' ? object.y2 : object.y1 + y,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
closePopup(id)
|
closePopup(id)
|
||||||
}
|
}
|
||||||
@ -98,7 +91,7 @@ export default function GridMove(props) {
|
|||||||
type="text"
|
type="text"
|
||||||
className="input-origin"
|
className="input-origin"
|
||||||
value={verticalSize}
|
value={verticalSize}
|
||||||
onChange={(e) => setVerticalSize(normalizeDigits(e.target.value))}
|
onChange={(e) => setVerticalSize(e.target.value)}
|
||||||
readOnly={!isAll && currentObject?.direction === 'vertical'}
|
readOnly={!isAll && currentObject?.direction === 'vertical'}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -126,7 +119,7 @@ export default function GridMove(props) {
|
|||||||
type="text"
|
type="text"
|
||||||
className="input-origin"
|
className="input-origin"
|
||||||
value={horizonSize}
|
value={horizonSize}
|
||||||
onChange={(e) => setHorizonSize(normalizeDigits(e.target.value))}
|
onChange={(e) => setHorizonSize(e.target.value)}
|
||||||
readOnly={!isAll && currentObject?.direction === 'horizontal'}
|
readOnly={!isAll && currentObject?.direction === 'horizontal'}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,8 +1,6 @@
|
|||||||
import Image from 'next/image'
|
import Image from 'next/image'
|
||||||
import { useMessage } from '@/hooks/useMessage'
|
import { useMessage } from '@/hooks/useMessage'
|
||||||
import { normalizeDecimalLimit, normalizeDigits } from '@/util/input-utils'
|
import { onlyNumberInputChange, onlyNumberWithDotInputChange } from '@/util/input-utils'
|
||||||
import { CalculatorInput } from '@/components/common/input/CalcInput'
|
|
||||||
import { useEffect } from 'react'
|
|
||||||
|
|
||||||
export default function Angle({ props }) {
|
export default function Angle({ props }) {
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
@ -16,39 +14,14 @@ export default function Angle({ props }) {
|
|||||||
<div className="outline-form">
|
<div className="outline-form">
|
||||||
<span className="mr10">{getMessage('modal.cover.outline.angle')}</span>
|
<span className="mr10">{getMessage('modal.cover.outline.angle')}</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"*/}
|
|
||||||
{/* value={angle1}*/}
|
|
||||||
{/* ref={angle1Ref}*/}
|
|
||||||
{/* onFocus={(e) => (angle1Ref.current.value = '')}*/}
|
|
||||||
{/* onChange={(e) => setAngle1(normalizeDecimalLimit(e.target.value, 2))}*/}
|
|
||||||
{/* placeholder="45"*/}
|
|
||||||
{/*/>*/}
|
|
||||||
<CalculatorInput
|
|
||||||
id=""
|
|
||||||
name=""
|
|
||||||
label=""
|
|
||||||
className="input-origin block"
|
className="input-origin block"
|
||||||
value={angle1}
|
value={angle1}
|
||||||
ref={angle1Ref}
|
ref={angle1Ref}
|
||||||
onChange={(value) => {
|
onFocus={(e) => (angle1Ref.current.value = '')}
|
||||||
// Calculate the final value first
|
onChange={(e) => onlyNumberWithDotInputChange(e, setAngle1)}
|
||||||
let finalValue = value;
|
|
||||||
const numValue = parseInt(value, 10);
|
|
||||||
if (!isNaN(numValue)) {
|
|
||||||
const clampedValue = Math.min(180, Math.max(-180, numValue));
|
|
||||||
finalValue = String(clampedValue);
|
|
||||||
}
|
|
||||||
// Set state once with the final value
|
|
||||||
setAngle1(finalValue);
|
|
||||||
}}
|
|
||||||
placeholder="45"
|
placeholder="45"
|
||||||
onFocus={() => (angle1Ref.current.value = '')}
|
|
||||||
options={{
|
|
||||||
allowNegative: true,
|
|
||||||
allowDecimal: true
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
@ -61,29 +34,14 @@ export default function Angle({ props }) {
|
|||||||
<div className="outline-form">
|
<div className="outline-form">
|
||||||
<span className="mr10">{getMessage('modal.cover.outline.length')}</span>
|
<span className="mr10">{getMessage('modal.cover.outline.length')}</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"*/}
|
|
||||||
{/* value={length1}*/}
|
|
||||||
{/* ref={length1Ref}*/}
|
|
||||||
{/* onFocus={(e) => (length1Ref.current.value = '')}*/}
|
|
||||||
{/* onChange={(e) => setLength1(normalizeDigits(e.target.value))}*/}
|
|
||||||
{/* placeholder="3000"*/}
|
|
||||||
{/*/>*/}
|
|
||||||
<CalculatorInput
|
|
||||||
id=""
|
|
||||||
name=""
|
|
||||||
label=""
|
|
||||||
className="input-origin block"
|
className="input-origin block"
|
||||||
value={length1}
|
value={length1}
|
||||||
ref={length1Ref}
|
ref={length1Ref}
|
||||||
onChange={(value) => setLength1(value)}
|
onFocus={(e) => (length1Ref.current.value = '')}
|
||||||
|
onChange={(e) => onlyNumberInputChange(e, setLength1)}
|
||||||
placeholder="3000"
|
placeholder="3000"
|
||||||
onFocus={() => (length1Ref.current.value = '')}
|
|
||||||
options={{
|
|
||||||
allowNegative: false,
|
|
||||||
allowDecimal: false
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import { useMessage } from '@/hooks/useMessage'
|
import { useMessage } from '@/hooks/useMessage'
|
||||||
import { normalizeDigits } from '@/util/input-utils'
|
import { onlyNumberInputChange } from '@/util/input-utils'
|
||||||
import { CalculatorInput } from '@/components/common/input/CalcInput'
|
|
||||||
|
|
||||||
export default function Diagonal({ props }) {
|
export default function Diagonal({ props }) {
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
@ -31,29 +30,14 @@ export default function Diagonal({ props }) {
|
|||||||
{getMessage('modal.cover.outline.length')}
|
{getMessage('modal.cover.outline.length')}
|
||||||
</span>
|
</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"*/}
|
|
||||||
{/* value={outerLineDiagonalLength}*/}
|
|
||||||
{/* ref={outerLineDiagonalLengthRef}*/}
|
|
||||||
{/* onFocus={(e) => (outerLineDiagonalLengthRef.current.value = '')}*/}
|
|
||||||
{/* onChange={(e) => setOuterLineDiagonalLength(normalizeDigits(e.target.value))}*/}
|
|
||||||
{/* placeholder="3000"*/}
|
|
||||||
{/*/>*/}
|
|
||||||
<CalculatorInput
|
|
||||||
id=""
|
|
||||||
name=""
|
|
||||||
label=""
|
|
||||||
className="input-origin block"
|
className="input-origin block"
|
||||||
value={outerLineDiagonalLength}
|
value={outerLineDiagonalLength}
|
||||||
ref={outerLineDiagonalLengthRef}
|
ref={outerLineDiagonalLengthRef}
|
||||||
onChange={(value) => setOuterLineDiagonalLength(value)}
|
onFocus={(e) => (outerLineDiagonalLengthRef.current.value = '')}
|
||||||
|
onChange={(e) => onlyNumberInputChange(e, setOuterLineDiagonalLength)}
|
||||||
placeholder="3000"
|
placeholder="3000"
|
||||||
onFocus={() => (outerLineDiagonalLengthRef.current.value = '')}
|
|
||||||
options={{
|
|
||||||
allowNegative: false,
|
|
||||||
allowDecimal: false
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
@ -68,29 +52,14 @@ export default function Diagonal({ props }) {
|
|||||||
<div className="outline-form">
|
<div className="outline-form">
|
||||||
<span className="mr10"> {getMessage('modal.cover.outline.length')}</span>
|
<span className="mr10"> {getMessage('modal.cover.outline.length')}</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"*/}
|
|
||||||
{/* value={length1}*/}
|
|
||||||
{/* ref={length1Ref}*/}
|
|
||||||
{/* onFocus={(e) => (length1Ref.current.value = '')}*/}
|
|
||||||
{/* onChange={(e) => setLength1(normalizeDigits(e.target.value))}*/}
|
|
||||||
{/* placeholder="3000"*/}
|
|
||||||
{/*/>*/}
|
|
||||||
<CalculatorInput
|
|
||||||
id=""
|
|
||||||
name=""
|
|
||||||
label=""
|
|
||||||
className="input-origin block"
|
className="input-origin block"
|
||||||
value={length1}
|
value={length1}
|
||||||
ref={length1Ref}
|
ref={length1Ref}
|
||||||
onChange={(value) => setLength1(value)}
|
onFocus={(e) => (length1Ref.current.value = '')}
|
||||||
|
onChange={(e) => onlyNumberInputChange(e, setLength1)}
|
||||||
placeholder="3000"
|
placeholder="3000"
|
||||||
onFocus={() => (length1Ref.current.value = '')}
|
|
||||||
options={{
|
|
||||||
allowNegative: false,
|
|
||||||
allowDecimal: false
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
@ -141,29 +110,14 @@ export default function Diagonal({ props }) {
|
|||||||
<div className="outline-form">
|
<div className="outline-form">
|
||||||
<span className="mr10"> {getMessage('modal.cover.outline.length')}</span>
|
<span className="mr10"> {getMessage('modal.cover.outline.length')}</span>
|
||||||
<div className="input-grid" style={{ width: '98px' }}>
|
<div className="input-grid" style={{ width: '98px' }}>
|
||||||
{/*<input*/}
|
<input
|
||||||
{/* type="text"*/}
|
type="text"
|
||||||
{/* className="input-origin block"*/}
|
|
||||||
{/* value={length2}*/}
|
|
||||||
{/* ref={length2Ref}*/}
|
|
||||||
{/* onChange={(e) => setLength2(normalizeDigits(e.target.value))}*/}
|
|
||||||
{/* readOnly={true}*/}
|
|
||||||
{/* placeholder="3000"*/}
|
|
||||||
{/*/>*/}
|
|
||||||
<CalculatorInput
|
|
||||||
id=""
|
|
||||||
name=""
|
|
||||||
label=""
|
|
||||||
className="input-origin block"
|
className="input-origin block"
|
||||||
value={length2}
|
value={length2}
|
||||||
ref={length2Ref}
|
ref={length2Ref}
|
||||||
onChange={(value) => setLength2(value)}
|
onChange={(e) => onlyNumberInputChange(e, setLength2)}
|
||||||
|
readOnly={true}
|
||||||
placeholder="3000"
|
placeholder="3000"
|
||||||
onFocus={() => (length2Ref.current.value = '')}
|
|
||||||
options={{
|
|
||||||
allowNegative: false,
|
|
||||||
allowDecimal: false
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||