Files
PM_test/views/main/jsm/overview/overviewCommon.js
2026-06-12 17:14:03 +09:00

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 사용 함수 끝 🔼🔼🔼🔼🔼🔼🔼🔼🔼🔼