Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into qcast-pub

# Conflicts:
#	src/locales/ja.json
This commit is contained in:
김민식 2025-05-19 13:48:15 +09:00
commit ec48350c05
30 changed files with 1181 additions and 273 deletions

View File

@ -1,6 +1,8 @@
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="http://1.248.227.176:5000"
SESSION_SECRET="i3iHH1yp2/2SpQSIySQ4bpyc4g0D+zCF9FAn5xUG0+Y="
@ -8,4 +10,18 @@ SESSION_SECRET="i3iHH1yp2/2SpQSIySQ4bpyc4g0D+zCF9FAn5xUG0+Y="
NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_yAS4QDalL9jgQ7vS"
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="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"
# 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="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"
NEXT_PUBLIC_AWS_S3_BASE_URL="//files.hanasys.jp"

View File

@ -1,6 +1,8 @@
NEXT_PUBLIC_API_SERVER_PATH="https://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://www.hanasys.jp/"
SESSION_SECRET="i3iHH1yp2/2SpQSIySQ4bpyc4g0D+zCF9FAn5xUG0+Y="
@ -10,4 +12,10 @@ NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secr
# 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_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="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"
NEXT_PUBLIC_AWS_S3_BASE_URL="//files.hanasys.jp"

3
.gitmessage.txt Normal file
View File

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

View File

