diff --git a/src/components/estimate/Estimate.jsx b/src/components/estimate/Estimate.jsx index 3f41b359..81d2a7e8 100644 --- a/src/components/estimate/Estimate.jsx +++ b/src/components/estimate/Estimate.jsx @@ -20,6 +20,7 @@ import ProductFeaturesPop from './popup/ProductFeaturesPop' import { v4 as uuidv4 } from 'uuid' export default function Estimate({ params }) { + const fixedKey = 'itemKey' const [itemChangeYn, setItemChangeYn] = useState(false) const { session } = useContext(SessionContext) const [objectNo, setObjectNo] = useState('') //물건번호 @@ -28,6 +29,8 @@ export default function Estimate({ params }) { const [files, setFiles] = useState([]) // 보내는 첨부파일 const [originFiles, setOriginFiles] = useState([]) //기존 첨부파일 + const [showPriceCd, setShowPriceCd] = useState('') + const [showContentCode, setShowContentCode] = useState('ATTR001') const [productFeaturesPopupOpen, setProductFeaturesPopupOpen] = useState(false) //견적특이사항 팝업 @@ -46,8 +49,6 @@ export default function Estimate({ params }) { const [storePriceList, setStorePriceList] = useState([]) //가격표시 option - const [tempPriceCd, setTempPriceCd] = useState('') - const [startDate, setStartDate] = useState(new Date()) const singleDatePickerProps = { startDate, @@ -182,7 +183,7 @@ export default function Estimate({ params }) { }) } - //가격표시 option 최초세팅 + //가격표시 option 목록 최초세팅 && 주문분류 변경시 useEffect(() => { if (state.estimateType !== '') { const param = { @@ -200,23 +201,35 @@ export default function Estimate({ params }) { } }, [state?.estimateType]) - //가격표시 option 변경시 useEffect(() => { - if (tempPriceCd !== '') { - const param = { - saleStoreId: session.storeId, - sapSalesStoreCd: session.custCd, - docTpCd: tempPriceCd, - } - - const apiUrl = `/api/estimate/price/store-price-list?${queryStringFormatter(param)}` - get({ url: apiUrl }).then((res) => { - if (isNotEmptyArray(res?.data)) { - setStorePriceList(res.data) - } - }) + if (state?.priceCd) { + setShowPriceCd(state.priceCd) } - }, [tempPriceCd]) + }, [state?.priceCd]) + + //가격 표시 option 변경 이벤트 + const onChangeStorePriceList = (priceCd) => { + const param = { + saleStoreId: session.storeId, + sapSalesStoreCd: session.custCd, + docTpCd: priceCd, + } + + //가격표시 바꾸만헀을때는 tempPriceCd에 바꾼값 관리 불필요? + //프라이싱 했을때 priceCd setState + //화면에 보여지는 값은 showPriceCd로 관리 + setShowPriceCd(priceCd) + //setState({ + // tempPriceCd: priceCd, + //}) + + const apiUrl = `/api/estimate/price/store-price-list?${queryStringFormatter(param)}` + get({ url: apiUrl }).then((res) => { + if (isNotEmptyArray(res?.data)) { + setStorePriceList(res.data) + } + }) + } //Pricing 버튼 const handlePricing = async (priceCd) => { @@ -224,24 +237,55 @@ export default function Estimate({ params }) { saleStoreId: session.storeId, sapSalesStoreCd: session.custCd, docTpCd: state.estimateType, - priceCd: session.storeLvl === '1' ? tempPriceCd : priceCd, - itemIdList: state.itemList, //아이템 최초정보로 호출 + priceCd: priceCd, + //itemIdList: state.itemList, //아이템 최초정보로 호출 delFlg 0인거만.. + itemIdList: state.itemList.filter((item) => item.delFlg === '0'), + } + + if (param.itemIdList.length > 0) { + let pass = true + + param.itemIdList.map((item) => { + if (item.itemId === '') { + pass = false + } + }) + + if (!pass) { + //Pricing이 누락된 아이템이 있습니다. Pricing을 진행해주세요. + return alert(getMessage('estimate.detail.showPrice.pricingBtn.noItemId')) + } } - // console.log('프라이싱파람::', param) await promisePost({ url: '/api/estimate/price/item-price-list', data: param }).then((res) => { + let updateList = [] if (res) { if (res.status === 200) { const data = res.data + //기존itemList랑 프라이싱결과랑 비교해서 단가만 업뎃 서로 갯수가 안맞을 수 있음 없는 itemId면 unitPrice 0으로 + //itemId로 비교해서 단가정보만 업데이트 if (data.result.code === 200) { if (isNotEmptyArray(data.data2)) { - //아이템쪽 다 새로고침............ - //성공후.. - //기존itemList랑 프라이싱결과랑 비교해서 단가만 업뎃 서로 갯수가 안맞을 수 있음 없는 itemId면 unitPrice 0으로 - //itemId로 비교해서 단가정보만 업데이트 - setState({ - priceCd: session.storeLvl === '1' ? tempPriceCd : priceCd, + state.itemList.map((item) => { + let checkYn = false + data.data2.map((item2) => { + if (item2.itemId === item.itemId) { + updateList.push({ ...item, unitPrice: item2.unitPrice }) + checkYn = true + } + }) + + if (!checkYn) { + updateList.push({ ...item, unitPrice: '0' }) + } }) + + setState({ + priceCd: priceCd, + itemList: updateList, + }) + + setItemChangeYn(true) } } } @@ -261,8 +305,71 @@ export default function Estimate({ params }) { setSelection(newSelection) } + // 수량 변경 + const onChangeAmount = (value, dispOrder, index) => { + //itemChangeFlg = 1, partAdd = 0 셋팅 + let amount = value + amount = Number(value.replace(/[^0-9]/g, '').replaceAll(',', '')) + if (isNaN(amount)) { + amount = 0 + } else { + amount = amount.toLocaleString() + } + + let updateList = [] + let updates = {} + updates.amount = amount + updates.itemChangeFlg = '1' + updates.partAdd = '0' + updates.saleTotPrice = (Number(amount.replaceAll(',', '')) * state.itemList[index].salePrice.replaceAll(',', '')).toLocaleString() + + updateList = state.itemList.map((item) => { + if (item.dispOrder === dispOrder) { + return { ...item, ...updates } + } else { + return item + } + }) + + setState({ + itemList: updateList, + }) + + setItemChangeYn(true) + } + + // 단가 변경 + const onChangeSalePrice = (value, dispOrder, index) => { + //itemChangeFlg, partAdd 받아온 그대로 + let salePrice + salePrice = Number(value.replace(/[^0-9]/g, '').replaceAll(',', '')) + if (isNaN(salePrice)) { + salePrice = 0 + } else { + salePrice = salePrice.toLocaleString() + } + let updateList = [] + let updates = {} + updates.salePrice = salePrice + updates.saleTotPrice = (Number(salePrice.replaceAll(',', '')) * state.itemList[index].amount.replaceAll(',', '')).toLocaleString() + + updateList = state.itemList.map((item) => { + if (item.dispOrder === dispOrder) { + return { ...item, ...updates } + } else { + return item + } + }) + + setState({ + itemList: updateList, + }) + + setItemChangeYn(true) + } + // 아이템 자동완성 검색시 아이템 변경 - const onChangeDisplayItem = (itemId, dispOrder) => { + const onChangeDisplayItem = (itemId, dispOrder, index) => { const param = { itemId: itemId, } @@ -290,17 +397,37 @@ 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 + //104671 + let bomList = res.itemBomList updateList = state.itemList.map((item) => { if (item.dispOrder === dispOrder) { return { ...item, ...updates } + } else if (item.paDispOrder === dispOrder) { + return { ...item, delFlg: '1' } } else { return item } }) - setState({ - itemList: updateList, - }) + //paDispOrder + if (bomList) { + bomList.map((bomItem, index) => { + let newItemDispOrder = Math.max(...state.itemList.map((item) => item.dispOrder)) + bomItem.dispOrder = index + 1 + newItemDispOrder + bomItem.delFlg = '0' + bomItem.objectNo = objectNo + bomItem.planNo = planNo + }) + + setState({ + itemList: [...updateList, ...bomList], + }) + } else { + setState({ + itemList: updateList, + }) + } setItemChangeYn(true) }) @@ -315,17 +442,31 @@ export default function Estimate({ params }) { if (row2 === row.dispOrder) { delList.push({ ...row }) } + if (row2 === row.paDispOrder) { + delList.push({ ...row }) + } }) }) const updateList = state.itemList.map((item) => { - const isDeleted = delList.some((row) => item.dispOrder === row.dispOrder) + const isDeleted = delList.some((row) => item.delFlg === '1' || item.dispOrder === row.dispOrder) return { ...item, delFlg: isDeleted ? '1' : '0', } }) + let delCnt = 0 + updateList.map((item) => { + if (item.delFlg === '1') { + delCnt++ + } + }) + + if (delCnt === updateList.length) { + return alert(getMessage('estimate.detail.save.requiredItem')) + } + setState({ itemList: updateList, }) @@ -336,12 +477,30 @@ export default function Estimate({ params }) { useEffect(() => { if (itemChangeYn) { - console.log('아이템에 뭔가 변화가 일어났어', itemChangeYn) - console.log('아이템상태가져오기::::::::::', state.itemList) - } + // console.log('아이템 상태 가져오기::::::::::', state.itemList) + // console.log('토탈쪽 셋팅해주기위한 함수::::::', itemList) + //delFlg 0인거 중에.. + //수량(PCS) : totAmount + //용량( Kw) : totVolKw + //공급가액 : supplyPrice + //부가세(10%) : vatPrice + //총액 :totPrice - //다시 false로 돌리기 여기서할지 가격정보 변경하는거 끝나고할지.. - setItemChangeYn(false) + 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) + } + }) + + setState({ + totAmount: totAmount, + }) + + setItemChangeYn(false) + } }, [itemChangeYn]) return ( @@ -534,10 +693,10 @@ export default function Estimate({ params }) { <>
- +
- +
@@ -778,8 +937,9 @@ export default function Estimate({ params }) { key={uuidv4()} className="select-light" onChange={(e) => { - setTempPriceCd(e.target.value) + onChangeStorePriceList(e.target.value) }} + value={showPriceCd} > {storePriceList.length > 0 && storePriceList.map((row) => )} @@ -790,9 +950,10 @@ export default function Estimate({ params }) { )} @@ -863,107 +1032,123 @@ export default function Estimate({ params }) { {state?.itemList.length > 0 && state.itemList.map((item, index) => { - return ( - - -
- onChangeSelect(item.dispOrder)} - checked={selection.has(item.dispOrder) ? true : false} - /> - -
- - {item?.dispOrder * 100} - -
-
- onChangeSelect(item.dispOrder)} + checked={selection.has(item.dispOrder) ? true : false} + /> +
- )} -
- - -
-
{item?.itemNo}
-
- {item?.fileUploadFlg === '1' && } - {item?.specialNoteCd && ( - - )} -
-
- - -
- { - //onChangeDisplayItem참고 - //itemChangeFlg = 1, partAdd = 0 셋팅 - console.log('수량변경::::::::', e.target.value) - }} - /> -
- - {item.unit} - -
-
- { - //onChangeDisplayItem참고 - //itemChangeFlg, partAdd 받아온 그대로 - console.log('단가변경:::::::', e.target.value) - }} - /> -
- {/*
- OPEN아이콘 처리 -
*/} -
- - {convertNumberToPriceDecimal(item?.saleTotPrice)} - - ) + /> + + + {item.unit} + +
+
+ { + onChangeSalePrice(e.target.value, item.dispOrder, index) + }} + /> +
+ {/*
+ OPEN아이콘 처리 +
*/} +
+ + {item?.saleTotPrice} + + + ) + } else { + return null + } })} diff --git a/src/components/estimate/popup/DocDownOptionPop.jsx b/src/components/estimate/popup/DocDownOptionPop.jsx index 0c859c59..128b2dd0 100644 --- a/src/components/estimate/popup/DocDownOptionPop.jsx +++ b/src/components/estimate/popup/DocDownOptionPop.jsx @@ -6,36 +6,74 @@ import { useRecoilValue } from 'recoil' import { floorPlanObjectState } from '@/store/floorPlanObjectAtom' export default function DocDownOptionPop({ planNo, setEstimatePopupOpen }) { - // console.log('플랜번호::::::::::::', planNo) const { getMessage } = useMessage() - const { promiseGet } = useAxios() + const { promisePost } = useAxios() + //EXCEL, PDF 구분 + const [schDownload, setSchDownload] = useState('EXCEL') //다운로드 파일 EXCEL const [schUnitPriceFlg, setSchUnitPriceFlg] = useState('0') - //견적제출서 표시명 const [schDisplayFlg, setSchSchDisplayFlg] = useState('0') - //가대 중량표 포함 - const [schWeightFlg, setSchWeightFlg] = useState('0') - //도면/시뮬레이션 파일 포함 - const [schDrawingFlg, setSchDrawingFlg] = useState('0') + //가대 중량표 포함(포함:1 미포함 : 0) + const [schWeightFlg, setSchWeightFlg] = useState('1') + //도면/시뮬레이션 파일 포함(포함:1 미포함 : 0) + const [schDrawingFlg, setSchDrawingFlg] = useState('1') // recoil 물건번호 const objectRecoil = useRecoilValue(floorPlanObjectState) //문서 다운로드 const handleFileDown = async () => { - // console.log('물건번호:::', objectRecoil.floorPlanObjectNo) - // console.log('planNo::', planNo) - // 고른 옵션값들 - //0 : 견적가 Excel 1 : 정가용Excel 2: 견적가 PDF 3 :정가용PDF - // console.log(schUnitPriceFlg) - // console.log(schDisplayFlg) - // console.log(schWeightFlg) - // console.log(schDrawingFlg) const url = '/api/estimate/excel-download' - const params = {} + let sendUnitPriceFlg + if (schUnitPriceFlg === '0') { + sendUnitPriceFlg = '0' + } else if (schUnitPriceFlg === '1') { + sendUnitPriceFlg = '1' + } else if (schUnitPriceFlg === '2') { + sendUnitPriceFlg = '0' + } else { + sendUnitPriceFlg = '1' + } + + const params = { + objectNo: objectRecoil.floorPlanObjectNo, + planNo: planNo, + schDownload: schDownload, + schUnitPriceFlg: sendUnitPriceFlg, + schDisplayFlg: schDisplayFlg, + schWeightFlg: schWeightFlg, + schDrawingFlg: schDrawingFlg, + } const options = { responseType: 'blob' } + await promisePost({ url: url, data: params, option: options }) + .then((resultData) => { + if (resultData) { + let fileName = 'unknow' + const blob = new Blob([resultData.data], { type: resultData.headers['content-type'] || 'application/octet-stream' }) + const fileUrl = window.URL.createObjectURL(blob) + + const link = document.createElement('a') + link.href = fileUrl + + //서버에서 내려오는 파일명 + const contentDisposition = resultData.headers['content-disposition'] + if (contentDisposition) { + fileName = contentDisposition.split('filename=')[1].replace(/['"]/g, '') + } + + link.download = fileName + document.body.appendChild(link) + link.click() + link.remove() + window.URL.revokeObjectURL(fileUrl) + } + }) + .catch((error) => { + console.log('::FileDownLoad Error::', error) + alert('File does not exist.') + }) } return ( @@ -74,28 +112,30 @@ export default function DocDownOptionPop({ planNo, setEstimatePopupOpen }) {
{ + setSchDownload('EXCEL') setSchUnitPriceFlg(e.target.value) }} /> - +
{ + setSchDownload('EXCEL') setSchUnitPriceFlg(e.target.value) }} /> - +
{ + setSchDownload('PDF') setSchUnitPriceFlg(e.target.value) }} /> - +
{ + setSchDownload('PDF') setSchUnitPriceFlg(e.target.value) }} /> - +
@@ -168,19 +210,6 @@ export default function DocDownOptionPop({ planNo, setEstimatePopupOpen }) {
- { - setSchWeightFlg(e.target.value) - }} - /> - -
-
+
+ { + setSchWeightFlg(e.target.value) + }} + /> + +
@@ -201,6 +243,19 @@ export default function DocDownOptionPop({ planNo, setEstimatePopupOpen }) {
+ { + setSchDrawingFlg(e.target.value) + }} + /> + +
+
-
- { - setSchDrawingFlg(e.target.value) - }} - /> - -
diff --git a/src/components/floor-plan/CanvasMenu.jsx b/src/components/floor-plan/CanvasMenu.jsx index 09758a1e..133cdfa2 100644 --- a/src/components/floor-plan/CanvasMenu.jsx +++ b/src/components/floor-plan/CanvasMenu.jsx @@ -184,7 +184,7 @@ export default function CanvasMenu(props) { }, [type, globalLocale]) useEffect(() => { - if (['2', '3'].includes(canvasSetting?.roofSizeSet?.toString())) { + if ([2, 3].some((num) => num === canvasSetting?.roofSizeSet)) { setMenuNumber(3) setType('surface') setCurrentMenu(MENU.BATCH_CANVAS.BATCH_DRAWING) @@ -195,8 +195,12 @@ export default function CanvasMenu(props) { } }, [canvasSetting]) + const checkMenuState = (menu) => { + return ([2, 3].some((num) => num === canvasSetting?.roofSizeSet) && menu.index === 2) || (menuNumber === 4 && menu.index === 2) + } + return ( -
+
num === menuNumber) ? 'active' : ''}`}>
    {canvasMenus.map((menu) => { @@ -205,11 +209,12 @@ export default function CanvasMenu(props) { key={`canvas-menu-${menu.index}`} className={`canvas-menu-item ${menuNumber === menu.index ? 'active' : ''}`} onClick={() => { - if (['2', '3'].includes(canvasSetting?.roofSizeSet?.toString()) && menu.index === 2) return + if ([2, 3].some((num) => num === canvasSetting?.roofSizeSet) && menu.index === 2) return + if (menuNumber === 4 && menu.index === 2) return onClickNav(menu) }} > - @@ -218,7 +223,7 @@ export default function CanvasMenu(props) { })}
- {menuNumber !== 6 && menuNumber !== 5 && ( + {![5, 6].some((num) => num === menuNumber) && ( <> {
@@ -248,7 +253,7 @@ export default function CanvasMenu(props) { handleZoom(false) }} > - {canvasZoom}% + {canvasZoom}%
-
- {(menuNumber === 2 || menuNumber === 3 || menuNumber === 4) && } +
num === menuNumber) ? 'active' : ''}`}> + {[2, 3, 4].some((num) => num === menuNumber) && }
{/* 견적서(menuNumber=== 5) 상세화면인경우 문서다운로드 팝업 */} {estimatePopupOpen && } diff --git a/src/components/floor-plan/modal/basic/step/Orientation.jsx b/src/components/floor-plan/modal/basic/step/Orientation.jsx index 85ff6b33..b080b1b1 100644 --- a/src/components/floor-plan/modal/basic/step/Orientation.jsx +++ b/src/components/floor-plan/modal/basic/step/Orientation.jsx @@ -1,6 +1,6 @@ import { forwardRef, useImperativeHandle, useState } from 'react' import { useMessage } from '@/hooks/useMessage' -import { useOrientation } from '@/hooks/popup/useOrientation' +import { useOrientation } from '@/hooks/module/useOrientation' import { getDegreeInOrientation } from '@/util/canvas-util' export const Orientation = forwardRef(({ tabNum }, ref) => { diff --git a/src/components/floor-plan/modal/flowDirection/FlowDirectionSetting.jsx b/src/components/floor-plan/modal/flowDirection/FlowDirectionSetting.jsx index a0573dd1..da49b07c 100644 --- a/src/components/floor-plan/modal/flowDirection/FlowDirectionSetting.jsx +++ b/src/components/floor-plan/modal/flowDirection/FlowDirectionSetting.jsx @@ -6,7 +6,7 @@ import { contextPopupPositionState } from '@/store/popupAtom' import { useMessage } from '@/hooks/useMessage' import { usePopup } from '@/hooks/usePopup' import { useSurfaceShapeBatch } from '@/hooks/surface/useSurfaceShapeBatch' -import { FLOW_DIRECTION_TYPE, useFlowDirectionSetting } from '@/hooks/popup/useFlowDirectionSetting' +import { FLOW_DIRECTION_TYPE, useFlowDirectionSetting } from '@/hooks/contextpopup/useFlowDirectionSetting' import { canvasState } from '@/store/canvasAtom' export default function FlowDirectionSetting(props) { const contextPopupPosition = useRecoilValue(contextPopupPositionState) diff --git a/src/components/floor-plan/modal/image/ImageSizeSetting.jsx b/src/components/floor-plan/modal/image/ImageSizeSetting.jsx deleted file mode 100644 index 309df494..00000000 --- a/src/components/floor-plan/modal/image/ImageSizeSetting.jsx +++ /dev/null @@ -1,42 +0,0 @@ -import WithDraggable from '@/components/common/draggable/WithDraggable' -import { useState } from 'react' -import { usePopup } from '@/hooks/usePopup' -import { useRecoilValue } from 'recoil' -import { contextPopupPositionState } from '@/store/popupAtom' -import { useMessage } from '@/hooks/useMessage' - -export default function ImageSizeSetting(props) { - const contextPopupPosition = useRecoilValue(contextPopupPositionState) - const { id, pos = contextPopupPosition, size, setSize } = props - const [sizeValue, setSizeValue] = useState(100) - const { getMessage } = useMessage() - const { closePopup } = usePopup() - - return ( - -
-
-

{getMessage('modal.image.size.setting')}

- -
-
-
- setSizeValue(e.target.value)} - /> - -
-
-
-
- ) -} diff --git a/src/components/floor-plan/modal/placementShape/PlacementShapeSetting.jsx b/src/components/floor-plan/modal/placementShape/PlacementShapeSetting.jsx index 6a69aaca..26d6f1b9 100644 --- a/src/components/floor-plan/modal/placementShape/PlacementShapeSetting.jsx +++ b/src/components/floor-plan/modal/placementShape/PlacementShapeSetting.jsx @@ -12,7 +12,6 @@ import { usePopup } from '@/hooks/usePopup' import SizeGuide from '@/components/floor-plan/modal/placementShape/SizeGuide' import MaterialGuide from '@/components/floor-plan/modal/placementShape/MaterialGuide' import WithDraggable from '@/components/common/draggable/WithDraggable' -import { SessionContext } from '@/app/SessionProvider' export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, setShowPlaceShapeModal }) { const [objectNo, setObjectNo] = useState('test123241008001') // 후에 삭제 필요 diff --git a/src/components/header/Header.jsx b/src/components/header/Header.jsx index 29cec36b..548387ef 100644 --- a/src/components/header/Header.jsx +++ b/src/components/header/Header.jsx @@ -4,7 +4,7 @@ import { Fragment, useCallback, useEffect, useState } from 'react' import Link from 'next/link' import { usePathname } from 'next/navigation' -import { useRecoilState, useRecoilValue } from 'recoil' +import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil' import { dimmedStore, sessionStore } from '@/store/commonAtom' import { useMessage } from '@/hooks/useMessage' @@ -16,6 +16,8 @@ import UserInfoModal from '@/components/myInfo/UserInfoModal' import { useAxios } from '@/hooks/useAxios' import { globalLocaleStore } from '@/store/localeAtom' +import { stuffSearchState } from '@/store/stuffAtom' + export const ToggleonMouse = (e, act, target) => { const listWrap = e.target.closest(target) const ListItem = Array.from(listWrap.childNodes) @@ -34,6 +36,8 @@ export const ToggleonMouse = (e, act, target) => { export default function Header(props) { const [userInfoModal, setUserInfoModal] = useState(false) + const resetStuffRecoil = useResetRecoilState(stuffSearchState) + const { userSession } = props const [sessionState, setSessionState] = useRecoilState(sessionStore) const { getMessage } = useMessage() @@ -184,7 +188,14 @@ export default function Header(props) { {userInfoModal && }
-
diff --git a/src/components/management/Stuff.jsx b/src/components/management/Stuff.jsx index 3dc9a2f4..e6b281c4 100644 --- a/src/components/management/Stuff.jsx +++ b/src/components/management/Stuff.jsx @@ -39,9 +39,9 @@ export default function Stuff() { const copyNo = async (value) => { try { await navigator.clipboard.writeText(value) - alert(getMessage('stuff.detail.header.message2')) + alert(getMessage('stuff.detail.header.successCopy')) } catch (error) { - alert(getMessage('stuff.detail.header.message3')) + alert(getMessage('stuff.detail.header.failCopy')) } } @@ -209,6 +209,7 @@ export default function Stuff() { endRow: pageNo * pageSize, schSelSaleStoreId: stuffSearchParams?.schOtherSelSaleStoreId ? stuffSearchParams.schOtherSelSaleStoreId : stuffSearchParams.schSelSaleStoreId, schSortType: 'R', + code: 'S', } setStuffSearch({ ...params, diff --git a/src/components/management/StuffDetail.jsx b/src/components/management/StuffDetail.jsx index ccb943c3..eea328d8 100644 --- a/src/components/management/StuffDetail.jsx +++ b/src/components/management/StuffDetail.jsx @@ -19,10 +19,15 @@ import { useCommonCode } from '@/hooks/common/useCommonCode' import StuffPlanQGrid from './StuffPlanQGrid' import { floorPlanObjectState } from '@/store/floorPlanObjectAtom' import { ManagementContext } from '@/app/management/ManagementProvider' +import DocDownOptionPop from '../estimate/popup/DocDownOptionPop' export default function StuffDetail() { const setFloorPlanObjectNo = useSetRecoilState(floorPlanObjectState) //견적서 화면용 물건번호리코일 + const [estimatePopupOpen, setEstimatePopupOpen] = useState(false) + + const [popPlanNo, setPopPlanNo] = useState('1') //default 1 + //공통코드 const { commonCode, findCommonCode } = useCommonCode() const [selOptions, setSelOptions] = useState('') //선택한 1차점 @@ -266,11 +271,12 @@ export default function StuffDetail() { type="button" className="grid-btn" onClick={() => { - console.log('엑셀버튼클릭') + setFloorPlanObjectNo({ floorPlanObjectNo: params.data.objectNo }) + handleEstimatePopup(params.data.planNo) }} > - {getMessage('stuff.detail.planGrid.btn2')} + {getMessage('stuff.detail.planGrid.docDownload')}
@@ -280,6 +286,12 @@ export default function StuffDetail() { ], }) + // 문서다운로드 팝업 오픈 셋팅 + const handleEstimatePopup = (planNo) => { + setPopPlanNo(planNo) + setEstimatePopupOpen(true) + } + useEffect(() => { if (objectNo) { setEditMode('EDIT') @@ -289,10 +301,12 @@ export default function StuffDetail() { } promiseGet({ url: `/api/object/${objectNo}/detail` }).then((res) => { if (res.status === 200) { - if (res.data != null) { + if (isObjectNotEmpty(res.data)) { setManagementState(res.data) } else { setManagementState({}) + alert(getMessage('stuff.detail.header.notExistObjectNo')) + router.push('/management/stuff') } if (isNotEmptyArray(res.data.planList)) { setPlanGridProps({ ...planGridProps, planGridData: res.data.planList }) @@ -302,6 +316,9 @@ export default function StuffDetail() { } else { setManagementState({}) setPlanGridProps({ ...planGridProps, planGridData: [] }) + + alert(getMessage('stuff.detail.header.notExistObjectNo')) + router.push('/management/stuff') } }) } else { @@ -2433,6 +2450,8 @@ export default function StuffDetail() { {showWindSpeedButtonValid && ( )} + + {estimatePopupOpen && } ) } diff --git a/src/components/management/StuffSearchCondition.jsx b/src/components/management/StuffSearchCondition.jsx index 4a625539..1c7ececb 100644 --- a/src/components/management/StuffSearchCondition.jsx +++ b/src/components/management/StuffSearchCondition.jsx @@ -116,8 +116,10 @@ export default function StuffSearchCondition() { schAddress: address, schObjectName: objectName, schDispCompanyName: dispCompanyName, - schSelSaleStoreId: stuffSearch?.schSelSaleStoreId, - schOtherSelSaleStoreId: stuffSearch?.schOtherSelSaleStoreId, + // schSelSaleStoreId: stuffSearch?.schSelSaleStoreId, + // schOtherSelSaleStoreId: stuffSearch?.schOtherSelSaleStoreId, + schSelSaleStoreId: schSelSaleStoreId, + schOtherSelSaleStoreId: otherSaleStoreId, schReceiveUser: receiveUser, schDateType: dateType, schFromDt: dayjs(startDate).format('YYYY-MM-DD'), @@ -157,6 +159,7 @@ export default function StuffSearchCondition() { setStuffSearch({ schSelSaleStoreId: '', schOtherSelSaleStoreId: '', + schDateType: 'U', }) } else { if (otherSaleStoreList.length > 1) { @@ -312,8 +315,8 @@ export default function StuffSearchCondition() { } else { //X누름 //화면에선 지우는데 리코일은 조회누르지 않으면 보존 - setSchSelSaleStoreId('') - // stuffSearch.schSelSaleStoreId = '' + // setSchSelSaleStoreId('') //값이 안비워짐.. + setSchSelSaleStoreId(null) //2차점 판매점목록비우기 setOtherSaleStoreList([]) @@ -333,7 +336,6 @@ export default function StuffSearchCondition() { if (session.storeLvl === '1') { if (stuffSearch.schOtherSelSaleStoreId === '') { // 화면에선 지우는데 조회누르기 전이면 리코일은 남김 - // stuffSearch.schSelSaleStoreId = '' setSchSelSaleStoreId(session.storeId) } else { // 화면에선 지우는데 조회누르기 전이면 리코일은 남김 @@ -357,6 +359,7 @@ export default function StuffSearchCondition() { setobjectName(stuffSearch.schObjectName ? stuffSearch.schObjectName : objectName) setDispCompanyName(stuffSearch.schDispCompanyName ? stuffSearch.schDispCompanyName : dispCompanyName) setReceiveUser(stuffSearch.schReceiveUser ? stuffSearch.schReceiveUser : receiveUser) + setDateType(stuffSearch.schDateType ? stuffSearch.schDateType : dateType) }, [stuffSearch]) useEffect(() => { @@ -638,7 +641,7 @@ export default function StuffSearchCondition() { type="radio" name="radio_ptype" id="radio_u" - checked={stuffSearch.schDateType === 'U' ? true : false} + checked={dateType === 'U' ? true : false} value={'U'} onChange={(e) => { setDateType(e.target.value) @@ -652,7 +655,7 @@ export default function StuffSearchCondition() { type="radio" name="radio_ptype" id="radio_r" - checked={stuffSearch.schDateType === 'R' ? true : false} + checked={dateType === 'R' ? true : false} value={'R'} onChange={(e) => { setDateType(e.target.value) diff --git a/src/components/simulator/Simulator.jsx b/src/components/simulator/Simulator.jsx index c909ca17..f3a26ede 100644 --- a/src/components/simulator/Simulator.jsx +++ b/src/components/simulator/Simulator.jsx @@ -105,8 +105,8 @@ export default function Simulator() { useEffect(() => { if (objectNo) { fetchObjectDetail(objectNo) + fetchSimulatorNotice() } - fetchSimulatorNotice() }, [objectNo, plan]) // 물건 상세 정보 조회 @@ -158,23 +158,25 @@ export default function Simulator() {
{getMessage('simulator.title.sub1')}
- {objectDetail.objectNo} (Plan No: {objectDetail.planNo}) + {objectDetail.objectNo} (Plan No: {plan?.id})
{/* 작성일 */}
{getMessage('simulator.title.sub2')}
-
{`${dayjs(objectDetail.drawingEstimateCreateDate).format('YYYY.MM.DD')}`}
+
+ {objectDetail.drawingEstimateCreateDate ? `${dayjs(objectDetail.drawingEstimateCreateDate).format('YYYY.MM.DD')}` : ''} +
{/* 시스템용량 */}
{getMessage('simulator.title.sub3')}
-
{convertNumberToPriceDecimal(objectDetail.capacity)}kW
+
{objectDetail.capacity ? `${convertNumberToPriceDecimal(objectDetail.capacity)}kW` : ''}
{/* 연간예측발전량 */}
{getMessage('simulator.title.sub4')}
-
{convertNumberToPriceDecimal(objectDetail.anlFrcsGnrt)}
+
{objectDetail.anlFrcsGnrt ? convertNumberToPriceDecimal(objectDetail.anlFrcsGnrt) : ''}
diff --git a/src/hooks/common/useCanvasConfigInitialize.js b/src/hooks/common/useCanvasConfigInitialize.js index a07d4a00..18d1a052 100644 --- a/src/hooks/common/useCanvasConfigInitialize.js +++ b/src/hooks/common/useCanvasConfigInitialize.js @@ -40,19 +40,19 @@ export function useCanvasConfigInitialize() { const flowTexts = canvas.getObjects().filter((obj) => obj.name === 'flowText') if (basicSetting.roofAngleSet === 'slope') { offsetTexts.forEach((obj) => { - obj.set({ text: `${obj.originText}-∠${obj.pitch}${angleUnit}` }) + obj.set({ text: `${!obj.originText ? '' : obj.originText + '-'}∠${obj.pitch}${angleUnit}` }) }) flowTexts.forEach((obj) => { - obj.set({ text: `${obj.originText}-∠${obj.pitch}${pitchText}` }) + obj.set({ text: `${!obj.originText ? '' : obj.originText + '-'}∠${obj.pitch}${pitchText}` }) }) } if (basicSetting.roofAngleSet === 'flat') { offsetTexts.forEach((obj) => { - obj.set({ text: `${obj.originText}-∠${getDegreeByChon(obj.pitch)}${angleUnit}` }) + obj.set({ text: `${!obj.originText ? '' : obj.originText + '-'}∠${getDegreeByChon(obj.pitch)}${angleUnit}` }) }) flowTexts.forEach((obj) => { - obj.set({ text: `${obj.originText}-∠${getDegreeByChon(obj.pitch)}${pitchText}` }) + obj.set({ text: `${!obj.originText ? '' : obj.originText + '-'}∠${getDegreeByChon(obj.pitch)}${pitchText}` }) }) } diff --git a/src/hooks/popup/useFlowDirectionSetting.js b/src/hooks/contextpopup/useFlowDirectionSetting.js similarity index 100% rename from src/hooks/popup/useFlowDirectionSetting.js rename to src/hooks/contextpopup/useFlowDirectionSetting.js diff --git a/src/hooks/floorPlan/estimate/useEstimateController.js b/src/hooks/floorPlan/estimate/useEstimateController.js index 4330a208..07f50ec8 100644 --- a/src/hooks/floorPlan/estimate/useEstimateController.js +++ b/src/hooks/floorPlan/estimate/useEstimateController.js @@ -59,6 +59,12 @@ export const useEstimateController = (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' + }) + } + setState(res) } }) @@ -76,7 +82,9 @@ export const useEstimateController = (planNo) => { } const addItem = () => { - const newItemDispOrder = Math.max(...state.itemList.map((item) => item.dispOrder)) + 1 + // const newItemDispOrder = (Math.max(...state.itemList.map((item) => item.dispOrder)) / 100 + 1) * 100 + let newItemDispOrder = Math.max(...state.itemList.map((item) => item.dispOrder)) + newItemDispOrder = (Math.floor(newItemDispOrder / 100) + 1) * 100 setState({ itemList: [ ...state.itemList, @@ -90,8 +98,8 @@ export const useEstimateController = (planNo) => { amount: '', //수량 unitPrice: '0', unit: '', //단위 - salePrice: '0', //단가 - saleTotPrice: '0', //금액(부가세별도) + salePrice: '', //단가 + saleTotPrice: '', //금액(부가세별도) itemChangeFlg: '1', //추가시 체인지플래그 1로 partAdd: '1', //NEW 체인지 플래그 delFlg: '0', //삭제 플래그 0 삭제하면 1 @@ -133,7 +141,7 @@ export const useEstimateController = (planNo) => { const handleEstimateSubmit = async () => { //0. 필수체크 let flag = true - console.log('::담긴 estimateData:::', estimateData) + // console.log('첨부파일:::::', estimateData.fileList) //첨부파일을 첨부안했는데 //아이템 fileUploadFlg가1(첨부파일 필수)이 1개라도 있는데 후일 자료 제출(fileFlg) 체크안했으면(0) alert 저장안돼 @@ -151,9 +159,8 @@ export const useEstimateController = (planNo) => { } if (flag) { - //1. 첨부파일 저장 + //1. 첨부파일 저장시작 const formData = new FormData() - console.log('첨부파일:!!!', estimateData.fileList) formData.append('file', estimateData.fileList) formData.append('objectNo', estimateData.objectNo) formData.append('planNo', estimateData.planNo) @@ -161,26 +168,30 @@ export const useEstimateController = (planNo) => { formData.append('userId', estimateData.userId) await post({ url: '/api/file/fileUpload', data: formData }) + //첨부파일저장끝 + //제품라인 추가했는데 아이템 안고르고 저장하면itemId=''은 날리고 나머지 저장하기 + estimateData.itemList = estimateData.itemList.filter((item) => item.itemId !== '') + + let delCnt = 0 + estimateData.itemList.map((item) => { + if (item.delFlg === '1') { + delCnt++ + } + }) + if (delCnt === estimateData.itemList.length) { + return alert(getMessage('estimate.detail.save.requiredItem')) + } + + console.log('최종 정보::;', estimateData) + console.log('최종 남은 아이템정보:::', estimateData.itemList) //2. 상세데이터 저장 - return + // return await promisePost({ url: `${ESTIMATE_API_ENDPOINT}/save-estimate`, data: estimateData }).then((res) => { if (res) { alert(getMessage('estimate.detail.save.alertMsg')) } }) - - // try { - // const result = await promisePost({ - // url: ESTIMATE_API_ENDPOINT, - // data: estimateData, - // }) - // alert(getMessage('estimate.detail.save.alertMsg')) - // return result - // } catch (error) { - // console.error('Failed to submit estimate:', error) - // throw error - // } } } diff --git a/src/hooks/popup/useOrientation.js b/src/hooks/module/useOrientation.js similarity index 74% rename from src/hooks/popup/useOrientation.js rename to src/hooks/module/useOrientation.js index 4b8f4574..b64fc171 100644 --- a/src/hooks/popup/useOrientation.js +++ b/src/hooks/module/useOrientation.js @@ -3,6 +3,7 @@ import { canvasState } from '@/store/canvasAtom' import { usePolygon } from '@/hooks/usePolygon' import { POLYGON_TYPE } from '@/common/common' import { compasDegAtom } from '@/store/orientationAtom' +import { useEffect } from 'react' // 모듈,회로 구성 탭 기본설정 > 방위설정 탭 export function useOrientation() { @@ -11,6 +12,16 @@ export function useOrientation() { const { drawDirectionArrow } = usePolygon() + useEffect(() => { + const roofs = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF) + roofs.forEach((roof) => { + roof.set({ + moduleCompass: null, + }) + drawDirectionArrow(roof) + }) + }, []) + const nextStep = () => { const roofs = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF) roofs.forEach((roof) => { diff --git a/src/hooks/option/useCanvasSetting.js b/src/hooks/option/useCanvasSetting.js index a585109a..6cb1d09c 100644 --- a/src/hooks/option/useCanvasSetting.js +++ b/src/hooks/option/useCanvasSetting.js @@ -277,7 +277,7 @@ export function useCanvasSetting() { optionName = ['outerLine', POLYGON_TYPE.WALL] break case 'gridDisplay': //그리드 표시 - optionName = ['lindGrid', 'dotGrid'] + optionName = ['lindGrid', 'dotGrid', 'tempGrid'] break case 'lineDisplay': //지붕선 표시 optionName = ['roof', POLYGON_TYPE.ROOF] diff --git a/src/hooks/option/useFirstOption.js b/src/hooks/option/useFirstOption.js deleted file mode 100644 index 8757040a..00000000 --- a/src/hooks/option/useFirstOption.js +++ /dev/null @@ -1,81 +0,0 @@ -import { useRecoilState, useRecoilValue } from 'recoil' -import { canvasState } from '@/store/canvasAtom' -import { useEffect } from 'react' -import { settingModalFirstOptionsState } from '@/store/settingAtom' -import { POLYGON_TYPE } from '@/common/common' - -export function useFirstOption() { - const canvas = useRecoilValue(canvasState) - - const [settingModalFirstOptions, setSettingModalFirstOptions] = useRecoilState(settingModalFirstOptionsState) - - useEffect(() => { - const option1 = settingModalFirstOptions.option1 - - // 'allocDisplay' 할당 표시 - // 'outlineDisplay' 외벽선 표시 'outerLine', POLYGON_TYPE.WALL - // 'gridDisplay' 그리드 표시 'lindGrid', 'dotGrid' - // 'lineDisplay' 지붕선 표시 'roof', POLYGON_TYPE.ROOF - // 'wordDisplay' 문자 표시 - // 'circuitNumDisplay' 회로번호 표시 - // 'flowDisplay' 흐름방향 표시 'arrow' - // 'trestleDisplay' 가대 표시 - // 'totalDisplay' 집계표 표시 - - let optionName //옵션명 - let optionSelected //옵션상태 - - for (let i = 0; i < option1.length; i++) { - switch (option1[i].column) { - case 'allocDisplay': //할당 표시 - optionName = ['1'] - break - case 'outlineDisplay': //외벽선 표시 - optionName = ['outerLine', POLYGON_TYPE.WALL] - break - case 'gridDisplay': //그리드 표시 - optionName = ['lineGrid', 'dotGrid', 'adsorptionPoint', 'tempGrid'] - break - case 'lineDisplay': //지붕선 표시 - optionName = ['roof', POLYGON_TYPE.ROOF] - break - case 'wordDisplay': //문자 표시 - optionName = ['commonText'] - break - case 'circuitNumDisplay': //회로번호 표시 - optionName = ['7'] - break - case 'flowDisplay': //흐름방향 표시 - optionName = ['arrow', 'flowText'] - break - case 'trestleDisplay': //가대 표시 - optionName = ['8'] - break - case 'totalDisplay': //집계표 표시 - optionName = ['9'] - break - } - // 표시 선택 상태(true/false) - optionSelected = option1[i].selected - - canvas - .getObjects() - .filter((obj) => optionName.includes(obj.name)) - //.filter((obj) => obj.name === optionName) - .forEach((obj) => { - obj.set({ visible: optionSelected }) - //obj.set({ visible: !obj.visible }) - }) - - canvas.renderAll() - - // console.log( - // 'optionName', - // optionName, - // canvas.getObjects().filter((obj) => optionName.includes(obj.name)), - // ) - } - }, [settingModalFirstOptions]) - - return { settingModalFirstOptions, setSettingModalFirstOptions } -} diff --git a/src/hooks/roofcover/useAuxiliaryDrawing.js b/src/hooks/roofcover/useAuxiliaryDrawing.js index 6e95e79c..1a0d87bd 100644 --- a/src/hooks/roofcover/useAuxiliaryDrawing.js +++ b/src/hooks/roofcover/useAuxiliaryDrawing.js @@ -455,9 +455,24 @@ export function useAuxiliaryDrawing(id) { name: 'auxiliaryLine', }) - lineHistory.current.push(line) + const historyLines = [...lineHistory.current] + + const hasSameLine = historyLines.some((history) => { + return ( + (isSamePoint(history.startPoint, line.startPoint) && isSamePoint(history.endPoint, line.endPoint)) || + (isSamePoint(history.startPoint, line.endPoint) && isSamePoint(history.endPoint, line.startPoint)) + ) + }) + mousePointerArr.current = [] clear() + + if (hasSameLine) { + canvas.remove(line) + return + } + + lineHistory.current.push(line) } const mouseDown = (e) => { diff --git a/src/hooks/roofcover/usePropertiesSetting.js b/src/hooks/roofcover/usePropertiesSetting.js index 3a1535a3..73a72e5a 100644 --- a/src/hooks/roofcover/usePropertiesSetting.js +++ b/src/hooks/roofcover/usePropertiesSetting.js @@ -125,6 +125,12 @@ export function usePropertiesSetting(id) { } const lines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') + const notSetAttributes = lines.filter((line) => !line.attributes?.type) + if (notSetAttributes.length > 0) { + alert('설정되지 않은 외벽선이 있습니다.') + return + } + lines.forEach((line) => { line.set({ attributes: line.attributes ? line.attributes : { offset: 0, type: LINE_TYPE.WALLLINE.WALL }, diff --git a/src/hooks/roofcover/useRoofShapeSetting.js b/src/hooks/roofcover/useRoofShapeSetting.js index af55e557..c26c61ec 100644 --- a/src/hooks/roofcover/useRoofShapeSetting.js +++ b/src/hooks/roofcover/useRoofShapeSetting.js @@ -377,20 +377,20 @@ export function useRoofShapeSetting(id) { } // 기존 wallLine, roofBase 제거 - /*canvas + canvas .getObjects() .filter((obj) => obj.name === POLYGON_TYPE.WALL) .forEach((line) => { canvas.remove(line) - })*/ + }) - /*canvas + canvas .getObjects() - .filter((obj) => obj.name === POLYGON_TYPE.ROOF) + .filter((obj) => obj.name === POLYGON_TYPE.ROOF && !obj.isFixed) .forEach((obj) => { canvas.remove(...obj.innerLines) canvas.remove(obj) - })*/ + }) const polygon = addPolygonByLines(outerLines, { name: POLYGON_TYPE.WALL, direction }) polygon.lines = [...outerLines] diff --git a/src/hooks/useContextMenu.js b/src/hooks/useContextMenu.js index 5dae113c..8e7028d4 100644 --- a/src/hooks/useContextMenu.js +++ b/src/hooks/useContextMenu.js @@ -446,19 +446,24 @@ export function useContextMenu() { ]) break case 'lineGrid': + case 'dotGrid': + case 'tempGrid': setContextMenu([ [ { id: 'gridMove', name: getMessage('modal.grid.move'), + component: , }, { id: 'gridCopy', name: getMessage('modal.grid.copy'), + component: , }, { id: 'gridColorEdit', name: getMessage('contextmenu.grid.color.edit'), + component: , }, { id: 'remove', diff --git a/src/locales/ja.json b/src/locales/ja.json index 64902f47..d5721a78 100644 --- a/src/locales/ja.json +++ b/src/locales/ja.json @@ -284,7 +284,6 @@ "modal.panel.batch.statistic.total": "合計", "modal.flow.direction.setting": "流れ方向の設定", "modal.flow.direction.setting.info": "流れ方向を選択してください。", - "modal.image.size.setting": "画像のサイズ変更", "modal.actual.size.setting": "実測値設定", "modal.actual.size.setting.info": "※隅棟・谷・棟の実際の寸法を入力してください。", "modal.actual.size.setting.not.exist.auxiliary.line": "실측치 입력할 보조선을 선택해 주세요(JA)", @@ -733,7 +732,7 @@ "stuff.detail.planGridHeader.pcTypeNo": "パワーコンディショナー", "stuff.detail.planGridHeader.management": "管理", "stuff.detail.planGrid.btn1": "見積書の照会", - "stuff.detail.planGrid.btn2": "Excel", + "stuff.detail.planGrid.docDownload": "文書のダウンロード", "stuff.grid.noData": "照会されたデータがありません", "length": "長さ", "height": "高さ", @@ -844,6 +843,7 @@ "estimate.detail.header.showPrice": "価格表示", "estimate.detail.header.unitPrice": "定価", "estimate.detail.showPrice.pricingBtn": "Pricing", + "estimate.detail.showPrice.pricingBtn.noItemId": "Pricingが欠落しているアイテムがあります。 Pricingを進めてください.", "estimate.detail.showPrice.description1": "製品価格 OPEN", "estimate.detail.showPrice.description2": "追加, 変更資材", "estimate.detail.showPrice.description3": "添付必須", @@ -860,25 +860,26 @@ "estimate.detail.docPopup.title": "ドキュメントダウンロードオプションの設定", "estimate.detail.docPopup.explane": "ダウンロードする文書のオプションを選択したら、 [文書のダウンロード]ボタンをクリックします.", "estimate.detail.docPopup.schUnitPriceFlg": "ダウンロードファイル", - "estimate.detail.docPopup.schUnitPriceFlg.schUnitPriceFlg0": "見積もり Excel", - "estimate.detail.docPopup.schUnitPriceFlg.schUnitPriceFlg1": "定価用 Excel", - "estimate.detail.docPopup.schUnitPriceFlg.schUnitPriceFlg2": "見積もり PDF", - "estimate.detail.docPopup.schUnitPriceFlg.schUnitPriceFlg3": "定価用 PDF", + "estimate.detail.docPopup.schUnitPriceFlg.excelFlg0": "見積もり Excel", + "estimate.detail.docPopup.schUnitPriceFlg.excelFlg1": "定価用 Excel", + "estimate.detail.docPopup.schUnitPriceFlg.pdfFlg0": "見積もり PDF", + "estimate.detail.docPopup.schUnitPriceFlg.pdfFlg1": "定価用 PDF", "estimate.detail.docPopup.schDisplayFlg": "見積提出先表示名", "estimate.detail.docPopup.schDisplayFlg.schDisplayFlg0": "販売店名", "estimate.detail.docPopup.schDisplayFlg.schDisplayFlg1": "案件名", "estimate.detail.docPopup.schWeightFlg": "架台重量表を含む", - "estimate.detail.docPopup.schWeightFlg.schWeightFlg0": "含む", - "estimate.detail.docPopup.schWeightFlg.schWeightFlg1": "含まない", + "estimate.detail.docPopup.schWeightFlg.schWeightFlg1": "含む", + "estimate.detail.docPopup.schWeightFlg.schWeightFlg0": "含まない", "estimate.detail.docPopup.schDrawingFlg": "図面/シミュレーションファイルを含む", - "estimate.detail.docPopup.schDrawingFlg.schDrawingFlg0": "含む", - "estimate.detail.docPopup.schDrawingFlg.schDrawingFlg1": "含まない", + "estimate.detail.docPopup.schDrawingFlg.schDrawingFlg1": "含む", + "estimate.detail.docPopup.schDrawingFlg.schDrawingFlg0": "含まない", "estimate.detail.docPopup.close": "閉じる", "estimate.detail.docPopup.docDownload": "文書のダウンロード", "estimate.detail.productFeaturesPopup.title": "製品特異事項", "estimate.detail.productFeaturesPopup.close": "閉じる", "estimate.detail.save.alertMsg": "保存されている見積書で製品を変更した場合、図面や回路には反映されません.", "estimate.detail.save.requiredMsg": "ファイル添付が必須のアイテムがあります。ファイルを添付するか、後日添付をチェックしてください.", + "estimate.detail.save.requiredItem": "製品は1つ以上登録する必要があります.", "estimate.detail.reset.confirmMsg": "保存した見積書情報が初期化され、図面情報が反映されます。本当に初期化しますか?", "simulator.title.sub1": "物件番号", "simulator.title.sub2": "作成日", diff --git a/src/locales/ko.json b/src/locales/ko.json index 5b47e7fd..fe4df330 100644 --- a/src/locales/ko.json +++ b/src/locales/ko.json @@ -289,7 +289,6 @@ "modal.panel.batch.statistic.total": "합계", "modal.flow.direction.setting": "흐름 방향 설정", "modal.flow.direction.setting.info": "흐름방향을 선택하세요.", - "modal.image.size.setting": "이미지 크기 조절", "modal.actual.size.setting": "실측치 설정", "modal.actual.size.setting.info": "※隅棟・谷・棟의 실제 치수를 입력해주세요.", "modal.actual.size.setting.not.exist.auxiliary.line": "실측치 입력할 보조선을 선택해 주세요", @@ -743,7 +742,7 @@ "stuff.detail.planGridHeader.pcTypeNo": "파워컨디셔너", "stuff.detail.planGridHeader.management": "관리", "stuff.detail.planGrid.btn1": "견적서 조회", - "stuff.detail.planGrid.btn2": "Excel", + "stuff.detail.planGrid.docDownload": "문서 다운로드", "stuff.grid.noData": "조회된 데이터가 없습니다", "length": "길이", "height": "높이", @@ -849,11 +848,12 @@ "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.calcFormula1": "(모듈용량 * 수량)÷100", "estimate.detail.sepcialEstimateProductInfo.calcFormula2": "PKG단가(W) * PKG용량(W)", "estimate.detail.header.showPrice": "가격표시", "estimate.detail.header.unitPrice": "정가", "estimate.detail.showPrice.pricingBtn": "Pricing", + "estimate.detail.showPrice.pricingBtn.noItemId": "Pricing이 누락된 아이템이 있습니다. Pricing을 진행해주세요.", "estimate.detail.showPrice.description1": "제품 가격 OPEN", "estimate.detail.showPrice.description2": "추가, 변경 자재", "estimate.detail.showPrice.description3": "첨부필수", @@ -870,25 +870,26 @@ "estimate.detail.docPopup.title": "문서다운로드 옵션설정", "estimate.detail.docPopup.explane": "다운로드할 문서 옵션을 선택한 후 문서 다운로드 버튼을 클릭합니다.", "estimate.detail.docPopup.schUnitPriceFlg": "다운로드 파일", - "estimate.detail.docPopup.schUnitPriceFlg.schUnitPriceFlg0": "견적가 Excel", - "estimate.detail.docPopup.schUnitPriceFlg.schUnitPriceFlg1": "정가용 Excel", - "estimate.detail.docPopup.schUnitPriceFlg.schUnitPriceFlg2": "견적가 PDF", - "estimate.detail.docPopup.schUnitPriceFlg.schUnitPriceFlg3": "정가용 PDF", + "estimate.detail.docPopup.schUnitPriceFlg.excelFlg0": "견적가 Excel", + "estimate.detail.docPopup.schUnitPriceFlg.excelFlg1": "정가용 Excel", + "estimate.detail.docPopup.schUnitPriceFlg.pdfFlg0": "견적가 PDF", + "estimate.detail.docPopup.schUnitPriceFlg.pdfFlg1": "정가용 PDF", "estimate.detail.docPopup.schDisplayFlg": "견적제출서 표시명", "estimate.detail.docPopup.schDisplayFlg.schDisplayFlg0": "판매점명", "estimate.detail.docPopup.schDisplayFlg.schDisplayFlg1": "안건명", "estimate.detail.docPopup.schWeightFlg": "가대 중량표 포함", - "estimate.detail.docPopup.schWeightFlg.schWeightFlg0": "포함", - "estimate.detail.docPopup.schWeightFlg.schWeightFlg1": "미포함", + "estimate.detail.docPopup.schWeightFlg.schWeightFlg1": "포함", + "estimate.detail.docPopup.schWeightFlg.schWeightFlg0": "미포함", "estimate.detail.docPopup.schDrawingFlg": "도면/시뮬레이션 파일 포함", - "estimate.detail.docPopup.schDrawingFlg.schDrawingFlg0": "포함", - "estimate.detail.docPopup.schDrawingFlg.schDrawingFlg1": "미포함", + "estimate.detail.docPopup.schDrawingFlg.schDrawingFlg1": "포함", + "estimate.detail.docPopup.schDrawingFlg.schDrawingFlg0": "미포함", "estimate.detail.docPopup.close": "닫기", "estimate.detail.docPopup.docDownload": "문서 다운로드", "estimate.detail.productFeaturesPopup.title": "제품특이사항", "estimate.detail.productFeaturesPopup.close": "닫기", "estimate.detail.save.alertMsg": "저장되었습니다. 견적서에서 제품을 변경할 경우, 도면 및 회로에 반영되지 않습니다.", "estimate.detail.save.requiredMsg": "파일첨부가 필수인 아이템이 있습니다. 파일을 첨부하거나 후일첨부를 체크해주십시오.", + "estimate.detail.save.requiredItem": "제품은 1개이상 등록해야 됩니다.", "estimate.detail.reset.confirmMsg": "저장된 견적서 정보가 초기화되고, 도면정보가 반영됩니다. 정말로 초기화 하시겠습니까?", "simulator.title.sub1": "물건번호", "simulator.title.sub2": "작성일", diff --git a/src/styles/_contents.scss b/src/styles/_contents.scss index 441fccb4..446e003f 100644 --- a/src/styles/_contents.scss +++ b/src/styles/_contents.scss @@ -28,6 +28,7 @@ background-color: #383838; transition: padding 0.17s ease-in-out; z-index: 999; + .canvas-menu-inner { position: relative; display: flex; @@ -36,14 +37,17 @@ background-color: #2c2c2c; height: 46.8px; z-index: 999; + .canvas-menu-list { display: flex; align-items: center; height: 100%; + .canvas-menu-item { display: flex; align-items: center; height: 100%; + button { display: flex; align-items: center; @@ -54,6 +58,7 @@ padding: 15px 20px; opacity: 0.55; transition: all 0.17s ease-in-out; + .menu-icon { display: block; width: 14px; @@ -62,53 +67,67 @@ background-position: center; background-size: contain; margin-right: 10px; + &.con00 { background-image: url(/static/images/canvas/menu_icon00.svg); } + &.con01 { background-image: url(/static/images/canvas/menu_icon01.svg); } + &.con02 { background-image: url(/static/images/canvas/menu_icon02.svg); } + &.con03 { background-image: url(/static/images/canvas/menu_icon03.svg); } + &.con04 { background-image: url(/static/images/canvas/menu_icon04.svg); } + &.con05 { background-image: url(/static/images/canvas/menu_icon05.svg); } + &.con06 { background-image: url(/static/images/canvas/menu_icon06.svg); } } } + &.active { background-color: #383838; + button { opacity: 1; } } } } + .canvas-side-btn-wrap { display: flex; align-items: center; margin-left: auto; + .select-box { width: 124px; margin: 0 5px; height: 30px; + > div { width: 100%; } } + .btn-from { display: flex; align-items: center; gap: 5px; + button { display: block; width: 30px; @@ -119,49 +138,63 @@ background-repeat: no-repeat; background-size: 15px 15px; transition: all 0.17s ease-in-out; + &.btn01 { background-image: url(../../public/static/images/canvas/side_icon03.svg); } + &.btn02 { background-image: url(../../public/static/images/canvas/side_icon02.svg); } + &.btn03 { background-image: url(../../public/static/images/canvas/side_icon01.svg); } + &.btn04 { background-image: url(../../public/static/images/canvas/side_icon04.svg); } + &.btn05 { background-image: url(../../public/static/images/canvas/side_icon05.svg); } + &.btn06 { background-image: url(../../public/static/images/canvas/side_icon06.svg); } + &.btn07 { background-image: url(../../public/static/images/canvas/side_icon07.svg); } + &.btn08 { background-image: url(../../public/static/images/canvas/side_icon08.svg); } + &.btn09 { background-image: url(../../public/static/images/canvas/side_icon09.svg); } + &.btn10 { background-image: url(../../public/static/images/canvas/side_icon10.svg); background-size: 15px 14px; } + &:hover { background-color: #1083e3; } + &.active { background-color: #1083e3; } } } + .ico-btn-from { display: flex; align-items: center; gap: 5px; + button { .ico { display: block; @@ -170,30 +203,37 @@ background-repeat: no-repeat; background-position: center; background-size: contain; + &.ico01 { background-image: url(../../public/static/images/canvas/ico-flx01.svg); } + &.ico02 { background-image: url(../../public/static/images/canvas/ico-flx02.svg); } + &.ico03 { background-image: url(../../public/static/images/canvas/ico-flx03.svg); } + &.ico04 { background-image: url(../../public/static/images/canvas/ico-flx04.svg); } } + .name { font-size: 12px; color: #fff; } } + &.form06 { .name { font-size: 13px; } } } + .vertical-horizontal { display: flex; min-width: 170px; @@ -203,11 +243,13 @@ background: #373737; line-height: 28px; overflow: hidden; + span { padding: 0 10px; font-size: 13px; color: #fff; } + button { margin-left: auto; height: 100%; @@ -218,12 +260,14 @@ padding: 0 7.5px; transition: all 0.17s ease-in-out; } + &.on { button { background-color: #1083e3; } } } + .size-control { display: flex; align-items: center; @@ -234,10 +278,13 @@ width: 100px; height: 30px; margin: 0 5px; + span { font-size: 13px; color: #fff; + cursor: pointer; } + .control-btn { display: block; width: 12px; @@ -245,9 +292,11 @@ background-repeat: no-repeat; background-size: cover; background-position: center; + &.minus { background-image: url(../../public/static/images/canvas/minus.svg); } + &.plus { background-image: url(../../public/static/images/canvas/plus.svg); } @@ -255,6 +304,7 @@ } } } + .canvas-depth2-wrap { position: absolute; top: -100%; @@ -263,20 +313,24 @@ width: 100%; height: 50px; transition: all 0.17s ease-in-out; + .canvas-depth2-inner { display: flex; align-items: center; padding: 0 40px; height: 100%; + .canvas-depth2-list { display: flex; align-items: center; height: 100%; + .canvas-depth2-item { display: flex; align-items: center; margin-right: 26px; height: 100%; + button { position: relative; opacity: 0.55; @@ -286,10 +340,12 @@ height: 100%; padding-right: 12px; } + &.active { button { opacity: 1; font-weight: 600; + &:after { content: ''; position: absolute; @@ -304,17 +360,20 @@ } } } + .canvas-depth2-btn-list { display: flex; align-items: center; margin-left: auto; height: 100%; + .depth2-btn-box { display: flex; align-items: center; margin-right: 34px; height: 100%; transition: all 0.17s ease-in-out; + button { position: relative; font-size: 12px; @@ -322,6 +381,7 @@ height: 100%; color: #fff; padding-right: 12px; + &:after { content: ''; position: absolute; @@ -333,19 +393,23 @@ background: url(../../public/static/images/canvas/depth2-arr.svg) no-repeat center; } } + &:last-child { margin-right: 0; } + &.mouse { opacity: 0.55; } } } } + &.active { top: 47px; } } + &.active { padding-bottom: 50px; } @@ -355,18 +419,23 @@ .canvas-content { padding-top: 46.8px; transition: all 0.17s ease-in-out; + .canvas-frame { height: calc(100vh - 129.3px); } + &.active { padding-top: calc(46.8px + 50px); + .canvas-frame { height: calc(100vh - 179.4px); } } } + .canvas-layout { padding-top: 37px; + .canvas-page-list { position: fixed; top: 92.8px; @@ -378,13 +447,16 @@ min-width: 1280px; transition: all 0.17s ease-in-out; z-index: 99; + &.active { top: calc(92.8px + 50px); } + .canvas-plane-wrap { display: flex; align-items: center; max-width: calc(100% - 45px); + .canvas-page-box { display: flex; align-items: center; @@ -393,6 +465,7 @@ border-right: 1px solid #000; min-width: 0; transition: all 0.17s ease-in-out; + span { display: flex; align-items: center; @@ -404,6 +477,7 @@ text-overflow: ellipsis; overflow: hidden; } + .close { flex: none; display: block; @@ -413,24 +487,30 @@ background: url(../../public/static/images/canvas/plan_close_gray.svg) no-repeat center; background-size: cover; } + &.on { background-color: #fff; + span { font-weight: 600; color: #101010; } + .close { background: url(../../public/static/images/canvas/plan_close_black.svg) no-repeat center; } + &:hover { background-color: #fff; } } + &:hover { background-color: #000; } } } + .plane-add { display: flex; align-items: center; @@ -440,6 +520,7 @@ background-color: #1c1c1c; border-right: 1px solid #000; transition: all 0.17s ease-in-out; + span { display: block; width: 9px; @@ -447,6 +528,7 @@ background: url(../../public/static/images/canvas/plane_add.svg) no-repeat center; background-size: cover; } + &:hover { background-color: #000; } @@ -476,6 +558,7 @@ margin: 0 auto; background-color: #fff; } + canvas { position: absolute; top: 0; @@ -496,20 +579,25 @@ border-bottom: 1px solid #000; background: #2c2c2c; z-index: 999; + .sub-header-inner { display: flex; align-items: center; height: 100%; padding: 0 100px; + .sub-header-title-wrap { display: flex; align-items: center; + .title-item { position: relative; padding: 0 24px; + a { display: flex; align-items: center; + .icon { width: 22px; height: 22px; @@ -517,11 +605,13 @@ background-repeat: no-repeat; background-position: center; background-size: cover; + &.drawing { background-image: url(../../public/static/images/main/drawing_icon.svg); } } } + &:after { content: ''; position: absolute; @@ -532,31 +622,38 @@ height: 16px; background-color: #d9d9d9; } + &:first-child { padding-left: 0; } + &:last-child { padding-right: 0; + &:after { display: none; } } } } + .sub-header-title { font-size: 16px; color: #fff; font-weight: 600; } + .sub-header-location { margin-left: auto; display: flex; align-items: center; + .location-item { position: relative; display: flex; align-items: center; padding: 0 10px; + span { display: flex; font-size: 12px; @@ -564,6 +661,7 @@ font-weight: normal; cursor: default; } + &:after { content: ''; position: absolute; @@ -574,14 +672,18 @@ height: 6px; background: url(../../public/static/images/main/loaction_arr.svg) no-repeat center; } + &:first-child { padding-left: 0; } + &:last-child { padding-right: 0; + span { color: #fff; } + &:after { display: none; } @@ -594,56 +696,68 @@ // sub content .sub-content { padding-top: 46px; + .sub-content-inner { max-width: 1760px; margin: 0 auto; padding: 20px 20px 0; + .sub-content-box { margin-bottom: 20px; + &:last-child { margin-bottom: 0; } } } + &.estimate { display: flex; flex-direction: column; padding-top: 0; + .sub-content-inner { flex: 1; width: 100%; } } } + .sub-table-box { padding: 20px; border-radius: 6px; border: 1px solid #e9eaed; background: #fff; box-shadow: 0px 3px 30px 0px rgba(0, 0, 0, 0.02); + .table-box-title-wrap { display: flex; align-items: center; margin-bottom: 15px; + .title-wrap { display: flex; align-items: center; + h3 { display: block; font-size: 15px; color: #101010; font-weight: 600; margin-right: 14px; + &.product { margin-right: 10px; } } + .product_tit { position: relative; font-size: 15px; font-weight: 600; color: #1083e3; padding-left: 10px; + &::before { content: ''; position: absolute; @@ -655,27 +769,33 @@ background-color: #d9d9d9; } } + .option { padding-left: 5px; font-size: 13px; color: #101010; font-weight: 400; } + .info-wrap { display: flex; align-items: center; + li { position: relative; padding: 0 6px; font-size: 12px; color: #101010; font-weight: normal; + span { font-weight: 600; + &.red { color: #e23d70; } } + &:after { content: ''; position: absolute; @@ -686,11 +806,14 @@ height: 11px; background-color: #d9d9d9; } + &:first-child { padding-left: 0; } + &:last-child { padding-right: 0; + &::after { display: none; } @@ -699,11 +822,13 @@ } } } + .left-unit-box { margin-left: auto; display: flex; align-items: center; } + .promise-gudie { display: block; font-size: 13px; @@ -711,15 +836,18 @@ color: #101010; margin-bottom: 20px; } + .important { color: #f00; } + .sub-center-footer { display: flex; align-items: center; justify-content: center; margin-top: 20px; } + .sub-right-footer { display: flex; align-items: center; @@ -727,6 +855,7 @@ margin-top: 20px; } } + .pagination-wrap { margin-top: 24px; } @@ -738,19 +867,23 @@ .infomation-box-wrap { display: flex; gap: 10px; + .sub-table-box { flex: 1; } + .info-title { font-size: 14px; font-weight: 500; color: #344356; margin-bottom: 10px; } + .info-inner { position: relative; font-size: 13px; color: #344356; + .copy-ico { position: absolute; bottom: 0; @@ -768,6 +901,7 @@ display: flex; align-items: center; margin-bottom: 10px; + &.one { .estimate-box { &:last-child { @@ -775,14 +909,17 @@ } } } + .estimate-box { flex: 1; display: flex; align-items: center; + &:last-child { flex: none; min-width: 220px; } + .estimate-tit { width: 105px; height: 30px; @@ -794,16 +931,19 @@ font-weight: 500; color: #344356; } + .estimate-name { font-size: 13px; color: #344356; margin-left: 14px; font-weight: 400; + &.blue { font-size: 16px; font-weight: 700; color: #1083e3; } + &.red { font-size: 16px; font-weight: 700; @@ -811,6 +951,7 @@ } } } + &:last-child { margin-bottom: 0; } @@ -819,9 +960,11 @@ // file drag box .drag-file-box { padding: 10px; + .btn-area { padding-bottom: 15px; border-bottom: 1px solid #ecf0f4; + .file-upload { display: inline-block; height: 30px; @@ -834,14 +977,17 @@ font-weight: 500; cursor: pointer; transition: background 0.15s ease-in-out; + &:hover { background-color: #607f9a; } } } + .drag-file-area { position: relative; margin-top: 15px; + p { position: absolute; top: 50%; @@ -853,9 +999,11 @@ cursor: default; } } + .file-list { .file-item { margin-bottom: 15px; + span { position: relative; font-size: 13px; @@ -863,6 +1011,7 @@ font-weight: 400; white-space: nowrap; padding-right: 55px; + button { position: absolute; top: 50%; @@ -874,6 +1023,7 @@ background-size: cover; } } + &:last-child { margin-bottom: 0; } @@ -892,22 +1042,27 @@ background-image: url(../../public/static/images/canvas/estiment_arr.svg); background-size: 11px 7px; border-radius: 2px; + &.up { rotate: 180deg; } + &.on { background-color: #fff; border-color: #c2d0dd; background-image: url(../../public/static/images/canvas/estiment_arr_color.svg); } } + .estimate-check-wrap { .estimate-check-inner { display: block; } + &.hide { border-bottom: 1px solid #ecf0f4; margin-bottom: 15px; + .estimate-check-inner { display: none; } @@ -919,11 +1074,13 @@ grid-template-columns: repeat(5, 1fr); border-radius: 3px; margin-bottom: 30px; + .special-note-check-item { padding: 14px 10px; border: 1px solid #ecf0f4; margin-top: -1px; margin-right: -1px; + &.act { background-color: #f7f9fa; } @@ -937,38 +1094,47 @@ max-height: 350px; overflow-y: auto; margin-bottom: 30px; + dl { margin-bottom: 35px; + &:last-child { margin-bottom: 0; } + dt { font-size: 13px; font-weight: 600; color: #1083e3; margin-bottom: 15px; } + dd { font-size: 12px; font-weight: 400; color: #45576f; margin-bottom: 8px; + &:last-child { margin-bottom: 0; } } } + &::-webkit-scrollbar { width: 4px; background-color: transparent; } + &::-webkit-scrollbar-thumb { background-color: #d9dee2; } + &::-webkit-scrollbar-track { background-color: transparent; } } + .esimate-wrap { margin-bottom: 20px; } @@ -977,27 +1143,33 @@ display: flex; align-items: center; margin-bottom: 15px; + .product-price-wrap { display: flex; align-items: center; + .product-price-tit { font-size: 13px; font-weight: 400; color: #45576f; margin-right: 10px; } + .select-wrap { width: 110px; } } + .product-edit-wrap { display: flex; align-items: center; margin-left: auto; + .product-edit-explane { display: flex; align-items: center; margin-right: 15px; + .explane-item { position: relative; display: flex; @@ -1005,6 +1177,7 @@ padding: 0 10px; font-size: 12px; font-weight: 400; + span { width: 20px; height: 20px; @@ -1013,6 +1186,7 @@ background-repeat: no-repeat; background-position: center; } + &:before { content: ''; position: absolute; @@ -1023,55 +1197,71 @@ height: 12px; background-color: #d9d9d9; } + &:first-child { padding-left: 0; + &::before { display: none; } } + &:last-child { padding-right: 0; } + &.item01 { color: #3bbb48; + span { background-image: url(../../public/static/images/sub/open_ico.svg); } } + &.item02 { color: #909000; + span { background-image: url(../../public/static/images/sub/change_ico.svg); } } + &.item03 { color: #0191c9; + span { background-image: url(../../public/static/images/sub/attachment_ico.svg); } } + &.item04 { color: #f16a6a; + span { background-image: url(../../public/static/images/sub/click_check_ico.svg); } } } } + .product-edit-btn { display: flex; align-items: center; + button { display: flex; align-items: center; + span { width: 13px; height: 13px; margin-right: 5px; background-size: cover; + &.plus { background: url(../../public/static/images/sub/plus_btn.svg) no-repeat center; } + &.minus { background: url(../../public/static/images/sub/minus_btn.svg) no-repeat center; } @@ -1086,22 +1276,28 @@ display: flex; gap: 20px; width: 100%; + .sub-table-box { height: 100%; } + .chart-inner { flex: 1; + .chart-box { margin-bottom: 30px; } } + .chart-table-wrap { display: flex; flex-direction: column; flex: none; width: 650px; + .sub-table-box { flex: 1; + &:first-child { margin-bottom: 20px; } @@ -1115,6 +1311,7 @@ border-collapse: collapse; border: 1px solid #ecf0f4; border-radius: 4px; + thead { th { padding: 4.5px 0; @@ -1126,6 +1323,7 @@ background-color: #f8f9fa; } } + tbody { td { font-size: 13px; @@ -1140,10 +1338,12 @@ .simulation-guide-wrap { display: flex; padding: 20px; + .simulation-tit-wrap { flex: none; padding-right: 40px; border-right: 1px solid #eeeeee; + span { display: block; position: relative; @@ -1151,6 +1351,7 @@ font-size: 15px; color: #14324f; font-weight: 600; + &::before { content: ''; position: absolute; @@ -1164,27 +1365,33 @@ } } } + .simulation-guide-box { flex: 1; padding-left: 40px; + dl { margin-bottom: 25px; + dt { font-size: 13px; color: #101010; font-weight: 600; margin-bottom: 5px; } + dd { font-size: 12px; color: #45576f; font-weight: 400; line-height: 24px; } + &:last-child { margin-bottom: 0; } } + ul, ol { list-style: unset; @@ -1200,6 +1407,7 @@ margin-right: 4px; border: 1px solid #ecf0f4; border-top: none; + .total-title { flex: 1; text-align: center; @@ -1207,6 +1415,7 @@ color: #344356; font-weight: 500; } + .total-num { flex: none; width: 121px; @@ -1224,12 +1433,14 @@ background-color: #f4f4f4; border-radius: 4px; margin-bottom: 15px; + .information-help-tit-wrap { position: relative; display: flex; align-items: center; padding-right: 40px; border-right: 1px solid #e0e0e3; + .help-tit-icon { width: 40px; height: 40px; @@ -1238,20 +1449,24 @@ background: #fff url(../../public/static/images/sub/information_help.svg) no-repeat center; background-size: 20px 20px; } + .help-tit { font-size: 13px; font-weight: 600; color: #45576f; } } + .information-help-guide { padding-left: 40px; + span { display: block; font-size: 12px; font-weight: 400; color: #45576f; margin-bottom: 7px; + &:last-child { margin-bottom: 0; } @@ -1266,6 +1481,7 @@ padding: 10px 0 30px 0; border-bottom: 1px solid #e5e5e5; margin-bottom: 24px; + .community-search-box { position: relative; display: flex; @@ -1276,16 +1492,19 @@ margin-bottom: 20px; border-radius: 2px; border: 1px solid #101010; + .community-input { width: 100%; height: 100%; font-size: 13px; font-weight: 400; color: #101010; + &::placeholder { color: #c8c8c8; } } + .community-search-ico { position: absolute; top: 50%; @@ -1299,10 +1518,12 @@ z-index: 3; } } + .community-search-keyword { font-size: 13px; font-weight: 400; color: #45576f; + span { font-weight: 600; color: #f16a6a; @@ -1315,6 +1536,7 @@ display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; + .file-down-item { display: flex; align-items: center; @@ -1323,6 +1545,7 @@ border: 1px solid #e5e5e5; background: #fff; transition: all 0.15s ease-in-out; + .file-item-info { .item-num { display: inline-block; @@ -1334,24 +1557,28 @@ color: #101010; margin-bottom: 15px; } + .item-name { font-size: 16px; color: #101010; font-weight: 500; margin-bottom: 13px; } + .item-date { font-size: 13px; font-weight: 400; color: #344356; } } + .file-down-box { display: flex; align-items: center; flex: none; margin-left: auto; height: 100%; + .file-down-btn { width: 36px; height: 36px; @@ -1359,6 +1586,7 @@ background-size: cover; } } + &:hover { background-color: #f4f4f7; } @@ -1388,6 +1616,7 @@ background-color: #fafafa; border: 1px solid #eee; padding: 0 10px; + input { font-size: 13px; font-weight: 400; @@ -1398,6 +1627,7 @@ flex: 1; background-color: inherit; } + .product-delete { flex: none; display: block; @@ -1417,6 +1647,7 @@ margin-right: 5px; } } + .canvas-menu-item { button { padding: 15px 15px; @@ -1425,6 +1656,7 @@ } } } + .canvas-depth2-wrap { .canvas-depth2-inner { .canvas-depth2-list { @@ -1455,6 +1687,7 @@ .canvas-frame { height: calc(100vh - 129.5px); } + &.active { .canvas-frame { height: calc(100vh - 179.5px); @@ -1474,18 +1707,22 @@ } } } + .canvas-side-btn-wrap { .btn-from { gap: 3px; } + .vertical-horizontal { margin-right: 3px; min-width: 150px; } + .select-box { width: 100px; margin: 0 3px; } + .size-control { width: 90px; margin: 0 3px; @@ -1498,6 +1735,7 @@ .sub-header-title { font-size: 15px; } + .sub-header-title-wrap { .title-item { a { diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js index 3ed8f9a1..9a12e4b3 100644 --- a/src/util/qpolygon-utils.js +++ b/src/util/qpolygon-utils.js @@ -1201,7 +1201,7 @@ export function removeDuplicatePolygons(polygons) { } export const isSamePoint = (a, b) => { - return Math.abs(Math.round(a.x) - Math.round(b.x)) <= 1 && Math.abs(Math.round(a.y) - Math.round(b.y)) <= 1 + return Math.abs(Math.round(a.x) - Math.round(b.x)) <= 2 && Math.abs(Math.round(a.y) - Math.round(b.y)) <= 2 } /**