초기 PM 소스 전체 업로드

This commit is contained in:
koj729
2026-06-12 17:14:03 +09:00
commit 4e33c9a02a
1769 changed files with 377797 additions and 0 deletions

View File

@@ -0,0 +1,9 @@
import { getData } from './overviewDataManager.js';
await getData();
// overviewCommon.js : 아래에 있는 js에서 사용하는 함수들을 모아놓은 js
// overviewDataManager.js : 과업개요 db 통신 추가/수정/삭제
// overviewModalManager.js : 과업개요 모달 인터랙션 js
// overviewPageRenderer.js : 과업개요 페이지를 그리는 랜더링 js
// overviewVariable.js : 과업개요에서 사용 되는 전역변수 js

View File

@@ -0,0 +1,514 @@
import { vars } from '../archive/variable.js';
import { overviewVars } from './overviewVariable.js';
//🔽🔽🔽🔽🔽🔽🔽🔽🔽🔽 공용 함수 시작 🔽🔽🔽🔽🔽🔽🔽🔽🔽🔽
// 공동도급 형식 자르기
export function splitStr(data) {
if (data === null || data === undefined) return;
if (data.includes('^&')) {
const splitStr = data.split('^&');
return splitStr;
}
}
// 날짜 포맷팅
export function sliceDate(date){
if(!date) return null;
const formattedDate = date.slice(0,4) + '-' + date.slice(4,6) + '-' + date.slice(6,8);
return formattedDate;
}
export function overviewVarsInit() {
if(overviewVars.sectionTabData)overviewVars.sectionTabData = JSON.parse(JSON.stringify(overviewVars.originalSectionTabData))
overviewVars.deleteTaskHistory = [];
if (!arraysEqual(overviewVars.filesArr, overviewVars.originalFilesArr)) overviewVars.filesArr = [...overviewVars.originalFilesArr];
if (!arraysEqual(overviewVars.filesSizeArr, overviewVars.originalFilesSizeArr))overviewVars.filesSizeArr = [...overviewVars.originalFilesSizeArr];
if (!arraysEqual(overviewVars.filesNameArr, overviewVars.originalFilesNameArr)) overviewVars.filesNameArr = [...overviewVars.originalFilesNameArr];
overviewDropboxInit();
}
function arraysEqual(a, b) {
if (!Array.isArray(a) || !Array.isArray(b)) return false;
if (a.length !== b.length) return false;
return a.every((value, index) => value === b[index]);
}
export function setOverviewType(){
// overseas분기용
if(!window.location.host.toLowerCase().includes('overseas.')){
document.querySelectorAll('.only-overseas').forEach(ele => {
ele.remove();
})
overviewVars.overseas = false;
} else {
document.querySelectorAll('.no-overseas').forEach(ele => {
ele.remove();
});
overviewVars.overseas = true;
}
}
//🔼🔼🔼🔼🔼🔼🔼🔼🔼🔼 공용 함수 끝 🔼🔼🔼🔼🔼🔼🔼🔼🔼🔼
//🔽🔽🔽🔽🔽🔽🔽🔽🔽🔽 Section-Left 사용 함수 시작 🔽🔽🔽🔽🔽🔽🔽🔽🔽🔽
// 이미지 중복 이름, 확장자 체크 함수
export function checkDuplicatesFileNameAndExt(files){
const duplicateNameArr = [];
const notAllowExtArr = [];
const acceptExt = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg']
files.forEach(file => {
const fileExt = file.name.split('.').pop().toLowerCase();
if(!acceptExt.includes(fileExt)){
notAllowExtArr.push(file);
return;
}
const isDuplicateFile = overviewVars.filesArr.some(f => f.name === file.name);
const isDuplicateFileName = overviewVars.filesNameArr.includes('overview/' + file.name);
const isDuplicateFileSize = overviewVars.filesSizeArr.some(f => f.size === file.size);
if(!isDuplicateFile && !isDuplicateFileName && !isDuplicateFileSize){
overviewVars.filesArr.push(file);
overviewVars.filesNameArr.push(file.name);
if(overviewVars.filesSizeArr[0] === 0 ) overviewVars.filesSizeArr = [];
overviewVars.filesSizeArr.push(file.size);
} else {
duplicateNameArr.push(file);
}
});
if(duplicateNameArr.length > 0 || notAllowExtArr.length > 0){
alert(`다음 확장자만 등록할 수 있습니다 : \n- ${acceptExt}\n \n` + '그 외에 확장자 또는 중복된 파일은 등록할 수 없습니다.');
}
}
// DropBox에 파일 리스트 생성 함수
export function displayFileName(){
const dropbox = document.querySelector('.overview-modal.section-left .dropbox');
if(overviewVars.filesNameArr.length > 0 ){
dropbox.innerHTML = '';
dropbox.style.backgroundColor = '#fff'
dropbox.style.alignItems = 'stretch';
dropbox.style.justifyContent = 'normal';
}
overviewVars.filesNameArr.forEach(file => {
const originalName = file;
const displayName = file.replace('overview/', '');
const wrapDiv = document.createElement('div');
wrapDiv.classList.add('file-wrap-div');
wrapDiv.dataset.filepath = file;
const iconImg = document.createElement('img');
iconImg.src = '/main/img/overview/icon-imageFile.svg';
iconImg.classList.add('dropbox-file-img');
const fileName = document.createElement('h3');
fileName.innerText = displayName;
fileName.style.width = '95%';
const button = document.createElement('button');
button.classList.add('xs-btn');
button.addEventListener('click', async ()=> {
const fileIndex = overviewVars.filesArr.findIndex(f => f.name === displayName);
if(fileIndex !== -1) overviewVars.filesArr.splice(fileIndex, 1);
const nameIndex = overviewVars.filesNameArr.findIndex(f => f === file);
// 배열 삭제 전 지워야될 key 값 저장
let key = overviewVars.filesNameArr[nameIndex];
if(nameIndex !== -1){
overviewVars.filesNameArr.splice(nameIndex, 1);
overviewVars.filesSizeArr.splice(nameIndex, 1);
}
// object-storage에 저장된 file만 s3 api 이용해서 삭제
if(document.querySelector(`.overview .swiper-slide[data-filepath="${file}"]`)){
overviewVars.deleteImgArr.push(key);
}
overviewDropboxInit()
wrapDiv.remove();
});
const buttonimg = document.createElement('img');
buttonimg.src = '/main/img/overview/icon-close-111.svg';
button.appendChild(buttonimg);
wrapDiv.append(iconImg, fileName, button);
dropbox.appendChild(wrapDiv);
});
}
export function overviewDropboxInit(){
if(overviewVars.filesNameArr.length === 0){
const dropbox = document.querySelector('.overview-modal.section-left .dropbox');
dropbox.innerHTML = `<div class="dropbox-top">
<img class="icon" src="/main/img/overview/icon-imageFile.svg"
alt="icon-dropfile">
<p>
여기로 파일을 드래그하거나 <br>
<strong style="border-bottom: 0.063rem solid #777;">우측 상단에서 이미지 파일을
업로드하세요.</strong>
</p>
</div>`
dropbox.style.justifyContent = 'center';
dropbox.style.alignItems = 'center';
dropbox.style.backgroundColor = '#eee';
}
}
// 접근용 presignedUrl 발급
export async function getPresignedURL(key) {
const res = await axios.get(`/${vars.project_id}/overview/generateGetImgUrl`, { params: { key: key } });
const url = res.data;
return url;
};
// 삭제용 presignedUrl 발급 이후에 minIO 삭제요청
export async function generateDeleteImgUrl(key) {
if (key === undefined) return;
try {
const res = await axios.delete(`/${vars.project_id}/overview/generateDeleteImgUrl`, { data: { key } });
let { url } = res.data;
await axios.delete(url)
return true;
} catch (err) {
console.error(err);
}
}
// 업로드용 presignedURL 발급
export async function uploadImgData(file) {
try {
const presignedUrlRes = await axios.post(`/${vars.project_id}/overview/generateUploadUrl`, { fileName: file.name });
let { url, key } = presignedUrlRes.data;
const bucketRes = await axios.put(url, file, { headers: { 'Content-type': file.type || 'application/octet-stream' } });
if (bucketRes.status == 200) {
return key;
}
} catch (error) {
console.error('이미지 업로드 실패: ', error)
}
}
//🔼🔼🔼🔼🔼🔼🔼🔼🔼🔼 Section-Left 사용 함수 끝 🔼🔼🔼🔼🔼🔼🔼🔼🔼🔼
//🔽🔽🔽🔽🔽🔽🔽🔽🔽🔽 Section-Middle 사용 함수 시작 🔽🔽🔽🔽🔽🔽🔽🔽🔽🔽
// 공동도급표 구분자 추가 함수
export function addJointContractDelimiter() {
let representativeCompany = document.querySelector('.overview-modal .joint-contract-company-name')?.innerText;
let companyName = '';
document.querySelectorAll('.overview-modal .joint-contract-company-name').forEach((name) => {
companyName += name.innerText.trim().replace(/,/g, '') + '^&';
if (companyName === '^&') companyName = '';
});
let companyShares = '';
document.querySelectorAll('.overview-modal .joint-contract-company-shares').forEach((shares) => {
companyShares += shares.innerText.trim().replace(/,/g, '') + '^&';
if (companyShares === '^&') companyShares = '';
});
let contractKrw = '';
document.querySelectorAll('.overview-modal .joint-contract-krw').forEach((krw) => {
contractKrw += krw.innerText.trim().replace(/,/g, '') + '^&';
if (contractKrw === '^&') contractKrw = '';
});
let contractUsd = '';
document.querySelectorAll('.overview-modal .joint-contract-usd').forEach((usd) => {
contractUsd += usd.innerText.trim().replace(/,/g, '') + '^&';
if (contractUsd === '^&') contractUsd = '';
});
return { representativeCompany, companyName, companyShares, contractKrw, contractUsd }
}
// 공동도급 문자열 입력 제한 함수
export function restrictToNumber(element, event) {
let text = element.innerText;
const shares = element.classList.contains('joint-contract-company-shares');
const payment = element.classList.contains('joint-contract-krw') || element.classList.contains('joint-contract-usd');
// 지분율 소수점 허용
if (shares) {
text = text.replace(/[^0-9.]/g, '');
// 소수점 중복 제한 (숫자, .)
const parts = text.split('.');
let integerPart = parts[0].replace(/^0+(?=\d)/,'');
if(integerPart === '') integerPart = '0'
if (parts.length > 1) {
text = integerPart + '.' + parts.slice(1).join('');
} else {
text = integerPart;
}
element.innerText = text;
} else {
// 문자열 제한 (오직 숫자만)
text = text.replace(/[^0-9]/g, '');
}
// 계약금 , 생성
if (payment) {
//계약금 ,(콤마) 생성
if (isNaN(parseFloat(text))) {
element.innerText = '';
} else {
element.innerText = parseFloat(text).toLocaleString();
}
if (!(event.data >= '0' && event.data <= '9')) showToolTip(element, '숫자만 입력 가능합니다.');
} else {
element.innerText = text;
if (!((event.data >= '0' && event.data <= '9') || event.data == '.')) showToolTip(element, '숫자와 .만 입력 가능합니다.');
}
// 커서 맨끝으로 이동시키기
const range = document.createRange();
const sel = window.getSelection();
range.selectNodeContents(element);
range.collapse(false);
sel.removeAllRanges();
sel.addRange(range);
}
// 문자열 입력했을때 툴팁
export function showToolTip(element, message) {
let tooltip = document.querySelector('.overview-modal-tooltip');
if (!tooltip) {
tooltip = document.createElement('div');
tooltip.className = 'overview-modal-tooltip';
document.body.appendChild(tooltip);
}
if (tooltip.dataset.active === 'true') return;
tooltip.textContent = message;
tooltip.style.opacity = '0.8';
tooltip.style.visibility = 'visible';
tooltip.dataset.active = 'true';
const rect = element.getBoundingClientRect();
tooltip.style.left = `${rect.left + window.scrollX + (rect.width / 2) - (tooltip.offsetWidth / 2)}px`;
tooltip.style.top = `${rect.bottom + window.scrollY + 5}px`;
setTimeout(() => {
tooltip.style.opacity = '0';
tooltip.style.visibility = 'hidden';
tooltip.dataset.active = 'false'
}, 1000);
}
// 공동도급 지분율,계약금 계산
export function calculateTotalJointContract(element) {
let totalValue = 0;
let className = element.className;
if (className === 'joint-contract-company-name' || className === 'total-joint-contract-company-name' || className === 'total-joint-contract-company-shares' || className === 'total-joint-contract-krw'|| className === 'total-joint-contract-usd' || className === 'sticky') return;
document.querySelectorAll(`.overview-modal .${className}`).forEach(joint => {
let value = parseFloat(joint.innerText.replaceAll(',', '').trim());
if (isNaN(value)) value = 0;
totalValue += value;
});
if (isNaN(totalValue)) totalValue = 0;
if (className === 'joint-contract-company-shares') {
// 지분율 100%를 넘겼을 때 강제로 남은 지분율만큼만 입력되도록
if(totalValue > 100){
const excess = totalValue - 100;
let current = parseFloat(element.innerText);
element.innerText = current-excess;
document.querySelector(`.overview-modal .total-${className}`).innerText = 100;
showToolTip(element, '지분율은 100%를 넘길 수 없습니다.');
return;
}
document.querySelector(`.overview-modal .total-${className}`).innerText = totalValue;
} else {
document.querySelector(`.overview-modal .total-${className}`).innerText = totalValue.toLocaleString();
}
}
export function restrictDuplicatedTabName(){
// 중복 탭 이름 제한
const tabs = document.querySelectorAll('.overview-modal .section-tabs .tab');
const tabValues = [];
for(let tab of tabs){
const input = tab.querySelector('input');
const val = input ? input.value.trim() : '';
if(tabValues.includes(val)){
alert('중복된 탭 이름이 있습니다.')
return false;
}
tabValues.push(val);
}
return true;
}
//🔼🔼🔼🔼🔼🔼🔼🔼🔼🔼 Section-Middle 사용 함수 끝 🔼🔼🔼🔼🔼🔼🔼🔼🔼🔼
//🔽🔽🔽🔽🔽🔽🔽🔽🔽🔽 Section-Right 사용 함수 시작 🔽🔽🔽🔽🔽🔽🔽🔽🔽🔽
export function formatHour(hour) {
const normalized = (hour + 24) % 24;
const h = Math.floor(normalized); // 정수 시간
const m = Math.round((normalized - h) * 60); // 소수점 -> 분
return `${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}`;
}
export function parseTimeToDecimal(timeStr) {
const [h, m] = timeStr.split(':').map(Number);
return h + (m / 60);
}
// 1시간당 1.75rem 기준으로 위치 계산
export function calculateRemPosition(currentTime) {
const currentHour = parseInt(currentTime.split(':')[0]);
const currentMinute = parseInt(currentTime.split(':')[1]);
const hourRem = 1.75
const minuteRem = 1.75 / 60;
const moveHour = currentHour * hourRem;
const moveMinute = currentMinute * minuteRem;
// left 0으로 놓았을때 00:00에 위치하도록 보정
const moveRem = moveHour + moveMinute - 3.5;
return moveRem
}
// 타임존 계산을 통해 어느 장소에서든 동일한 결과값을 보장
export function getTimeToTimeZone(timeZone) {
const now = new Date();
const formatter = new Intl.DateTimeFormat('en-US', {
hour: '2-digit',
minute: '2-digit',
hour12: false,
timeZone: timeZone
});
return formatter.format(now);
}
// 연속일정 Color 변경 함수 --> 연속일정에 백그라운드 컬러를 그대로 넣었다간 일정이 보여지지 않아 투명도를 높인 색깔로 변경하여 연속일정에 적용
export function changeColor(color) {
let dataColor = null;
switch (color) {
case "#F21D0D": dataColor = "#FEE9E7"; break;
case "#B92ED1": dataColor = "#F8EBFB"; break;
case "#6D3DC2": dataColor = "#F1ECF9"; break;
case "#0D8DF2": dataColor = "#E7F4FE"; break;
case "#4DB251": dataColor = "#EEF8EE"; break;
case "#FFBF00": dataColor = "#FFF9E6"; break;
case "#A0705F": dataColor = "#F6F1EF"; break;
case "#7F7F7F": dataColor = "#F3F3F3"; break;
case "#688897": dataColor = "#F0F4F5"; break;
default: dataColor = "#FFFFFF";
}
return dataColor;
}
// 주요일정 모달 작동시 현재시간 적용
export function setDefaultScheduleTime() {
const now = new Date();
const yyyy = now.getFullYear();
const mm = (now.getMonth() + 1).toString().padStart(2, '0');
const dd = now.getDate().toString().padStart(2, '0');
const hh = now.getHours().toString().padStart(2, '0');
const min = now.getMinutes().toString().padStart(2, '0');
const today = `${yyyy}-${mm}-${dd}`;
const timeNow = `${hh}:${min}`;
const oneHourLater = new Date(now.getTime() + 60 * 60 * 1000);
const hh2 = oneHourLater.getHours().toString().padStart(2, '0');
const min2 = oneHourLater.getMinutes().toString().padStart(2, '0');
const timeLater = `${hh2}:${min2}`;
document.querySelector('.startDate').value = today;
document.querySelector('.endDate').value = today;
document.querySelector('.startTime').value = timeNow;
document.querySelector('.endTime').value = timeLater;
document.querySelector('.startTime').disabled = false
document.querySelector('.endTime').disabled = false
};
export function setIssueEditMode(isEdit){
const editBtn = document.querySelector('.overview .xs-btn-type.issue-edit');
const saveBtn = document.querySelector('.overview .xs-btn-type.issue-save');
const cancleBtn = document.querySelector('.overview .xs-btn-type.issue-cancle');
const issueMessage = document.querySelector('.overview .box.issue .wrap .message')
const issueContent = document.querySelector('.overview .box.issue .wrap .issue-content');
editBtn.style.display = isEdit ? 'none' : 'block';
saveBtn.style.display = isEdit ? 'block' : 'none';
cancleBtn.style.display = isEdit ? 'block' : 'none';
issueContent.disabled = !isEdit;
issueContent.style.color = isEdit ? '#ff3d00' : '#111';
if(isEdit){
issueMessage.classList.add('active');
issueMessage.innerText = '내용을 작성/수정한 후 저장 버튼을 눌러주세요';
issueMessage.style.color = '#111';
} else {
issueMessage.classList.remove('active');
issueMessage.innerText = '수정 버턴을 눌러 내용을 작성/수정할 수 있습니다.';
issueMessage.style.color = '#777';
}
if(isEdit && issueContent.value.length > 0) issueMessage.style.color = '#ddd';
}
export function applyUtcOffsetTime(offset){
const now = new Date();
const hourOffset = Math.trunc(offset/3600);
let minuteOffset = 0;
if(offset % 3600 !==0){
minuteOffset = (offset/3600) - hourOffset;
if(minuteOffset == 0.75) minuteOffset = 45;
if(minuteOffset == 0.5) minuteOffset = 30;
if(minuteOffset == 0.25) minuteOffset = 15;
}
const hours = now.getUTCHours() + hourOffset;
const minute = now.getUTCMinutes() + minuteOffset;
const date = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(), hours, minute))
return `${date.getUTCHours().toString().padStart(2,'0')}:${date.getUTCMinutes().toString().padStart(2,'0')}`;
}
export function fillPartialBlock(block, fraction, isStart) {
if (!block || fraction === 0) return;
const halfBlock = block.querySelectorAll('.timetable-half-block');
const quarterBlock = block.querySelectorAll('.timetable-quarter-block');
if(isStart){
if(fraction == 0.25){
quarterBlock[1].classList.add('work-hour');
halfBlock[1].classList.add('work-hour');
}
if(fraction == 0.5)halfBlock[1].classList.add('work-hour');
if(fraction == 0.75)quarterBlock[3].classList.add('work-hour');
} else {
if(fraction == 0.25)quarterBlock[0].classList.add('work-hour');
if(fraction == 0.5)halfBlock[0].classList.add('work-hour');
if(fraction == 0.75){
halfBlock[0].classList.add('work-hour');
quarterBlock[2].classList.add('work-hour');
}
}
}
//🔼🔼🔼🔼🔼🔼🔼🔼🔼🔼 Section-Right 사용 함수 끝 🔼🔼🔼🔼🔼🔼🔼🔼🔼🔼

