514 lines
20 KiB
JavaScript
514 lines
20 KiB
JavaScript
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 사용 함수 끝 🔼🔼🔼🔼🔼🔼🔼🔼🔼🔼
|