Merge branch 'dev' into dev-yj
This commit is contained in:
commit
da90a6d26a
@ -1,9 +1,53 @@
|
||||
'ues client'
|
||||
|
||||
import { ErrorBoundary } from 'next/dist/client/components/error-boundary'
|
||||
import ServerError from '../error'
|
||||
// import { ErrorBoundary } from 'next/dist/client/components/error-boundary'
|
||||
// import ServerError from '../error'
|
||||
import { createContext, useEffect, useReducer, useState } from 'react'
|
||||
|
||||
export const FloorPlanProvider = ({ children }) => {
|
||||
console.log('FloorPlanProvider')
|
||||
return <ErrorBoundary fallback={<ServerError />}>{children}</ErrorBoundary>
|
||||
const reducer = (prevState, nextState) => {
|
||||
return { ...prevState, ...nextState }
|
||||
}
|
||||
|
||||
const defaultEstimateData = {
|
||||
estimateDate: new Date(), //견적일
|
||||
charger: '', //담당자
|
||||
objectName: '', //안건명
|
||||
objectNameOmit: '', //경칭코드
|
||||
estimateType: '', //주문분류
|
||||
remarks: '', //비고
|
||||
estimateOption: '', //견적특이사항
|
||||
itemList: [],
|
||||
fileList: [],
|
||||
fileFlg: '0', //후일 자료 제출 (체크 1 노체크 0)
|
||||
priceCd: '',
|
||||
}
|
||||
|
||||
export const FloorPlanContext = createContext({
|
||||
floorPlanState: {},
|
||||
setFloorPlanState: () => {},
|
||||
estimateContextState: {},
|
||||
setEstimateContextState: () => {},
|
||||
})
|
||||
|
||||
const FloorPlanProvider = ({ children }) => {
|
||||
const [floorPlanState, setFloorPlanState] = useState({
|
||||
// 플랜 파일 업로드 모달 오픈 제어
|
||||
refFileModalOpen: false,
|
||||
// 플랜 회전 모드 제어
|
||||
toggleRotate: false,
|
||||
})
|
||||
|
||||
const [estimateContextState, setEstimateContextState] = useReducer(reducer, defaultEstimateData)
|
||||
|
||||
useEffect(() => {
|
||||
console.log('🚀 ~ floorPlanState:', floorPlanState)
|
||||
}, [floorPlanState])
|
||||
|
||||
return (
|
||||
<FloorPlanContext.Provider value={{ floorPlanState, setFloorPlanState, estimateContextState, setEstimateContextState }}>
|
||||
{children}
|
||||
</FloorPlanContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export default FloorPlanProvider
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
import FloorPlan from '@/components/floor-plan/FloorPlan'
|
||||
import { FloorPlanProvider } from './FloorPlanProvider'
|
||||
import FloorPlanProvider from './FloorPlanProvider'
|
||||
import CanvasLayout from '@/components/floor-plan/CanvasLayout'
|
||||
|
||||
export default function FloorPlanLayout({ children }) {
|
||||
|
||||
@ -80,79 +80,32 @@ export default function Login() {
|
||||
e.preventDefault()
|
||||
const formData = new FormData(e.target)
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
// 임시 로그인 처리
|
||||
setSession({
|
||||
userId: 'NEW0166102',
|
||||
saleStoreId: null,
|
||||
name: null,
|
||||
mail: null,
|
||||
tel: null,
|
||||
storeId: 'TEMP02',
|
||||
userNm: 'ㅇㅇ6610',
|
||||
userNmKana: '신규사용자 16610',
|
||||
category: '인상6610',
|
||||
telNo: '336610',
|
||||
fax: null,
|
||||
email: 't10t@naver.com',
|
||||
pwdInitYn: 'Y',
|
||||
storeLvl: '1',
|
||||
groupId: '60000',
|
||||
custCd: '100000',
|
||||
})
|
||||
setSessionState({
|
||||
userId: 'NEW0166102',
|
||||
saleStoreId: null,
|
||||
name: null,
|
||||
mail: null,
|
||||
tel: null,
|
||||
storeId: 'TEMP02',
|
||||
userNm: 'ㅇㅇ6610',
|
||||
userNmKana: '신규사용자 16610',
|
||||
category: '인상6610',
|
||||
telNo: '336610',
|
||||
fax: null,
|
||||
email: 't10t@naver.com',
|
||||
pwdInitYn: 'Y',
|
||||
storeLvl: '1',
|
||||
groupId: '60000',
|
||||
custCd: '100000',
|
||||
})
|
||||
if (chkLoginId) {
|
||||
Cookies.set('chkLoginId', formData.get('id'), { expires: 7 })
|
||||
} else {
|
||||
Cookies.remove('chkLoginId')
|
||||
// 로그인 처리 시작
|
||||
const param = {
|
||||
loginId: formData.get('id'),
|
||||
pwd: formData.get('password'),
|
||||
}
|
||||
router.push('/')
|
||||
// 임시 로그인 처리 끝
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
// 로그인 처리 시작 - ** 상단 임시 로그인 추후 삭제 필요 **
|
||||
// const param = {
|
||||
// loginId: formData.get('id'),
|
||||
// pwd: formData.get('password'),
|
||||
// }
|
||||
// await promisePost({ url: '/api/login/v1.0/login', data: param })
|
||||
// .then((res) => {
|
||||
// if (res) {
|
||||
// if (res.data.result.resultCode === 'S') {
|
||||
// setSession(res.data.data)
|
||||
// setSessionState(res.data.data)
|
||||
// // ID SAVE 체크되어 있는 경우, 쿠키 저장
|
||||
// if (chkLoginId) {
|
||||
// Cookies.set('chkLoginId', formData.get('id'), { expires: 7 })
|
||||
// } else {
|
||||
// Cookies.remove('chkLoginId')
|
||||
// }
|
||||
// router.push('/')
|
||||
// } else {
|
||||
// alert(res.data.result.resultMsg)
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// .catch((error) => {
|
||||
// alert(error.response.data.message)
|
||||
// })
|
||||
await promisePost({ url: '/api/login/v1.0/login', data: param })
|
||||
.then((res) => {
|
||||
if (res) {
|
||||
if (res.data.result.resultCode === 'S') {
|
||||
setSession(res.data.data)
|
||||
setSessionState(res.data.data)
|
||||
// ID SAVE 체크되어 있는 경우, 쿠키 저장
|
||||
if (chkLoginId) {
|
||||
Cookies.set('chkLoginId', formData.get('id'), { expires: 7 })
|
||||
} else {
|
||||
Cookies.remove('chkLoginId')
|
||||
}
|
||||
router.push('/')
|
||||
} else {
|
||||
alert(res.data.result.resultMsg)
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
alert(error.response.data.message)
|
||||
})
|
||||
}
|
||||
|
||||
// 비밀번호 초기화 관련
|
||||
|
||||
@ -58,7 +58,7 @@ export default function Estimate({ params }) {
|
||||
const objectRecoil = useRecoilValue(floorPlanObjectState)
|
||||
|
||||
//견적서 상세데이터
|
||||
const { state, setState, addItem, handleEstimateFileDownload } = useEstimateController(params.pid)
|
||||
const { estimateContextState, setEstimateContextState, addItem, handleEstimateFileDownload } = useEstimateController(params.pid)
|
||||
|
||||
//견적특이사항 List
|
||||
const [specialNoteList, setSpecialNoteList] = useState([])
|
||||
@ -105,9 +105,9 @@ export default function Estimate({ params }) {
|
||||
let url = `/api/estimate/special-note-list`
|
||||
get({ url: url }).then((res) => {
|
||||
if (isNotEmptyArray(res)) {
|
||||
if (state?.estimateOption) {
|
||||
if (estimateContextState?.estimateOption) {
|
||||
res.map((row) => {
|
||||
let estimateOption = state?.estimateOption?.split('、')
|
||||
let estimateOption = estimateContextState?.estimateOption?.split('、')
|
||||
row.text = false
|
||||
estimateOption.map((row2) => {
|
||||
if (row2 === row.code) {
|
||||
@ -119,16 +119,21 @@ export default function Estimate({ params }) {
|
||||
}
|
||||
}
|
||||
})
|
||||
}, [state?.estimateOption])
|
||||
}, [estimateContextState?.estimateOption])
|
||||
|
||||
//견적일 set
|
||||
useEffect(() => {
|
||||
let estimateDate = dayjs(startDate).format('YYYY-MM-DD')
|
||||
setState({ estimateDate: estimateDate })
|
||||
setEstimateContextState({ estimateDate: estimateDate })
|
||||
}, [startDate])
|
||||
|
||||
//API데이터로 견적일 셋팅
|
||||
useEffect(() => {
|
||||
//선택된 견적특이사항 setState
|
||||
setStartDate(estimateContextState?.estimateDate)
|
||||
}, [estimateContextState?.estimateDate])
|
||||
|
||||
useEffect(() => {
|
||||
//선택된 견적특이사항 setEstimateContextState
|
||||
if (isNotEmptyArray(specialNoteList)) {
|
||||
const liveCheckedData = specialNoteList.filter((row) => row.text === true)
|
||||
|
||||
@ -138,7 +143,7 @@ export default function Estimate({ params }) {
|
||||
}
|
||||
|
||||
const newData = data.join('、')
|
||||
setState({ estimateOption: newData })
|
||||
setEstimateContextState({ estimateOption: newData })
|
||||
}
|
||||
}, [specialNoteList])
|
||||
|
||||
@ -148,23 +153,23 @@ export default function Estimate({ params }) {
|
||||
event.stopPropagation()
|
||||
}
|
||||
|
||||
// 추가한 첨부파일 state에 넣기
|
||||
// 추가한 첨부파일 estimateContextState에 넣기
|
||||
useEffect(() => {
|
||||
if (isNotEmptyArray(files)) {
|
||||
files.map((row) => {
|
||||
setState({ fileList: row.data })
|
||||
setEstimateContextState({ fileList: row.data })
|
||||
})
|
||||
} else {
|
||||
setState({ fileList: [] })
|
||||
setEstimateContextState({ fileList: [] })
|
||||
}
|
||||
}, [files])
|
||||
|
||||
//상세에서 내려온 첨부파일 set 만들기
|
||||
useEffect(() => {
|
||||
if (isNotEmptyArray(state.fileList)) {
|
||||
setOriginFiles(state.fileList)
|
||||
if (isNotEmptyArray(estimateContextState.fileList)) {
|
||||
setOriginFiles(estimateContextState.fileList)
|
||||
}
|
||||
}, [state?.fileList])
|
||||
}, [estimateContextState?.fileList])
|
||||
|
||||
// 기존첨부파일 삭제
|
||||
const deleteOriginFile = async (objectNo, no) => {
|
||||
@ -176,7 +181,7 @@ export default function Estimate({ params }) {
|
||||
await promisePost({ url: 'api/file/fileDelete', data: delParams }).then((res) => {
|
||||
if (res.status === 204) {
|
||||
setOriginFiles(originFiles.filter((file) => file.objectNo === objectNo && file.no !== no))
|
||||
setState({
|
||||
setEstimateContextState({
|
||||
fileList: originFiles.filter((file) => file.objectNo === objectNo && file.no !== no),
|
||||
})
|
||||
}
|
||||
@ -185,11 +190,11 @@ export default function Estimate({ params }) {
|
||||
|
||||
//가격표시 option 목록 최초세팅 && 주문분류 변경시
|
||||
useEffect(() => {
|
||||
if (state.estimateType !== '') {
|
||||
if (estimateContextState.estimateType !== '') {
|
||||
const param = {
|
||||
saleStoreId: session.storeId,
|
||||
sapSalesStoreCd: session.custCd,
|
||||
docTpCd: state?.estimateType,
|
||||
docTpCd: estimateContextState?.estimateType,
|
||||
}
|
||||
|
||||
const apiUrl = `/api/estimate/price/store-price-list?${queryStringFormatter(param)}`
|
||||
@ -198,14 +203,16 @@ export default function Estimate({ params }) {
|
||||
setStorePriceList(res.data)
|
||||
}
|
||||
})
|
||||
|
||||
setItemChangeYn(true)
|
||||
}
|
||||
}, [state?.estimateType])
|
||||
}, [estimateContextState?.estimateType])
|
||||
|
||||
useEffect(() => {
|
||||
if (state?.priceCd) {
|
||||
setShowPriceCd(state.priceCd)
|
||||
if (estimateContextState?.priceCd) {
|
||||
setShowPriceCd(estimateContextState.priceCd)
|
||||
}
|
||||
}, [state?.priceCd])
|
||||
}, [estimateContextState?.priceCd])
|
||||
|
||||
//가격 표시 option 변경 이벤트
|
||||
const onChangeStorePriceList = (priceCd) => {
|
||||
@ -215,13 +222,9 @@ export default function Estimate({ params }) {
|
||||
docTpCd: priceCd,
|
||||
}
|
||||
|
||||
//가격표시 바꾸만헀을때는 tempPriceCd에 바꾼값 관리 불필요?
|
||||
//프라이싱 했을때 priceCd setState
|
||||
//프라이싱 했을때 priceCd setEstimateContextState
|
||||
//화면에 보여지는 값은 showPriceCd로 관리
|
||||
setShowPriceCd(priceCd)
|
||||
//setState({
|
||||
// tempPriceCd: priceCd,
|
||||
//})
|
||||
|
||||
const apiUrl = `/api/estimate/price/store-price-list?${queryStringFormatter(param)}`
|
||||
get({ url: apiUrl }).then((res) => {
|
||||
@ -232,14 +235,14 @@ export default function Estimate({ params }) {
|
||||
}
|
||||
|
||||
//Pricing 버튼
|
||||
const handlePricing = async (priceCd) => {
|
||||
const handlePricing = async (showPriceCd) => {
|
||||
const param = {
|
||||
saleStoreId: session.storeId,
|
||||
sapSalesStoreCd: session.custCd,
|
||||
docTpCd: state.estimateType,
|
||||
priceCd: priceCd,
|
||||
//itemIdList: state.itemList, //아이템 최초정보로 호출 delFlg 0인거만..
|
||||
itemIdList: state.itemList.filter((item) => item.delFlg === '0'),
|
||||
docTpCd: estimateContextState.estimateType,
|
||||
priceCd: showPriceCd,
|
||||
//itemIdList: estimateContextState.itemList, //아이템 최초정보로 호출 delFlg 0인거만..
|
||||
itemIdList: estimateContextState.itemList.filter((item) => item.delFlg === '0'),
|
||||
}
|
||||
|
||||
if (param.itemIdList.length > 0) {
|
||||
@ -266,7 +269,7 @@ export default function Estimate({ params }) {
|
||||
//itemId로 비교해서 단가정보만 업데이트
|
||||
if (data.result.code === 200) {
|
||||
if (isNotEmptyArray(data.data2)) {
|
||||
state.itemList.map((item) => {
|
||||
estimateContextState.itemList.map((item) => {
|
||||
let checkYn = false
|
||||
data.data2.map((item2) => {
|
||||
if (item2.itemId === item.itemId) {
|
||||
@ -280,8 +283,8 @@ export default function Estimate({ params }) {
|
||||
}
|
||||
})
|
||||
|
||||
setState({
|
||||
priceCd: priceCd,
|
||||
setEstimateContextState({
|
||||
priceCd: showPriceCd,
|
||||
itemList: updateList,
|
||||
})
|
||||
|
||||
@ -305,11 +308,31 @@ export default function Estimate({ params }) {
|
||||
setSelection(newSelection)
|
||||
}
|
||||
|
||||
//주택PKG input 변경
|
||||
const onChangePkgAsp = (value) => {
|
||||
if (estimateContextState.estimateType === 'YJSS') {
|
||||
let pkgAsp = Number(value.replace(/[^0-9]/g, '').replaceAll(',', ''))
|
||||
if (isNaN(pkgAsp)) {
|
||||
pkgAsp = 0
|
||||
} else {
|
||||
pkgAsp = pkgAsp.toLocaleString()
|
||||
}
|
||||
//현재 PKG용량값 가져오기
|
||||
|
||||
let totVolKw = estimateContextState.totVolKw * 1000
|
||||
let pkgTotPrice = pkgAsp * totVolKw
|
||||
|
||||
setEstimateContextState({
|
||||
pkgAsp: pkgAsp,
|
||||
pkgTotPrice: pkgTotPrice.toFixed(3),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 수량 변경
|
||||
const onChangeAmount = (value, dispOrder, index) => {
|
||||
//itemChangeFlg = 1, partAdd = 0 셋팅
|
||||
let amount = value
|
||||
amount = Number(value.replace(/[^0-9]/g, '').replaceAll(',', ''))
|
||||
let amount = Number(value.replace(/[^0-9]/g, '').replaceAll(',', ''))
|
||||
if (isNaN(amount)) {
|
||||
amount = 0
|
||||
} else {
|
||||
@ -321,9 +344,9 @@ export default function Estimate({ params }) {
|
||||
updates.amount = amount
|
||||
updates.itemChangeFlg = '1'
|
||||
updates.partAdd = '0'
|
||||
updates.saleTotPrice = (Number(amount.replaceAll(',', '')) * state.itemList[index].salePrice.replaceAll(',', '')).toLocaleString()
|
||||
updates.saleTotPrice = (Number(amount.replaceAll(',', '')) * estimateContextState.itemList[index].salePrice.replaceAll(',', '')).toLocaleString()
|
||||
|
||||
updateList = state.itemList.map((item) => {
|
||||
updateList = estimateContextState.itemList.map((item) => {
|
||||
if (item.dispOrder === dispOrder) {
|
||||
return { ...item, ...updates }
|
||||
} else {
|
||||
@ -331,7 +354,7 @@ export default function Estimate({ params }) {
|
||||
}
|
||||
})
|
||||
|
||||
setState({
|
||||
setEstimateContextState({
|
||||
itemList: updateList,
|
||||
})
|
||||
|
||||
@ -341,8 +364,7 @@ export default function Estimate({ params }) {
|
||||
// 단가 변경
|
||||
const onChangeSalePrice = (value, dispOrder, index) => {
|
||||
//itemChangeFlg, partAdd 받아온 그대로
|
||||
let salePrice
|
||||
salePrice = Number(value.replace(/[^0-9]/g, '').replaceAll(',', ''))
|
||||
let salePrice = Number(value.replace(/[^0-9]/g, '').replaceAll(',', ''))
|
||||
if (isNaN(salePrice)) {
|
||||
salePrice = 0
|
||||
} else {
|
||||
@ -351,9 +373,9 @@ export default function Estimate({ params }) {
|
||||
let updateList = []
|
||||
let updates = {}
|
||||
updates.salePrice = salePrice
|
||||
updates.saleTotPrice = (Number(salePrice.replaceAll(',', '')) * state.itemList[index].amount.replaceAll(',', '')).toLocaleString()
|
||||
updates.saleTotPrice = (Number(salePrice.replaceAll(',', '')) * estimateContextState.itemList[index].amount.replaceAll(',', '')).toLocaleString()
|
||||
|
||||
updateList = state.itemList.map((item) => {
|
||||
updateList = estimateContextState.itemList.map((item) => {
|
||||
if (item.dispOrder === dispOrder) {
|
||||
return { ...item, ...updates }
|
||||
} else {
|
||||
@ -361,7 +383,7 @@ export default function Estimate({ params }) {
|
||||
}
|
||||
})
|
||||
|
||||
setState({
|
||||
setEstimateContextState({
|
||||
itemList: updateList,
|
||||
})
|
||||
|
||||
@ -377,7 +399,7 @@ export default function Estimate({ params }) {
|
||||
let updateList = []
|
||||
let updates = {}
|
||||
get({ url: apiUrl }).then((res) => {
|
||||
console.log('아이템상세정보:::::::', res)
|
||||
// console.log('아이템상세정보:::::::', res)
|
||||
updates.objectNo = objectNo
|
||||
updates.planNo = planNo
|
||||
updates.itemId = res.itemId
|
||||
@ -397,11 +419,12 @@ export default function Estimate({ params }) {
|
||||
updates.specialNoteCd = res.spnAttrCds
|
||||
updates.itemGroup = res.itemGroup
|
||||
updates.delFlg = '0' // 삭제플래그 0
|
||||
updates.saleTotPrice = res.salePrice * state.itemList[index].amount
|
||||
// updates.saleTotPrice = res.salePrice * estimateContextState.itemList[index].amount
|
||||
updates.saleTotPrice = '0' //추가때는 수량을 안받아서 합계를 무조건 0으로
|
||||
//104671
|
||||
let bomList = res.itemBomList
|
||||
|
||||
updateList = state.itemList.map((item) => {
|
||||
updateList = estimateContextState.itemList.map((item) => {
|
||||
if (item.dispOrder === dispOrder) {
|
||||
return { ...item, ...updates }
|
||||
} else if (item.paDispOrder === dispOrder) {
|
||||
@ -413,18 +436,18 @@ export default function Estimate({ params }) {
|
||||
//paDispOrder
|
||||
if (bomList) {
|
||||
bomList.map((bomItem, index) => {
|
||||
let newItemDispOrder = Math.max(...state.itemList.map((item) => item.dispOrder))
|
||||
let newItemDispOrder = Math.max(...estimateContextState.itemList.map((item) => item.dispOrder))
|
||||
bomItem.dispOrder = index + 1 + newItemDispOrder
|
||||
bomItem.delFlg = '0'
|
||||
bomItem.objectNo = objectNo
|
||||
bomItem.planNo = planNo
|
||||
})
|
||||
|
||||
setState({
|
||||
setEstimateContextState({
|
||||
itemList: [...updateList, ...bomList],
|
||||
})
|
||||
} else {
|
||||
setState({
|
||||
setEstimateContextState({
|
||||
itemList: updateList,
|
||||
})
|
||||
}
|
||||
@ -437,7 +460,7 @@ export default function Estimate({ params }) {
|
||||
const removeItem = () => {
|
||||
const array = [...selection]
|
||||
let delList = []
|
||||
state.itemList.filter((row) => {
|
||||
estimateContextState.itemList.filter((row) => {
|
||||
array.map((row2) => {
|
||||
if (row2 === row.dispOrder) {
|
||||
delList.push({ ...row })
|
||||
@ -448,7 +471,7 @@ export default function Estimate({ params }) {
|
||||
})
|
||||
})
|
||||
|
||||
const updateList = state.itemList.map((item) => {
|
||||
const updateList = estimateContextState.itemList.map((item) => {
|
||||
const isDeleted = delList.some((row) => item.delFlg === '1' || item.dispOrder === row.dispOrder)
|
||||
return {
|
||||
...item,
|
||||
@ -467,7 +490,7 @@ export default function Estimate({ params }) {
|
||||
return alert(getMessage('estimate.detail.save.requiredItem'))
|
||||
}
|
||||
|
||||
setState({
|
||||
setEstimateContextState({
|
||||
itemList: updateList,
|
||||
})
|
||||
|
||||
@ -477,31 +500,94 @@ export default function Estimate({ params }) {
|
||||
|
||||
useEffect(() => {
|
||||
if (itemChangeYn) {
|
||||
// console.log('아이템 상태 가져오기::::::::::', state.itemList)
|
||||
// console.log('토탈쪽 셋팅해주기위한 함수::::::', itemList)
|
||||
//delFlg 0인거 중에..
|
||||
//수량(PCS) : totAmount
|
||||
//용량( Kw) : totVolKw
|
||||
//공급가액 : supplyPrice
|
||||
//부가세(10%) : vatPrice
|
||||
//총액 :totPrice
|
||||
|
||||
let totAmount = 0
|
||||
let amount = 0
|
||||
state.itemList.map((item) => {
|
||||
if (item.delFlg === '0') {
|
||||
amount = item.amount.replace(/[^0-9]/g, '').replaceAll(',', '')
|
||||
totAmount += Number(amount)
|
||||
}
|
||||
})
|
||||
let totVolKw = 0
|
||||
let supplyPrice = 0
|
||||
let vatPrice = 0
|
||||
let totPrice = 0
|
||||
let addPkgPrice = 0
|
||||
if (estimateContextState.estimateType === 'YJOD') {
|
||||
estimateContextState.itemList.map((item) => {
|
||||
if (item.delFlg === '0') {
|
||||
const amount = Number(item.amount.replace(/[^0-9]/g, '').replaceAll(',', ''))
|
||||
const price = Number(item.saleTotPrice.replaceAll(',', ''))
|
||||
|
||||
setState({
|
||||
totAmount: totAmount,
|
||||
})
|
||||
if (item.moduleFlg === '1') {
|
||||
//용량(Kw)은 모듈플래그 1만 합산
|
||||
const volKw = (item.pnowW * amount) / 1000
|
||||
totVolKw += volKw
|
||||
}
|
||||
// const price
|
||||
totAmount += amount
|
||||
supplyPrice += price
|
||||
}
|
||||
})
|
||||
|
||||
vatPrice = supplyPrice * 0.1
|
||||
totPrice = supplyPrice + vatPrice
|
||||
|
||||
setEstimateContextState({
|
||||
totAmount: totAmount,
|
||||
totVolKw: totVolKw.toFixed(3),
|
||||
supplyPrice: supplyPrice.toFixed(3),
|
||||
vatPrice: vatPrice.toFixed(3),
|
||||
totPrice: totPrice.toFixed(3),
|
||||
})
|
||||
} else {
|
||||
//YJSS
|
||||
estimateContextState.itemList.map((item) => {
|
||||
if (item.delFlg === '0') {
|
||||
const amount = Number(item.amount.replace(/[^0-9]/g, '').replaceAll(',', ''))
|
||||
const price = Number(item.saleTotPrice.replaceAll(',', ''))
|
||||
const salePrice = Number(item.salePrice.replaceAll(',', ''))
|
||||
|
||||
if (item.moduleFlg === '1') {
|
||||
//용량(Kw)은 모듈플래그 1만 합산
|
||||
const volKw = (item.pnowW * amount) / 1000
|
||||
totVolKw += volKw
|
||||
}
|
||||
if (item.pkgMaterialFlg === '1') {
|
||||
const pkgPrice = amount * salePrice
|
||||
//YJSS는 PKG제외상품들만 모아서 수량 * 단가를 공급가액에 추가로 더해줌
|
||||
addPkgPrice += pkgPrice
|
||||
}
|
||||
|
||||
// const price
|
||||
totAmount += amount
|
||||
supplyPrice += price
|
||||
}
|
||||
})
|
||||
|
||||
vatPrice = supplyPrice * 0.1
|
||||
totPrice = supplyPrice + vatPrice
|
||||
|
||||
setEstimateContextState({
|
||||
totAmount: totAmount,
|
||||
totVolKw: totVolKw.toFixed(3),
|
||||
supplyPrice: supplyPrice.toFixed(3),
|
||||
vatPrice: vatPrice.toFixed(3),
|
||||
totPrice: totPrice.toFixed(3),
|
||||
})
|
||||
}
|
||||
|
||||
setItemChangeYn(false)
|
||||
}
|
||||
}, [itemChangeYn])
|
||||
}, [itemChangeYn, estimateContextState.itemList])
|
||||
|
||||
//안건명 인풋 변경
|
||||
const handleBlurObjectName = (e) => {
|
||||
setEstimateContextState({ objectName: e.target.value })
|
||||
}
|
||||
|
||||
//담당자 인풋 변경
|
||||
const handleBlurCharger = (e) => {
|
||||
setEstimateContextState({ charger: e.target.value })
|
||||
}
|
||||
|
||||
//비고 인풋 변경
|
||||
const handleBlurRemarks = (e) => {
|
||||
setEstimateContextState({ remarks: e.target.value })
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="sub-content estimate">
|
||||
@ -518,17 +604,21 @@ export default function Estimate({ params }) {
|
||||
</div>
|
||||
<div className="estimate-box">
|
||||
<div className="estimate-tit">{getMessage('estimate.detail.docNo')}</div>
|
||||
<div className="estimate-name">{state.docNo}</div>
|
||||
<div className="estimate-name">{estimateContextState.docNo}</div>
|
||||
</div>
|
||||
<div className="estimate-box">
|
||||
<div className="estimate-tit">{getMessage('estimate.detail.drawingEstimateCreateDate')}</div>
|
||||
<div className="estimate-name">
|
||||
{state?.drawingEstimateCreateDate ? `${dayjs(state.drawingEstimateCreateDate).format('YYYY.MM.DD')}` : ''}
|
||||
{estimateContextState?.drawingEstimateCreateDate
|
||||
? `${dayjs(estimateContextState.drawingEstimateCreateDate).format('YYYY.MM.DD')}`
|
||||
: ''}
|
||||
</div>
|
||||
</div>
|
||||
<div className="estimate-box">
|
||||
<div className="estimate-tit">{getMessage('estimate.detail.lastEditDatetime')}</div>
|
||||
<div className="estimate-name">{state?.lastEditDatetime ? `${dayjs(state.lastEditDatetime).format('YYYY.MM.DD HH:mm')}` : ''}</div>
|
||||
<div className="estimate-name">
|
||||
{estimateContextState?.lastEditDatetime ? `${dayjs(estimateContextState.lastEditDatetime).format('YYYY.MM.DD HH:mm')}` : ''}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -554,7 +644,7 @@ export default function Estimate({ params }) {
|
||||
<tr>
|
||||
{/* 1차 판매점명 */}
|
||||
<th>{getMessage('estimate.detail.saleStoreId')}</th>
|
||||
<td>{state?.firstSaleStoreName}</td>
|
||||
<td>{estimateContextState?.firstSaleStoreName}</td>
|
||||
{/* 견적일 */}
|
||||
<th>
|
||||
{getMessage('estimate.detail.estimateDate')} <span className="important">*</span>
|
||||
@ -568,22 +658,14 @@ export default function Estimate({ params }) {
|
||||
<tr>
|
||||
{/* 2차 판매점명 */}
|
||||
<th>{getMessage('estimate.detail.otherSaleStoreId')}</th>
|
||||
<td>{state?.agencySaleStoreName}</td>
|
||||
<td>{estimateContextState?.agencySaleStoreName}</td>
|
||||
{/* 담당자 */}
|
||||
<th>
|
||||
{getMessage('estimate.detail.receiveUser')} <span className="important">*</span>
|
||||
</th>
|
||||
<td>
|
||||
<div className="input-wrap" style={{ width: '350px' }}>
|
||||
<input
|
||||
type="text"
|
||||
className="input-light"
|
||||
defaultValue={state?.charger}
|
||||
onChange={(e) => {
|
||||
//담당자 charger
|
||||
setState({ charger: e.target.value })
|
||||
}}
|
||||
/>
|
||||
<input type="text" className="input-light" defaultValue={estimateContextState?.charger} onBlur={handleBlurCharger} />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@ -595,15 +677,7 @@ export default function Estimate({ params }) {
|
||||
<td colSpan={3}>
|
||||
<div className="form-flex-wrap">
|
||||
<div className="input-wrap mr5" style={{ width: '610px' }}>
|
||||
<input
|
||||
type="text"
|
||||
className="input-light"
|
||||
defaultValue={state?.objectName}
|
||||
onChange={(e) => {
|
||||
//안건명 objectName
|
||||
setState({ objectName: e.target.value })
|
||||
}}
|
||||
/>
|
||||
<input type="text" className="input-light" defaultValue={estimateContextState?.objectName} onBlur={handleBlurObjectName} />
|
||||
</div>
|
||||
<div className="select-wrap" style={{ width: '200px' }}>
|
||||
<Select
|
||||
@ -615,17 +689,17 @@ export default function Estimate({ params }) {
|
||||
options={honorificCodeList}
|
||||
onChange={(e) => {
|
||||
if (isObjectNotEmpty(e)) {
|
||||
setState({ objectNameOmit: e.clCodeNm })
|
||||
setEstimateContextState({ objectNameOmit: e.clCodeNm })
|
||||
} else {
|
||||
setState({ objectNameOmit: '' })
|
||||
setEstimateContextState({ objectNameOmit: '' })
|
||||
}
|
||||
}}
|
||||
getOptionLabel={(x) => x.clCodeNm}
|
||||
getOptionValue={(x) => x.clCode}
|
||||
isClearable={true}
|
||||
isClearable={false}
|
||||
isSearchable={false}
|
||||
value={honorificCodeList.filter(function (option) {
|
||||
return option.clCodeNm === state.objectNameOmit
|
||||
return option.clCodeNm === estimateContextState.objectNameOmit
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
@ -635,7 +709,7 @@ export default function Estimate({ params }) {
|
||||
<tr>
|
||||
{/* 물건정보에서 입력한 메모 */}
|
||||
<th>{getMessage('estimate.detail.objectRemarks')}</th>
|
||||
<td colSpan={3}>{state?.objectRemarks}</td>
|
||||
<td colSpan={3}>{estimateContextState?.objectRemarks}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
{/* 주문분류 */}
|
||||
@ -650,13 +724,13 @@ export default function Estimate({ params }) {
|
||||
name="estimateType"
|
||||
id="YJSS"
|
||||
value={'YJSS'}
|
||||
checked={state?.estimateType === 'YJSS' ? true : false}
|
||||
checked={estimateContextState?.estimateType === 'YJSS' ? true : false}
|
||||
onChange={(e) => {
|
||||
//주문분류
|
||||
setState({ estimateType: e.target.value })
|
||||
setEstimateContextState({ estimateType: e.target.value })
|
||||
}}
|
||||
/>
|
||||
<label htmlFor="YJSS">住宅PKG</label>
|
||||
<label htmlFor="YJSS">{getMessage('estimate.detail.estimateType.yjss')}</label>
|
||||
</div>
|
||||
<div className="d-check-radio light">
|
||||
<input
|
||||
@ -664,12 +738,12 @@ export default function Estimate({ params }) {
|
||||
name="estimateType"
|
||||
id="YJOD"
|
||||
value={'YJOD'}
|
||||
checked={state?.estimateType === 'YJOD' ? true : false}
|
||||
checked={estimateContextState?.estimateType === 'YJOD' ? true : false}
|
||||
onChange={(e) => {
|
||||
setState({ estimateType: e.target.value })
|
||||
setEstimateContextState({ estimateType: e.target.value })
|
||||
}}
|
||||
/>
|
||||
<label htmlFor="YJOD">積上げ( YJOD )</label>
|
||||
<label htmlFor="YJOD">{getMessage('estimate.detail.estimateType.yjod')}</label>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
@ -678,20 +752,20 @@ export default function Estimate({ params }) {
|
||||
{/* 지붕재・사양시공 최대4개*/}
|
||||
<th>{getMessage('estimate.detail.roofCns')}</th>
|
||||
<td colSpan={3}>
|
||||
{state?.roofMaterialIdMulti?.split('、').map((row, index) => {
|
||||
{estimateContextState?.roofMaterialIdMulti?.split('、').map((row, index) => {
|
||||
//지붕재
|
||||
let roofList = row
|
||||
let roofListLength = state?.roofMaterialIdMulti?.split('、').length
|
||||
let roofListLength = estimateContextState?.roofMaterialIdMulti?.split('、').length
|
||||
let style = 'mb5'
|
||||
if (roofListLength == index + 1) {
|
||||
style = ''
|
||||
}
|
||||
//사양시공
|
||||
let constructSpecificationMulti = state?.constructSpecificationMulti?.split('、')
|
||||
let constructSpecificationMulti = estimateContextState?.constructSpecificationMulti?.split('、')
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={`form-flex-wrap ${style}`} key={uuidv4()}>
|
||||
<div className={`form-flex-wrap ${style}`} key={fixedKey}>
|
||||
<div className="input-wrap mr5" style={{ width: '610px' }} key={`roof${index}`}>
|
||||
<input type="text" className="input-light" value={roofList} readOnly />
|
||||
</div>
|
||||
@ -709,15 +783,7 @@ export default function Estimate({ params }) {
|
||||
<th>{getMessage('estimate.detail.remarks')}</th>
|
||||
<td colSpan={3}>
|
||||
<div className="input-wrap">
|
||||
<input
|
||||
type="text"
|
||||
className="input-light"
|
||||
defaultValue={state?.remarks}
|
||||
onChange={(e) => {
|
||||
//비고
|
||||
setState({ remarks: e.target.value })
|
||||
}}
|
||||
/>
|
||||
<input type="text" className="input-light" defaultValue={estimateContextState?.remarks} onBlur={handleBlurRemarks} />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@ -732,9 +798,9 @@ export default function Estimate({ params }) {
|
||||
<input
|
||||
type="checkbox"
|
||||
id="next"
|
||||
checked={state?.fileFlg === '0' ? false : true}
|
||||
checked={estimateContextState?.fileFlg === '0' ? false : true}
|
||||
onChange={(e) => {
|
||||
setState({
|
||||
setEstimateContextState({
|
||||
fileFlg: e.target.checked ? '1' : '0',
|
||||
})
|
||||
}}
|
||||
@ -876,28 +942,28 @@ export default function Estimate({ params }) {
|
||||
<div className="estimate-list-wrap one">
|
||||
<div className="estimate-box">
|
||||
<div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.totAmount')}</div>
|
||||
<div className="estimate-name blue">{convertNumberToPriceDecimal(state?.totAmount)}</div>
|
||||
<div className="estimate-name blue">{convertNumberToPriceDecimal(estimateContextState?.totAmount)}</div>
|
||||
</div>
|
||||
<div className="estimate-box">
|
||||
<div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.totVolKw')}</div>
|
||||
<div className="estimate-name blue">{convertNumberToPriceDecimal(state?.totVolKw)}</div>
|
||||
<div className="estimate-name blue">{convertNumberToPriceDecimal(estimateContextState?.totVolKw)}</div>
|
||||
</div>
|
||||
<div className="estimate-box">
|
||||
<div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.supplyPrice')}</div>
|
||||
<div className="estimate-name blue">{convertNumberToPriceDecimal(state?.supplyPrice)}</div>
|
||||
<div className="estimate-name blue">{convertNumberToPriceDecimal(estimateContextState?.supplyPrice)}</div>
|
||||
</div>
|
||||
<div className="estimate-box">
|
||||
<div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.vatPrice')}</div>
|
||||
<div className="estimate-name blue">{convertNumberToPriceDecimal(state?.vatPrice)}</div>
|
||||
<div className="estimate-name blue">{convertNumberToPriceDecimal(estimateContextState?.vatPrice)}</div>
|
||||
</div>
|
||||
<div className="estimate-box">
|
||||
<div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.totPrice')}</div>
|
||||
<div className="estimate-name red">{convertNumberToPriceDecimal(state?.totPrice)}</div>
|
||||
<div className="estimate-name red">{convertNumberToPriceDecimal(estimateContextState?.totPrice)}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* YJOD면 아래영역 숨김 */}
|
||||
<div className="common-table bt-able" style={{ display: state?.estimateType === 'YJSS' ? '' : 'none' }}>
|
||||
<div className="common-table bt-able" style={{ display: estimateContextState?.estimateType === 'YJSS' ? '' : 'none' }}>
|
||||
<table>
|
||||
<colgroup>
|
||||
<col style={{ width: '160px' }} />
|
||||
@ -915,13 +981,20 @@ export default function Estimate({ params }) {
|
||||
</th>
|
||||
<td>
|
||||
<div className="input-wrap">
|
||||
<input type="text" className="input-light" />
|
||||
<input
|
||||
type="text"
|
||||
className="input-light"
|
||||
value={estimateContextState?.pkgAsp}
|
||||
onChange={(e) => {
|
||||
onChangePkgAsp(e.target.value)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
<th>{getMessage('estimate.detail.sepcialEstimateProductInfo.pkgWeight')}</th>
|
||||
<td>{getMessage('estimate.detail.sepcialEstimateProductInfo.calcFormula1')}</td>
|
||||
<td>{convertNumberToPriceDecimal(estimateContextState?.totVolKw)}</td>
|
||||
<th>{getMessage('estimate.detail.sepcialEstimateProductInfo.pkgPrice')}</th>
|
||||
<td>{getMessage('estimate.detail.sepcialEstimateProductInfo.calcFormula2')}</td>
|
||||
<td>{convertNumberToPriceDecimal(estimateContextState?.pkgTotPrice)}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@ -953,7 +1026,7 @@ export default function Estimate({ params }) {
|
||||
type="button"
|
||||
className="btn-origin grey ml5"
|
||||
onClick={() => {
|
||||
handlePricing(state?.priceCd)
|
||||
handlePricing(showPriceCd)
|
||||
}}
|
||||
>
|
||||
{getMessage('estimate.detail.showPrice.pricingBtn')}
|
||||
@ -1030,121 +1103,113 @@ export default function Estimate({ params }) {
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{state?.itemList.length > 0 &&
|
||||
state.itemList.map((item, index) => {
|
||||
{estimateContextState?.itemList.length > 0 &&
|
||||
estimateContextState.itemList.map((item, index) => {
|
||||
if (item.delFlg === '0') {
|
||||
return (
|
||||
<>
|
||||
<tr key={fixedKey}>
|
||||
<td className="al-c">
|
||||
<div className="d-check-box light no-text">
|
||||
<input
|
||||
type="checkbox"
|
||||
id={item?.dispOrder}
|
||||
disabled={item?.paDispOrder ? true : false}
|
||||
onChange={() => onChangeSelect(item.dispOrder)}
|
||||
checked={selection.has(item.dispOrder) ? true : false}
|
||||
<tr key={item?.dispOrder || index}>
|
||||
<td className="al-c">
|
||||
<div className="d-check-box light no-text">
|
||||
<input
|
||||
type="checkbox"
|
||||
id={item?.dispOrder}
|
||||
disabled={!!item?.paDispOrder}
|
||||
onChange={() => onChangeSelect(item.dispOrder)}
|
||||
checked={!!selection.has(item.dispOrder)}
|
||||
/>
|
||||
<label htmlFor={item?.dispOrder}></label>
|
||||
</div>
|
||||
</td>
|
||||
<td className="al-r">{item?.dispOrder}</td>
|
||||
<td>
|
||||
<div className="form-flex-wrap">
|
||||
<div className="select-wrap mr5">
|
||||
<Select
|
||||
id="long-value-select1"
|
||||
instanceId="long-value-select1"
|
||||
className="react-select-custom"
|
||||
classNamePrefix="custom"
|
||||
placeholder="Select"
|
||||
options={displayItemList}
|
||||
onChange={(e) => {
|
||||
if (isObjectNotEmpty(e)) {
|
||||
onChangeDisplayItem(e.itemId, item.dispOrder, index)
|
||||
}
|
||||
}}
|
||||
getOptionLabel={(x) => x.itemName}
|
||||
getOptionValue={(x) => x.itemId}
|
||||
isClearable={false}
|
||||
isDisabled={!!item?.paDispOrder}
|
||||
value={displayItemList.filter(function (option) {
|
||||
return option.itemId === item.itemId
|
||||
})}
|
||||
/>
|
||||
<label htmlFor={item?.dispOrder}></label>
|
||||
</div>
|
||||
</td>
|
||||
<td className="al-r">{item?.dispOrder}</td>
|
||||
<td>
|
||||
<div className="form-flex-wrap">
|
||||
<div className="select-wrap mr5">
|
||||
<Select
|
||||
id="long-value-select1"
|
||||
instanceId="long-value-select1"
|
||||
className="react-select-custom"
|
||||
classNamePrefix="custom"
|
||||
placeholder="Select"
|
||||
options={displayItemList}
|
||||
onChange={(e) => {
|
||||
if (isObjectNotEmpty(e)) {
|
||||
onChangeDisplayItem(e.itemId, item.dispOrder, index)
|
||||
}
|
||||
}}
|
||||
getOptionLabel={(x) => x.itemName}
|
||||
getOptionValue={(x) => x.itemId}
|
||||
isClearable={false}
|
||||
isDisabled={item?.paDispOrder ? true : false}
|
||||
value={displayItemList.filter(function (option) {
|
||||
return option.itemId === item.itemId
|
||||
})}
|
||||
/>
|
||||
{item?.itemChangeFlg === '1' && (
|
||||
<div className="btn-area">
|
||||
<span className="tb_ico change_check"></span>
|
||||
</div>
|
||||
{item?.itemChangeFlg === '1' && (
|
||||
<div className="btn-area">
|
||||
<span className="tb_ico change_check"></span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div className="form-flex-wrap">
|
||||
<div className="name">{item?.itemNo}</div>
|
||||
<div className="icon-wrap">
|
||||
{item?.fileUploadFlg === '1' && <span className="tb_ico file_check"></span>}
|
||||
{item?.specialNoteCd && (
|
||||
<button
|
||||
type="button"
|
||||
className="grid-tip"
|
||||
onClick={() => {
|
||||
setProductFeaturesPopupOpen(true)
|
||||
setShowProductFeatureData(item?.specialNoteCd)
|
||||
}}
|
||||
></button>
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div className="form-flex-wrap">
|
||||
<div className="name">{item?.itemNo}</div>
|
||||
<div className="icon-wrap">
|
||||
{item?.fileUploadFlg === '1' && <span className="tb_ico file_check"></span>}
|
||||
{item?.specialNoteCd && (
|
||||
<button
|
||||
type="button"
|
||||
className="grid-tip"
|
||||
onClick={() => {
|
||||
setProductFeaturesPopupOpen(true)
|
||||
setShowProductFeatureData(item?.specialNoteCd)
|
||||
}}
|
||||
></button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div className="input-wrap" style={{ width: '100%' }}>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div className="input-wrap" style={{ width: '100%' }}>
|
||||
<input
|
||||
type="text"
|
||||
className="input-light al-r"
|
||||
value={item?.amount}
|
||||
disabled={item.itemId === '' || !!item?.paDispOrder}
|
||||
onChange={(e) => {
|
||||
onChangeAmount(e.target.value, item.dispOrder, index)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
<td>{item.unit}</td>
|
||||
<td>
|
||||
<div className="form-flex-wrap">
|
||||
<div className="input-wrap mr5">
|
||||
<input
|
||||
type="text"
|
||||
className="input-light al-r"
|
||||
value={item?.amount}
|
||||
disabled={item.itemId === '' ? true : item?.paDispOrder ? true : false}
|
||||
value={convertNumberToPriceDecimal(item?.salePrice.replaceAll(',', ''))}
|
||||
disabled={
|
||||
estimateContextState?.estimateType === 'YJSS'
|
||||
? item?.paDispOrder
|
||||
? true
|
||||
: item.pkgMaterialFlg !== '1'
|
||||
: item.itemId === '' || !!item?.paDispOrder
|
||||
}
|
||||
onChange={(e) => {
|
||||
onChangeAmount(e.target.value, item.dispOrder, index)
|
||||
onChangeSalePrice(e.target.value, item.dispOrder, index)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
<td>{item.unit}</td>
|
||||
<td>
|
||||
<div className="form-flex-wrap">
|
||||
<div className="input-wrap mr5">
|
||||
<input
|
||||
type="text"
|
||||
className="input-light al-r"
|
||||
value={item?.salePrice}
|
||||
disabled={
|
||||
state?.estimateType === 'YJSS'
|
||||
? item?.paDispOrder
|
||||
? true
|
||||
: item.pkgMaterialFlg === '1'
|
||||
? false
|
||||
: true
|
||||
: item.itemId === ''
|
||||
? true
|
||||
: item?.paDispOrder
|
||||
? true
|
||||
: false
|
||||
}
|
||||
onChange={(e) => {
|
||||
onChangeSalePrice(e.target.value, item.dispOrder, index)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/* <div className="btn-area">
|
||||
{/* <div className="btn-area">
|
||||
<span className="tb_ico open_check">OPEN아이콘 처리</span>
|
||||
</div> */}
|
||||
</div>
|
||||
</td>
|
||||
<td className="al-r">{item?.saleTotPrice}</td>
|
||||
</tr>
|
||||
</>
|
||||
</div>
|
||||
</td>
|
||||
<td className="al-r">{convertNumberToPriceDecimal(item?.saleTotPrice.replaceAll(',', ''))}</td>
|
||||
</tr>
|
||||
)
|
||||
} else {
|
||||
return null
|
||||
|
||||
313
src/components/estimate/popup/EstimateCopyPop.jsx
Normal file
313
src/components/estimate/popup/EstimateCopyPop.jsx
Normal file
@ -0,0 +1,313 @@
|
||||
'use client'
|
||||
import { useEffect, useState, useContext, useRef } from 'react'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { useAxios } from '@/hooks/useAxios'
|
||||
import Select, { components } from 'react-select'
|
||||
import { SessionContext } from '@/app/SessionProvider'
|
||||
import { isEmptyArray, isObjectNotEmpty } from '@/util/common-utils'
|
||||
import { useEstimateController } from '@/hooks/floorPlan/estimate/useEstimateController'
|
||||
|
||||
export default function EstimateCopyPop({ planNo, setEstimateCopyPopupOpen }) {
|
||||
const { getMessage } = useMessage()
|
||||
const { get } = useAxios()
|
||||
|
||||
const { handleEstimateCopy, state } = useEstimateController(planNo)
|
||||
|
||||
const { session } = useContext(SessionContext)
|
||||
|
||||
const [saleStoreList, setSaleStoreList] = useState([]) // 판매점 리스트
|
||||
const [favoriteStoreList, setFavoriteStoreList] = useState([]) //즐겨찾기한 판매점목록
|
||||
const [showSaleStoreList, setShowSaleStoreList] = useState([]) //보여줄 판매점목록
|
||||
const [otherSaleStoreList, setOtherSaleStoreList] = useState([])
|
||||
const [originOtherSaleStoreList, setOriginOtherSaleStoreList] = useState([])
|
||||
|
||||
const [saleStoreId, setSaleStoreId] = useState('') //선택한 1차점
|
||||
const [otherSaleStoreId, setOtherSaleStoreId] = useState('') //선택한 1차점 이외
|
||||
|
||||
const [sendPlanNo, setSendPlanNo] = useState('1')
|
||||
const [copyReceiveUser, setCopyReceiveUser] = useState('')
|
||||
|
||||
const ref = useRef() //2차점 자동완성 초기화용
|
||||
|
||||
useEffect(() => {
|
||||
let url
|
||||
let firstList
|
||||
let favList
|
||||
let otherList
|
||||
if (session.storeId === 'T01') {
|
||||
url = `/api/object/saleStore/${session?.storeId}/firstList?userId=${session?.userId}`
|
||||
} else {
|
||||
if (session.storeLvl === '1') {
|
||||
url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=1&userId=${session?.userId}`
|
||||
} else {
|
||||
//T01 or 1차점만 복사버튼 노출됨으로
|
||||
// url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=1&userId=${session?.userId}`
|
||||
}
|
||||
}
|
||||
|
||||
get({ url: url }).then((res) => {
|
||||
if (!isEmptyArray(res)) {
|
||||
res.map((row) => {
|
||||
//자동완성 검색을 위한 필드명 셋팅
|
||||
row.value = row.saleStoreId
|
||||
row.label = row.saleStoreName
|
||||
})
|
||||
|
||||
if (session.storeId === 'T01') {
|
||||
firstList = res
|
||||
//T01을 첫번째로 정렬
|
||||
firstList.sort((a, b) => (a.saleStoreId !== 'T01') - (b.saleStoreId !== 'T01') || a.saleStoreId - b.saleStoreId)
|
||||
favList = firstList.filter((row) => row.saleStoreId === 'T01' || row.priority !== 'B')
|
||||
|
||||
setSaleStoreList(firstList)
|
||||
setFavoriteStoreList(favList)
|
||||
setShowSaleStoreList(favList)
|
||||
setSaleStoreId(session?.storeId)
|
||||
|
||||
// T01때 디폴트로 T01셋팅하고 하위 2차목록 조회하기 바꾸면(onSelectionChange..) 바꾼거로 2차목록 조회하기
|
||||
url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=0&userId=${session?.userId}`
|
||||
get({ url: url }).then((res) => {
|
||||
if (!isEmptyArray(res)) {
|
||||
res.map((row) => {
|
||||
row.value == row.saleStoreId
|
||||
row.label = row.saleStoreName
|
||||
})
|
||||
otherList = res
|
||||
setOtherSaleStoreList(otherList)
|
||||
setOriginOtherSaleStoreList(otherList)
|
||||
} else {
|
||||
setOtherSaleStoreList([])
|
||||
}
|
||||
})
|
||||
} else {
|
||||
if (session.storeLvl === '1') {
|
||||
firstList = res
|
||||
favList = res.filter((row) => row.priority !== 'B')
|
||||
otherList = res.filter((row) => row.firstAgentYn === 'N')
|
||||
setSaleStoreList(firstList)
|
||||
setFavoriteStoreList(firstList)
|
||||
setShowSaleStoreList(firstList)
|
||||
setSaleStoreId(firstList[0].saleStoreId)
|
||||
|
||||
setOtherSaleStoreList(otherList)
|
||||
} else {
|
||||
//T01 or 1차점만 복사버튼 노출됨으로
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (planNo) {
|
||||
setSendPlanNo(planNo)
|
||||
}
|
||||
}, [planNo])
|
||||
|
||||
useEffect(() => {
|
||||
if (state?.charger) {
|
||||
setCopyReceiveUser(state.charger)
|
||||
}
|
||||
}, [state.charger])
|
||||
|
||||
//T01 1차점 자동완성 인풋때 목록 변환
|
||||
const onInputChange = (key) => {
|
||||
if (key !== '') {
|
||||
setShowSaleStoreList(saleStoreList)
|
||||
} else {
|
||||
setShowSaleStoreList(favoriteStoreList)
|
||||
}
|
||||
}
|
||||
|
||||
// 1차점 변경 이벤트
|
||||
const onSelectionChange = (key) => {
|
||||
if (isObjectNotEmpty(key)) {
|
||||
if (key.saleStoreId === saleStoreId) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (isObjectNotEmpty(key)) {
|
||||
setSaleStoreId(key.saleStoreId)
|
||||
const url = `/api/object/saleStore/${key.saleStoreId}/list?firstFlg=0&userId=${session?.userId}`
|
||||
let otherList
|
||||
get({ url: url }).then((res) => {
|
||||
if (!isEmptyArray(res)) {
|
||||
res.map((row) => {
|
||||
row.value = row.saleStoreId
|
||||
row.label = row.saleStoreName
|
||||
})
|
||||
|
||||
otherList = res
|
||||
setOtherSaleStoreList(otherList)
|
||||
setOtherSaleStoreId('')
|
||||
} else {
|
||||
setOtherSaleStoreId('')
|
||||
setOtherSaleStoreList([])
|
||||
}
|
||||
})
|
||||
} else {
|
||||
setSaleStoreId('')
|
||||
//otherSaleStoreId는 onSelectionChange2에서 초기화됨
|
||||
setOtherSaleStoreList(originOtherSaleStoreList)
|
||||
handleClear()
|
||||
}
|
||||
}
|
||||
|
||||
// 2차점 변경 이벤트
|
||||
const onSelectionChange2 = (key) => {
|
||||
if (isObjectNotEmpty(key)) {
|
||||
if (key.saleStoreId === otherSaleStoreId) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (isObjectNotEmpty(key)) {
|
||||
setOtherSaleStoreId(key.saleStoreId)
|
||||
} else {
|
||||
setOtherSaleStoreId('')
|
||||
}
|
||||
}
|
||||
|
||||
//2차점 자동완성 초기화
|
||||
const handleClear = () => {
|
||||
if (ref.current) {
|
||||
ref.current.clearValue()
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="modal-popup">
|
||||
<div className="modal-dialog">
|
||||
<div className="modal-content">
|
||||
<div className="modal-header">
|
||||
<h1 className="title">{getMessage('estimate.detail.estimateCopyPopup.title')}</h1>
|
||||
<button
|
||||
type="button"
|
||||
className="modal-close"
|
||||
onClick={() => {
|
||||
setEstimateCopyPopupOpen(false)
|
||||
}}
|
||||
>
|
||||
{getMessage('estimate.detail.estimateCopyPopup.close')}
|
||||
</button>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<div className="modal-body-inner">
|
||||
<div className="explane">{getMessage('estimate.detail.estimateCopyPopup.explane')}</div>
|
||||
<div className="estimate-copy-info-wrap">
|
||||
<div className="estimate-copy-info-item">
|
||||
<div className="estimate-copy-info-tit">
|
||||
{getMessage('estimate.detail.estimateCopyPopup.label.saleStoreId')} <span className="red">*</span>
|
||||
</div>
|
||||
{session.storeId === 'T01' && (
|
||||
<div className="estimate-copy-info-box">
|
||||
<div className="estimate-copy-sel">
|
||||
<Select
|
||||
id="long-value-select1"
|
||||
instanceId="long-value-select1"
|
||||
className="react-select-custom"
|
||||
classNamePrefix="custom"
|
||||
placeholder="Select"
|
||||
options={showSaleStoreList}
|
||||
onInputChange={onInputChange}
|
||||
onChange={onSelectionChange}
|
||||
getOptionLabel={(x) => x.saleStoreName}
|
||||
getOptionValue={(x) => x.saleStoreId}
|
||||
isClearable={true}
|
||||
isDisabled={false}
|
||||
value={saleStoreList.filter(function (option) {
|
||||
return option.saleStoreId === saleStoreId
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
<div className="estimate-copy-id">{saleStoreId}</div>
|
||||
</div>
|
||||
)}
|
||||
{session.storeId !== 'T01' && session.storeLvl === '1' && (
|
||||
<div className="estimate-copy-info-box">
|
||||
<div className="estimate-copy-sel">
|
||||
<Select
|
||||
id="long-value-select1"
|
||||
instanceId="long-value-select1"
|
||||
className="react-select-custom"
|
||||
classNamePrefix="custom"
|
||||
placeholder="Select"
|
||||
options={showSaleStoreList[0]}
|
||||
onChange={onSelectionChange}
|
||||
getOptionLabel={(x) => x.saleStoreName}
|
||||
getOptionValue={(x) => x.saleStoreId}
|
||||
isClearable={false}
|
||||
isDisabled={session?.storeLvl !== '1' ? true : session?.storeId !== 'T01' ? true : false}
|
||||
value={showSaleStoreList.filter(function (option) {
|
||||
return option.saleStoreId === saleStoreId
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
<div className="estimate-copy-id">{saleStoreId}</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="estimate-copy-info-item">
|
||||
<div className="estimate-copy-info-tit">{getMessage('estimate.detail.estimateCopyPopup.label.otherSaleStoreId')}</div>
|
||||
<div className="estimate-copy-info-box">
|
||||
<div className="estimate-copy-sel">
|
||||
<Select
|
||||
id="long-value-select2"
|
||||
instanceId="long-value-select2"
|
||||
className="react-select-custom"
|
||||
classNamePrefix="custom"
|
||||
placeholder="Select"
|
||||
ref={ref}
|
||||
options={otherSaleStoreList}
|
||||
onChange={onSelectionChange2}
|
||||
getOptionLabel={(x) => x.saleStoreName}
|
||||
getOptionValue={(x) => x.saleStoreId}
|
||||
isClearable={true}
|
||||
isDisabled={otherSaleStoreList.length > 0 ? false : true}
|
||||
value={otherSaleStoreList.filter(function (option) {
|
||||
return option.saleStoreId === otherSaleStoreId
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
<div className="estimate-copy-id">{otherSaleStoreId}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="estimate-copy-info-item">
|
||||
<div className="estimate-copy-info-tit">
|
||||
{getMessage('estimate.detail.estimateCopyPopup.label.receiveUser')} <span className="red">*</span>
|
||||
</div>
|
||||
<div className="input-wrap">
|
||||
<input
|
||||
type="text"
|
||||
className="input-light"
|
||||
required
|
||||
defaultValue={state?.charger}
|
||||
onChange={(e) => {
|
||||
setCopyReceiveUser(e.target.value)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="footer-btn-wrap">
|
||||
<button type="button" className="btn-origin grey mr5" onClick={() => setEstimateCopyPopupOpen(false)}>
|
||||
{getMessage('estimate.detail.estimateCopyPopup.close')}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="btn-origin navy"
|
||||
onClick={() => {
|
||||
handleEstimateCopy(sendPlanNo, copyReceiveUser, saleStoreId, otherSaleStoreId)
|
||||
}}
|
||||
>
|
||||
{getMessage('estimate.detail.estimateCopyPopup.copyBtn')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -14,6 +14,7 @@ import { useCanvasConfigInitialize } from '@/hooks/common/useCanvasConfigInitial
|
||||
import { MENU } from '@/common/common'
|
||||
import PanelBatchStatistics from '@/components/floor-plan/modal/panelBatch/PanelBatchStatistics'
|
||||
import { totalDisplaySelector } from '@/store/settingAtom'
|
||||
import ImgLoad from '@/components/floor-plan/modal/ImgLoad'
|
||||
|
||||
export default function CanvasFrame() {
|
||||
const canvasRef = useRef(null)
|
||||
@ -76,6 +77,8 @@ export default function CanvasFrame() {
|
||||
MENU.MODULE_CIRCUIT_SETTING.PLAN_ORIENTATION,
|
||||
].includes(currentMenu) &&
|
||||
totalDisplay && <PanelBatchStatistics />}
|
||||
{/* 이미지 로드 팝업 */}
|
||||
<ImgLoad />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useContext, useEffect, useState } from 'react'
|
||||
|
||||
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
|
||||
|
||||
@ -35,6 +35,8 @@ import { MENU } from '@/common/common'
|
||||
import { useEstimateController } from '@/hooks/floorPlan/estimate/useEstimateController'
|
||||
import { estimateState } from '@/store/floorPlanObjectAtom'
|
||||
import DocDownOptionPop from '../estimate/popup/DocDownOptionPop'
|
||||
import { FloorPlanContext } from '@/app/floor-plan/FloorPlanProvider'
|
||||
import EstimateCopyPop from '../estimate/popup/EstimateCopyPop'
|
||||
|
||||
export default function CanvasMenu(props) {
|
||||
const { menuNumber, setMenuNumber } = props
|
||||
@ -59,6 +61,7 @@ export default function CanvasMenu(props) {
|
||||
const { handleEstimateSubmit } = useEstimateController()
|
||||
const estimateRecoilState = useRecoilValue(estimateState)
|
||||
const [estimatePopupOpen, setEstimatePopupOpen] = useState(false)
|
||||
const [estimateCopyPopupOpen, setEstimateCopyPopupOpen] = useState(false)
|
||||
|
||||
const { getMessage } = useMessage()
|
||||
const { currentCanvasPlan, saveCanvas } = usePlan()
|
||||
@ -67,6 +70,7 @@ export default function CanvasMenu(props) {
|
||||
const commonUtils = useRecoilValue(commonUtilsState)
|
||||
const { commonFunctions } = useCommonUtils()
|
||||
const SelectOption = [{ name: '瓦53A' }, { name: '瓦53A' }]
|
||||
const { floorPlanState, setFloorPlanState } = useContext(FloorPlanContext)
|
||||
|
||||
const onClickNav = (menu) => {
|
||||
setMenuNumber(menu.index)
|
||||
@ -161,20 +165,6 @@ export default function CanvasMenu(props) {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 견적서 복사버튼
|
||||
* (견적서 번호(estimateRecoilState.docNo)가 생성된 이후 버튼 활성화 )
|
||||
* T01관리자 계정 및 1차판매점에게만 제공
|
||||
*/
|
||||
|
||||
const handleEstimateCopy = () => {
|
||||
// console.log('estimateRecoilState::', estimateRecoilState)
|
||||
//objectNo, planNo
|
||||
console.log('복사')
|
||||
console.log('물건정보+도면+견적서를 모두 복사')
|
||||
console.log('견적서 가격은 정가를 표시')
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (globalLocale === 'ko') {
|
||||
setAppMessageState(KO)
|
||||
@ -240,7 +230,10 @@ export default function CanvasMenu(props) {
|
||||
<QSelectBox title={'瓦53A'} option={SelectOption} />
|
||||
</div>
|
||||
<div className="btn-from">
|
||||
<button className="btn10"></button>
|
||||
<button
|
||||
className={`btn10 ${floorPlanState.refFileModalOpen && 'active'}`}
|
||||
onClick={() => setFloorPlanState({ ...floorPlanState, refFileModalOpen: true })}
|
||||
></button>
|
||||
{/*<button className="btn04" onClick={() => setShowCanvasSettingModal(true)}></button>*/}
|
||||
<button className="btn04" onClick={handlePopup}></button>
|
||||
<button className="btn05"></button>
|
||||
@ -279,7 +272,6 @@ export default function CanvasMenu(props) {
|
||||
<span className="ico ico02"></span>
|
||||
<span>{getMessage('plan.menu.estimate.save')}</span>
|
||||
</button>
|
||||
{/* {estimateRecoilState?.docNo != null && ( */}
|
||||
<button
|
||||
className="btn-frame gray ico-flx"
|
||||
onClick={() => {
|
||||
@ -289,17 +281,18 @@ export default function CanvasMenu(props) {
|
||||
<span className="ico ico03"></span>
|
||||
<span>{getMessage('plan.menu.estimate.reset')}</span>
|
||||
</button>
|
||||
{/* )} */}
|
||||
|
||||
<button
|
||||
className="btn-frame gray ico-flx"
|
||||
onClick={() => {
|
||||
handleEstimateCopy()
|
||||
}}
|
||||
>
|
||||
<span className="ico ico04"></span>
|
||||
<span>{getMessage('plan.menu.estimate.copy')}</span>
|
||||
</button>
|
||||
{estimateRecoilState?.docNo !== null && (sessionState.storeId === 'T01' || sessionState.storeLvl === '1') && (
|
||||
<button
|
||||
className="btn-frame gray ico-flx"
|
||||
onClick={() => {
|
||||
setEstimateCopyPopupOpen(true)
|
||||
}}
|
||||
>
|
||||
<span className="ico ico04"></span>
|
||||
<span>{getMessage('plan.menu.estimate.copy')}</span>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
@ -324,6 +317,8 @@ export default function CanvasMenu(props) {
|
||||
</div>
|
||||
{/* 견적서(menuNumber=== 5) 상세화면인경우 문서다운로드 팝업 */}
|
||||
{estimatePopupOpen && <DocDownOptionPop planNo={estimateRecoilState?.planNo} setEstimatePopupOpen={setEstimatePopupOpen} />}
|
||||
{/* 견적서(menuNumber ===5)복사 팝업 */}
|
||||
{estimateCopyPopupOpen && <EstimateCopyPop planNo={estimateRecoilState?.planNo} setEstimateCopyPopupOpen={setEstimateCopyPopupOpen} />}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
import { useContext, useEffect } from 'react'
|
||||
import { FloorPlanContext } from '@/app/floor-plan/FloorPlanProvider'
|
||||
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { useRefFiles } from '@/hooks/common/useRefFiles'
|
||||
import { usePlan } from '@/hooks/usePlan'
|
||||
@ -20,9 +23,24 @@ export default function ImgLoad() {
|
||||
} = useRefFiles()
|
||||
const { currentCanvasPlan } = usePlan()
|
||||
const { getMessage } = useMessage()
|
||||
const { floorPlanState, setFloorPlanState } = useContext(FloorPlanContext)
|
||||
|
||||
const handleModal = () => {
|
||||
setFloorPlanState({ ...floorPlanState, refFileModalOpen: false })
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
console.log('🚀 ~ ImgLoad ~ floorPlanState.refFileModalOpen:', floorPlanState.refFileModalOpen)
|
||||
console.log('🚀 ~ ImgLoad ~ currentCanvasPlan:', currentCanvasPlan)
|
||||
}, [floorPlanState.refFileModalOpen])
|
||||
|
||||
useEffect(() => {
|
||||
const refFileMethod = currentCanvasPlan?.mapPositionAddress === null ? '1' : '2'
|
||||
setRefFileMethod(refFileMethod)
|
||||
}, [currentCanvasPlan])
|
||||
|
||||
return (
|
||||
<WithDraggable isShow={true}>
|
||||
<WithDraggable isShow={floorPlanState.refFileModalOpen} pos={{ x: 1000, y: 200 }} handle=".modal">
|
||||
<div className={`modal-pop-wrap r`}>
|
||||
<div className="modal-head">
|
||||
<h1 className="title">{getMessage('common.input.file')}</h1>
|
||||
@ -32,7 +50,11 @@ export default function ImgLoad() {
|
||||
<div className="img-flex-box">
|
||||
<span className="normal-font mr10">サイズ調整と回転</span>
|
||||
<label className="toggle-btn">
|
||||
<input type="checkbox" />
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={floorPlanState.toggleRotate}
|
||||
onChange={(e) => setFloorPlanState({ ...floorPlanState, toggleRotate: e.target.checked })}
|
||||
/>
|
||||
<span className="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
@ -48,13 +70,18 @@ export default function ImgLoad() {
|
||||
<span className="img-edit"></span>
|
||||
ファイルの追加
|
||||
</label>
|
||||
<input type="file" id="img_file" style={{ display: 'none' }} onChange={(e) => handleRefFile(e.target.files[0])} />
|
||||
<input
|
||||
type="file"
|
||||
id="img_file"
|
||||
style={{ display: 'none' }}
|
||||
onChange={refFileMethod === '1' ? (e) => handleRefFile(e.target.files[0]) : () => {}}
|
||||
/>
|
||||
</div>
|
||||
<div className="img-name-wrap">
|
||||
{/* <input type="text" className="input-origin al-l" defaultValue={'IMG_Name.PNG'} readOnly />
|
||||
<button className="img-check"></button> */}
|
||||
{currentCanvasPlan?.bgImageName === null ? (
|
||||
<input type="text" className="input-origin al-l" defaultValue={''} value={refImage ? refImage.name : ''} readOnly />
|
||||
<input type="text" className="input-origin al-l" value={refImage ? refImage.name : ''} readOnly />
|
||||
) : (
|
||||
<input type="text" className="input-origin al-l" value={currentCanvasPlan?.bgImageName} readOnly />
|
||||
)}
|
||||
@ -64,13 +91,13 @@ export default function ImgLoad() {
|
||||
</div>
|
||||
<div className="img-load-item">
|
||||
<div className="d-check-radio pop">
|
||||
<input type="radio" name="radio03" id="ra06" value={'2'} onChange={(e) => handleRefFileMethod(e)} checked={refFileMethod === '2'} />
|
||||
<label htmlFor="ra06">アドレスを読み込む</label>
|
||||
<input type="radio" name="radio03" id="ra07" value={'2'} onChange={(e) => handleRefFileMethod(e)} checked={refFileMethod === '2'} />
|
||||
<label htmlFor="ra07">アドレスを読み込む</label>
|
||||
</div>
|
||||
<div className="img-flex-box for-address">
|
||||
<input type="text" className="input-origin al-l mr10" placeholder={'住所入力'} />
|
||||
<input type="text" className="input-origin al-l mr10" placeholder={'住所入力'} value={currentCanvasPlan?.mapPositionAddress} />
|
||||
<div className="img-edit-wrap">
|
||||
<button className="img-edit-btn" onClick={handleMapImageDown}>
|
||||
<button className="img-edit-btn" onClick={refFileMethod === '2' ? handleMapImageDown : () => {}}>
|
||||
完了
|
||||
</button>
|
||||
</div>
|
||||
@ -80,7 +107,9 @@ export default function ImgLoad() {
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid-btn-wrap">
|
||||
<button className="btn-frame modal act">完了</button>
|
||||
<button className="btn-frame modal act" onClick={handleModal}>
|
||||
完了
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,84 +0,0 @@
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import WithDraggable from '@/components/common/draggable/WithDraggable'
|
||||
import { usePopup } from '@/hooks/usePopup'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { contextPopupPositionState } from '@/store/popupAtom'
|
||||
import { useState } from 'react'
|
||||
|
||||
export default function AuxiliaryCopy(props) {
|
||||
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
||||
const { id, pos = contextPopupPosition } = props
|
||||
const { getMessage } = useMessage()
|
||||
const { closePopup } = usePopup()
|
||||
const [arrow1, setArrow1] = useState(null)
|
||||
const [arrow2, setArrow2] = useState(null)
|
||||
return (
|
||||
<WithDraggable isShow={true} pos={pos}>
|
||||
<div className={`modal-pop-wrap xm mount`}>
|
||||
<div className="modal-head">
|
||||
<h1 className="title">{getMessage('modal.auxiliary.copy')} </h1>
|
||||
<button className="modal-close" onClick={() => closePopup(id)}>
|
||||
닫기
|
||||
</button>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<div className="grid-option-tit">{getMessage('modal.auxiliary.copy.info')}</div>
|
||||
<div className="grid-option-wrap">
|
||||
<div className="grid-option-box">
|
||||
<div className="move-form">
|
||||
<p className="mb5">{getMessage('length')}</p>
|
||||
<div className="input-move-wrap mb5">
|
||||
<div className="input-move">
|
||||
<input type="text" className="input-origin" defaultValue={910} />
|
||||
</div>
|
||||
<span>mm</span>
|
||||
<div className="direction-move-wrap">
|
||||
<button
|
||||
className={`direction up ${arrow1 === '↑' ? 'act' : ''}`}
|
||||
onClick={() => {
|
||||
setArrow1('↑')
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' }))
|
||||
}}
|
||||
></button>
|
||||
<button
|
||||
className={`direction down ${arrow1 === '↓' ? 'act' : ''}`}
|
||||
onClick={() => {
|
||||
setArrow1('↓')
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' }))
|
||||
}}
|
||||
></button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="input-move-wrap">
|
||||
<div className="input-move">
|
||||
<input type="text" className="input-origin" defaultValue={910} />
|
||||
</div>
|
||||
<span>mm</span>
|
||||
<div className="direction-move-wrap">
|
||||
<button
|
||||
className={`direction left ${arrow2 === '←' ? 'act' : ''}`}
|
||||
onClick={() => {
|
||||
setArrow2('←')
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowLeft' }))
|
||||
}}
|
||||
></button>
|
||||
<button
|
||||
className={`direction right ${arrow2 === '→' ? 'act' : ''}`}
|
||||
onClick={() => {
|
||||
setArrow2('→')
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowRight' }))
|
||||
}}
|
||||
></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid-btn-wrap">
|
||||
<button className="btn-frame modal act">{getMessage('modal.common.save')}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</WithDraggable>
|
||||
)
|
||||
}
|
||||
@ -1,37 +1,62 @@
|
||||
'use client'
|
||||
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import WithDraggable from '@/components/common/draggable/WithDraggable'
|
||||
import { usePopup } from '@/hooks/usePopup'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { contextPopupPositionState } from '@/store/popupAtom'
|
||||
import { usePopup } from '@/hooks/usePopup'
|
||||
import { useState } from 'react'
|
||||
import { currentObjectState } from '@/store/canvasAtom'
|
||||
import { useLine } from '@/hooks/useLine'
|
||||
import { useAuxiliaryDrawing } from '@/hooks/roofcover/useAuxiliaryDrawing'
|
||||
|
||||
export default function AuxiliaryMove(props) {
|
||||
export default function AuxiliaryEdit(props) {
|
||||
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
|
||||
const { id, pos = contextPopupPosition } = props
|
||||
const { id, pos = contextPopupPosition, type } = props
|
||||
const { getMessage } = useMessage()
|
||||
const { closePopup } = usePopup()
|
||||
const { move, copy } = useAuxiliaryDrawing()
|
||||
const [verticalSize, setVerticalSize] = useState('0')
|
||||
const [horizonSize, setHorizonSize] = useState('0')
|
||||
const [arrow1, setArrow1] = useState(null)
|
||||
const [arrow2, setArrow2] = useState(null)
|
||||
const { addLine, removeLine } = useLine()
|
||||
const currentObject = useRecoilValue(currentObjectState)
|
||||
const handleSave = () => {
|
||||
if (type === 'copy') {
|
||||
if (currentObject) {
|
||||
copy(
|
||||
currentObject,
|
||||
arrow2 === '←' ? Number(horizonSize) * -1 : Number(horizonSize),
|
||||
arrow1 === '↑' ? Number(verticalSize) * -1 : Number(verticalSize),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
move(
|
||||
currentObject,
|
||||
arrow2 === '←' ? Number(horizonSize) * -1 : Number(horizonSize),
|
||||
arrow1 === '↑' ? Number(verticalSize) * -1 : Number(verticalSize),
|
||||
)
|
||||
}
|
||||
|
||||
closePopup(id)
|
||||
}
|
||||
return (
|
||||
<WithDraggable isShow={true} pos={pos}>
|
||||
<div className={`modal-pop-wrap xm mount`}>
|
||||
<div className="modal-head">
|
||||
<h1 className="title">{getMessage('modal.auxiliary.move')}</h1>
|
||||
<h1 className="title">{getMessage(type === 'copy' ? 'modal.auxiliary.copy' : 'modal.auxiliary.move')} </h1>
|
||||
<button className="modal-close" onClick={() => closePopup(id)}>
|
||||
닫기
|
||||
</button>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<div className="grid-option-tit">{getMessage('modal.auxiliary.move.info')}</div>
|
||||
<div className="grid-option-tit">{getMessage(type === 'copy' ? 'modal.auxiliary.copy.info' : 'modal.auxiliary.move.info')}</div>
|
||||
<div className="grid-option-wrap">
|
||||
<div className="grid-option-box">
|
||||
<div className="move-form">
|
||||
<p className="mb5">{getMessage('length')}</p>
|
||||
<div className="input-move-wrap mb5">
|
||||
<div className="input-move">
|
||||
<input type="text" className="input-origin" defaultValue={910} />
|
||||
<input type="text" className="input-origin" value={verticalSize} onChange={(e) => setVerticalSize(e.target.value)} />
|
||||
</div>
|
||||
<span>mm</span>
|
||||
<div className="direction-move-wrap">
|
||||
@ -53,7 +78,7 @@ export default function AuxiliaryMove(props) {
|
||||
</div>
|
||||
<div className="input-move-wrap">
|
||||
<div className="input-move">
|
||||
<input type="text" className="input-origin" defaultValue={910} />
|
||||
<input type="text" className="input-origin" value={horizonSize} onChange={(e) => setHorizonSize(e.target.value)} />
|
||||
</div>
|
||||
<span>mm</span>
|
||||
<div className="direction-move-wrap">
|
||||
@ -77,7 +102,9 @@ export default function AuxiliaryMove(props) {
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid-btn-wrap">
|
||||
<button className="btn-frame modal act">{getMessage('modal.common.save')}</button>
|
||||
<button className="btn-frame modal act" onClick={handleSave}>
|
||||
{getMessage('modal.common.save')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -116,8 +116,6 @@ export default function StuffSearchCondition() {
|
||||
schAddress: address,
|
||||
schObjectName: objectName,
|
||||
schDispCompanyName: dispCompanyName,
|
||||
// schSelSaleStoreId: stuffSearch?.schSelSaleStoreId,
|
||||
// schOtherSelSaleStoreId: stuffSearch?.schOtherSelSaleStoreId,
|
||||
schSelSaleStoreId: schSelSaleStoreId,
|
||||
schOtherSelSaleStoreId: otherSaleStoreId,
|
||||
schReceiveUser: receiveUser,
|
||||
@ -315,8 +313,10 @@ export default function StuffSearchCondition() {
|
||||
} else {
|
||||
//X누름
|
||||
//화면에선 지우는데 리코일은 조회누르지 않으면 보존
|
||||
// setSchSelSaleStoreId('') //값이 안비워짐..
|
||||
setSchSelSaleStoreId(null)
|
||||
setSchSelSaleStoreId('')
|
||||
setOtherSaleStoreId('')
|
||||
stuffSearch.schSelSaleStoreId = ''
|
||||
stuffSearch.schOtherSelSaleStoreId = ''
|
||||
|
||||
//2차점 판매점목록비우기
|
||||
setOtherSaleStoreList([])
|
||||
|
||||
@ -28,9 +28,7 @@ export default function StuffSubHeader({ type }) {
|
||||
const param = {
|
||||
pid: '1',
|
||||
}
|
||||
//확인필요
|
||||
const url = `/floor-plan?${queryStringFormatter(param)}`
|
||||
console.log(url)
|
||||
router.push(url)
|
||||
}
|
||||
|
||||
@ -107,7 +105,7 @@ export default function StuffSubHeader({ type }) {
|
||||
<span>{getMessage('header.menus.management')}</span>
|
||||
</li>
|
||||
<li className="location-item">
|
||||
<span>{getMessage('header.menus.management.newStuff')}</span>
|
||||
<span>{getMessage('header.menus.management.detail')}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</>
|
||||
|
||||
@ -41,6 +41,7 @@ export default function Simulator() {
|
||||
|
||||
// 차트 관련
|
||||
const [chartData, setChartData] = useState([])
|
||||
|
||||
const data = {
|
||||
labels: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
|
||||
datasets: [
|
||||
@ -118,13 +119,30 @@ export default function Simulator() {
|
||||
// 파워컨디셔너 조회
|
||||
const [pcsInfoList, setPcsInfoList] = useState([])
|
||||
|
||||
// 타입별 list 조회
|
||||
const [hatsudenryouAll, setHatsudenryouAll] = useState([])
|
||||
const [hatsudenryouAllSnow, setHatsudenryouAllSnow] = useState([])
|
||||
const [hatsudenryouPeakcutAll, setHatsudenryouPeakcutAll] = useState([])
|
||||
const [hatsudenryouPeakcutAllSnow, setHatsudenryouPeakcutAllSnow] = useState([])
|
||||
|
||||
const fetchObjectDetail = async (objectNo) => {
|
||||
const apiUrl = `/api/pwrGnrSimulation/calculations?objectNo=${objectNo}&planNo=${plan?.id}`
|
||||
|
||||
const resultData = await get({ url: apiUrl })
|
||||
if (resultData) {
|
||||
setObjectDetail(resultData)
|
||||
if (resultData.frcPwrGnrList) {
|
||||
setChartData(resultData.frcPwrGnrList)
|
||||
if (resultData.hatsudenryouAll) {
|
||||
setHatsudenryouAll(resultData.hatsudenryouAll)
|
||||
}
|
||||
if (resultData.hatsudenryouAllSnow) {
|
||||
setHatsudenryouAllSnow(resultData.hatsudenryouAllSnow)
|
||||
}
|
||||
if (resultData.hatsudenryouPeakcutAll) {
|
||||
setHatsudenryouPeakcutAll(resultData.hatsudenryouPeakcutAll)
|
||||
}
|
||||
if (resultData.hatsudenryouPeakcutAllSnow) {
|
||||
setHatsudenryouPeakcutAllSnow(resultData.hatsudenryouPeakcutAllSnow)
|
||||
setChartData(resultData.hatsudenryouPeakcutAllSnow)
|
||||
}
|
||||
if (resultData.pcsList) {
|
||||
setPcsInfoList(resultData.pcsList)
|
||||
@ -148,6 +166,26 @@ export default function Simulator() {
|
||||
})
|
||||
}
|
||||
|
||||
// 차트 데이터 변경 시, list type 셋팅
|
||||
const [pwrGnrSimType, setPwrGnrSimType] = useState('D')
|
||||
const handleChartChangeData = (type) => {
|
||||
setPwrGnrSimType(type)
|
||||
switch (type) {
|
||||
case 'A':
|
||||
setChartData(hatsudenryouAll)
|
||||
break
|
||||
case 'B':
|
||||
setChartData(hatsudenryouAllSnow)
|
||||
break
|
||||
case 'C':
|
||||
setChartData(hatsudenryouPeakcutAll)
|
||||
break
|
||||
case 'D':
|
||||
setChartData(hatsudenryouPeakcutAllSnow)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="sub-content estimate">
|
||||
<div className="sub-content-inner">
|
||||
@ -176,7 +214,7 @@ export default function Simulator() {
|
||||
{/* 연간예측발전량 */}
|
||||
<div className="estimate-box">
|
||||
<div className="estimate-tit">{getMessage('simulator.title.sub4')}</div>
|
||||
<div className="estimate-name">{objectDetail.anlFrcsGnrt ? convertNumberToPriceDecimal(objectDetail.anlFrcsGnrt) : ''}</div>
|
||||
<div className="estimate-name">{chartData[chartData.length - 1]}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="estimate-list-wrap">
|
||||
@ -208,6 +246,22 @@ export default function Simulator() {
|
||||
<div className="chart-inner">
|
||||
<div className="sub-table-box">
|
||||
<div className="chart-box">
|
||||
{/* chart */}
|
||||
<div className="select-wrap">
|
||||
<select
|
||||
style={{ width: '30%' }}
|
||||
className="select-light"
|
||||
defaultValue={`D`}
|
||||
onChange={(e) => {
|
||||
handleChartChangeData(e.target.value)
|
||||
}}
|
||||
>
|
||||
<option value={`A`}>積雪考慮なし(ピークカットなし発電量)</option>
|
||||
<option value={`B`}>積雪考慮なし(ピークカットあり発電量)</option>
|
||||
<option value={`C`}>積雪考慮あり(ピークカットなし発電量)</option>
|
||||
<option value={`D`}>積雪考慮あり(ピークカットあり発電量)</option>
|
||||
</select>
|
||||
</div>
|
||||
<Bar ref={chartRef} data={data} options={options} />
|
||||
</div>
|
||||
<div className="table-box-title-wrap">
|
||||
@ -239,7 +293,7 @@ export default function Simulator() {
|
||||
{chartData.length > 0 ? (
|
||||
<tr>
|
||||
{chartData.map((data) => (
|
||||
<td key={data}>{convertNumberToPriceDecimal(data)}</td>
|
||||
<td key={data}>{data}</td>
|
||||
))}
|
||||
</tr>
|
||||
) : (
|
||||
|
||||
@ -1,8 +1,30 @@
|
||||
import { menuNumberState } from '@/store/menuAtom'
|
||||
import { useRecoilState } from 'recoil'
|
||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||
import { useEffect } from 'react'
|
||||
import { canvasState } from '@/store/canvasAtom'
|
||||
import { usePolygon } from '@/hooks/usePolygon'
|
||||
import { POLYGON_TYPE } from '@/common/common'
|
||||
|
||||
export const useCanvasMenu = () => {
|
||||
const [menuNumber, setMenuNumber] = useRecoilState(menuNumberState)
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
const { drawDirectionArrow } = usePolygon()
|
||||
|
||||
useEffect(() => {
|
||||
/*
|
||||
* 모듈,회로 구성을 벗어나면 방향 표시 초기화 필요
|
||||
* */
|
||||
if (!canvas) return
|
||||
if (![4, 5].includes(menuNumber)) {
|
||||
canvas
|
||||
.getObjects()
|
||||
.filter((obj) => obj.name === POLYGON_TYPE.ROOF)
|
||||
.forEach((obj) => {
|
||||
obj.set('moduleCompass', null)
|
||||
drawDirectionArrow(obj)
|
||||
})
|
||||
}
|
||||
}, [menuNumber])
|
||||
|
||||
return {
|
||||
menuNumber,
|
||||
|
||||
@ -6,27 +6,11 @@ import { estimateState, floorPlanObjectState } from '@/store/floorPlanObjectAtom
|
||||
import { isObjectNotEmpty } from '@/util/common-utils'
|
||||
import { SessionContext } from '@/app/SessionProvider'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
|
||||
const reducer = (prevState, nextState) => {
|
||||
return { ...prevState, ...nextState }
|
||||
}
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { FloorPlanContext } from '@/app/floor-plan/FloorPlanProvider'
|
||||
|
||||
// Constants
|
||||
const ESTIMATE_API_ENDPOINT = '/api/estimates' // API 엔드포인트 정의
|
||||
|
||||
const defaultEstimateData = {
|
||||
estimateDate: new Date(), //견적일
|
||||
charger: '', //담당자
|
||||
objectName: '', //안건명
|
||||
objectNameOmit: '', //경칭코드
|
||||
estimateType: '', //주문분류
|
||||
remarks: '', //비고
|
||||
estimateOption: '', //견적특이사항
|
||||
itemList: [],
|
||||
fileList: [],
|
||||
fileFlg: '0', //후일 자료 제출 (체크 1 노체크 0)
|
||||
priceCd: '',
|
||||
}
|
||||
const ESTIMATE_API_ENDPOINT = '/api/estimate' // API 엔드포인트 정의
|
||||
|
||||
// Helper functions
|
||||
const updateItemInList = (itemList, dispOrder, updates) => {
|
||||
@ -34,6 +18,7 @@ const updateItemInList = (itemList, dispOrder, updates) => {
|
||||
}
|
||||
|
||||
export const useEstimateController = (planNo) => {
|
||||
const router = useRouter()
|
||||
const { session } = useContext(SessionContext)
|
||||
const globalLocaleState = useRecoilValue(globalLocaleStore)
|
||||
const objectRecoil = useRecoilValue(floorPlanObjectState)
|
||||
@ -41,31 +26,32 @@ export const useEstimateController = (planNo) => {
|
||||
|
||||
const { getMessage } = useMessage()
|
||||
|
||||
const { get, post, promisePost } = useAxios(globalLocaleState)
|
||||
const { promiseGet, post, promisePost } = useAxios(globalLocaleState)
|
||||
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const [state, setState] = useReducer(reducer, defaultEstimateData)
|
||||
const { estimateContextState, setEstimateContextState } = useContext(FloorPlanContext)
|
||||
|
||||
useEffect(() => {
|
||||
if (!isLoading) {
|
||||
if (planNo && !isLoading) {
|
||||
if (objectRecoil.floorPlanObjectNo && planNo) {
|
||||
fetchSetting()
|
||||
fetchSetting(objectRecoil.floorPlanObjectNo, planNo)
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
// 상세 조회
|
||||
const fetchSetting = async () => {
|
||||
const fetchSetting = async (objectNo, planNo) => {
|
||||
try {
|
||||
await get({ url: `/api/estimate/${objectRecoil.floorPlanObjectNo}/${planNo}/detail` }).then((res) => {
|
||||
if (isObjectNotEmpty(res)) {
|
||||
if (res.itemList.length > 0) {
|
||||
res.itemList.map((item) => {
|
||||
item.delFlg = '0'
|
||||
})
|
||||
await promiseGet({ url: `/api/estimate/${objectNo}/${planNo}/detail` }).then((res) => {
|
||||
if (res.status === 200) {
|
||||
if (isObjectNotEmpty(res.data)) {
|
||||
if (res.data.itemList.length > 0) {
|
||||
res.data.itemList.map((item) => {
|
||||
item.delFlg = '0'
|
||||
})
|
||||
}
|
||||
setEstimateContextState(res.data)
|
||||
}
|
||||
|
||||
setState(res)
|
||||
}
|
||||
})
|
||||
setIsLoading(true)
|
||||
@ -76,18 +62,17 @@ export const useEstimateController = (planNo) => {
|
||||
}
|
||||
|
||||
const updateItem = (dispOrder, updates) => {
|
||||
setState({
|
||||
itemList: updateItemInList(state.itemList, dispOrder, updates),
|
||||
setEstimateContextState({
|
||||
itemList: updateItemInList(estimateContextState.itemList, dispOrder, updates),
|
||||
})
|
||||
}
|
||||
|
||||
const addItem = () => {
|
||||
// const newItemDispOrder = (Math.max(...state.itemList.map((item) => item.dispOrder)) / 100 + 1) * 100
|
||||
let newItemDispOrder = Math.max(...state.itemList.map((item) => item.dispOrder))
|
||||
let newItemDispOrder = Math.max(...estimateContextState.itemList.map((item) => item.dispOrder))
|
||||
newItemDispOrder = (Math.floor(newItemDispOrder / 100) + 1) * 100
|
||||
setState({
|
||||
setEstimateContextState({
|
||||
itemList: [
|
||||
...state.itemList,
|
||||
...estimateContextState.itemList,
|
||||
{
|
||||
objectNo: objectRecoil.floorPlanObjectNo,
|
||||
planNo: planNo,
|
||||
@ -109,8 +94,8 @@ export const useEstimateController = (planNo) => {
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setEstimateData({ ...state, userId: session.userId, sapSalesStoreCd: session.custCd })
|
||||
}, [state])
|
||||
setEstimateData({ ...estimateContextState, userId: session.userId, sapSalesStoreCd: session.custCd })
|
||||
}, [estimateContextState])
|
||||
|
||||
// 첨부파일 다운로드
|
||||
const handleEstimateFileDownload = async (originFile) => {
|
||||
@ -141,24 +126,41 @@ export const useEstimateController = (planNo) => {
|
||||
const handleEstimateSubmit = async () => {
|
||||
//0. 필수체크
|
||||
let flag = true
|
||||
if (estimateData.charger.trim().length === 0) {
|
||||
flag = false
|
||||
return alert(getMessage('estimate.detail.save.requiredCharger'))
|
||||
}
|
||||
|
||||
if (estimateData.objectName.trim().length === 0) {
|
||||
flag = false
|
||||
return alert(getMessage('estimate.detail.save.requiredObjectName'))
|
||||
}
|
||||
|
||||
if (isNaN(Date.parse(estimateData.estimateDate))) {
|
||||
flag = false
|
||||
return alert(getMessage('estimate.detail.save.requiredEstimateDate'))
|
||||
}
|
||||
|
||||
// console.log('첨부파일:::::', estimateData.fileList)
|
||||
//첨부파일을 첨부안했는데
|
||||
//아이템 fileUploadFlg가1(첨부파일 필수)이 1개라도 있는데 후일 자료 제출(fileFlg) 체크안했으면(0) alert 저장안돼
|
||||
let fileFlg = true
|
||||
if (estimateData.fileList.length < 1) {
|
||||
if (estimateData.itemList.length > 1) {
|
||||
estimateData.itemList.map((row) => {
|
||||
if (row.fileUploadFlg === '1') {
|
||||
if (estimateData.fileFlg === '0') {
|
||||
alert(getMessage('estimate.detail.save.requiredMsg'))
|
||||
flag = false
|
||||
if (fileFlg) {
|
||||
if (estimateData.fileFlg === '0') {
|
||||
fileFlg = false
|
||||
return alert(getMessage('estimate.detail.save.requiredFileUpload'))
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (flag) {
|
||||
if (flag && fileFlg) {
|
||||
//1. 첨부파일 저장시작
|
||||
const formData = new FormData()
|
||||
formData.append('file', estimateData.fileList)
|
||||
@ -184,24 +186,62 @@ export const useEstimateController = (planNo) => {
|
||||
}
|
||||
|
||||
console.log('최종 정보::;', estimateData)
|
||||
console.log('최종 남은 아이템정보:::', estimateData.itemList)
|
||||
|
||||
//2. 상세데이터 저장
|
||||
// return
|
||||
await promisePost({ url: `${ESTIMATE_API_ENDPOINT}/save-estimate`, data: estimateData }).then((res) => {
|
||||
if (res) {
|
||||
if (res.status === 201) {
|
||||
alert(getMessage('estimate.detail.save.alertMsg'))
|
||||
//어디로 보낼지
|
||||
fetchSetting(objectRecoil.floorPlanObjectNo, estimateData.planNo)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 견적서 복사버튼
|
||||
* (견적서 번호(estimateData.docNo)가 생성된 이후 버튼 활성화 )
|
||||
* T01관리자 계정 및 1차판매점에게만 제공
|
||||
*/
|
||||
const handleEstimateCopy = async (sendPlanNo, copyReceiveUser, saleStoreId, otherSaleStoreId) => {
|
||||
if (saleStoreId === '') {
|
||||
return alert(getMessage('estimate.detail.productFeaturesPopup.requiredStoreId'))
|
||||
}
|
||||
|
||||
if (copyReceiveUser.trim().length === 0) {
|
||||
return alert(getMessage('estimate.detail.productFeaturesPopup.requiredReceiveUser'))
|
||||
}
|
||||
const params = {
|
||||
saleStoreId: session.storeId,
|
||||
sapSalesStoreCd: session.custCd,
|
||||
objectNo: objectRecoil.floorPlanObjectNo,
|
||||
planNo: sendPlanNo,
|
||||
copySaleStoreId: otherSaleStoreId ? otherSaleStoreId : saleStoreId,
|
||||
copyReceiveUser: copyReceiveUser,
|
||||
userId: session.userId,
|
||||
}
|
||||
|
||||
// return
|
||||
await promisePost({ url: '/api/estimate/save-estimate-copy', data: params }).then((res) => {
|
||||
if (res.status === 201) {
|
||||
if (isObjectNotEmpty(res.data)) {
|
||||
let newObjectNo = res.data.objectNo
|
||||
alert(getMessage('estimate.detail.estimateCopyPopup.copy.alertMessage'))
|
||||
router.push(`/management/stuff/detail?objectNo=${newObjectNo.toString()}`, { scroll: false })
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
state,
|
||||
setState,
|
||||
estimateContextState,
|
||||
setEstimateContextState,
|
||||
updateItem,
|
||||
addItem,
|
||||
handleEstimateSubmit,
|
||||
fetchSetting,
|
||||
handleEstimateFileDownload,
|
||||
handleEstimateCopy,
|
||||
}
|
||||
}
|
||||
|
||||
@ -47,6 +47,8 @@ export function useModuleBasicSetting() {
|
||||
flowDirection: roof.direction,
|
||||
})
|
||||
|
||||
setupSurface.setViewLengthText(false)
|
||||
|
||||
canvas.add(setupSurface)
|
||||
//지붕면 선택 금지
|
||||
roof.set({
|
||||
@ -567,6 +569,7 @@ export function useModuleBasicSetting() {
|
||||
lineCol: col,
|
||||
lineRow: row,
|
||||
})
|
||||
tempModule.setViewLengthText(false)
|
||||
canvas?.add(tempModule)
|
||||
moduleSetupArray.push(tempModule)
|
||||
}
|
||||
@ -596,6 +599,106 @@ export function useModuleBasicSetting() {
|
||||
})
|
||||
|
||||
setModuleIsSetup(moduleSetupArray)
|
||||
|
||||
console.log(calculateForApi(moduleSetupArray))
|
||||
}
|
||||
|
||||
const calculateForApi = (moduleSetupArray) => {
|
||||
const centerPoints = []
|
||||
moduleSetupArray.forEach((module, index) => {
|
||||
module.tempIndex = index
|
||||
const { x, y } = module.getCenterPoint()
|
||||
const { width, height } = module
|
||||
centerPoints.push({ x, y, width, height })
|
||||
const circle = new fabric.Circle({
|
||||
radius: 5,
|
||||
fill: 'red',
|
||||
name: 'redCircle',
|
||||
left: x - 5,
|
||||
top: y - 5,
|
||||
index: index,
|
||||
selectable: false,
|
||||
})
|
||||
canvas.add(circle)
|
||||
})
|
||||
|
||||
//완전 노출 하면
|
||||
let exposedBottom = 0
|
||||
// 반 노출 하면
|
||||
let exposedHalfBottom = 0
|
||||
// 완전 노출 상면
|
||||
let exposedTop = 0
|
||||
//반 노출 상면
|
||||
let exposedHalfTop = 0
|
||||
// 완전 접면
|
||||
let touchDimension = 0
|
||||
//반접면
|
||||
let halfTouchDimension = 0
|
||||
|
||||
// 노출하면 체크
|
||||
centerPoints.forEach((centerPoint, index) => {
|
||||
const { x, y, width, height } = centerPoint
|
||||
// centerPoints중에 현재 centerPoint와 x값이 같고, y값이 y-height값과 같은 centerPoint가 있는지 확인
|
||||
const bottomCell = centerPoints.filter((centerPoint) => centerPoint.x === x && Math.abs(centerPoint.y - (y + height)) < 2)
|
||||
if (bottomCell.length === 1) {
|
||||
touchDimension++
|
||||
return
|
||||
}
|
||||
|
||||
// 바로 아래에 셀이 없는 경우 물떼세 배치가 왼쪽 되어있는 셀을 찾는다.
|
||||
const leftBottomCnt = centerPoints.filter(
|
||||
(centerPoint) => Math.abs(centerPoint.x - (x - width / 2)) < 2 && Math.abs(centerPoint.y - (y + height)) < 2,
|
||||
).length
|
||||
const rightBottomCnt = centerPoints.filter(
|
||||
(centerPoint) => Math.abs(centerPoint.x - (x + width / 2)) < 2 && Math.abs(centerPoint.y - (y + height)) < 2,
|
||||
).length
|
||||
if (leftBottomCnt + rightBottomCnt === 2) {
|
||||
touchDimension++
|
||||
return
|
||||
}
|
||||
if (leftBottomCnt + rightBottomCnt === 1) {
|
||||
halfTouchDimension++
|
||||
exposedHalfBottom++
|
||||
return
|
||||
}
|
||||
if (leftBottomCnt + rightBottomCnt === 0) {
|
||||
exposedBottom++
|
||||
return
|
||||
}
|
||||
})
|
||||
// 노출상면 체크
|
||||
|
||||
centerPoints.forEach((centerPoint, index) => {
|
||||
const { x, y, width, height } = centerPoint
|
||||
const topCell = centerPoints.filter((centerPoint) => centerPoint.x === x && Math.abs(centerPoint.y - (y - height)) < 2)
|
||||
if (topCell.length === 1) {
|
||||
return
|
||||
}
|
||||
|
||||
const leftTopCnt = centerPoints.filter(
|
||||
(centerPoint) => Math.abs(centerPoint.x - (x - width) < 2) && Math.abs(centerPoint.y - (y - height)) < 2,
|
||||
).length
|
||||
const rightTopCnt = centerPoints.filter(
|
||||
(centerPoint) => Math.abs(centerPoint.x - (x + width / 2) < 2) && Math.abs(centerPoint.y - (y - height)) < 2,
|
||||
).length
|
||||
|
||||
if (leftTopCnt + rightTopCnt === 1) {
|
||||
exposedHalfTop++
|
||||
return
|
||||
}
|
||||
if (leftTopCnt + rightTopCnt === 0) {
|
||||
exposedTop++
|
||||
return
|
||||
}
|
||||
})
|
||||
return {
|
||||
exposedBottom,
|
||||
exposedHalfBottom,
|
||||
exposedTop,
|
||||
exposedHalfTop,
|
||||
touchDimension,
|
||||
halfTouchDimension,
|
||||
}
|
||||
}
|
||||
|
||||
const coordToTurfPolygon = (points) => {
|
||||
|
||||
@ -15,14 +15,12 @@ import {
|
||||
outerLineLength2State,
|
||||
outerLineTypeState,
|
||||
} from '@/store/outerLineAtom'
|
||||
import { calculateIntersection, distanceBetweenPoints, findClosestPoint, isPointOnLine, polygonToTurfPolygon } from '@/util/canvas-util'
|
||||
import { calculateIntersection, distanceBetweenPoints, findClosestPoint, isPointOnLine } from '@/util/canvas-util'
|
||||
import { fabric } from 'fabric'
|
||||
import { useAdsorptionPoint } from '@/hooks/useAdsorptionPoint'
|
||||
import { useSwal } from '@/hooks/useSwal'
|
||||
import { booleanPointInPolygon } from '@turf/turf'
|
||||
import { usePopup } from '@/hooks/usePopup'
|
||||
import { calculateAngle, isSamePoint } from '@/util/qpolygon-utils'
|
||||
import { QPolygon } from '@/components/fabric/QPolygon'
|
||||
import { POLYGON_TYPE } from '@/common/common'
|
||||
|
||||
// 보조선 작성
|
||||
@ -122,6 +120,38 @@ export function useAuxiliaryDrawing(id) {
|
||||
setOuterLineDiagonalLength(0)
|
||||
}
|
||||
|
||||
const move = (object, x, y) => {
|
||||
const line = copy(object, x, y)
|
||||
canvas.remove(object)
|
||||
canvas.setActiveObject(line)
|
||||
}
|
||||
|
||||
const copy = (object, x, y) => {
|
||||
return addLine([object.x1 + x, object.y1 + y, object.x2 + x, object.y2 + y], {
|
||||
stroke: 'red',
|
||||
strokeWidth: 1,
|
||||
selectable: true,
|
||||
name: 'auxiliaryLine',
|
||||
})
|
||||
}
|
||||
|
||||
const addBisectorLine = (target) => {
|
||||
const slope = (target.y2 - target.y1) / (target.x2 - target.x1)
|
||||
const bisectorSlope = -1 / slope
|
||||
const length = target.length
|
||||
const dx = length / Math.sqrt(1 + bisectorSlope * bisectorSlope)
|
||||
const dy = bisectorSlope * dx
|
||||
const endX = (target.x1 + target.x2) / 2
|
||||
const endY = (target.y1 + target.y2) / 2
|
||||
|
||||
addLine([dx, dy, endX, endY], {
|
||||
stroke: 'red',
|
||||
strokeWidth: 1,
|
||||
selectable: true,
|
||||
name: 'auxiliaryLine',
|
||||
})
|
||||
}
|
||||
|
||||
const keydown = {
|
||||
outerLine: (e) => {
|
||||
if (mousePointerArr.current.length === 0) {
|
||||
@ -130,7 +160,7 @@ export function useAuxiliaryDrawing(id) {
|
||||
// 포커스가 length1에 있지 않으면 length1에 포커스를 줌
|
||||
const activeElem = document.activeElement
|
||||
if (activeElem !== length1Ref.current) {
|
||||
length1Ref.current.focus()
|
||||
length1Ref?.current?.focus()
|
||||
}
|
||||
|
||||
const key = e.key
|
||||
@ -180,7 +210,7 @@ export function useAuxiliaryDrawing(id) {
|
||||
|
||||
const activeElem = document.activeElement
|
||||
if (activeElem !== length1Ref.current && activeElem !== length2Ref.current) {
|
||||
length1Ref.current.focus()
|
||||
length1Ref?.current?.focus()
|
||||
}
|
||||
|
||||
switch (key) {
|
||||
@ -871,5 +901,8 @@ export function useAuxiliaryDrawing(id) {
|
||||
handleRollback,
|
||||
buttonAct,
|
||||
setButtonAct,
|
||||
move,
|
||||
copy,
|
||||
addBisectorLine,
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { canvasState, currentObjectState } from '@/store/canvasAtom'
|
||||
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
|
||||
import { canvasState, currentMenuState, currentObjectState } from '@/store/canvasAtom'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { setSurfaceShapePattern } from '@/util/canvas-util'
|
||||
import { useSwal } from '@/hooks/useSwal'
|
||||
@ -10,6 +10,9 @@ import { POLYGON_TYPE } from '@/common/common'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import ActualSizeSetting from '@/components/floor-plan/modal/roofAllocation/ActualSizeSetting'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import useMenu from '@/hooks/common/useMenu'
|
||||
import { useCanvasMenu } from '@/hooks/common/useCanvasMenu'
|
||||
import { menuTypeState } from '@/store/menuAtom'
|
||||
|
||||
// 지붕면 할당
|
||||
export function useRoofAllocationSetting(id) {
|
||||
@ -21,6 +24,8 @@ export function useRoofAllocationSetting(id) {
|
||||
const { getMessage } = useMessage()
|
||||
const currentObject = useRecoilValue(currentObjectState)
|
||||
const { swalFire } = useSwal()
|
||||
const { setMenuNumber } = useCanvasMenu()
|
||||
const setMenuType = useSetRecoilState(menuTypeState)
|
||||
const roofMaterials = [
|
||||
{
|
||||
id: 'A',
|
||||
@ -132,6 +137,9 @@ export function useRoofAllocationSetting(id) {
|
||||
setValues(values.filter((value) => value.id !== id))
|
||||
}
|
||||
|
||||
const { handleMenu } = useMenu()
|
||||
const [currentMenu, setCurrentMenu] = useRecoilState(currentMenuState)
|
||||
|
||||
// 선택한 지붕재로 할당
|
||||
const handleSave = () => {
|
||||
// 모두 actualSize 있으면 바로 적용 없으면 actualSize 설정
|
||||
@ -213,6 +221,8 @@ export function useRoofAllocationSetting(id) {
|
||||
})
|
||||
setEditingLines([])
|
||||
closeAll()
|
||||
setMenuNumber(3)
|
||||
setMenuType('surface')
|
||||
}
|
||||
|
||||
const setLineSize = (id, size) => {
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
|
||||
import { currentMenuState, currentObjectState } from '@/store/canvasAtom'
|
||||
import { canvasState, currentMenuState, currentObjectState } from '@/store/canvasAtom'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { MENU } from '@/common/common'
|
||||
import AuxiliaryMove from '@/components/floor-plan/modal/auxiliary/AuxiliaryMove'
|
||||
import AuxiliarySize from '@/components/floor-plan/modal/auxiliary/AuxiliarySize'
|
||||
import { usePopup } from '@/hooks/usePopup'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
@ -11,7 +10,7 @@ import GridCopy from '@/components/floor-plan/modal/grid/GridCopy'
|
||||
import ColorPickerModal from '@/components/common/color-picker/ColorPickerModal'
|
||||
import { gridColorState } from '@/store/gridAtom'
|
||||
import { contextPopupPositionState, contextPopupState } from '@/store/popupAtom'
|
||||
import AuxiliaryCopy from '@/components/floor-plan/modal/auxiliary/AuxiliaryCopy'
|
||||
import AuxiliaryEdit from '@/components/floor-plan/modal/auxiliary/AuxiliaryEdit'
|
||||
import SizeSetting from '@/components/floor-plan/modal/object/SizeSetting'
|
||||
import RoofMaterialSetting from '@/components/floor-plan/modal/object/RoofMaterialSetting'
|
||||
import DormerOffset from '@/components/floor-plan/modal/object/DormerOffset'
|
||||
@ -34,8 +33,10 @@ import CircuitNumberEdit from '@/components/floor-plan/modal/module/CircuitNumbe
|
||||
import { useObjectBatch } from '@/hooks/object/useObjectBatch'
|
||||
import { useSurfaceShapeBatch } from '@/hooks/surface/useSurfaceShapeBatch'
|
||||
import { fontSelector, globalFontAtom } from '@/store/fontAtom'
|
||||
import { useLine } from '@/hooks/useLine'
|
||||
|
||||
export function useContextMenu() {
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
const currentMenu = useRecoilValue(currentMenuState) // 현재 메뉴
|
||||
const setContextPopupPosition = useSetRecoilState(contextPopupPositionState) // 현재 메뉴
|
||||
const [contextMenu, setContextMenu] = useRecoilState(contextMenuListState) // 메뉴.object 별 context menu
|
||||
@ -53,6 +54,7 @@ export function useContextMenu() {
|
||||
const { moveObjectBatch } = useObjectBatch({})
|
||||
const { moveSurfaceShapeBatch } = useSurfaceShapeBatch()
|
||||
const [globalFont, setGlobalFont] = useRecoilState(globalFontAtom)
|
||||
const { addLine, removeLine } = useLine()
|
||||
const commonTextFont = useRecoilValue(fontSelector('commonText'))
|
||||
|
||||
const currentMenuSetting = () => {
|
||||
@ -130,22 +132,57 @@ export function useContextMenu() {
|
||||
id: 'auxiliaryMove',
|
||||
name: `${getMessage('contextmenu.auxiliary.move')}(M)`,
|
||||
shortcut: ['m', 'M'],
|
||||
component: <AuxiliaryMove id={popupId} />,
|
||||
component: <AuxiliaryEdit id={popupId} type={'move'} />,
|
||||
},
|
||||
{
|
||||
id: 'auxiliaryCopy',
|
||||
name: `${getMessage('contextmenu.auxiliary.copy')}(C)`,
|
||||
shortcut: ['c', 'C'],
|
||||
component: <AuxiliaryCopy id={popupId} />,
|
||||
component: <AuxiliaryEdit id={popupId} type={'copy'} />,
|
||||
},
|
||||
{
|
||||
id: 'auxiliaryRemove',
|
||||
shortcut: ['d', 'D'],
|
||||
name: `${getMessage('contextmenu.auxiliary.remove')}(D)`,
|
||||
fn: () => {
|
||||
canvas.remove(currentObject)
|
||||
canvas.discardActiveObject()
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'auxiliaryVerticalBisector',
|
||||
name: getMessage('contextmenu.auxiliary.vertical.bisector'),
|
||||
fn: () => {
|
||||
const slope = (currentObject.y2 - currentObject.y1) / (currentObject.x2 - currentObject.x1)
|
||||
const length = currentObject.length
|
||||
|
||||
let startX, startY, endX, endY
|
||||
if (slope === 0) {
|
||||
startX = endX = (currentObject.x1 + currentObject.x2) / 2
|
||||
startY = currentObject.y2 - length / 2
|
||||
endY = currentObject.y2 + length / 2
|
||||
} else if (slope === Infinity) {
|
||||
startX = currentObject.x1 - length / 2
|
||||
startY = endY = (currentObject.y1 + currentObject.y2) / 2
|
||||
endX = currentObject.x1 + length / 2
|
||||
} else {
|
||||
const bisectorSlope = -1 / slope
|
||||
const dx = length / 2 / Math.sqrt(1 + bisectorSlope * bisectorSlope)
|
||||
const dy = bisectorSlope * dx
|
||||
|
||||
startX = (currentObject.x1 + currentObject.x2) / 2 + dx
|
||||
startY = (currentObject.y1 + currentObject.y2) / 2 + dy
|
||||
endX = (currentObject.x1 + currentObject.x2) / 2 - dx
|
||||
endY = (currentObject.y1 + currentObject.y2) / 2 - dy
|
||||
}
|
||||
|
||||
addLine([startX, startY, endX, endY], {
|
||||
stroke: 'red',
|
||||
strokeWidth: 1,
|
||||
selectable: true,
|
||||
name: 'auxiliaryLine',
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'auxiliaryCut',
|
||||
@ -472,6 +509,7 @@ export function useContextMenu() {
|
||||
{
|
||||
id: 'removeAll',
|
||||
name: getMessage('contextmenu.remove.all'),
|
||||
fn: () => {},
|
||||
},
|
||||
],
|
||||
])
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
"header.menus.home": "ホームへv",
|
||||
"header.menus.management": "物品及び図面管理",
|
||||
"header.menus.management.newStuff": "新規 物件 登録",
|
||||
"header.menus.management.detail": "物件詳細",
|
||||
"header.menus.management.stuffList": "物件の状況",
|
||||
"header.menus.community": "コミュニティ",
|
||||
"header.menus.community.notice": "お知らせ",
|
||||
@ -160,7 +161,7 @@
|
||||
"plan.menu.estimate.docDown": "文書のダウンロード",
|
||||
"plan.menu.estimate.save": "保存",
|
||||
"plan.menu.estimate.reset": "初期化",
|
||||
"plan.menu.estimate.copy": "コピー",
|
||||
"plan.menu.estimate.copy": "見積書のコピー",
|
||||
"plan.menu.simulation": "発展シミュレーション",
|
||||
"plan.menu.simulation.excel": "Excel",
|
||||
"plan.menu.simulation.pdf": "PDF",
|
||||
@ -822,6 +823,8 @@
|
||||
"estimate.detail.objectName": "案件名",
|
||||
"estimate.detail.objectRemarks": "メモ",
|
||||
"estimate.detail.estimateType": "注文分類",
|
||||
"estimate.detail.estimateType.yjss": "住宅PKG",
|
||||
"estimate.detail.estimateType.yjod": "積上げ( YJOD )",
|
||||
"estimate.detail.roofCns": "屋根材・仕様施工",
|
||||
"estimate.detail.remarks": "備考",
|
||||
"estimate.detail.fileFlg": "後日資料提出",
|
||||
@ -838,8 +841,6 @@
|
||||
"estimate.detail.sepcialEstimateProductInfo.pkgUnitPrice": "住宅PKG単価 (W)",
|
||||
"estimate.detail.sepcialEstimateProductInfo.pkgWeight": "PKG容量 (Kw)",
|
||||
"estimate.detail.sepcialEstimateProductInfo.pkgPrice": "PKG金額",
|
||||
"estimate.detail.sepcialEstimateProductInfo.calcFormula1": "(モジュール容量 × 数量)÷1000",
|
||||
"estimate.detail.sepcialEstimateProductInfo.calcFormula2": "PKG単価 (W)×PKG容量(W)",
|
||||
"estimate.detail.header.showPrice": "価格表示",
|
||||
"estimate.detail.header.unitPrice": "定価",
|
||||
"estimate.detail.showPrice.pricingBtn": "Pricing",
|
||||
@ -875,11 +876,24 @@
|
||||
"estimate.detail.docPopup.schDrawingFlg.schDrawingFlg0": "含まない",
|
||||
"estimate.detail.docPopup.close": "閉じる",
|
||||
"estimate.detail.docPopup.docDownload": "文書のダウンロード",
|
||||
"estimate.detail.estimateCopyPopup.title": "見積もり",
|
||||
"estimate.detail.estimateCopyPopup.explane": "見積書をコピーする販売店を設定します。見積もりは定価にコピーされます.",
|
||||
"estimate.detail.estimateCopyPopup.label.saleStoreId": "一次販売店名 / ID",
|
||||
"estimate.detail.estimateCopyPopup.label.otherSaleStoreId": "二次販売店名 / ID",
|
||||
"estimate.detail.estimateCopyPopup.label.receiveUser": "担当者",
|
||||
"estimate.detail.estimateCopyPopup.close": "閉じる",
|
||||
"estimate.detail.estimateCopyPopup.copyBtn": "見積もり",
|
||||
"estimate.detail.estimateCopyPopup.copy.alertMessage": "見積書がコピーされました. コピーした商品情報に移動します.",
|
||||
"estimate.detail.productFeaturesPopup.title": "製品特異事項",
|
||||
"estimate.detail.productFeaturesPopup.close": "閉じる",
|
||||
"estimate.detail.productFeaturesPopup.requiredStoreId": "一次販売店は必須です.",
|
||||
"estimate.detail.productFeaturesPopup.requiredReceiveUser": "担当者は必須です.",
|
||||
"estimate.detail.save.alertMsg": "保存されている見積書で製品を変更した場合、図面や回路には反映されません.",
|
||||
"estimate.detail.save.requiredMsg": "ファイル添付が必須のアイテムがあります。ファイルを添付するか、後日添付をチェックしてください.",
|
||||
"estimate.detail.save.requiredFileUpload": "ファイル添付が必須のアイテムがあります。ファイルを添付するか、後日添付をチェックしてください.",
|
||||
"estimate.detail.save.requiredItem": "製品は1つ以上登録する必要があります.",
|
||||
"estimate.detail.save.requiredCharger": "担当者は必須です.",
|
||||
"estimate.detail.save.requiredObjectName": "案件名は必須です.",
|
||||
"estimate.detail.save.requiredEstimateDate": "見積日は必須です.",
|
||||
"estimate.detail.reset.confirmMsg": "保存した見積書情報が初期化され、図面情報が反映されます。本当に初期化しますか?",
|
||||
"simulator.title.sub1": "物件番号",
|
||||
"simulator.title.sub2": "作成日",
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
"header.menus.home": "Home",
|
||||
"header.menus.management": "물건 및 도면 관리",
|
||||
"header.menus.management.newStuff": "신규 물건 등록",
|
||||
"header.menus.management.detail": "물건 상세",
|
||||
"header.menus.management.stuffList": "물건 현황",
|
||||
"header.menus.community": "커뮤니티",
|
||||
"header.menus.community.notice": "공지",
|
||||
@ -164,7 +165,7 @@
|
||||
"plan.menu.estimate.docDown": "문서 다운로드",
|
||||
"plan.menu.estimate.save": "저장",
|
||||
"plan.menu.estimate.reset": "초기화",
|
||||
"plan.menu.estimate.copy": "복사",
|
||||
"plan.menu.estimate.copy": "견적서 복사",
|
||||
"plan.menu.simulation": "발전 시뮬레이션",
|
||||
"plan.menu.simulation.excel": "Excel",
|
||||
"plan.menu.simulation.pdf": "PDF",
|
||||
@ -832,6 +833,8 @@
|
||||
"estimate.detail.objectName": "안건명",
|
||||
"estimate.detail.objectRemarks": "메모",
|
||||
"estimate.detail.estimateType": "주문분류",
|
||||
"estimate.detail.estimateType.yjss": "住宅PKG",
|
||||
"estimate.detail.estimateType.yjod": "積上げ( YJOD )",
|
||||
"estimate.detail.roofCns": "지붕재・사양시공",
|
||||
"estimate.detail.remarks": "비고",
|
||||
"estimate.detail.fileFlg": "후일자료제출",
|
||||
@ -848,8 +851,6 @@
|
||||
"estimate.detail.sepcialEstimateProductInfo.pkgUnitPrice": "주택PKG단가 (W)",
|
||||
"estimate.detail.sepcialEstimateProductInfo.pkgWeight": "PKG 용량 (Kw)",
|
||||
"estimate.detail.sepcialEstimateProductInfo.pkgPrice": "PKG 금액",
|
||||
"estimate.detail.sepcialEstimateProductInfo.calcFormula1": "(모듈용량 * 수량)÷100",
|
||||
"estimate.detail.sepcialEstimateProductInfo.calcFormula2": "PKG단가(W) * PKG용량(W)",
|
||||
"estimate.detail.header.showPrice": "가격표시",
|
||||
"estimate.detail.header.unitPrice": "정가",
|
||||
"estimate.detail.showPrice.pricingBtn": "Pricing",
|
||||
@ -885,11 +886,24 @@
|
||||
"estimate.detail.docPopup.schDrawingFlg.schDrawingFlg0": "미포함",
|
||||
"estimate.detail.docPopup.close": "닫기",
|
||||
"estimate.detail.docPopup.docDownload": "문서 다운로드",
|
||||
"estimate.detail.estimateCopyPopup.title": "견적복사",
|
||||
"estimate.detail.estimateCopyPopup.explane": "견적서를 복사할 판매점을 설정하십시오. 견적서는 정가로 복사됩니다.",
|
||||
"estimate.detail.estimateCopyPopup.label.saleStoreId": "1차 판매점명 / ID",
|
||||
"estimate.detail.estimateCopyPopup.label.otherSaleStoreId": "2차 판매점명 / ID",
|
||||
"estimate.detail.estimateCopyPopup.label.receiveUser": "담당자",
|
||||
"estimate.detail.estimateCopyPopup.close": "닫기",
|
||||
"estimate.detail.estimateCopyPopup.copyBtn": "견적복사",
|
||||
"estimate.detail.estimateCopyPopup.copy.alertMessage": "견적서가 복사되었습니다. 복사된 물건정보로 이동합니다.",
|
||||
"estimate.detail.productFeaturesPopup.title": "제품특이사항",
|
||||
"estimate.detail.productFeaturesPopup.close": "닫기",
|
||||
"estimate.detail.productFeaturesPopup.requiredStoreId": "1차 판매점은 필수값 입니다.",
|
||||
"estimate.detail.productFeaturesPopup.requiredReceiveUser": "담당자는 필수값 입니다.",
|
||||
"estimate.detail.save.alertMsg": "저장되었습니다. 견적서에서 제품을 변경할 경우, 도면 및 회로에 반영되지 않습니다.",
|
||||
"estimate.detail.save.requiredMsg": "파일첨부가 필수인 아이템이 있습니다. 파일을 첨부하거나 후일첨부를 체크해주십시오.",
|
||||
"estimate.detail.save.requiredFileUpload": "파일첨부가 필수인 아이템이 있습니다. 파일을 첨부하거나 후일첨부를 체크해주십시오.",
|
||||
"estimate.detail.save.requiredItem": "제품은 1개이상 등록해야 됩니다.",
|
||||
"estimate.detail.save.requiredCharger": "담당자는 필수값 입니다.",
|
||||
"estimate.detail.save.requiredObjectName": "안건명은 필수값 입니다.",
|
||||
"estimate.detail.save.requiredEstimateDate": "견적일은 필수값 입니다.",
|
||||
"estimate.detail.reset.confirmMsg": "저장된 견적서 정보가 초기화되고, 도면정보가 반영됩니다. 정말로 초기화 하시겠습니까?",
|
||||
"simulator.title.sub1": "물건번호",
|
||||
"simulator.title.sub2": "작성일",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user