View File

@@ -0,0 +1,453 @@
import { vars } from '../archive/variable.js';
import { checkProjectInactive } from '../main.js';
import { drawList, generateCalendar, startTimer, updateTimeBar, makeTaskHistory } from './overviewPageRenderer.js';
import { fileDragAndDrop } from './overviewModalManager.js';
import { addJointContractDelimiter, restrictDuplicatedTabName, uploadImgData, setIssueEditMode, generateDeleteImgUrl, setOverviewType } from './overviewCommon.js';
import { headerBtnForceClick, closeInitProgress } from '../archive/common.js';
import { overviewVars } from './overviewVariable.js';
// overviewDataManager.js는 db 통신처럼 데이터가 실질적으로 저장되고 삭제되는 함수 및 dom 조작을 모아놓은 js이다.
// getData는 tb_overview에 데이터를 가져와서 데이터를 함수들에게 뿌려주는 역할을 한다.
export async function getData() {
if (checkProjectInactive()) return;
try {
const res = await axios.get(`/${vars.project_id}/overview/getData`, {
params: { projectId: vars.project_id },
});
if (res.data.success) {
const data = res.data.data;
await setOverviewType();
await drawList(data);
//위치도 개요도 드래그앤 드롭
fileDragAndDrop();
// 현재 날짜로 달력 생성
generateCalendar(overviewVars.currentYear, overviewVars.currentMonth);
// 교류시간 동작
if(overviewVars.overseas)startTimer(data[0]?.nation_nm);
//////// pm-bcmf 연결용 테스트 코드 - 파라미터로 전달받은 시작경로로 화면 전환 (depth1)
if (vars.startPathDepth1) {
let headerBtn = document.querySelector(`body > .header .center .left.wrap .menu-tab .btn[data-resource-path="/${vars.startPathDepth1}"]`);
if (headerBtn) await headerBtnForceClick(headerBtn);
}
// 과업개요 생성 후
if (!vars.startPathDepth1 && !vars.startPathDepth2 && !vars.startPathDepth3) {
// // 시작경로가 없으면 초기 프로그레스 종료
// console.log('******** 과업개요 렌더링 후 초기 프로그레스 종료 (시작경로 없음)');
await closeInitProgress();
} else {
if (document.querySelectorAll('body > .header .center .menu-tab .folder-btn').length == 0) {
// // 시작경로가 있는데 헤더폴더버튼이 없으면 초기 프로그레스 종료
// console.log('******** 과업개요 렌더링 후 초기 프로그레스 종료 (시작경로 있음, 헤더폴더버튼 없음)');
await closeInitProgress();
} else {
if (JSON.parse(vars.userInfoString).user_id.includes('bcmf-') && vars.project_id == 'dsdj2') {
// 시작경로, 헤더폴더버튼이 있고 현재 계정이 bcmf계정이고 현재 프로젝트가 대산당진2공구인 경우 과업개요 숨김
document.querySelector('.overview-main').style.display = 'none';
} else {
// 시작경로, 헤더폴더버튼이 있고 현재 계정이 bcmf계정이 아니거나 현재 프로젝트가 대산당진2공구가 아닌 경우 과업개요 숨김
// console.log('******** 과업개요 렌더링 후 초기 프로그레스 종료 (현재 계정이 bcmf계정이 아니거나 현재 프로젝트가 대산당진2공구가 아님)');
// console.log(`현재 계정: ${JSON.parse(vars.userInfoString).user_id}`);
// console.log(`현재 프로젝트: ${vars.project_id}`);
closeInitProgress();
}
}
}
} else {
console.error("getData error");
}
} catch (error) {
console.error("getData error", error);
}
}
//🔽🔽🔽🔽🔽🔽🔽🔽🔽🔽 Section-Left 저장 시작 🔽🔽🔽🔽🔽🔽🔽🔽🔽🔽
// Section-left 저장
document.querySelector('.overview-modal.section-left .section-left-save')?.addEventListener('click', async () => {
const modal = document.querySelector('.overview-modal.section-left');
const modalWrapper = document.querySelector('.overview-modal-wrapper');
// // 시설규모 탭 제목만 입력 후 저장할때 경고문
// for(const key in overviewVars.sectionTabData){
// if (overviewVars.sectionTabData[key].length === 0) return alert('시설규모 탭에 입력되지 않은 항목이 있습니다. 작성 후 저장해 주세요.');
// }
// // 중복된 탭 이름 사용하지 못하게
// const duplicatedTabName = restrictDuplicatedTabName();
// if(!duplicatedTabName) return;
let locationImgKey;
let originFileSize;
try{
// 이미지 파일 업로드
for(let i = 0; overviewVars.filesArr.length > i; i++){
await uploadImgData(overviewVars.filesArr[i], overviewVars.filesArr.type);
}
// 이미지 파일 objectKey 형식으로 변환
for(let j = 0; overviewVars.filesNameArr.length > j; j++){
if(!overviewVars.filesNameArr[j].includes('overview/')) overviewVars.filesNameArr[j] = 'overview/'+ overviewVars.filesNameArr[j];
}
// JSON 문자화 시킨다음에 db에 삽입
locationImgKey = JSON.stringify(overviewVars.filesNameArr);
originFileSize = JSON.stringify(overviewVars.filesSizeArr);
// 파일 사이즈가 빈 배열일때 기본값 설정
if(originFileSize === '[]') originFileSize = '[0]';
} catch (err) {
console.error(err);
}
if(overviewVars.deleteImgArr.length > 0){
for(let i = 0; overviewVars.deleteImgArr.length > i; i++){
await generateDeleteImgUrl(overviewVars.deleteImgArr[i]);
}
await axios.post(`/${vars.project_id}/overview/updateOverviewImgData`, { projectId: vars.project_id, locationImgKey : locationImgKey, originFileSize : originFileSize });
}
const projectId = vars.project_id;
const businessPurpose = modal.querySelector('.business-purpose').value;
const performanceArea = modal.querySelector('.performance-area').value;
const referenceArea = modal.querySelector('.reference-area').value;
const facilityOverview = modal.querySelector('.facility-overview').value;
let data = {projectId, businessPurpose, performanceArea, referenceArea, facilityOverview, originFileSize, locationImgKey };
//overseas용 분기
if(!overviewVars.overseas){
data.nation = modal.querySelector('.nation').value;
data.continent = modal.querySelector('.continent').value;
} else {
data.nation = modal.querySelector('.nation').innerText;
data.continent = modal.querySelector('.continent').innerText;
}
// minIO에 저장, DB 저장이 완료되면 minIO에 기존에 있던 사진을 삭제
try {
const res = await axios.post(`/${projectId}/overview/saveSectionLeftData`, data);
if (res.data.message === '200') {
try{
// 탭만 입력했을때 기본값 세팅
for (const title in overviewVars.sectionTabData) {
if (!overviewVars.sectionTabData[title] || overviewVars.sectionTabData[title].length === 0) {
// 기본값 세팅
overviewVars.sectionTabData[title] = [ { key: '', value: '', id: null, projectId } ];
}
}
await upsertSectionTabData();
const secondRes = await axios.post(`/${projectId}/overview/saveSectionLeftTabData`, overviewVars.sectionTabData);
if (secondRes.data.message === '200') overviewVars.sectionTabData = {};
} catch(err){
console.error(err, 'sectionLeftTabData 저장 오류')
}
alert('저장이 완료되었습니다.');
modal.style.display = 'none';
modalWrapper.style.display = 'none';
getData();
updateTimeBar();
} else {
alert('파일 저장에 실패하였습니다.');
}
}catch(err){
console.error('S3 이미지 삭제 오류 : ' , err);
}
});
async function upsertSectionTabData() {
const deleteArr = [];
// 섹션별 비교
for (let sectionName in overviewVars.originalSectionTabData) {
const originalArr = overviewVars.originalSectionTabData[sectionName];
const currentArr = overviewVars.sectionTabData[sectionName] || [];
// currentArr에 없는 항목만 삭제 대상으로
const toDelete = originalArr.filter(item => {
return !currentArr.some(c => c.id === item.id);
});
if (toDelete.length > 0) {
deleteArr.push(...toDelete);
}
}
// 삭제 API 호출
try {
const deleteRes = await axios.delete(`/${vars.project_id}/overview/deleteCellData`, { data: { deleteArr: deleteArr }});
if(deleteRes.data.message === 200)console.log('overviewSectionLeftCellData 삭제 완료');
} catch (err) {
console.error('삭제 실패', cell.id, err);
}
}
// 🔼🔼🔼🔼🔼🔼🔼🔼🔼🔼 Section-Left 저장 끝 🔼🔼🔼🔼🔼🔼🔼🔼🔼🔼
//🔽🔽🔽🔽🔽🔽🔽🔽🔽🔽 Section-Middle 저장, 삭제 시작 🔽🔽🔽🔽🔽🔽🔽🔽🔽🔽
// section-middle 저장
document.querySelector('.overview-modal.section-middle .btn-active')?.addEventListener('click', async () => {
const modal = document.querySelector('.overview-modal.section-middle');
const modalWrapper = document.querySelector('.overview-modal-wrapper');
const projectId = vars.project_id;
const abbreviatedName = modal.querySelector('.abbreviated-name').value;
const taskNmEn = modal.querySelector('.task-nm-en').value;
const taskPurpose = modal.querySelector('.task-purpose').value;
const orderSizeUsd = modal.querySelector('.order-size-usd').value;
const orderSizeKrw = modal.querySelector('.order-size-krw').value;
const clientOrigin = modal.querySelector('.client-origin').value;
const financial = modal.querySelector('.financial').value;
const financialCountry = modal.querySelector('.financial-country').value;
const selectionMethod = modal.querySelector('.selection-method').value;
const supportDepartment = modal.querySelector('.support-department').value;
const supportManagerNm = modal.querySelector('.support-manager-nm').value;
const completionDate = modal.querySelector('.completion-date').value;
let data = {projectId, abbreviatedName, taskNmEn, taskPurpose, orderSizeUsd, orderSizeKrw, clientOrigin, financial, financialCountry, selectionMethod, supportDepartment, supportManagerNm, completionDate};
// overseas용 분기
if(!overviewVars.overseas){
data.projectNo = modal.querySelector('.project-no').value
data.taskType = modal.querySelector('.task-type').value
data.bid = modal.querySelector('.bid').value
data.relativeClient = modal.querySelector('.client').value
data.department = modal.querySelector('.department').value
data.taskNmKr = modal.querySelector('.task-nm-kr').value;
data.scheduledCommencementDate = modal.querySelector('.scheuled-commencement-date').value;
data.contractPeriod = modal.querySelector('.contract-period').value;
data.projectManagerNm = modal.querySelector('.projectmanager-nm').value;
data.managerNm = modal.querySelector('.manager-nm').value;
data.contractDate = modal.querySelector('.contract-date').value;
data.commencementDate = modal.querySelector('.commencement-date').value;
data.originalCompletionDate = modal.querySelector('.scheduled-completion-date').value;
// 공동도급 데이터 DB 삽입을 위한 식별자(^&)추가
const jointContract = addJointContractDelimiter();
data.jointContractComapnyName = jointContract.companyName;
data.jointContractShares = jointContract.companyShares;
data.jointContractKrw = jointContract.contractKrw;
data.jointContractUsd = jointContract.contractUsd;
data.representativeCompany = jointContract.representativeCompany;
} else {
data.projectNo = modal.querySelector('.project-no').innerText;
data.taskType = modal.querySelector('.task-type').innerText;
data.bid = modal.querySelector('.bid').innerText;
data.relativeClient = modal.querySelector('.client').innerText;
data.department = modal.querySelector('.department').innerText;
data.taskNmKr = modal.querySelector('.task-nm-kr').innerText;
data.scheduledCommencementDate = modal.querySelector('.scheuled-commencement-date').innerText;
data.contractPeriod = modal.querySelector('.contract-period').innerText.replace('개월', '');
data.projectManagerNm = modal.querySelector('.projectmanager-nm').innerText;
data.managerNm = modal.querySelector('.manager-nm').innerText;
data.contractDate = modal.querySelector('.contract-date').innerText.replaceAll('-', '');
data.commencementDate = modal.querySelector('.commencement-date').innerText.replaceAll('-', '');
data.originalCompletionDate = modal.querySelector('.scheduled-completion-date').innerText.replaceAll('-', '');
// 공동도급 데이터 DB 삽입을 위한 식별자(^&)추가
const jointContract = addJointContractDelimiter();
data.jointContractComapnyName = jointContract.companyName;
data.jointContractShares = jointContract.companyShares;
data.jointContractKrw = jointContract.contractKrw;
data.jointContractUsd = jointContract.contractUsd;
data.representativeCompany = jointContract.representativeCompany;
}
try {
const res = await axios.post(`/${projectId}/overview/saveSectionMiddleData`, data);
if (res.data.message = '200') {
alert('저장이 완료되었습니다.');
modal.style.display = 'none';
modalWrapper.style.display = 'none';
getData();
updateTimeBar();
} else {
alert('오류가 발생하였습니다.');
}
} catch (err) {
console.log(err);
}
});
// Section-Middle - 과업중지 이력 저장
document.querySelector('.overview-modal.task-period .modal-footer .btn-active')?.addEventListener('click', async () => {
const liList = document.querySelectorAll('.overview-modal.task-period .overview-modal-body ul li input');
const modal = document.querySelector('.overview-modal.task-period');
const modalWrapper = document.querySelector('.overview-modal-wrapper');
const saveLiArr = [];
if(overviewVars.deleteTaskHistory.length > 0)await deleteTaskHistory();
for (let i = 0; i < liList.length; i += 6) {
// 과업중지이력의 Value가 전부 미입력 상태일때는 저장하지 않는다.
const values = [liList[i].value, liList[i+1].value, liList[i+2].value, liList[i+3].value, liList[i+4].value, liList[i+5].value];
if(values.every(value => value === ''))continue;
saveLiArr.push({
projectId: vars.project_id,
order: liList[i].value,
suspensionDate: liList[i + 1].value,
suspensionReason: liList[i + 2].value,
resumptionDate: liList[i + 3].value,
consultationContent: liList[i + 4].value,
changeDate: liList[i + 5].value
});
}
try {
const res = await axios.post(`/${vars.project_id}/overview/saveTaskHistoryData`, saveLiArr);
if (res.data.message === '200') {
alert('과업중지 이력이 저장되었습니다.');
makeTaskHistory();
modal.style.display = 'none';
modalWrapper.style.display = 'none';
}
}
catch (err) {
alert('과업중지 이력 저장에 실패하였습니다.');
}
});
export async function deleteTaskHistory() {
if(overviewVars.deleteTaskHistory.length > 0){
try{
const res = await axios.delete(`/${vars.project_id}/overview/deleteTaskPeriodData`, { data: { deleteArr : overviewVars.deleteTaskHistory } });
if(res.data.message === '200') overviewVars.deleteTaskHistory = [];
} catch(err) {
console.error(err, '과업중지이력 삭제 실패');
}
}
}
// 🔼🔼🔼🔼🔼🔼🔼🔼🔼🔼 Section-Middle 저장, 삭제 끝 🔼🔼🔼🔼🔼🔼🔼🔼🔼🔼
//🔽🔽🔽🔽🔽🔽🔽🔽🔽🔽 Section-Right 저장, 삭제 시작 🔽🔽🔽🔽🔽🔽🔽🔽🔽🔽
// Section-Right calendar 주요일정 저장
document.querySelector('.overview-modal.schedule .schedule-save')?.addEventListener('click', async () => {
const modal = document.querySelector('.overview-modal.schedule');
const modalWrapper = document.querySelector('.overview-modal-wrapper');
const projectId = vars.project_id;
const country = document.querySelector('.overview-modal.schedule input[type=radio]:checked').value;
const color = document.querySelector('.overview-modal .color .label-color.on').dataset.color;
const title = document.querySelector('.overview-modal.schedule .schedule-title').value;
const content = document.querySelector('.overview-modal.schedule .schedule-content').value;
const startDate = document.querySelector('.overview-modal.schedule .startDate').value;
const startTime = document.querySelector('.overview-modal.schedule .startTime').value;
const endDate = document.querySelector('.overview-modal.schedule .endDate').value;
const endTime = document.querySelector('.overview-modal.schedule .endTime').value;
const scheduleId = document.querySelector('.overview-modal.schedule').dataset.scheduleId;
if (!title) return alert('일정의 제목을 입력해주세요.')
// 주요일정 하루종일 기능
const alldayChecked = document.querySelector('.overview-modal.schedule .custom-checkbox.all-day input').checked;
const startDateTimeStr = `${startDate}T${startTime}`;
const endDateTimeStr = `${endDate}T${endTime}`;
if(alldayChecked === true){
//날짜 유효성 검증
const startDay = new Date(startDate);
const endDay = new Date(endDate);
if(startDay > endDay) return alert('시작일은 종료일보다 늦을 수 없습니다. 다시 설정해주십시오.');
} else {
//날짜 + 시간 유효성 검증
const startDateTime = new Date(startDateTimeStr);
const endDateTime = new Date(endDateTimeStr);
if (startDateTime > endDateTime)return alert('시작일은 종료일보다 늦을 수 없습니다. 다시 설정해주십시오.');
}
let data = { scheduleId, projectId, country, color, title, content, startDateTimeStr, endDateTimeStr }
// 기존일정 id 삽입
if (scheduleId !== '') data.scheduleId = scheduleId;
try {
const res = await axios.post(`/${projectId}/overview/saveScheduleData`, data);
if (res.data.message == '200') {
modal.style.display = 'none';
modalWrapper.style.display = 'none';
alert('일정 저장이 완료되었습니다.');
generateCalendar(overviewVars.currentYear, overviewVars.currentMonth);
}
} catch (err) {
console.log(err);
}
});
// Section-Right calendar 주요일정 삭제
document.querySelector('.overview-modal.schedule .schedule-delete')?.addEventListener('click', async () => {
if (confirm('일정을 삭제하시겠습니까?')) {
const modal = document.querySelector('.overview-modal.schedule');
const modalWrapper = document.querySelector('.overview-modal-wrapper');
const scheduleId = document.querySelector('.overview-modal.schedule').dataset.scheduleId;
const res = await axios.delete(`/${vars.project_id}/overview/deleteScheduleData`, { data: { scheduleId } });
if (res.data.message = '200') {
alert('일정이 삭제되었습니다.');
modal.style.display = 'none';
modalWrapper.style.display = 'none';
generateCalendar(overviewVars.currentYear, overviewVars.currentMonth);
} else {
alert('삭제에 실패하였습니다.')
}
}
});
// 주요 현안 및 이슈
document.querySelector('.overview .xs-btn-type.issue-edit')?.addEventListener('click', () => {
setIssueEditMode(true)
});
// 메모 입력시 내용의 길이에 따라
document.querySelector('.overview .box.issue .wrap .issue-content')?.addEventListener('input', () => {
const issueMessage = document.querySelector('.overview .box.issue .wrap .message')
const issueContent = document.querySelector('.overview .box.issue .wrap .issue-content');
if(issueContent.value.length > 0){
issueMessage.style.color = '#ddd';
} else {
issueMessage.style.color = '#111';
}
});
document.querySelector('.overview .xs-btn-type.issue-save')?.addEventListener('click', async () => {
const projectId = vars.project_id;
const issueContent = document.querySelector('.overview .box.issue .wrap .issue-content');
const issueData = issueContent.value;
let data = {projectId, issueData}
try{
const result = await axios.post(`/${projectId}/overview/saveIssueData`, data);
if(result.data.message === '200'){
alert('주요 현안 및 이슈 저장 성공');
setIssueEditMode(false)
overviewVars.issueData = issueData;
}
} catch (err) {
console.error(err)
}
});
document.querySelector('.overview .xs-btn-type.issue-cancle')?.addEventListener('click', () => {
const issueContent = document.querySelector('.overview .box.issue .wrap .issue-content');
issueContent.value = overviewVars.issueData;
setIssueEditMode(false);
});
// 🔼🔼🔼🔼🔼🔼🔼🔼🔼🔼 Section-Right 저장, 삭제 끝 🔼🔼🔼🔼🔼🔼🔼🔼🔼🔼

