초기 PM 소스 전체 업로드
This commit is contained in:
9
views/main/jsm/overview/index.js
Normal file
9
views/main/jsm/overview/index.js
Normal 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
|
||||
514
views/main/jsm/overview/overviewCommon.js
Normal file
514
views/main/jsm/overview/overviewCommon.js
Normal 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 사용 함수 끝 🔼🔼🔼🔼🔼🔼🔼🔼🔼🔼
|
||||
453
views/main/jsm/overview/overviewDataManager.js
Normal file
453
views/main/jsm/overview/overviewDataManager.js
Normal 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 저장, 삭제 끝 🔼🔼🔼🔼🔼🔼🔼🔼🔼🔼
|
||||
765
views/main/jsm/overview/overviewModalManager.js
Normal file
765
views/main/jsm/overview/overviewModalManager.js
Normal 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 모달 끝 🔼🔼🔼🔼🔼🔼🔼🔼🔼🔼
|
||||
1130
views/main/jsm/overview/overviewPageRenderer.js
Normal file
1130
views/main/jsm/overview/overviewPageRenderer.js
Normal file
File diff suppressed because it is too large
Load Diff
44
views/main/jsm/overview/overviewVariable.js
Normal file
44
views/main/jsm/overview/overviewVariable.js
Normal 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;
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user