@ -12,6 +12,7 @@
"serve": "node server.js"
},
"dependencies": {
"@aws-sdk/client-s3": "^3.772.0",
"ag-grid-react": "^32.0.2",
"axios": "^1.7.8",
"big.js": "^6.2.2",
@ -24,7 +25,7 @@
"js-cookie": "^3.0.5",
"mathjs": "^13.0.2",
"mssql": "^11.0.1",
"next": "14.2.26",
"next": "14.2.28",
"next-international": "^1.2.4",
"react": "^18",
"react-chartjs-2": "^5.2.0",
@ -38,6 +39,7 @@
"react-responsive-modal": "^6.4.2",
"react-select": "^5.8.1",
"recoil": "^0.7.7",
"sharp": "^0.33.5",
"sqlite": "^5.1.1",
"sqlite3": "^5.1.7",
"sweetalert2": "^11.14.1",

Binary file not shown.

View File

@ -0,0 +1,69 @@
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 = `cads/${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)
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 })
}
}

View File

@ -0,0 +1,133 @@
import { NextResponse } from 'next/server'
import { S3Client, PutObjectCommand, DeleteObjectCommand, 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,
},
})
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 {
const checkResult = checkArea({ width, height, left, top })
// Get the image from S3
const { Body } = await s3.send(
new GetObjectCommand({
Bucket,
Key,
}),
)
// Convert stream to buffer
const chunks = []
for await (const chunk of Body) {
chunks.push(chunk)
}
const imageBuffer = Buffer.concat(chunks)
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
}
}
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/${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 })
}
}

View File

@ -0,0 +1,78 @@
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 })
}
}

View File

@ -0,0 +1,72 @@
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 })
}
}

View File

@ -125,6 +125,11 @@ export const TRESTLE_MATERIAL = {
BRACKET: 'bracket',
}
export const MODULE_SETUP_TYPE = {
LAYOUT: 'layout',
AUTO: 'auto',
}
export const SAVE_KEY = [
'selectable',
'name',

View File

@ -60,7 +60,7 @@ export default function Estimate({}) {
const [cableItemList, setCableItemList] = useState([]) //
const [cableItem, setCableItem] = useState('') //
const [cableDbItem, setCableDbItem] = useState('') //
const [startDate, setStartDate] = useState(new Date())
const singleDatePickerProps = {
startDate,
@ -98,7 +98,7 @@ export default function Estimate({}) {
}
const initEstimate = (currPid = currentPid) => {
console.log('🚀 ~ initEstimate ~ currPid:', currPid)
// console.log('🚀 ~ initEstimate ~ currPid:', currPid)
closeAll()
setObjectNo(objectRecoil.floorPlanObjectNo)
@ -117,6 +117,7 @@ export default function Estimate({}) {
item.value = item.clRefChr1
item.label = item.clRefChr2
})
// console.log(code2)
setCableItemList(code2)
}
@ -152,7 +153,7 @@ export default function Estimate({}) {
}
useEffect(() => {
console.log('🚀 ~ Estimate ~ selectedPlan:', selectedPlan)
// console.log('🚀 ~ Estimate ~ selectedPlan:', selectedPlan)
if (selectedPlan) initEstimate(selectedPlan.planNo)
}, [selectedPlan])
@ -739,6 +740,18 @@ export default function Estimate({}) {
setCableItem(value)
}
/* 케이블 select 변경시 */
const onChangeDisplayDoubleCableItem = (value, itemList) => {
itemList.map((item, index) => {
if (item.dispCableFlg === '1' && item.itemTpCd === 'M12') {
if (value !== '') {
onChangeDisplayItem(value, item.dispOrder, index, true)
}
}
})
setCableDbItem(value)
}
// /
const onChangeDisplayItem = (itemId, dispOrder, index, flag) => {
const param = {
@ -1088,15 +1101,20 @@ export default function Estimate({}) {
item.showSaleTotPrice = '0'
}
if (item.dispCableFlg === '1' && item.itemTpCd !== 'M12') {
if (item.dispCableFlg === '1' ) {
dispCableFlgCnt++
setCableItem(item.itemId)
if(item.itemTpCd === 'M12') {
setCableDbItem(item.itemId)
}else{
setCableItem(item.itemId)
}
}
}
})
if (dispCableFlgCnt === 0) {
setCableItem('100038')
setCableDbItem('100037')
}
let pkgAsp = estimateContextState.pkgAsp ? Number(estimateContextState.pkgAsp.replaceAll(',', '')) : 0
@ -1159,14 +1177,20 @@ export default function Estimate({}) {
dispCableFlgCnt++
}
if (item.dispCableFlg === '1' && item.itemTpCd !== 'M12') {
setCableItem(item.itemId)
if (item.dispCableFlg === '1'){
if(item.itemTpCd === 'M12') {
setCableDbItem(item.itemId)
}else{
setCableItem(item.itemId)
}
}
}
})
if (dispCableFlgCnt === 0) {
setCableItem('100038')
setCableDbItem('100037')
}
totals.vatPrice = totals.supplyPrice * 0.1
@ -1227,6 +1251,7 @@ export default function Estimate({}) {
if (dispCableFlgCnt === 0) {
setCableItem('100038')
setCableDbItem('100037')
}
}
}, [estimateContextState?.itemList, cableItemList])
@ -1831,6 +1856,7 @@ export default function Estimate({}) {
</button>
</div>
<div className="product-price-wrap ml10">
<div className="product-price-tit">{getMessage('estimate.detail.header.singleCable')}</div>
<div className="select-wrap">
<select
className="select-light"
@ -1840,11 +1866,34 @@ export default function Estimate({}) {
value={cableItem}
>
{cableItemList.length > 0 &&
cableItemList.map((row) => (
<option key={row.clRefChr1} value={row.clRefChr1}>
{row.clRefChr2}
</option>
))}
cableItemList.map((row) => {
if(!row.clRefChr2.includes('S')){
return <option key={row.clRefChr1} value={row.clRefChr1}>
{row.clRefChr2}
</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>
</div>
</div>
@ -1927,7 +1976,7 @@ export default function Estimate({}) {
<input
type="checkbox"
id={item?.dispOrder}
disabled={!!item?.paDispOrder || item.dispCableFlg === '1'}
disabled={!!item?.paDispOrder || item.dispCableFlg === '1X'}
onChange={() => onChangeSelect(item.dispOrder)}
checked={!!selection.has(item.dispOrder)}
/>

View File

@ -37,7 +37,7 @@ export default function CanvasLayout({ children }) {
<button
key={`plan-${plan.id}`}
className={`canvas-page-box ${plan.isCurrent === true ? 'on' : ''}`}
onClick={() => handleCurrentPlan(plan.id)}
onClick={() => (plan.isCurrent ? '' : handleCurrentPlan(plan.id))}
>
<span>{`Plan ${plan.planNo}`}</span>
{plans.length > 1 && !pathname.includes('/estimate') && !pathname.includes('/simulator') && (

View File

@ -120,7 +120,7 @@ export default function ImgLoad() {
value={refImage ? (refImage?.name ?? '') : (currentCanvasPlan?.bgImageName ?? '')}
readOnly
/>
{refImage && <button className="img-check" onClick={handleFileDelete}></button>}
{currentCanvasPlan?.bgImageName && <button className="img-check" onClick={handleFileDelete}></button>}
</div>
</div>
</div>

View File

@ -1,4 +1,4 @@
import { POLYGON_TYPE } from '@/common/common'
import { POLYGON_TYPE, MODULE_SETUP_TYPE } from '@/common/common'
import WithDraggable from '@/components/common/draggable/WithDraggable'
import { Orientation } from '@/components/floor-plan/modal/basic/step/Orientation'
import PitchPlacement from '@/components/floor-plan/modal/basic/step/pitch/PitchPlacement'
@ -331,10 +331,13 @@ export default function BasicSetting({ id, pos = { x: 50, y: 230 } }) {
<button className={`btn-frame modal mr5 ${isManualModuleLayoutSetup ? 'act' : ''}`} onClick={handleManualModuleLayoutSetup}>
{getMessage('modal.module.basic.setting.row.batch')}
</button>
<button className="btn-frame modal mr5" onClick={() => autoModuleSetup(MODULE_SETUP_TYPE.LAYOUT, layoutSetup)}>
{getMessage('modal.module.basic.setting.auto.row.batch')}
</button>
<button className={`btn-frame modal mr5 ${isManualModuleSetup ? 'act' : ''}`} onClick={handleManualModuleSetup}>
{getMessage('modal.module.basic.setting.passivity.placement')}
</button>
<button className="btn-frame modal act" onClick={() => autoModuleSetup()}>
<button className="btn-frame modal act mr5" onClick={() => autoModuleSetup(MODULE_SETUP_TYPE.AUTO)}>
{getMessage('modal.module.basic.setting.auto.placement')}
</button>
</>

View File

@ -37,7 +37,7 @@ const Placement = forwardRef((props, refs) => {
const resetModuleSetupOption = useResetRecoilState(moduleSetupOptionState)
const [colspan, setColspan] = useState(1)
const [moduleRowColArray, setModuleRowColArray] = useRecoilState(moduleRowColArrayState)
const moduleRowColArray = useRecoilValue(moduleRowColArrayState)
//
useEffect(() => {
@ -64,9 +64,9 @@ const Placement = forwardRef((props, refs) => {
}
}, [])
useEffect(() => {
console.log('moduleRowColArray', moduleRowColArray)
}, [moduleRowColArray])
// useEffect(() => {
// console.log('moduleRowColArray', moduleRowColArray)
// }, [moduleRowColArray])
//
useEffect(() => {
@ -180,8 +180,8 @@ const Placement = forwardRef((props, refs) => {
))}
</thead>
<tbody>
{selectedModules.itemList &&
selectedModules.itemList.map((item, index) => (
{selectedModules?.itemList &&
selectedModules?.itemList?.map((item, index) => (
<tr key={index}>
<td className="al-c">
<div className="d-check-box no-text pop">
@ -329,22 +329,25 @@ const Placement = forwardRef((props, refs) => {
<tr>
<th rowSpan={2} style={{ width: '22%' }}></th>
{selectedModules &&
selectedModules.itemList.map((item) => (
<th colSpan={colspan}>
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.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>}
</>
))}
{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>
@ -356,10 +359,11 @@ const Placement = forwardRef((props, refs) => {
<span className="name">{item.addRoof?.roofMatlNmJp}</span>
</div>
</td>
{moduleRowColArray[index]?.map((item) => (
{moduleRowColArray[index]?.map((item, index2) => (
<>
<td className="al-c">{item.moduleMaxRows}</td>
{colspan > 1 && <td className="al-c">{item.mixModuleMaxRows}</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>

View File

@ -20,6 +20,8 @@ import { stuffSearchState } from '@/store/stuffAtom'
import { QcastContext } from '@/app/QcastProvider'
import { usePopup } from '@/hooks/usePopup'
import { commonCodeState } from '@/store/commonCodeAtom'
import { isObjectNotEmpty } from '@/util/common-utils'
export const ToggleonMouse = (e, act, target) => {
const listWrap = e.target.closest(target)
@ -67,6 +69,21 @@ export default function Header(props) {
const [SelectOptions, setSelectOptions] = useState([])
const [commonCode, setCommonCode] = useRecoilState(commonCodeState)
const { promiseGet } = useAxios()
useEffect(() => {
const getCommonCode = async () => {
await promiseGet({ url: '/api/commcode/qc-comm-code' }).then((res) => {
setCommonCode(Object.groupBy(res.data, ({ clHeadCd }) => clHeadCd))
})
}
if (!isObjectNotEmpty(commonCode)) {
getCommonCode()
}
}, [])
const getAutoLoginParam = async () => {
await promisePost({ url: '/api/login/v1.0/user/login/autoLoginEncryptData', data: { loginId: userSession.userId } })
.then((res) => {

View File

@ -37,6 +37,7 @@ export function useCanvasPopupStatusController(param = 1) {
* @returns
*/
const getModuleSelection = async (popupTypeParam) => {
if (!currentCanvasPlan.objectNo || !currentCanvasPlan.planNo) return
const result = await promiseGet({
url: `/api/v1/canvas-popup-status?objectNo=${currentCanvasPlan.objectNo}&planNo=${currentCanvasPlan.planNo}&popupType=${popupTypeParam}`,
})
@ -48,7 +49,7 @@ export function useCanvasPopupStatusController(param = 1) {
return null
})
return result.data
return result?.data
}
/**
@ -59,7 +60,7 @@ export function useCanvasPopupStatusController(param = 1) {
let resultData = {}
for (let i = 1; i < 3; i++) {
const result = await getModuleSelection(i)
if (!result.objectNo) return
if (!result?.objectNo) return
if (i === 1) {
if (result.popupStatus && unescapeString(result.popupStatus)) {
const data = JSON.parse(unescapeString(result.popupStatus))

View File

@ -22,9 +22,8 @@ import { useAxios } from '../useAxios'
* const honorificCodes = findCommonCode(200800);
*/
export const useCommonCode = () => {
const [commonCode, setCommonCode] = useRecoilState(commonCodeState)
const globalLocale = useRecoilValue(globalLocaleStore)
const { promiseGet } = useAxios()
const [commonCode, setCommonCode] = useRecoilState(commonCodeState)
const findCommonCode = (key) => {
const resultCodes = commonCode[key]?.map((code) => {
@ -41,18 +40,6 @@ export const useCommonCode = () => {
return resultCodes
}
useEffect(() => {
const getCommonCode = async () => {
await promiseGet({ url: '/api/commcode/qc-comm-code' }).then((res) => {
setCommonCode(Object.groupBy(res.data, ({ clHeadCd }) => clHeadCd))
})
}
if (!isObjectNotEmpty(commonCode)) {
getCommonCode()
}
}, [])
return {
commonCode,
findCommonCode,

View File

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

View File

@ -1,5 +1,5 @@
import { useEffect, useRef, useState } from 'react'
import { useRecoilState } from 'recoil'
import { useRecoilState, useSetRecoilState } from 'recoil'
import { useSwal } from '@/hooks/useSwal'
import { useAxios } from '../useAxios'
@ -7,6 +7,7 @@ import { currentCanvasPlanState } from '@/store/canvasAtom'
import { useCanvas } from '@/hooks/useCanvas'
import { deleteBackGroundImage, setBackGroundImage } from '@/lib/imageActions'
import { settingModalFirstOptionsState } from '@/store/settingAtom'
import { popSpinnerState } from '@/store/popupAtom'
/**
* 배경 이미지 관리
@ -15,6 +16,9 @@ import { settingModalFirstOptionsState } from '@/store/settingAtom'
* 이미지 -> 캔버스 배경에 이미지 로드
* 주소 -> 구글 맵에서 주소 검색 이미지로 다운로드 받아서 캔버스 배경에 이미지 로드
* .dwg -> api를 통해서 .png로 변환 캔버스 배경에 이미지 로드
*
* setCurrentBgImage 이미지를 세팅하면 도면에 배경으로 로딩된다.
* 다만 S3 Response에 aws 고유 주소가 나오는데 여기서는 cloudfront 사용을 위해서 NEXT_PUBLIC_AWS_S3_BASE_URL 도메인을 사용한다.
* @returns {object}
*/
export function useRefFiles() {
@ -28,7 +32,8 @@ export function useRefFiles() {
const [settingModalFirstOptions, setSettingModalFirstOptions] = useRecoilState(settingModalFirstOptionsState)
const { handleBackImageLoadToCanvas } = useCanvas()
const { swalFire } = useSwal()
const { get, post } = useAxios()
const { get, post, del } = useAxios()
const setPopSpinnerStore = useSetRecoilState(popSpinnerState)
useEffect(() => {
if (refFileMethod === '1') {
@ -39,6 +44,7 @@ export function useRefFiles() {
}, [refFileMethod])
/**
* 최초 input type="file" 대한 이벤트
* 파일 불러오기 버튼 컨트롤
* @param {*} file
*/
@ -59,6 +65,10 @@ export function useRefFiles() {
}
}
/**
* 허용하는 파일인지 체크한다
* @param {File} file
*/
const refFileSetting = (file) => {
console.log('🚀 ~ refFileSetting ~ file:', file)
if (file.name.split('.').pop() === 'dwg') {
@ -77,34 +87,40 @@ export function useRefFiles() {
}
/**
* 파일 삭제
* 이미지 파일 삭제
*/
const handleFileDelete = async () => {
swalFire({
text: '삭제하시겠습니까?',
type: 'confirm',
confirmFn: async () => {
setRefImage(null)
setCurrentCanvasPlan((prev) => ({ ...prev, bgImageName: null }))
setPopSpinnerStore(true)
console.log('🚀 ~ handleFileDelete ~ handleFileDelete:', refImage)
console.log('🚀 ~ handleFileDelete ~ currentCanvasPlan.bgImageName:', currentCanvasPlan.bgImageName)
await del({ url: `${process.env.NEXT_PUBLIC_API_HOST_URL}/api/image/upload?fileName=${currentCanvasPlan.bgImageName}` })
setCurrentBgImage(null)
await deleteBackGroundImage({
objectId: currentCanvasPlan.id,
planNo: currentCanvasPlan.planNo,
})
setPopSpinnerStore(false)
},
})
}
/**
* 주소 삭제
* 주소 이미지 삭제
*/
const handleAddressDelete = async () => {
swalFire({
text: '삭제하시겠습니까?',
type: 'confirm',
confirmFn: async () => {
console.log('🚀 ~ handleAddressDelete ~ handleAddressDelete:', refImage)
console.log('🚀 ~ handleAddressDelete ~ currentCanvasPlan.bgImageName:', currentCanvasPlan.bgImageName)
await del({ url: `${process.env.NEXT_PUBLIC_API_HOST_URL}/api/image/map?fileName=${currentCanvasPlan.bgImageName}` })
setMapPositionAddress('')
setCurrentBgImage(null)
setCurrentCanvasPlan((prev) => ({ ...prev, mapPositionAddress: null }))
await deleteBackGroundImage({
objectId: currentCanvasPlan.id,
planNo: currentCanvasPlan.planNo,
@ -114,7 +130,7 @@ export function useRefFiles() {
}
/**
* 주소로 구글 이미지 다운로드
* 주소로 구글 이미지 다운로드하여 캔버스 배경으로 로드
*/
const handleMapImageDown = async () => {
console.log('🚀 ~ handleMapImageDown ~ handleMapImageDown:')
@ -133,15 +149,16 @@ export function useRefFiles() {
}))
const res = await get({
url: `${process.env.NEXT_PUBLIC_HOST_URL}/map/convert?q=${queryRef.current.value}&fileNm=${currentCanvasPlan.id}&zoom=20`,
url: `${process.env.NEXT_PUBLIC_API_HOST_URL}/api/image/map?q=${queryRef.current.value}&fileNm=${currentCanvasPlan.id}&zoom=20`,
})
console.log('🚀 ~ handleMapImageDown ~ res:', res)
setCurrentBgImage(`${process.env.NEXT_PUBLIC_HOST_URL}${res.filePath}`)
setCurrentBgImage(`${process.env.NEXT_PUBLIC_AWS_S3_BASE_URL}/${res.fileName}`)
await setBackGroundImage({
objectId: currentCanvasPlan.id,
planNo: currentCanvasPlan.planNo,
imagePath: `${process.env.NEXT_PUBLIC_HOST_URL}${res.filePath}`,
// imagePath: `${process.env.NEXT_PUBLIC_HOST_URL}${res.filePath}`,
imagePath: `${res.filePath}`,
})
}
@ -149,16 +166,26 @@ export function useRefFiles() {
* 배경 이미지 로드를 위한 세팅
*/
useEffect(() => {
if (!currentBgImage) {
return
}
// if (!currentBgImage) {
// return
// }
console.log('🚀 ~ useEffect ~ currentBgImage:', currentBgImage)
handleBackImageLoadToCanvas(currentBgImage)
setCurrentCanvasPlan((prev) => ({
...prev,
bgImageName: refImage?.name ?? null,
mapPositionAddress: queryRef.current.value,
}))
if (currentBgImage) {
handleBackImageLoadToCanvas(currentBgImage)
setCurrentCanvasPlan((prev) => ({
...prev,
// bgImageName: refImage?.name ?? null,
bgImageName: currentBgImage.split('/').pop(),
mapPositionAddress: queryRef.current.value,
}))
} else {
setRefImage(null)
setCurrentCanvasPlan((prev) => ({
...prev,
bgImageName: null,
mapPositionAddress: null,
}))
}
}, [currentBgImage])
/**
@ -166,6 +193,7 @@ export function useRefFiles() {
* @param {*} file
*/
const handleUploadImageRefFile = async (file) => {
setPopSpinnerStore(true)
const newOption1 = settingModalFirstOptions.option1.map((option) => ({
...option,
selected: option.column === 'imageDisplay' ? true : option.selected,
@ -180,20 +208,22 @@ export function useRefFiles() {
formData.append('file', file)
const res = await post({
url: `${process.env.NEXT_PUBLIC_HOST_URL}/image/upload`,
url: `${process.env.NEXT_PUBLIC_API_HOST_URL}/api/image/upload`,
data: formData,
})
console.log('🚀 ~ handleUploadImageRefFile ~ res:', res)
setCurrentBgImage(`${process.env.NEXT_PUBLIC_HOST_URL}${res.filePath}`)
setCurrentBgImage(`${process.env.NEXT_PUBLIC_AWS_S3_BASE_URL}/${res.fileName}`)
setRefImage(file)
const params = {
objectId: currentCanvasPlan.id,
planNo: currentCanvasPlan.planNo,
imagePath: `${process.env.NEXT_PUBLIC_HOST_URL}${res.filePath}`,
// imagePath: `${process.env.NEXT_PUBLIC_HOST_URL}${res.filePath}`,
imagePath: `${res.filePath}`,
}
console.log('🚀 ~ handleUploadImageRefFile ~ params:', params)
await setBackGroundImage(params)
setPopSpinnerStore(false)
}
/**
@ -204,15 +234,28 @@ export function useRefFiles() {
const formData = new FormData()
formData.append('file', file)
/** 캐드 도면 파일 변환 */
const res = await post({ url: converterUrl, data: formData })
console.log('🚀 ~ handleUploadConvertRefFile ~ res:', res)
/** 캐드 도면 파일 업로드 */
const result = await post({
url: `${process.env.NEXT_PUBLIC_HOST_URL}/cad/convert`,
url: `${process.env.NEXT_PUBLIC_API_HOST_URL}/api/image/cad`,
data: res,
})
console.log('🚀 ~ handleUploadConvertRefFile ~ result:', result)
setCurrentBgImage(`${process.env.NEXT_PUBLIC_HOST_URL}${result.filePath}`)
setRefImage(res.Files[0].FileData)
setCurrentBgImage(`${process.env.NEXT_PUBLIC_AWS_S3_BASE_URL}/${res.fileName}`)
setRefImage(file)
const params = {
objectId: currentCanvasPlan.id,
planNo: currentCanvasPlan.planNo,
// imagePath: `${process.env.NEXT_PUBLIC_HOST_URL}${res.filePath}`,
imagePath: `${result.filePath}`,
}
console.log('🚀 ~ handleUploadImageRefFile ~ params:', params)
await setBackGroundImage(params)
}
/**

View File

@ -79,7 +79,8 @@ export function useImgLoader() {
/** 이미지 크롭 요청 */
const result = await post({
url: `${process.env.NEXT_PUBLIC_HOST_URL}/image/canvas`,
// url: `${process.env.NEXT_PUBLIC_API_HOST_URL}/image/canvas`,
url: `${process.env.NEXT_PUBLIC_API_HOST_URL}/api/image/canvas`,
data: formData,
})
console.log('🚀 ~ handleCanvasToPng ~ result:', result)

File diff suppressed because it is too large Load Diff

View File

@ -96,6 +96,10 @@ export function useOuterLineWall(id, propertiesId) {
}
addCanvasMouseEventListener('mouse:down', mouseDown)
addDocumentEventListener('contextmenu', document, (e) => {
handleRollback()
})
clear()
return () => {
initEvent()
@ -690,6 +694,7 @@ export function useOuterLineWall(id, propertiesId) {
if (points.length === 0) {
return
}
enterCheck(e)
// 포커스가 length1에 있지 않으면 length1에 포커스를 줌
const activeElem = document.activeElement
if (activeElem !== length1Ref.current) {
@ -754,6 +759,7 @@ export function useOuterLineWall(id, propertiesId) {
if (points.length === 0) {
return
}
enterCheck(e)
const key = e.key
const activeElem = document.activeElement
@ -787,6 +793,7 @@ export function useOuterLineWall(id, propertiesId) {
if (points.length === 0) {
return
}
enterCheck(e)
const key = e.key
switch (key) {
case 'Down': // IE/Edge에서 사용되는 값
@ -812,6 +819,7 @@ export function useOuterLineWall(id, propertiesId) {
if (points.length === 0) {
return
}
enterCheck(e)
const key = e.key
switch (key) {
case 'Enter': {
@ -836,7 +844,7 @@ export function useOuterLineWall(id, propertiesId) {
if (points.length === 0) {
return
}
enterCheck(e)
const key = e.key
switch (key) {
case 'Down': // IE/Edge에서 사용되는 값
@ -902,6 +910,12 @@ export function useOuterLineWall(id, propertiesId) {
isFix.current = true
}
const enterCheck = (e) => {
if (e.key === 'Enter') {
handleFix()
}
}
return {
points,
setPoints,

View File

@ -92,6 +92,9 @@ export function usePlacementShapeDrawing(id) {
}
addCanvasMouseEventListener('mouse:down', mouseDown)
addDocumentEventListener('contextmenu', document, (e) => {
handleRollback()
})
clear()
}, [verticalHorizontalMode, points, adsorptionPointAddMode, adsorptionPointMode, adsorptionRange, interval, tempGridMode])
@ -175,7 +178,7 @@ export function usePlacementShapeDrawing(id) {
}
}
/*
/*
mouseMove
*/
const roofs = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
@ -184,7 +187,7 @@ mouseMove
const { getAdsorptionPoints } = useAdsorptionPoint()
const mouseMove = (e) => {
removeMouseLine();
removeMouseLine()
const pointer = canvas.getPointer(e.e)
const roofsPoints = roofs.map((roof) => roof.points).flat()
roofAdsorptionPoints.current = [...roofsPoints]
@ -246,7 +249,6 @@ mouseMove
})
canvas?.add(horizontalLine, verticalLine)
canvas?.renderAll()
}
useEffect(() => {
@ -768,6 +770,7 @@ mouseMove
if (points.length === 0) {
return
}
enterCheck(e)
// 포커스가 length1에 있지 않으면 length1에 포커스를 줌
const activeElem = document.activeElement
@ -833,6 +836,7 @@ mouseMove
if (points.length === 0) {
return
}
enterCheck(e)
const key = e.key
const activeElem = document.activeElement
@ -866,6 +870,7 @@ mouseMove
if (points.length === 0) {
return
}
enterCheck(e)
const key = e.key
switch (key) {
case 'Down': // IE/Edge에서 사용되는 값
@ -891,6 +896,7 @@ mouseMove
if (points.length === 0) {
return
}
enterCheck(e)
const key = e.key
switch (key) {
case 'Enter': {
@ -915,6 +921,7 @@ mouseMove
if (points.length === 0) {
return
}
enterCheck(e)
const key = e.key
switch (key) {
@ -979,6 +986,12 @@ mouseMove
isFix.current = true
}
const enterCheck = (e) => {
if (e.key === 'Enter') {
handleFix()
}
}
return {
points,
setPoints,

View File

@ -72,16 +72,12 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) {
length5 = surfaceRefs.length5.current.value
}
console.log(' before length : ', length1, length2, length3, length4, length5)
length1 = parseFloat(length1 === undefined ? 0 : Number(length1 / 10).toFixed(1))
length2 = parseFloat(length2 === undefined ? 0 : Number(length2 / 10).toFixed(1))
length3 = parseFloat(length3 === undefined ? 0 : Number(length3 / 10).toFixed(1))
length4 = parseFloat(length4 === undefined ? 0 : Number(length4 / 10).toFixed(1))
length5 = parseFloat(length5 === undefined ? 0 : Number(length5 / 10).toFixed(1))
console.log(' after length : ', length1, length2, length3, length4, length5)
let isDrawing = true
let obj = null
let points = []

View File

@ -4,7 +4,7 @@ import { v4 as uuidv4 } from 'uuid'
import { canvasSizeState, canvasState, canvasZoomState, currentMenuState, currentObjectState } from '@/store/canvasAtom'
import { QPolygon } from '@/components/fabric/QPolygon'
import { fontSelector } from '@/store/fontAtom'
import { MENU } from '@/common/common'
import { MENU, POLYGON_TYPE } from '@/common/common'
// 캔버스에 필요한 이벤트
export function useCanvasEvent() {
@ -204,9 +204,20 @@ export function useCanvasEvent() {
if (selected?.length > 0) {
selected.forEach((obj) => {
if (obj.type === 'QPolygon' && currentMenu !== MENU.MODULE_CIRCUIT_SETTING.BASIC_SETTING) {
// if (obj.type === 'QPolygon' && currentMenu !== MENU.MODULE_CIRCUIT_SETTING.BASIC_SETTING) {
if (obj.type === 'QPolygon') {
const originStroke = obj.stroke
obj.set({ stroke: 'red' })
obj.bringToFront()
if (currentMenu === MENU.MODULE_CIRCUIT_SETTING.BASIC_SETTING) {
if (obj.name === POLYGON_TYPE.MODULE) {
obj.set({ strokeWidth: 3 })
}
if (obj.name === POLYGON_TYPE.ROOF) {
canvas.discardActiveObject()
obj.set({ stroke: originStroke })
}
}
}
})
canvas.renderAll()
@ -218,10 +229,13 @@ export function useCanvasEvent() {
if (deselected?.length > 0) {
deselected.forEach((obj) => {
if (obj.type === 'QPolygon' && currentMenu !== MENU.MODULE_CIRCUIT_SETTING.BASIC_SETTING) {
if (obj.type === 'QPolygon') {
if (obj.name !== 'moduleSetupSurface') {
obj.set({ stroke: 'black' })
}
if (obj.name === POLYGON_TYPE.MODULE && currentMenu === MENU.MODULE_CIRCUIT_SETTING.BASIC_SETTING) {
obj.set({ strokeWidth: 0.3 })
}
}
})
}
@ -234,17 +248,24 @@ export function useCanvasEvent() {
if (deselected?.length > 0) {
deselected.forEach((obj) => {
if (obj.type === 'QPolygon' && currentMenu !== MENU.MODULE_CIRCUIT_SETTING.BASIC_SETTING) {
if (obj.type === 'QPolygon') {
obj.set({ stroke: 'black' })
if (obj.name === POLYGON_TYPE.MODULE && currentMenu === MENU.MODULE_CIRCUIT_SETTING.BASIC_SETTING) {
//모듈 미선택시 라인 두께 변경
obj.set({ strokeWidth: 0.3 })
}
}
})
}
if (selected?.length > 0) {
selected.forEach((obj) => {
if (obj.type === 'QPolygon' && currentMenu !== MENU.MODULE_CIRCUIT_SETTING.BASIC_SETTING) {
if (obj.type === 'QPolygon') {
obj.set({ stroke: 'red' })
obj.bringToFront()
if (obj.name === POLYGON_TYPE.MODULE && currentMenu === MENU.MODULE_CIRCUIT_SETTING.BASIC_SETTING) {
//모듈 선택시 라인 두께 변경
obj.set({ strokeWidth: 3 })
}
}
})
}

View File

@ -31,6 +31,8 @@ export function useEstimate() {
* @param {Object} estimateParam - 견적서 저장 데이터
*/
const saveEstimate = async (estimateParam) => {
console.log('managementState', managementState)
const userId = loginUserState.userId
const saleStoreId = managementState.saleStoreId
const objectNo = currentCanvasPlan.objectNo

View File

@ -407,12 +407,21 @@ export function usePlan(params = {}) {
}
})
} else {
if (!currentCanvasPlan || currentCanvasPlan.id !== newCurrentId) {
await saveCanvas(true)
clearRecoilState()
}
setCurrentCanvasPlan(plans.find((plan) => plan.id === newCurrentId))
setPlans((plans) => plans.map((plan) => ({ ...plan, isCurrent: plan.id === newCurrentId })))
swalFire({
text: getMessage('plan.message.confirm.save'),
type: 'confirm',
confirmFn: async () => {
//저장 전에 플랜이 이동되어 state가 변경되는 이슈가 있음
await saveCanvas(true)
clearRecoilState()
setCurrentCanvasPlan(plans.find((plan) => plan.id === newCurrentId))
setPlans((plans) => plans.map((plan) => ({ ...plan, isCurrent: plan.id === newCurrentId })))
},
denyFn: async () => {
setCurrentCanvasPlan(plans.find((plan) => plan.id === newCurrentId))
setPlans((plans) => plans.map((plan) => ({ ...plan, isCurrent: plan.id === newCurrentId })))
},
})
}
}
@ -447,9 +456,25 @@ export function usePlan(params = {}) {
* @param {string} objectNo - 물건번호
*/
const handleAddPlan = async (userId, objectNo) => {
let isSelected = false
if (currentCanvasPlan?.id) {
await saveCanvas(false)
swalFire({
text: getMessage('plan.message.confirm.save'),
type: 'confirm',
confirmFn: async () => {
//저장 전에 플랜이 이동되어 state가 변경되는 이슈가 있음
await saveCanvas(true)
handleAddPlanCopyConfirm(userId, objectNo)
},
denyFn: async () => {
handleAddPlanCopyConfirm(userId, objectNo)
},
})
}
}
const handleAddPlanCopyConfirm = async (userId, objectNo) => {
if (JSON.parse(currentCanvasData()).objects.length > 0) {
swalFire({
text: `Plan ${currentCanvasPlan.planNo} ` + getMessage('plan.message.confirm.copy'),
@ -471,7 +496,6 @@ export function usePlan(params = {}) {
setIsGlobalLoading(false)
}
}
/**
* 물건번호(object) plan 삭제 (canvas 삭제 planNo 삭제)
*

View File

@ -153,9 +153,10 @@
"modal.module.basic.setting.pitch.module.column.amount": "列数",
"modal.module.basic.setting.pitch.module.column.margin": "左右間隔",
"modal.module.basic.setting.prev": "前に戻る",
"modal.module.basic.setting.row.batch": "段・列数指定配置",
"modal.module.basic.setting.row.batch": "レイアウト指定",
"modal.module.basic.setting.passivity.placement": "手動配置",
"modal.module.basic.setting.auto.placement": "自動配置",
"modal.module.basic.setting.auto.row.batch": "自動レイアウト指定",
"plan.menu.module.circuit.setting.circuit.trestle.setting": "回路設定",
"modal.circuit.trestle.setting": "回路設定",
"modal.circuit.trestle.setting.alloc.trestle": "架台配置",
@ -345,9 +346,9 @@
"modal.actual.size.setting.not.exist.size": "実際の寸法の長さを入力してください",
"modal.actual.size.setting.plane.size.length": "廊下寸法の長さ",
"modal.actual.size.setting.actual.size.length": "実寸長",
"plan.message.confirm.save": "プラン保存しますか?",
"plan.message.confirm.copy": "プランコピーしますか?",
"plan.message.confirm.delete": "プラン削除しますか?",
"plan.message.confirm.save": "プラン保存しますか?",
"plan.message.confirm.copy": "プランコピーしますか?",
"plan.message.confirm.delete": "プラン削除しますか?",
"plan.message.save": "保存されました。",
"plan.message.delete": "削除されました。",
"plan.message.leave": "物件状況(リスト)に移動しますか? [はい]を選択した場合は保存して移動します。",
@ -943,6 +944,8 @@
"estimate.detail.sepcialEstimateProductInfo.pkgPrice": "PKG金額",
"estimate.detail.header.showPrice": "価格表示",
"estimate.detail.header.unitPrice": "定価",
"estimate.detail.header.singleCable": "片端ケーブル長さ",
"estimate.detail.header.doubleCable": "両端ケーブル長さ",
"estimate.detail.showPrice.pricingBtn": "Pricing",
"estimate.detail.showPrice.pricingBtn.noItemId": "Pricingが欠落しているアイテムがあります。 Pricingを進めてください。",
"estimate.detail.showPrice.pricingBtn.confirm": "価格登録初期化されますがよろしいですか?",
@ -1074,9 +1077,9 @@
"module.layout.setup.max.count.multiple": "モジュール{0}の単体での最大段数は{1}、最大列数は{2}です。 (JA)",
"roofAllocation.not.found": "割り当てる屋根がありません。 (JA)",
"modal.module.basic.setting.module.placement.max.size.check": "屋根材別モジュールの単体の単体での最大段数、2種混合の段数を確認して下さい",
"modal.module.basic.setting.module.placement.max.row": "単体での最大段数",
"modal.module.basic.setting.module.placement.max.rows.multiple": "2種混合時の最大段数",
"modal.module.basic.setting.module.placement.max.row": "単体で\rの最大段数",
"modal.module.basic.setting.module.placement.max.rows.multiple": "2種混合時\rの最大段数",
"modal.module.basic.setting.module.placement.mix.asg.yn.error": "混合インストール不可能なモジュールです。 (JA)",
"modal.module.basic.setting.module.placement.mix.asg.yn": "混合",
"modal.module.basic.setting.layoutpassivity.placement": "layout配置 (JA)"
"modal.module.basic.setting.module.placement.over.max.row": "{0} 最大段数超過しました。最大段数表を参考にしてください。"
}

View File

@ -156,7 +156,8 @@
"modal.module.basic.setting.prev": "이전",
"modal.module.basic.setting.row.batch": "단·열수 지정 배치",
"modal.module.basic.setting.passivity.placement": "수동 배치",
"modal.module.basic.setting.auto.placement": "설정값으로 자동 배치",
"modal.module.basic.setting.auto.placement": "자동 배치",
"modal.module.basic.setting.auto.row.batch": "자동 단·열수 지정 배치 ",
"plan.menu.module.circuit.setting.circuit.trestle.setting": "회로설정",
"modal.circuit.trestle.setting": "회로설정",
"modal.circuit.trestle.setting.alloc.trestle": "가대할당",
@ -944,6 +945,8 @@
"estimate.detail.sepcialEstimateProductInfo.pkgPrice": "PKG 금액",
"estimate.detail.header.showPrice": "가격표시",
"estimate.detail.header.unitPrice": "정가",
"estimate.detail.header.singleCable": "편당케이블 길이",
"estimate.detail.header.doubleCable": "양단케이블 길이",
"estimate.detail.showPrice.pricingBtn": "Pricing",
"estimate.detail.showPrice.pricingBtn.noItemId": "Pricing이 누락된 아이템이 있습니다. 제품 선택 후 Pricing을 진행해주세요.",
"estimate.detail.showPrice.pricingBtn.confirm": "가격등록을 초기화 하시겠습니까?",
@ -1079,5 +1082,5 @@
"modal.module.basic.setting.module.placement.max.rows.multiple": "2종 혼합 최대단수",
"modal.module.basic.setting.module.placement.mix.asg.yn.error": "혼합 설치 불가능한 모듈입니다.",
"modal.module.basic.setting.module.placement.mix.asg.yn": "혼합",
"modal.module.basic.setting.layoutpassivity.placement": "레이아웃 배치"
"modal.module.basic.setting.module.placement.over.max.row": "{0}의 최대단수를 초과했습니다. 최대단수표를 참고해 주세요."
}