View File

@@ -0,0 +1,765 @@
import { vars } from '../archive/variable.js';
import { displayFileName, checkDuplicatesFileNameAndExt , restrictToNumber, calculateTotalJointContract, showToolTip, overviewVarsInit } from './overviewCommon.js';
import { makeTaskHistory } from './overviewPageRenderer.js';
import { overviewVars } from './overviewVariable.js';
//🔽🔽🔽🔽🔽🔽🔽🔽🔽🔽 Overview 모달 공통 사용 시작 🔽🔽🔽🔽🔽🔽🔽🔽🔽🔽
// 모달CLSOE
const modalNames = ['facility-size', 'section-left', 'section-middle', 'schedule-list', 'schedule', 'task-period'];
modalNames.forEach(name => {
const modal = document.querySelector(`.overview-modal.${name}`);
if (!modal) return;
const closeBtn = modal.querySelector('.overview-modal-header--end img');
const footerCloseBtn = modal.querySelector('.modal-footer .btn-close');
closeBtn?.addEventListener('click', () => {
modal.style.display = 'none';
document.querySelector('.overview-modal-wrapper').style.display = 'none';
// 변수 초기화
overviewVarsInit();
});
footerCloseBtn?.addEventListener('click', () => {
modal.style.display = 'none';
document.querySelector('.overview-modal-wrapper').style.display = 'none';
// 변수 초기화
overviewVarsInit();
});
});
// 모달 영역 외에 클릭했을때 모달 꺼지는 이벤트
// document.querySelector('.overview-modal-wrapper')?.addEventListener('pointerdown', (e) => {
// document.querySelector('.overview-modal-wrapper').style.display = 'none';
// document.querySelectorAll('.overview-modal').forEach(modal => {
// if (!modal.contains(e.target)) {
// modal.style.display = 'none';
// }
// });
// });
// 🔼🔼🔼🔼🔼🔼🔼🔼🔼🔼 Overview 모달 공통 사용 끝 🔼🔼🔼🔼🔼🔼🔼🔼🔼🔼
//🔽🔽🔽🔽🔽🔽🔽🔽🔽🔽 Secion-Left 모달 시작 🔽🔽🔽🔽🔽🔽🔽🔽🔽🔽
// 위치도/개요도 드래그앤드롭
export function fileDragAndDrop() {
const dropbox = document.querySelector('.overview-modal .dropbox');
dropbox.addEventListener('dragenter', (e) => {
e.preventDefault();
dropbox.style.background = '#999';
});
dropbox.addEventListener('dragover', (e) => {
e.preventDefault();
dropbox.style.background = '#999';
});
dropbox.addEventListener('dragleave', (e) => {
e.preventDefault();
dropbox.style.background = '#e6e8ea';
});
dropbox.addEventListener('drop', (e) => {
e.preventDefault();
dropbox.style.background = '#e6e8ea';
const files = Array.from(e.dataTransfer.files);
// 중복 이름과 확장자 체크
checkDuplicatesFileNameAndExt(files);
displayFileName();
});
};
// section-left 모달 열기
document.querySelector('.overview .box-header--right.section-left-edit')?.addEventListener('click', () => {
const modal = document.querySelector('.overview-modal.section-left');
const modalWrapper = document.querySelector('.overview-modal-wrapper');
modal.querySelector('.business-purpose').value = document.querySelector('.overview .box-body.business-purpose').innerText;
modal.querySelector('.performance-area').value = document.querySelector('.overview .box-body.location-map-detail .performance-area').innerText;
modal.querySelector('.reference-area').value = document.querySelector('.overview .box-body.location-map-detail .reference-area').innerText;
modal.querySelector('.facility-overview').value = document.querySelector('.overview .box-body.facility-size .facility-overview').innerText;
//overseas용 분기
if(!overviewVars.overseas){
modal.querySelector('.nation').value = document.querySelector('.overview .box-body.location-map-detail .nation-nm').innerText;
modal.querySelector('.continent').value = document.querySelector('.overview .box-body.location-map-detail .continent').innerText;
} else {
modal.querySelector('.nation').innerText = document.querySelector('.overview .box-body.location-map-detail .nation-nm').innerText;
modal.querySelector('.continent').innerText = document.querySelector('.overview .box-body.location-map-detail .continent').innerText;
}
overviewVarsInit();
if(overviewVars.filesNameArr.length > 0) displayFileName();
modal.querySelectorAll('input[type=file]').forEach(input => { input.value = ''; })
makeTabCell(overviewVars.originalSectionTabData);
// 토글 처리
if (modal.style.display === 'block') {
modal.style.display = 'none';
return;
}
modal.style.display = 'block';
modalWrapper.style.display = 'block';
});
// section-left 모달 시설규모 탭,셀 생성 함수
export function makeTabCell(data) {
const tabContainer = document.querySelector('.section-tabs');
const contentContainer = document.querySelector('.section-content');
// 가로 스크롤 이벤트
tabContainer.addEventListener('wheel', (e) => {
e.preventDefault();
tabContainer.scrollLeft += e.deltaY;
});
tabContainer.innerHTML = '';
contentContainer.innerHTML = '';
// 데이터 없을 때 기본값
if (Object.keys(data).length === 0) {
data = { '': [{ key: '', value: '', id: '' }] };
}
const titles = Object.keys(data);
titles.forEach((title, index) => {
const tab = createTab(title, index === 0);
bindTabEvents(tab, contentContainer);
tabContainer.appendChild(tab);
});
// 탭 추가 버튼
const addBtn = document.createElement('button');
addBtn.classList.add('xs-btn', 'add');
addBtn.innerHTML = `<img class="icon" src="/main/img/overview/icon-add.svg" alt="icon-add">`;
tabContainer.appendChild(addBtn);
addBtn.addEventListener('click', () => {
const newTab = createTab('', true);
bindTabEvents(newTab, contentContainer, true);
tabContainer.insertBefore(newTab, addBtn);
newTab.click();
});
// 첫번째 탭 클릭
if (titles.length > 0) {
tabContainer.querySelector('.tab')?.click();
}
}
// 탭 생성
function createTab(title, isActive) {
const tab = document.createElement('div');
tab.className = 'tab';
if (isActive) tab.classList.add('click-on');
const input = document.createElement('input');
input.className = 'tab-style-input';
input.type = 'text';
input.value = title;
input.placeholder = '탭 제목 입력';
const closeBtn = document.createElement('button');
closeBtn.className = 'xs-btn';
closeBtn.innerHTML = `<img class="icon" src="/main/img/overview/icon-close-111.svg">`;
tab.appendChild(input);
tab.appendChild(closeBtn);
return tab;
}
// 탭 클릭, 제목입력, 삭제버튼 이벤트
function bindTabEvents(tab, contentContainer, isNew = false) {
const input = tab.querySelector('input');
const closeBtn = tab.querySelector('button');
// 탭 클릭 이벤트
tab.addEventListener('click', () => {
document.querySelectorAll('.section-tabs .tab').forEach(t => t.classList.remove('click-on'));
tab.classList.add('click-on');
renderCells(input.value.trim(), contentContainer);
});
// 탭 제목 input 변경 이벤트
let previousSection = input.value.trim();
input.addEventListener('input', () => {
const section = String(input.value.trim());
// 탭 제목이 빈값 일때 삭제 처리
if (section === '') {
if (previousSection && overviewVars.sectionTabData[previousSection]) delete overviewVars.sectionTabData[previousSection];
showToolTip(input, '탭 제목이 없어 셀 내용이 초기화되었습니다.')
// 셀 초기화
document.querySelectorAll('.section-content .section-content--cell').forEach((cell, index) => {
if(index === 0){
cell.querySelectorAll('input').forEach(input => input.value = '');
} else{
// 셀 추가 버튼을 건너뜀
if(cell.className === 'section-content--cell add-cell-btn') return;
cell.remove();
}
});
previousSection = '';
return;
}
// 탭 제목이 같을때 처리 막기
if (section !== previousSection && overviewVars.sectionTabData[section]){
showToolTip(input, '이미 존재하는 탭 이름입니다. 다른 이름을 입력해주세요.')
input.value = previousSection;
return;
}
// 탭 이름이 변경될때
if (previousSection && previousSection !== section) {
if (overviewVars.sectionTabData[previousSection]) {
overviewVars.sectionTabData[section] = overviewVars.sectionTabData[previousSection];
delete overviewVars.sectionTabData[previousSection];
} else {
overviewVars.sectionTabData[section] = [];
}
} else if (!overviewVars.sectionTabData[section]) {
overviewVars.sectionTabData[section] = [];
}
previousSection = section;
});
// 탭 삭제 이벤트
closeBtn.addEventListener('click', async (e) => {
e.stopPropagation();
const title = input.value;
const tabLength = document.querySelectorAll('.section-tabs .tab').length;
const tabInput = document.querySelector('.section-tabs .tab input');
const cell = document.querySelectorAll('.section-content--cell input');
const emptyTab = !tabInput?.value.trim();
const emptyCell = Array.from(cell).every(input => !input.value.trim());
// 탭이 한 개 남았고 공백일 경우 삭제 막기
if (tabLength === 1 && emptyTab && emptyCell) return;
try {
if (tabLength === 1) {
delete overviewVars.sectionTabData[title];
document.querySelector('.section-tabs .tab input').value = '';
document.querySelectorAll('.section-content .section-content--cell').forEach((cell, index) => {
if(index === 0){
cell.querySelectorAll('input').forEach(input => input.value = '');
} else{
// 셀 추가 버튼을 건너뜀
if(cell.className === 'section-content--cell add-cell-btn') return;
cell.remove();
}
});
} else {
delete overviewVars.sectionTabData[title];
tab.remove();
const allTabs = document.querySelectorAll('.section-tabs .tab');
const lastTab = allTabs[allTabs.length - 1];
if (lastTab) lastTab.click();
}
} catch (err) {
console.error(err);
}
});
}
// 셀 렌더딩
function renderCells(title, container) {
container.innerHTML = '';
let cells = overviewVars.sectionTabData[title] || [{ key: '', value: '', id: '' }];
// 타이틀만 있고 셀의 내용이 없을때 기본값 설정
if(cells.length === 0) cells = [{ key: '', value: '', id: '' }];
cells.forEach(cell => {
const row = createCell(cell);
bindCellEvents(row, title);
container.appendChild(row);
});
if (!container._hasInputEvent) {
container.addEventListener('input', (e) => {
if (!e.target.matches('input')) return;
const section = document.querySelector('.tab.click-on input').value.trim();
if (section === '') {
container.querySelectorAll('.section-content--cell input').forEach(cell => {
cell.value = ''
});
showToolTip(e.target, '탭의 제목을 먼저 입력해주세요')
return
}
const keyInputs = container.querySelectorAll('input.short-input');
const valueInputs = container.querySelectorAll('input.middle-input');
const keyValueList = [];
for (let i = 0; i < keyInputs.length; i++) {
const key = keyInputs[i].value;
const value = valueInputs[i] ? valueInputs[i].value : '';
const id = keyInputs[i].dataset.id ? parseInt(keyInputs[i].dataset.id) : null;
const projectId = vars.project_id;
keyValueList.push({ key, value, id, projectId });
}
overviewVars.sectionTabData[section] = keyValueList;
});
container._hasInputEvent = true;
}
// 셀 추가 버튼
const addCell = document.createElement('div');
addCell.className = 'section-content--cell add-cell-btn';
addCell.innerHTML = `
<img class="icon" src="/main/img/overview/icon-add-primary.svg" alt="icon-add-primary">
<h3>셀추가</h3>`;
addCell.addEventListener('click', () => {
const newCell = createCell({ key: '', value: '', id: null });
bindCellEvents(newCell, title);
container.insertBefore(newCell, addCell);
});
container.appendChild(addCell);
}
// 셀 생성
function createCell(cell) {
const row = document.createElement('div');
row.className = 'section-content--cell';
row.innerHTML = `
<input class="short-input" type="text" value="${cell.key ?? ''}" data-id="${cell.id ?? ''}" placeholder="셀 제목 입력">
<input class="middle-input" type="text" value="${cell.value ?? ''}" data-id="${cell.id ?? ''}" placeholder="셀 내용 입력">
`;
const btn = document.createElement('button');
btn.classList.add('xs-btn', 'cell-remove');
btn.innerHTML = `<img src="/main/img/overview/icon-close-111.svg">`;
row.appendChild(btn);
return row;
}
// 셀 삭제 버튼 이벤트 추가
function bindCellEvents(row, title) {
const btn = row.querySelector('.cell-remove');
const cellId = row.querySelector('input')?.dataset.id;
btn.addEventListener('click', async () => {
try {
// 기본생성된 셀 이벤트에서 title이 들어오지 않는 경우가 있어서 값을 직접 넣어줌
title = document.querySelector('.section-tabs .tab.click-on input').value;
const cells = document.querySelectorAll('.section-content--cell');
if (cellId) {
// DB 삭제 성공 시 sectionTabData 업데이트
const index = overviewVars.sectionTabData[title].findIndex(item => item.id == cellId);
if (index !== -1) overviewVars.sectionTabData[title].splice(index, 1);
} else {
// 탭 입력이 되지않고 sectionTabData에 data가 없을때 row 삭제처리
if(!overviewVars.sectionTabData[title]){
if(cells.length > 2){
return row.remove();
} else {
return;
}
}
const index = overviewVars.sectionTabData[title].findIndex(item => item.key === row.querySelector('.short-input')?.value && item.value === row.querySelector('.middle-input')?.value);
if(index !== -1) overviewVars.sectionTabData[title].splice(index, 1);
}
// 셀이 하나뿐이면 value 초기화, 아니면 삭제
if (cells.length === 2) {
row.querySelectorAll('input').forEach(input => input.value = '');
} else {
row.remove();
}
} catch (err) {
console.error(err);
}
});
}
// Section-left 시설규모 모달 OPEN
document.querySelector('.overview .box-header--right.facility-size button')?.addEventListener('click', async () => {
const modalWrapper = document.querySelector('.overview-modal-wrapper');
const modal = document.querySelector('.overview-modal.facility-size');
//토글
if (modal.style.display === 'block') {
modal.style.display = 'none';
modalWrapper.style.display = 'none';
return;
}
try {
const res = await axios.get(`/${vars.project_id}/overview/getFacilitySizeData`, { params: { projectId: vars.project_id } });
if (res.data.message === '200') {
const modalBody = modal.querySelector('.overview-modal-body');
modalBody.innerHTML = '';
const dataArr = res.data.data;
if (dataArr.length === 0) {
modalBody.innerHTML = `<img src="/main/img/overview/non-facilitySize-image.svg">`
}
let currentTitle = '';
for (let i = 0; i < dataArr.length; i++) {
if (dataArr[i].title !== currentTitle) {
currentTitle = dataArr[i].title;
const boxHeader = document.createElement('div');
boxHeader.className = 'box-header';
const h3 = document.createElement('h3');
h3.textContent = currentTitle;
boxHeader.appendChild(h3);
modalBody.appendChild(boxHeader);
}
const typeWrap = document.createElement('div');
typeWrap.className = 'type--wrap';
const left = document.createElement('div');
left.className = 'type--wrap-left';
const p = document.createElement('p');
p.textContent = dataArr[i].key;
left.appendChild(p);
const right = document.createElement('div');
right.className = 'type--wrap-right';
const h4 = document.createElement('h4');
h4.textContent = dataArr[i].value;
right.appendChild(h4);
typeWrap.appendChild(left);
typeWrap.appendChild(right);
modalBody.appendChild(typeWrap);
}
}
modal.style.display = 'block';
modalWrapper.style.display = 'block';
} catch (err) {
console.log(err);
}
});
// section-left 위치도/개요도 이미지 저장
document.querySelector('.overview-modal.section-left .overview-img-file-input')?.addEventListener('change', () => {
const files = Array.from(document.querySelector('.overview-modal.section-left .overview-img-file-input').files);
// 중복 이름과 확장자 체크
checkDuplicatesFileNameAndExt(files);
// 등록된 파일의 이름을 추가
displayFileName();
// 초기화
document.querySelector('.overview-modal.section-left .overview-img-file-input').value = '';
});
// 🔼🔼🔼🔼🔼🔼🔼🔼🔼🔼 Secion-Left 모달 끝 🔼🔼🔼🔼🔼🔼🔼🔼🔼🔼
//🔽🔽🔽🔽🔽🔽🔽🔽🔽🔽 Secion-Middle 모달 시작 🔽🔽🔽🔽🔽🔽🔽🔽🔽🔽
// section-middle 모달 열기
document.querySelector('.overview .box-header--right.section-middle-edit')?.addEventListener('click', () => {
const modal = document.querySelector('.overview-modal.section-middle');
const modalWrapper = document.querySelector('.overview-modal-wrapper');
modal.querySelector('.abbreviated-name').value = document.querySelector('.overview .abbreviated-name').innerText;
modal.querySelector('.task-nm-en').value = document.querySelector('.overview .task-nm-en').innerText;
modal.querySelector('.task-purpose').value = document.querySelector('.overview .task-purpose').innerText;
modal.querySelector('.order-size-usd').value = document.querySelector('.overview .order-size-usd').innerText;
modal.querySelector('.order-size-krw').value = document.querySelector('.overview .order-size-krw').innerText;
modal.querySelector('.client-origin').value = document.querySelector('.overview .client-origin').innerText;
modal.querySelector('.financial').value = document.querySelector('.financial').innerText;
modal.querySelector('.financial-country').value = document.querySelector('.overview .financial-country').innerText;
modal.querySelector('.selection-method').value = document.querySelector('.overview .selection-method').innerText;
modal.querySelector('.overview-table-container').innerHTML = document.querySelector('.overview .overview-table-container ').innerHTML;
modal.querySelector('.support-department').value = document.querySelector('.overview .support-department').innerText;
modal.querySelector('.support-manager-nm').value = document.querySelector('.overview .support-manager-nm').innerText;
modal.querySelector('.completion-date').value = document.querySelector('.overview .completion-date').innerText;
// overseas용 분기
if(!overviewVars.overseas){
modal.querySelector('.project-no').value = document.querySelector('.overview .project-no').innerText;
modal.querySelector('.task-type').value = document.querySelector('.overview .task-type').innerText;
modal.querySelector('.bid').value = document.querySelector('.overview .bid').innerText;
modal.querySelector('.client').value = document.querySelector('.overview .client').innerText;
modal.querySelector('.department').value = document.querySelector('.overview .department').innerText;
modal.querySelector('.currency-code').value = document.querySelector('.overview .currency-code').innerText;
modal.querySelector('.task-nm-kr').value = document.querySelector('.overview .task-nm-kr').innerText;
modal.querySelector('.contract-period').value = document.querySelector('.overview .contract-period').innerText;
modal.querySelector('.projectmanager-nm').value = document.querySelector('.overview .projectmanager-nm').innerText;
modal.querySelector('.manager-nm').value = document.querySelector('.overview .manager-nm').innerText;
modal.querySelector('.contract-date').value = document.querySelector('.overview .contract-date').innerText;
modal.querySelector('.commencement-date').value = document.querySelector('.overview .commencement-date').innerText;
modal.querySelector('.scheduled-completion-date').value = document.querySelector('.overview .scheduled-completion-date').innerText;
modal.querySelector('.scheuled-commencement-date').value = document.querySelector('.overview .scheuled-commencement-date').innerText;
modal.querySelectorAll('.joint-contract-company-name').forEach(company => { company.contentEditable = true; company.style.color = '#FF3D00'; })
// 공동도급 수정, 색상변화, 숫자 제한
modal.querySelectorAll('.joint-contract-company-shares, .joint-contract-krw, .joint-contract-usd').forEach(joint => {
joint.contentEditable = true;
joint.style.color = '#FF3D00';
joint.addEventListener('input', (event) => {
restrictToNumber(joint, event);
calculateTotalJointContract(joint);
});
});
} else {
modal.querySelector('.project-no').innerText = document.querySelector('.overview .project-no').innerText;
modal.querySelector('.task-type').innerText = document.querySelector('.overview .task-type').innerText;
modal.querySelector('.bid').innerText = document.querySelector('.overview .bid').innerText;
modal.querySelector('.client').innerText = document.querySelector('.overview .client').innerText;
modal.querySelector('.department').innerText = document.querySelector('.overview .department').innerText;
modal.querySelector('.currency-code').innerText = document.querySelector('.overview .currency-code').innerText;
modal.querySelector('.task-nm-kr').innerText = document.querySelector('.overview .task-nm-kr').innerText;
modal.querySelector('.contract-period').innerText = document.querySelector('.overview .contract-period').innerText;
modal.querySelector('.projectmanager-nm').innerText = document.querySelector('.overview .projectmanager-nm').innerText;
modal.querySelector('.manager-nm').innerText = document.querySelector('.overview .manager-nm').innerText;
modal.querySelector('.contract-date').innerText = document.querySelector('.overview .contract-date').innerText;
modal.querySelector('.commencement-date').innerText = document.querySelector('.overview .commencement-date').innerText;
modal.querySelector('.scheduled-completion-date').innerText = document.querySelector('.overview .scheduled-completion-date').innerText;
modal.querySelector('.scheuled-commencement-date').innerText = document.querySelector('.overview .scheuled-commencement-date').innerText;
}
const jointTable = modal.querySelector('.overview-table-container')
// 탭 컨테이너 가로스크롤 휠 이벤트 추가
jointTable.addEventListener('wheel', (e)=> {
e.preventDefault();
jointTable.scrollLeft += e.deltaY;
});
// 토글 처리
if (modal.style.display === 'block') {
modal.style.display = 'none';
return;
}
modal.style.display = 'block';
modalWrapper.style.display = 'block';
});
// section-middle 과업기간 모달 열기
document.querySelector('.overview .box-header--right.task-period')?.addEventListener('click', async () => {
const modal = document.querySelector('.overview-modal.task-period');
const modalWrapper = document.querySelector('.overview-modal-wrapper');
modal.querySelector('.contract-date').innerText = document.querySelector('.overview .contract-date').innerText;
modal.querySelector('.commencement-date').innerText = document.querySelector('.overview .commencement-date').innerText;
modal.querySelector('.scheduled-completion-date').innerText = document.querySelector('.overview .scheduled-completion-date').innerText;
modal.querySelector('.completion-date').innerText = document.querySelector('.overview .completion-date').innerText;
// 과업 중지 이력 생성
makeTaskHistory();
// 토글 처리
if (modal.style.display === 'block') {
modal.style.display = 'none';
return;
}
modal.style.display = 'block';
modalWrapper.style.display = 'block';
});
// section-middle 과업중지 이력 리스트 추가
document.querySelector('.overview-modal.task-period .box-header--right button')?.addEventListener('click', () => {
const ul = document.querySelector('.overview-modal.task-period .overview-modal-body ul');
const li = document.createElement('li');
// 차수
const numberDiv = document.createElement('div');
numberDiv.className = 'work-list-number';
const orderInput = document.createElement('input');
orderInput.type = 'text';
orderInput.className = 'order work-list-number';
numberDiv.appendChild(orderInput);
// 중지일자
const dateDiv = document.createElement('div');
dateDiv.className = 'work-list-date';
const dateInput = document.createElement('input');
dateInput.className = 'suspension-date work-list-date';
dateInput.type = 'date';
dateDiv.appendChild(dateInput);
// 중지사유
const reasonDiv = document.createElement('div');
reasonDiv.className = 'work-list-script';
const reasonInput = document.createElement('input');
reasonInput.className = 'suspension-reason work-list-script';
reasonInput.type = 'text';
reasonDiv.appendChild(reasonInput);
// 재개일자
const redateDiv = document.createElement('div');
redateDiv.className = 'work-list-redate';
const redateInput = document.createElement('input');
redateInput.className = 'resumption-date work-list-redate';
redateInput.type = 'date';
redateDiv.appendChild(redateInput);
// 협의내용
const detailDiv = document.createElement('div');
detailDiv.className = 'work-list-detail';
const consultInput = document.createElement('input');
consultInput.className = 'consuletation-content work-list-detail';
consultInput.type = 'text';
detailDiv.appendChild(consultInput);
// 변경 일자
const changeDiv = document.createElement('div');
changeDiv.className = 'work-list-changedate';
const changeInput = document.createElement('input');
changeInput.className = 'change-date work-list-changedate';
changeInput.type = 'date';
changeDiv.appendChild(changeInput);
const btn = document.createElement('button');
btn.className = 'xs-btn-type';
const img = document.createElement('img');
img.className = 'icon';
img.src = '/main/img/overview/icon-close-111.svg';
img.alt = 'icon-close-111';
btn.appendChild(img);
// 과업 중지이력 리스트 개별 삭제버튼
btn.addEventListener('click', async (e) => {
li.remove();
});
li.append(numberDiv, dateDiv, reasonDiv, redateDiv, detailDiv, changeDiv, btn);
ul.appendChild(li);
});
if(!overviewVars.overseas){
// section-middle 공동도급 열 추가
document.querySelector('.overview-modal.section-middle .joint-contract-add')?.addEventListener('click', () => {
const table = document.querySelector('.overview-modal.section-middle .overview-table-container table');
const headerRow = table.querySelector('thead tr');
const currentCols = headerRow.querySelectorAll('th').length;
// 총계와 주관사
const newIndex = currentCols - 1;
document.querySelector('.overview-modal .total-joint-contract-company-name').innerText = newIndex;
const newTh = document.createElement('th');
if (newIndex < 2) {
newTh.innerText = '주관사';
} else {
newTh.innerText = `공동도급${newIndex - 1}`;
}
headerRow.appendChild(newTh);
const tbodyRows = table.querySelectorAll('tbody tr');
const defaultValues = ['회사명', '0', '0', '0'];
const classList = ['joint-contract-company-name', 'joint-contract-company-shares', 'joint-contract-krw', 'joint-contract-usd'];
tbodyRows.forEach((row, idx) => {
const td = document.createElement('td');
td.className = classList[idx];
td.innerText = defaultValues[idx];
td.contentEditable = true;
td.style.color = '#FF3D00';
// 공동도급 숫자 제한
if (idx > 0) {
td.addEventListener('input', (event) => {
restrictToNumber(td, event);
calculateTotalJointContract(td);
});
}
row.appendChild(td);
});
});
// section-middle 공동도급 열 삭제
document.querySelector('.overview-modal.section-middle .joint-contract-delete')?.addEventListener('click', () => {
const table = document.querySelector('.overview-modal.section-middle .overview-table-container table');
const theadRow = table.querySelector('thead tr');
const ths = theadRow.querySelectorAll('th');
// 주관사까지 삭제했을때 계산할 값이 없으므로 초기화
if (ths.length === 3){
document.querySelector('.overview-modal .total-joint-contract-company-name').innerText = 0;
document.querySelectorAll('.overview-modal .total-joint-contract-company-shares, .overview-modal .total-joint-contract-krw, .overview-modal .total-joint-contract-usd').forEach(el => el.innerText = 0);
}
if (ths.length <= 2) {
alert("삭제할 수 있는 열이 없습니다.");
return;
}
// 마지막 th 삭제
theadRow.removeChild(ths[ths.length - 1]);
// tbody의 마지막 td 삭제
const tbodyRows = table.querySelectorAll('tbody tr');
tbodyRows.forEach(row => {
const tds = row.querySelectorAll('td');
if (tds.length > 2) {
row.removeChild(tds[tds.length - 1]);
}
});
// 삭제시에 총계 계산
document.querySelectorAll('.overview-modal.section-middle .overview-table-container td').forEach(td => {
calculateTotalJointContract(td);
});
// 총 회사 수 업데이트
document.querySelector('.overview-modal .total-joint-contract-company-name').innerText = ths.length - 3;
});
}
// 🔼🔼🔼🔼🔼🔼🔼🔼🔼🔼 Secion-Middle 모달 끝 🔼🔼🔼🔼🔼🔼🔼🔼🔼🔼
//🔽🔽🔽🔽🔽🔽🔽🔽🔽🔽 Secion-Right 모달 시작 🔽🔽🔽🔽🔽🔽🔽🔽🔽🔽
// section-right 캘린더 주요일정 하루종일 버튼
document.querySelector('.overview-modal.schedule .custom-checkbox.all-day')?.addEventListener('change', () => {
const allDay = document.querySelector('.overview-modal.schedule .custom-checkbox.all-day input');
const startTime = document.querySelector('.overview-modal.schedule .startTime');
const endTime = document.querySelector('.overview-modal.schedule .endTime');
const isChecked = allDay.checked;
startTime.disabled = isChecked;
endTime.disabled = isChecked;
if (isChecked) {
startTime.value = '';
endTime.value = '';
} else {
startTime.value = '00:00';
endTime.value = '23:59';
}
});
// section-right 주요일정 Color 클릭 효과
document.querySelectorAll('.overview-modal .color .label-color')?.forEach(btn => {
btn.addEventListener('click', (e) => {
document.querySelectorAll('.overview-modal .color .label-color').forEach(label => {
label.classList.remove('on');
});
e.target.classList.add('on');
});
});
// 🔼🔼🔼🔼🔼🔼🔼🔼🔼🔼 Secion-Right 모달 끝 🔼🔼🔼🔼🔼🔼🔼🔼🔼🔼

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,44 @@
export const overviewVars = {
// 시설규모 탭안에 셀요소들을 담아놓은 객체
sectionTabData : {},
originalSectionTabData : {},
// 캘린더 현재년도
currentYear : new Date().getFullYear(),
// 캘린더 현재월
currentMonth : new Date().getMonth(),
// 공동도급 정렬 배열
companyArr : [],
// 이미지 파일들을 담은 전역변수
filesArr : [],
// 이미지 파일이름만을 담은 전역변수
filesNameArr : [],
// 이미지 파일들의 size를 담은 전역변수
filesSizeArr : [],
originalFilesArr : [],
originalFilesNameArr : [],
originalFilesSizeArr : [],
deleteImgArr : [],
// DB에 있는 주요 현안 및 이슈 담은 전역변수
issueData : {},
// 삭제할 과업중지 이력을 모아놓을 배열
deleteTaskHistory : [],
}
// window.overviewVars = overviewVars;