초기 PM 소스 전체 업로드
This commit is contained in:
11
views/main/jsm/archive/ai_prompt/prompt.json
Normal file
11
views/main/jsm/archive/ai_prompt/prompt.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"title": "DocumentSummary",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"파일제목": { "type": "string" },
|
||||
"파일내용요약": { "type": "string" }
|
||||
},
|
||||
"required": [
|
||||
"파일제목", "파일내용요약"
|
||||
]
|
||||
}
|
||||
7
views/main/jsm/archive/ai_prompt/prompt.txt
Normal file
7
views/main/jsm/archive/ai_prompt/prompt.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
오타나 줄바꿈 오류가 있을 수 있으니 의미를 유추하여 정확한 정보를 추출해주세요.
|
||||
정확성이 매우 중요하므로 반드시 파일에 포함된 텍스트만 사용하여 작성해주세요. 추론하거나 임의로 보충하지 마세요.
|
||||
불필요한 설명 문구(예: "다음은 문서에 작성된 내용입니다", "문서 기반으로 요약해 드립니다" 등)와 특수문자(*등)는 포함하지 마세요.
|
||||
한 문장이 끝나면 다음줄에서 문장을 시작해주세요. 가독성 좋게 표출해주세요.
|
||||
모든 문서는 꼭 개조식으로 정리해서 표출해주세요.
|
||||
|
||||
1. 파일 내용 요약: 파일 내용을 요약해주세요. 반드시 한글로 작성합니다.
|
||||
5
views/main/jsm/archive/ai_prompt/rpsm_prompt.txt
Normal file
5
views/main/jsm/archive/ai_prompt/rpsm_prompt.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
project_name 값을 실제 프로젝트명으로 사용하고, "project_name"이라는 단어를 그대로 쓰지 마.
|
||||
project_name 값이 null 이면 'err: project name is null' 이라는 단어가 출력되게 해줘
|
||||
project_name에 대한 온라인 반응을 최신 보도와 공식 문서 중심으로 정리해줘. 추측은 금지야 .
|
||||
아래 예제 형식처럼 글만 나오게 해줘. '결과입니다' 같은 불필요한 말은 하지마.
|
||||
글 정리는 개조식으로 진행해줘.
|
||||
503
views/main/jsm/archive/common.js
Normal file
503
views/main/jsm/archive/common.js
Normal file
@@ -0,0 +1,503 @@
|
||||
import { vars } from './variable.js';
|
||||
|
||||
// 파일 사이즈 변환
|
||||
export function formatBytes(bytes) {
|
||||
const abs = Math.abs(bytes);
|
||||
let size, unit;
|
||||
|
||||
if (abs < 1024) {
|
||||
size = addCommasToNumber(bytes);
|
||||
unit = ' Bytes';
|
||||
} else if (abs < 1048576) {
|
||||
size = addCommasToNumber((bytes / 1024).toFixed(2));
|
||||
unit = ' KB';
|
||||
} else if (abs < 1073741824) {
|
||||
size = addCommasToNumber((bytes / 1048576).toFixed(2));
|
||||
unit = ' MB';
|
||||
} else if (abs < 1099511627776) {
|
||||
size = addCommasToNumber((bytes / 1073741824).toFixed(2));
|
||||
unit = ' GB';
|
||||
} else {
|
||||
size = addCommasToNumber((bytes / 1099511627776).toFixed(2));
|
||||
unit = ' TB';
|
||||
}
|
||||
|
||||
if (size != 0) {
|
||||
let decimalPointNum = size.split('.')[1];
|
||||
if (decimalPointNum) {
|
||||
// 소수점 아래 숫자가 00인 경우
|
||||
if (decimalPointNum == '00') size = size.split('.')[0];
|
||||
// 소수점 아래 두번째 숫자가 0인 경우
|
||||
else if (decimalPointNum[1] == '0') size = `${size.split('.')[0]}.${decimalPointNum[0]}`;
|
||||
}
|
||||
}
|
||||
|
||||
return size + unit;
|
||||
}
|
||||
|
||||
// 1000 단위 콤마 추가
|
||||
// export function addCommasToNumber(num) {
|
||||
// return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
||||
// }
|
||||
|
||||
export function addCommasToNumber(num) {
|
||||
const isNegative = Number(num) < 0;
|
||||
const parts = Math.abs(Number(num)).toString().split('.');
|
||||
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
||||
|
||||
const formatted = parts.join('.');
|
||||
return isNegative ? `-${formatted}` : formatted;
|
||||
}
|
||||
|
||||
export function getDepth(path) {
|
||||
path = path.startsWith('/') ? path.slice(1) : path;
|
||||
let pathSplit = path.split('/');
|
||||
let result = pathSplit.length;
|
||||
return result;
|
||||
}
|
||||
|
||||
export function updatePartialPath(sourcePath, targetPath) {
|
||||
let sourcePathSplit = sourcePath.split('/');
|
||||
let targetPathSplit = targetPath.split('/');
|
||||
for (let i = 0; i < sourcePathSplit.length; i++) {
|
||||
targetPathSplit[i] = sourcePathSplit[i];
|
||||
}
|
||||
|
||||
return targetPathSplit.join('/');
|
||||
}
|
||||
|
||||
// 파일명, 확장자 분리
|
||||
export function splitBaseAndExt(target, isLowerExt) {
|
||||
let targetSplit = target.split('.');
|
||||
let ext = targetSplit.pop();
|
||||
if (isLowerExt) ext = ext.toLowerCase();
|
||||
let base = targetSplit.join('.');
|
||||
return { ext: ext, base: base }
|
||||
}
|
||||
|
||||
// 선택한 대상이 포함된 상위 폴더 경로 추출
|
||||
export function splitTopPathAndTargetName(resourcePath) {
|
||||
let resourcePathSplit = resourcePath.split('/');
|
||||
let targetName = resourcePathSplit.pop();
|
||||
let topPath = resourcePathSplit.join('/');
|
||||
if (!topPath || topPath == '' || topPath == null) topPath = '/';
|
||||
return { targetName: targetName, topPath: topPath };
|
||||
}
|
||||
|
||||
export function extractPathByLength(target, count) {
|
||||
if (!Array.isArray(target)) {
|
||||
target = target.split('/');
|
||||
}
|
||||
const parts = target.filter(Boolean).slice(0, count);
|
||||
return '/' + parts.join('/');
|
||||
}
|
||||
|
||||
// 특수문자를 체크하는 정규표현식
|
||||
export function hasSpecialChar(target) {
|
||||
// const pattern = /[\/\\;:?`'"<>|#%&*]/;
|
||||
const pattern = /[\/\\:*?'"`<>|#]/;
|
||||
return pattern.test(target);
|
||||
}
|
||||
|
||||
// 동일한 폴더명/파일명 존재 여부 확인
|
||||
export function existEqualName(type, comparisonPath, targetName) {
|
||||
let result;
|
||||
if (comparisonPath != '' && comparisonPath != undefined) {
|
||||
result = getTypeDataFromFolderData(type, comparisonPath)[targetName];
|
||||
} else {
|
||||
// result = vars.currentTreeObject[type][targetName];
|
||||
result = vars.allTreeObject[type][targetName];
|
||||
}
|
||||
|
||||
// 헤더 버튼 이름 변경 시 페이지 버튼과 이름 같은 경우 변경 불가
|
||||
let pageBtnNameArr = ['과업개요', '공문'];
|
||||
if (comparisonPath == '/' && result == undefined) result = pageBtnNameArr.includes(targetName);
|
||||
|
||||
return result;
|
||||
|
||||
// folderData에서 타입(폴더/파일)과 경로를 사용해서 해당 경로의 타입별 데이터 가져오기
|
||||
function getTypeDataFromFolderData(type, path) {
|
||||
let data;
|
||||
if (type == 'folder') data = vars.allTreeObject[type];
|
||||
if (type == 'file') data = vars.currentTreeObject[type];
|
||||
|
||||
let pathSplit = path.split('/');
|
||||
pathSplit.shift();
|
||||
// if (pathSplit[0] == '') return data;
|
||||
|
||||
for (let i = 0; i < pathSplit.length; i++) {
|
||||
if (data[pathSplit[i]]) {
|
||||
if (type == 'folder') data = data[pathSplit[i]]['child'][type];
|
||||
if (type == 'file') data = data;
|
||||
} else {
|
||||
data = data;
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
export function getNextFileName(nameArr, newName) {
|
||||
const dotIdx = newName.lastIndexOf('.');
|
||||
const base = dotIdx !== -1 ? newName.slice(0, dotIdx) : newName;
|
||||
const ext = dotIdx !== -1 ? newName.slice(dotIdx) : '';
|
||||
const set = new Set(nameArr);
|
||||
|
||||
let maxNum = 0;
|
||||
const regex = new RegExp(`^${escapeRegExp(base)}(?: \\((\\d+)\\))?${escapeRegExp(ext)}$`);
|
||||
for (const name of set) {
|
||||
const match = name.match(regex);
|
||||
if (match) {
|
||||
const num = match[1] ? parseInt(match[1], 10) : 0;
|
||||
if (num > maxNum) maxNum = num;
|
||||
}
|
||||
}
|
||||
|
||||
if (maxNum === 0 && !set.has(newName)) return newName;
|
||||
return `${base} (${maxNum + 1})${ext}`;
|
||||
|
||||
function escapeRegExp(str) {
|
||||
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
}
|
||||
}
|
||||
|
||||
export function getDataFromTreeObject(path, type, data) {
|
||||
let parentData;
|
||||
if (!data) data = vars.allTreeObject;
|
||||
let pathSplit = path.split('/');
|
||||
pathSplit.shift();
|
||||
|
||||
for (let i = 0; i < pathSplit.length; i++) {
|
||||
if (i != pathSplit.length - 1) {
|
||||
if (data['folder'][pathSplit[i]]) {
|
||||
data = data['folder'][pathSplit[i]]['child'];
|
||||
}
|
||||
} else {
|
||||
if (type == 'folder') {
|
||||
parentData = data;
|
||||
data = data['folder'][pathSplit[i]];
|
||||
}
|
||||
if (type == 'file') {
|
||||
if (data['file'][pathSplit[i]]) {
|
||||
parentData = data;
|
||||
data = data['file'][pathSplit[i]];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return { parentData: parentData, data: data };
|
||||
}
|
||||
|
||||
export function getCurrentTreeObject(path) {
|
||||
let data = vars.allTreeObject;
|
||||
let pathSplit = path.split('/');
|
||||
pathSplit.shift();
|
||||
for (let i = 0; i < pathSplit.length; i++) {
|
||||
if (i != pathSplit.length - 1) {
|
||||
if (data['folder'][pathSplit[i]]) {
|
||||
data = data['folder'][pathSplit[i]]['child'];
|
||||
}
|
||||
} else {
|
||||
if (data['folder'][pathSplit[i]]) data = data['folder'][pathSplit[i]]['child'];
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
export function targetFocus(target, behavior = 'smooth') {
|
||||
target.scrollIntoView({
|
||||
behavior: behavior, // 부드럽게
|
||||
block: 'center', // 화면 중앙에 위치하도록 (start, end, nearest 도 가능)
|
||||
inline: 'center'
|
||||
});
|
||||
target.focus();
|
||||
}
|
||||
|
||||
export function pxToRem(px) {
|
||||
let remPx = parseFloat(getComputedStyle(document.documentElement).fontSize);
|
||||
return px / remPx;
|
||||
}
|
||||
|
||||
export function buildResourcePathFromSegments(rows) {
|
||||
if (!Array.isArray(rows)) rows = [rows];
|
||||
let result = [];
|
||||
rows.map(row => {
|
||||
let resourcePath = '/';
|
||||
let maxDepth = 8;
|
||||
let arr = [];
|
||||
for (let i = 0; i < maxDepth; i++) {
|
||||
let num = i+1;
|
||||
let segment = row[`path${num}`];
|
||||
if (segment) arr.push(segment);
|
||||
}
|
||||
if (arr.length != 0) resourcePath += arr.join('/');
|
||||
result.push(resourcePath);
|
||||
})
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function headerBtnForceClick(headerBtn) {
|
||||
headerBtn.click();
|
||||
}
|
||||
|
||||
export function treeBtnForceClick(treeBtn) {
|
||||
treeBtn.click();
|
||||
}
|
||||
|
||||
export function closeInitProgress() {
|
||||
document.querySelector('.init-progress').style.display = 'none';
|
||||
}
|
||||
|
||||
export async function getMyDownloadList(){
|
||||
let res = await axios.get(`${vars.path_name}/getMyDownloadList`, {params : {user_id : JSON.parse(vars.userInfoString).user_id}});
|
||||
// console.log(res.data);
|
||||
if(res.data.length > 0){
|
||||
if(document.querySelector('.download-btn .download-reddot')){
|
||||
document.querySelector('.download-btn .download-reddot').style.display = 'block';
|
||||
}
|
||||
}else{
|
||||
if(document.querySelector('.download-btn .download-reddot')){
|
||||
document.querySelector('.download-btn .download-reddot').style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
let ul = document.querySelector('.download-modal__body_list');
|
||||
ul.innerHTML = '';
|
||||
for(let i = 0; i < res.data.length; i++){
|
||||
let li = document.createElement('li');
|
||||
li.classList.add('download-modal__body_list_item');
|
||||
|
||||
let expireDate = new Date(res.data[i].expire_date);
|
||||
let now = new Date();
|
||||
let yesterday = new Date(now.getTime() - 24 * 60 * 60 * 1000);
|
||||
|
||||
let state = (expireDate < yesterday)? `<img class="--button__xsmall-icon --disable" src="/main/img/archive/icon__download--111.svg" alt="/" />`: (!res.data[i].made)?`준비중`:`<img class="--button__xsmall-icon downloadFolder__btn__icon" src="/main/img/archive/icon__download--111.svg" alt="/" />`
|
||||
let expire =(state == '준비중')?state :(expireDate < yesterday)? '만료됨': formatDateTime(expireDate);
|
||||
|
||||
li.innerHTML = `
|
||||
<img class="--icon" src="/main/img/header/header-folder-icon.svg" alt="/" />
|
||||
<div class="download-modal__body_list_item_text">
|
||||
<h4 class="download-modal__body_list_item_text_title">
|
||||
${res.data[i].name}
|
||||
</h4>
|
||||
<p class="download-modal__body_list_item_text_subtitle">
|
||||
${res.data[i].project_nm}${res.data[i].path}
|
||||
</p>
|
||||
</div>
|
||||
<h6 class="download-modal__body_list_item_time">
|
||||
${expire}
|
||||
</h6>
|
||||
<div class="download-modal__body_list_item_status">${state}</div>
|
||||
`;
|
||||
|
||||
ul.appendChild(li);
|
||||
|
||||
li.querySelector('.downloadFolder__btn__icon')?.addEventListener('click', () => {
|
||||
window.location.href = res.data[i].url;
|
||||
})
|
||||
}
|
||||
|
||||
function formatDateTime(date) {
|
||||
const padZero = (num) => String(num).padStart(2, '0');
|
||||
|
||||
const year = date.getFullYear();
|
||||
const month = padZero(date.getMonth() + 1); // getMonth()는 0부터 시작하므로 +1
|
||||
const day = padZero(date.getDate());
|
||||
const hours = padZero(date.getHours());
|
||||
const minutes = padZero(date.getMinutes());
|
||||
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}`;
|
||||
}
|
||||
}
|
||||
|
||||
export async function openNewWindowViewer() {
|
||||
let isRecycleBinModal = document.querySelector('.recycle-bin-modal').style.display == 'flex';
|
||||
let lastListItem = (isRecycleBinModal) ? vars.lastListItem_bin : vars.lastListItem;
|
||||
|
||||
let resourcePath = lastListItem.dataset.resourcePath;
|
||||
let dataId = lastListItem.dataset.id;
|
||||
let isLowerExt = true, ext = splitBaseAndExt(resourcePath, isLowerExt).ext;
|
||||
|
||||
let thumbnail_key = getDataFromTreeObject(resourcePath, 'file', vars.currentTreeObject).data?.thumbnailKey;
|
||||
|
||||
//Presigned URL
|
||||
let PresignedUrl = undefined;
|
||||
let getDataInfoParams = {
|
||||
userInfoString: vars.userInfoString,
|
||||
storageType: vars.storageType,
|
||||
dataIdArr: [dataId],
|
||||
isRemoved: isRecycleBinModal,
|
||||
debug: "'우측 미리보기 새 창으로 열기 버튼 클릭 이벤트'에서 실행"
|
||||
}
|
||||
let getDataInfoRes = await axios.post(`${vars.path_name}/getDataInfo`, { params: getDataInfoParams } );
|
||||
if (getDataInfoRes.data.message == 'getDataInfo_success') {
|
||||
let objectKey = getDataInfoRes.data.result.popup_key;
|
||||
if(objectKey == undefined || objectKey == `` || objectKey == null){
|
||||
return;
|
||||
}
|
||||
let generateDownloadUrlParams = {
|
||||
objectKey: objectKey,
|
||||
resourcePath: resourcePath
|
||||
}
|
||||
let generateDownloadUrlRes = await axios.post(`${vars.path_name}/generateDownloadUrl`, generateDownloadUrlParams);
|
||||
if (generateDownloadUrlRes.data.message == 'generateDownloadUrl_success') {
|
||||
PresignedUrl = generateDownloadUrlRes.data.url;
|
||||
}
|
||||
}
|
||||
//Presigned URL end
|
||||
|
||||
|
||||
thumbnail_key = encodeURIComponent(thumbnail_key);
|
||||
resourcePath = encodeURIComponent(resourcePath);
|
||||
let fullPath = encodeURIComponent(PresignedUrl);
|
||||
|
||||
// 뷰어 기본 width값은 현재 화면 width값의 절반
|
||||
let denominator = 2;
|
||||
// url 뷰어는 현재 화면 width값과 동일하게 설정
|
||||
if (ext == 'url') denominator = 1;
|
||||
|
||||
let width = screen.width / denominator;
|
||||
let height = screen.height;
|
||||
let left = screen.width * 2 + 10;
|
||||
|
||||
let open_ext = `pdf`;
|
||||
switch(ext){
|
||||
case 'pdf' :
|
||||
case 'hwp' :
|
||||
case 'hwpx' :
|
||||
case 'xls' :
|
||||
case 'xlsm' :
|
||||
case 'ppt' :
|
||||
case 'pptx' :
|
||||
case 'doc' :
|
||||
case 'docx' :
|
||||
case 'dwg' :
|
||||
case 'dxf' :
|
||||
case 'grm' :
|
||||
open_ext = 'pdf';
|
||||
break
|
||||
case 'gsim' :
|
||||
open_ext = 'gsim';
|
||||
break
|
||||
case 'ifc' :
|
||||
open_ext = 'ifc';
|
||||
break
|
||||
case 'png' :
|
||||
case 'jpg' :
|
||||
case 'jpeg' :
|
||||
case 'webp' :
|
||||
case 'gif' :
|
||||
open_ext = 'png';
|
||||
break
|
||||
case 'mp4' :
|
||||
case 'mov' :
|
||||
case 'webm' :
|
||||
open_ext = 'mp4';
|
||||
break
|
||||
case 'log' :
|
||||
case 'txt' :
|
||||
case 'md' :
|
||||
open_ext = 'txt';
|
||||
break
|
||||
case 'url' :
|
||||
open_ext = 'url';
|
||||
break;
|
||||
case 'zip' :
|
||||
open_ext = 'zip';
|
||||
break
|
||||
case 'glb':
|
||||
case 'gltf':
|
||||
case 'obj':
|
||||
case 'stl':
|
||||
case 'fbx':
|
||||
case '3dm':
|
||||
open_ext = 'glb';
|
||||
break;
|
||||
case 'html':
|
||||
open_ext = 'html';
|
||||
break;
|
||||
}
|
||||
|
||||
//presigned url은 ext를 읽을수 없엉...
|
||||
const jsonData = JSON.stringify({$ext : open_ext});
|
||||
const encodedData = btoa(jsonData);
|
||||
|
||||
// 3d모델뷰어 썸네일 생성을 위해 dataId, path_name 추가
|
||||
// let popup = window.open(`/popup?path=${fullPath}&data=${encodedData}`, '', `width=${width}, height=${height}, left=${left},`);
|
||||
let popup = window.open(`/popup?thumbnail_key=${thumbnail_key}&path_name=${vars.path_name}&resourcePath=${resourcePath}&dataId=${dataId}&path=${fullPath}&data=${encodedData}`, '', `width=${width}, height=${height}, left=${left},`);
|
||||
}
|
||||
|
||||
export function initFileAreaUI(fileAreaMode) {
|
||||
let listContainer = document.querySelector('.archive-main .archive-main-center .list-container');
|
||||
let isRecycleBinModal = document.querySelector('.recycle-bin-modal').style.display == 'flex';
|
||||
if (isRecycleBinModal) listContainer = document.querySelector('.recycle-bin-modal .recycle-bin-wrap .list-container');
|
||||
|
||||
let listHeader = listContainer.querySelector('.list-header');
|
||||
let listBody = listContainer.querySelector('.list-body');
|
||||
let listBodyItemWrap = listBody.querySelector('.list-item-wrap');
|
||||
let mapContainer = listBody.querySelector('.map-container');
|
||||
|
||||
let archiveMainRight = document.querySelector('.archive-main-right');
|
||||
let viewerHeader = archiveMainRight.querySelector('.viewer-header');
|
||||
|
||||
if (fileAreaMode == 'map') {
|
||||
listHeader.style.display = 'none';
|
||||
listBody.style.height = '100%';
|
||||
listBody.style.overflowY = 'hidden';
|
||||
listBodyItemWrap.style.display = 'none';
|
||||
if (mapContainer) mapContainer.style.display = 'flex';
|
||||
|
||||
archiveMainRight.style.minWidth = '36rem';
|
||||
archiveMainRight.style.maxWidth = '36rem';
|
||||
archiveMainRight.style.height = 'calc(100% - 2.75rem)';
|
||||
archiveMainRight.style.position = 'absolute';
|
||||
archiveMainRight.style.right = '0.625rem';
|
||||
archiveMainRight.style.top = '0.5rem';
|
||||
archiveMainRight.style.background = '#fffa';
|
||||
archiveMainRight.style.backdropFilter = 'blur(0.2rem)';
|
||||
archiveMainRight.style.webkitBackdropFilter = 'blur(0.2rem)';
|
||||
archiveMainRight.style.borderRadius = '0.25rem';
|
||||
archiveMainRight.style.boxShadow = '#ccc 0 0 0rem 0.0625rem, #0c0c0db3 0rem 0.5rem 1rem -0.25rem';
|
||||
viewerHeader.style.borderRadius = '0.25rem 0.25rem 0 0';
|
||||
} else {
|
||||
listHeader.style.display = 'flex';
|
||||
listBody.style.height = 'calc(100% - 2.25rem)';
|
||||
listBody.style.overflowY = 'scroll';
|
||||
listBodyItemWrap.style.display = 'flex';
|
||||
if (mapContainer) mapContainer.style.display = 'none';
|
||||
|
||||
archiveMainRight.style.minWidth = '41rem';
|
||||
archiveMainRight.style.maxWidth = '41rem';
|
||||
archiveMainRight.style.height = '100%';
|
||||
archiveMainRight.style.position = 'relative';
|
||||
archiveMainRight.style.right = 'unset';
|
||||
archiveMainRight.style.top = 'unset';
|
||||
archiveMainRight.style.background = '#fff';
|
||||
archiveMainRight.style.backdropFilter = 'none';
|
||||
archiveMainRight.style.webkitBackdropFilter = 'none';
|
||||
archiveMainRight.style.borderRadius = 'unset';
|
||||
archiveMainRight.style.boxShadow = 'none';
|
||||
viewerHeader.style.borderRadius = 'unset';
|
||||
}
|
||||
}
|
||||
|
||||
export function syncGroupStyle() {
|
||||
let listContainer = document.querySelector('.archive-main .archive-main-center .list-container');
|
||||
let isRecycleBinModal = document.querySelector('.recycle-bin-modal').style.display == 'flex';
|
||||
if (isRecycleBinModal) listContainer = document.querySelector('.recycle-bin-modal .recycle-bin-wrap .list-container');
|
||||
|
||||
listContainer.querySelectorAll('.main-list-item').forEach(item => {
|
||||
if (listContainer.querySelectorAll(`.list-item.selected[data-main-file-name="${item.dataset.mainFileName}"]`).length > 0) {
|
||||
listContainer.querySelectorAll(`.list-item[data-main-file-name="${item.dataset.mainFileName}"]`).forEach(item => {
|
||||
item.classList.add('group-style');
|
||||
item.classList.remove('non-selected');
|
||||
})
|
||||
} else {
|
||||
listContainer.querySelectorAll(`.list-item[data-main-file-name="${item.dataset.mainFileName}"]`).forEach(item => {
|
||||
item.classList.remove('group-style');
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
2190
views/main/jsm/archive/dataManager.js
Normal file
2190
views/main/jsm/archive/dataManager.js
Normal file
File diff suppressed because it is too large
Load Diff
2976
views/main/jsm/archive/eventManager.js
Normal file
2976
views/main/jsm/archive/eventManager.js
Normal file
File diff suppressed because it is too large
Load Diff
348
views/main/jsm/archive/fileDrag.js
Normal file
348
views/main/jsm/archive/fileDrag.js
Normal file
@@ -0,0 +1,348 @@
|
||||
import { vars } from './variable.js';
|
||||
import {
|
||||
targetFocus,
|
||||
syncGroupStyle
|
||||
} from './common.js';
|
||||
import {
|
||||
resetViewer,
|
||||
} from './pageRenderer.js';
|
||||
|
||||
let archiveMain = document.querySelector('.archive-main');
|
||||
let viewerContainer = archiveMain?.querySelector('.archive-main-right .viewer-container');
|
||||
|
||||
addFileDragEvent(document.querySelector('.archive-main-center .list-container .list-body'), 'file-area');
|
||||
addFileDragEvent(document.querySelector('.recycle-bin-modal .list-container .list-body'), 'recycle-bin');
|
||||
|
||||
export function addFileDragEvent(listBody, target) {
|
||||
let draggingStartTarget, draggingEndTarget;
|
||||
let draggingStart = false, isDragging = false;
|
||||
let isCtrl = false, isShift = false;
|
||||
let processedItems = new Set();
|
||||
let originalSelection = new Set();
|
||||
|
||||
let multiSelectBox = document.createElement('div');
|
||||
multiSelectBox.style.position = 'absolute';
|
||||
multiSelectBox.style.border = '1px solid rgba(0, 120, 215, 1)';
|
||||
multiSelectBox.style.background = 'rgba(0, 120, 215, 0.2)';
|
||||
multiSelectBox.style.zIndex = '9999';
|
||||
multiSelectBox.style.pointerEvents = 'none';
|
||||
multiSelectBox.style.display = 'none';
|
||||
listBody.appendChild(multiSelectBox);
|
||||
|
||||
vars.preventNextClick = false;
|
||||
|
||||
let startX = 0;
|
||||
let startY = 0;
|
||||
let prevClientX = 0;
|
||||
let prevClientY = 0;
|
||||
|
||||
let autoScrollDirection = null;
|
||||
let autoScrollFrameId = null;
|
||||
const scrollSpeed = 8;
|
||||
const scrollMargin = 36;
|
||||
|
||||
function autoScrollLoop() {
|
||||
if (!autoScrollDirection) return;
|
||||
|
||||
// 더 작으면 느리고 부드러움
|
||||
if (autoScrollDirection === 'down') {
|
||||
listBody.scrollTop += scrollSpeed;
|
||||
} else if (autoScrollDirection === 'up') {
|
||||
listBody.scrollTop -= scrollSpeed;
|
||||
}
|
||||
|
||||
autoScrollFrameId = requestAnimationFrame(autoScrollLoop);
|
||||
}
|
||||
|
||||
listBody.addEventListener('pointerdown', (e) => {
|
||||
// 마우스 좌클릭이 아닌 경우 리턴
|
||||
if (e.button !== 0) return;
|
||||
// 갤러리 폴더 지도화면인 경우 리턴
|
||||
if (target == 'file-area' && listBody.querySelector('.map-container').style.display === 'flex') return;
|
||||
|
||||
draggingStartTarget = e.target;
|
||||
draggingStart = true;
|
||||
isDragging = false;
|
||||
isCtrl = e.ctrlKey;
|
||||
isShift = e.shiftKey;
|
||||
processedItems.clear();
|
||||
if (target == 'file-area') originalSelection = new Set(vars.multiSelectListItemArr);
|
||||
if (target == 'recycle-bin') originalSelection = new Set(vars.multiSelectListItemArr_bin);
|
||||
vars.preventNextClick = false;
|
||||
|
||||
// if (isCtrl) {
|
||||
// vars.lastListItem = e.target.closest('.list-item');
|
||||
// } else {
|
||||
// listBody.querySelectorAll('.list-item').forEach(listItem => {
|
||||
// listItem.classList.remove('selected');
|
||||
// listItem.classList.remove('group-style');
|
||||
// })
|
||||
|
||||
// vars.multiSelectListItemArr = [];
|
||||
// }
|
||||
|
||||
const dragStartListItem = e.target.closest('.list-item');
|
||||
if (dragStartListItem && !isShift) {
|
||||
if (target == 'file-area') vars.lastListItem = dragStartListItem;
|
||||
if (target == 'recycle-bin') vars.lastListItem_bin = dragStartListItem;
|
||||
}
|
||||
|
||||
if (!isCtrl) {
|
||||
// Ctrl이 아닐 경우 기존 선택 모두 해제
|
||||
listBody.querySelectorAll('.list-item').forEach(listItem => {
|
||||
listItem.classList.remove('selected');
|
||||
listItem.classList.remove('group-style');
|
||||
});
|
||||
|
||||
if (target == 'file-area') vars.multiSelectListItemArr = [];
|
||||
if (target == 'recycle-bin') vars.multiSelectListItemArr_bin = [];
|
||||
}
|
||||
|
||||
const bodyRect = listBody.getBoundingClientRect();
|
||||
startX = e.clientX + listBody.scrollLeft - bodyRect.left;
|
||||
startY = e.clientY + listBody.scrollTop - bodyRect.top;
|
||||
|
||||
prevClientX = e.clientX;
|
||||
prevClientY = e.clientY;
|
||||
|
||||
Object.assign(multiSelectBox.style, {
|
||||
left: `${startX}px`,
|
||||
top: `${startY}px`,
|
||||
width: `0px`,
|
||||
height: `0px`,
|
||||
display: 'none'
|
||||
});
|
||||
});
|
||||
|
||||
listBody.addEventListener('pointermove', (e) => {
|
||||
// 마우스 좌클릭이 아닌 경우 리턴
|
||||
if (e.buttons !== 1) return;
|
||||
// list-body에서 마우스 드래그가 시작된게 아닌 경우 리턴
|
||||
if (!draggingStart) return;
|
||||
|
||||
if (e.clientX === prevClientX && e.clientY === prevClientY) return;
|
||||
prevClientX = e.clientX;
|
||||
prevClientY = e.clientY;
|
||||
|
||||
const listBodyRect = listBody.getBoundingClientRect();
|
||||
|
||||
const currX = e.clientX + listBody.scrollLeft - listBodyRect.left;
|
||||
const currY = e.clientY + listBody.scrollTop - listBodyRect.top;
|
||||
|
||||
const dx = currX - startX;
|
||||
const dy = currY - startY;
|
||||
|
||||
if (Math.abs(dx) > 5 || Math.abs(dy) > 5) {
|
||||
isDragging = true;
|
||||
vars.preventNextClick = true;
|
||||
|
||||
const left = Math.min(startX, currX);
|
||||
const top = Math.min(startY, currY);
|
||||
const width = Math.abs(dx);
|
||||
const height = Math.abs(dy);
|
||||
|
||||
Object.assign(multiSelectBox.style, {
|
||||
left: `${left}px`,
|
||||
top: `${top}px`,
|
||||
width: `${width}px`,
|
||||
height: `${height}px`,
|
||||
display: 'block'
|
||||
});
|
||||
|
||||
const boxRect = multiSelectBox.getBoundingClientRect();
|
||||
|
||||
listBody.querySelectorAll('.list-item .wrap').forEach(elem => {
|
||||
const rect = elem.getBoundingClientRect();
|
||||
const overlaps = !(
|
||||
rect.right < boxRect.left ||
|
||||
rect.left > boxRect.right ||
|
||||
rect.bottom < boxRect.top ||
|
||||
rect.top > boxRect.bottom
|
||||
);
|
||||
|
||||
let listItem = elem.parentElement;
|
||||
|
||||
if (isCtrl) {
|
||||
if (overlaps && !processedItems.has(listItem)) {
|
||||
processedItems.add(listItem);
|
||||
|
||||
// ✅ 토글: 원래 선택되어 있었으면 해제, 아니면 선택
|
||||
if (originalSelection.has(listItem)) {
|
||||
listItem.classList.remove('selected');
|
||||
listItem.classList.remove('group-style');
|
||||
if (target == 'file-area') vars.multiSelectListItemArr = vars.multiSelectListItemArr.filter(item => item !== listItem);
|
||||
if (target == 'recycle-bin') vars.multiSelectListItemArr_bin = vars.multiSelectListItemArr_bin.filter(item => item !== listItem);
|
||||
} else {
|
||||
listItem.classList.add('selected');
|
||||
listItem.classList.add('group-style');
|
||||
if (target == 'file-area') vars.multiSelectListItemArr.push(listItem);
|
||||
if (target == 'recycle-bin') vars.multiSelectListItemArr_bin.push(listItem);
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ 박스에서 벗어난 항목 복원
|
||||
if (!overlaps && processedItems.has(listItem)) {
|
||||
processedItems.delete(listItem);
|
||||
|
||||
if (originalSelection.has(listItem)) {
|
||||
listItem.classList.add('selected');
|
||||
listItem.classList.add('group-style');
|
||||
if (target == 'file-area') {
|
||||
if (!vars.multiSelectListItemArr.includes(listItem)) vars.multiSelectListItemArr.push(listItem);
|
||||
}
|
||||
if (target == 'recycle-bin') {
|
||||
if (!vars.multiSelectListItemArr_bin.includes(listItem)) vars.multiSelectListItemArr_bin.push(listItem);
|
||||
}
|
||||
} else {
|
||||
listItem.classList.remove('selected');
|
||||
listItem.classList.remove('group-style');
|
||||
if (target == 'file-area') vars.multiSelectListItemArr = vars.multiSelectListItemArr.filter(item => item !== listItem);
|
||||
if (target == 'recycle-bin') vars.multiSelectListItemArr_bin = vars.multiSelectListItemArr_bin.filter(item => item !== listItem);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
listItem.classList.toggle('selected', overlaps);
|
||||
listItem.classList.toggle('group-style', overlaps);
|
||||
|
||||
if (target == 'file-area') {
|
||||
if (overlaps && !vars.multiSelectListItemArr.includes(listItem)) vars.multiSelectListItemArr.push(listItem);
|
||||
}
|
||||
if (target == 'recycle-bin') {
|
||||
if (overlaps && !vars.multiSelectListItemArr_bin.includes(listItem)) vars.multiSelectListItemArr_bin.push(listItem);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (e.clientY > listBodyRect.bottom - scrollMargin) {
|
||||
if (autoScrollDirection !== 'down') {
|
||||
cancelAnimationFrame(autoScrollFrameId);
|
||||
autoScrollDirection = 'down';
|
||||
autoScrollLoop();
|
||||
}
|
||||
} else if (e.clientY < listBodyRect.top + scrollMargin) {
|
||||
if (autoScrollDirection !== 'up') {
|
||||
cancelAnimationFrame(autoScrollFrameId);
|
||||
autoScrollDirection = 'up';
|
||||
autoScrollLoop();
|
||||
}
|
||||
} else {
|
||||
if (autoScrollDirection !== null) {
|
||||
cancelAnimationFrame(autoScrollFrameId);
|
||||
autoScrollDirection = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('pointerup', async (e) => {
|
||||
// list-body에서 마우스 드래그가 시작된게 아닌 경우 리턴
|
||||
if (!draggingStart) return;
|
||||
|
||||
if (target == 'file-area') {
|
||||
if (vars.lastHeaderBtn) targetFocus(vars.lastHeaderBtn);
|
||||
if (vars.lastMainTreeItem) targetFocus(vars.lastMainTreeItem);
|
||||
}
|
||||
|
||||
if (isShift && isDragging) {
|
||||
const dragStartListItem = draggingStartTarget.closest('.list-item');
|
||||
if (dragStartListItem) {
|
||||
if (target == 'file-area') vars.lastListItem = dragStartListItem;
|
||||
if (target == 'recycle-bin') vars.lastListItem_bin = dragStartListItem;
|
||||
}
|
||||
}
|
||||
|
||||
multiSelectBox.style.display = 'none';
|
||||
draggingEndTarget = e.target;
|
||||
draggingStart = false;
|
||||
isDragging = false;
|
||||
isCtrl = false;
|
||||
isShift = false;
|
||||
|
||||
// 드래그가 아이템 바깥 영역에서만 진행되고, 드래그에 포함된 아이템이 하나도 없을 때 뷰어 초기화
|
||||
if (
|
||||
(
|
||||
(draggingStartTarget.matches('.list-body') && draggingEndTarget.matches('.list-body'))
|
||||
|| (draggingStartTarget.matches('.list-item-wrap') && draggingEndTarget.matches('.list-item-wrap'))
|
||||
)
|
||||
&&
|
||||
(
|
||||
(target == 'file-area' && vars.multiSelectListItemArr.length == 0)
|
||||
|| (target == 'recycle-bin' && vars.multiSelectListItemArr_bin.length == 0)
|
||||
)
|
||||
)
|
||||
{
|
||||
// 모든 아이템에서 non-selected 클래스 삭제
|
||||
document.querySelectorAll('.grid-item').forEach(gridItem => {
|
||||
gridItem.classList.remove('non-selected')
|
||||
})
|
||||
// if (viewerContainer.style.display == 'flex') resetViewer();
|
||||
resetViewer();
|
||||
|
||||
vars.lastContextTarget = undefined;
|
||||
if (target == 'file-area') {
|
||||
vars.lastListGroupTarget = undefined;
|
||||
vars.lastListItem = undefined;
|
||||
// vars.lastContextTarget = undefined;
|
||||
}
|
||||
if (target == 'recycle-bin') {
|
||||
vars.lastListGroupTarget_bin = undefined;
|
||||
vars.lastListItem_bin = undefined;
|
||||
// vars.lastContextTarget_bin = undefined;
|
||||
}
|
||||
|
||||
// userSelected 삭제
|
||||
let me = JSON.parse(vars.userInfoString);
|
||||
me.selected = undefined;
|
||||
vars.socket.emit('fileSelect',{me : me});
|
||||
} else {
|
||||
if (target == 'file-area') {
|
||||
// 모든 아이템에 non-selected 클래스 추가
|
||||
document.querySelectorAll('.grid-item').forEach(gridItem => {
|
||||
gridItem.classList.add('non-selected')
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 드래그 시작한 대상과 종료된 대상이 다르면 뷰어 초기화
|
||||
if (draggingStartTarget != draggingEndTarget) {
|
||||
if (target == 'file-area') {
|
||||
vars.lastListItem = undefined;
|
||||
vars.lastListGroupTarget = undefined;
|
||||
}
|
||||
if (target == 'recycle-bin') {
|
||||
vars.lastListItem_bin = undefined;
|
||||
vars.lastListGroupTarget_bin = undefined;
|
||||
}
|
||||
if (viewerContainer.style.display == 'flex') resetViewer();
|
||||
}
|
||||
|
||||
// 드래그가 끝나고 pointerup 이벤트가 발생하는 시점에 selected 클래스가 있는 listItem만 필터링
|
||||
if (target == 'file-area') vars.multiSelectListItemArr = vars.multiSelectListItemArr.filter(item => item.classList.contains('selected'));
|
||||
if (target == 'recycle-bin') vars.multiSelectListItemArr_bin = vars.multiSelectListItemArr_bin.filter(item => item.classList.contains('selected'));
|
||||
|
||||
// vars.multiSelectListItemArr/vars.multiSelectListItemArr_bin에 포함된 아이템에서 non-selected 클래스 삭제
|
||||
if (target == 'file-area') {
|
||||
vars.multiSelectListItemArr.forEach(item => {
|
||||
item.classList.remove('non-selected');
|
||||
})
|
||||
}
|
||||
if (target == 'recycle-bin') {
|
||||
vars.multiSelectListItemArr_bin.forEach(item => {
|
||||
item.classList.remove('non-selected');
|
||||
})
|
||||
}
|
||||
|
||||
await syncGroupStyle();
|
||||
|
||||
// 드래그 시작한 대상의 부모요소와 종료된 대상의 부모요소가 다르면 뷰어 초기화
|
||||
// if (draggingStartTarget.parentElement != draggingEndTarget.parentElement) {
|
||||
// if (viewerContainer.style.display == 'flex') resetViewer();
|
||||
// }
|
||||
|
||||
if (target == 'file-area') vars.lastSelectTarget = vars.multiSelectListItemArr[0];
|
||||
if (target == 'recycle-bin') vars.lastSelectTarget_bin = vars.multiSelectListItemArr_bin[0];
|
||||
|
||||
cancelAnimationFrame(autoScrollFrameId);
|
||||
autoScrollDirection = null;
|
||||
});
|
||||
}
|
||||
59
views/main/jsm/archive/index.js
Normal file
59
views/main/jsm/archive/index.js
Normal file
@@ -0,0 +1,59 @@
|
||||
import { vars } from './variable.js';
|
||||
import * as socketManager from './socketManager.js';
|
||||
import * as eventManager from './eventManager.js';
|
||||
import * as fileDrag from './fileDrag.js';
|
||||
|
||||
import {
|
||||
preparePageRendering,
|
||||
renderSizeBar,
|
||||
renderLog
|
||||
} from './pageRenderer.js';
|
||||
|
||||
export let isLoaded = false;
|
||||
export function loadArchive() {
|
||||
isLoaded = true;
|
||||
}
|
||||
|
||||
//// 뷰어 요약 ai버튼 삭제
|
||||
// document.querySelector('.ai-btn').remove();
|
||||
|
||||
//// archive controller에 useEncrypt 변수 설정
|
||||
// let setUseEncryptRes = await axios.put(`${vars.path_name}/setUseEncrypt`, {useEncrypt: vars.convertOption.useEncrypt});
|
||||
|
||||
let pageRanderingOption = {
|
||||
isInit: true,
|
||||
// favorite: 즐겨찾기 한 경로
|
||||
scope: 'headerBtn'
|
||||
}
|
||||
await preparePageRendering(pageRanderingOption);
|
||||
|
||||
//// 폴더 사용 용량 표시
|
||||
await renderSizeBar();
|
||||
|
||||
//// 활동 정보
|
||||
let isInit = true;
|
||||
await renderLog(isInit);
|
||||
|
||||
// ol.map = undefined;
|
||||
// ol.map = new ol.Map({
|
||||
// target: 'map-container',
|
||||
// layers: [vars.baseLayer],
|
||||
// // layers: [vars.roadLayer, vars.satelliteLayer, vars.hybridLayer],
|
||||
// view: new ol.View({
|
||||
// projection: 'EPSG:3857',
|
||||
// center: ol.proj.fromLonLat([127.8, 35.9]),
|
||||
// zoom: 7,
|
||||
// constrainResolution: true,
|
||||
// smoothResolutionConstraint: true
|
||||
// })
|
||||
// });
|
||||
|
||||
// proj4.defs([
|
||||
// ['EPSG:5185', '+proj=tmerc +lat_0=38 +lon_0=125 +k=1 +x_0=200000 +y_0=600000 +ellps=GRS80 +units=m +no_defs'],
|
||||
// ['EPSG:5186', '+proj=tmerc +lat_0=38 +lon_0=127 +k=1 +x_0=200000 +y_0=600000 +ellps=GRS80 +units=m +no_defs'],
|
||||
// ['EPSG:5187', '+proj=tmerc +lat_0=38 +lon_0=129 +k=1 +x_0=200000 +y_0=600000 +ellps=GRS80 +units=m +no_defs'],
|
||||
// ['EPSG:5188', '+proj=tmerc +lat_0=38 +lon_0=131 +k=1 +x_0=200000 +y_0=600000 +ellps=GRS80 +units=m +no_defs'],
|
||||
// ['EPSG:4978', '+proj=geocent +datum=WGS84 +units=m +no_defs'],
|
||||
// ['EPSG:4326', '+proj=longlat +datum=WGS84 +no_defs'],
|
||||
// ])
|
||||
// ol.proj.proj4.register(proj4);
|
||||
241
views/main/jsm/archive/logFilter.js
Normal file
241
views/main/jsm/archive/logFilter.js
Normal file
@@ -0,0 +1,241 @@
|
||||
import { vars } from './variable.js';
|
||||
import { renderLog } from './pageRenderer.js';
|
||||
|
||||
const modal = document.querySelector('.log-filter');
|
||||
const customDisplay = modal.querySelector('.custom-select-display');
|
||||
const customList = modal.querySelector('.custom-select-list');
|
||||
const userSelect = document.querySelector('#log-user-select');
|
||||
|
||||
// 기본 로그필터 정보 불러오기
|
||||
export async function getDefaultFilter() {
|
||||
// 해당 프로젝트에 존재하는 모든 사용자
|
||||
let userLogRes = await axios.get(`${vars.path_name}/getUserLog`);
|
||||
if (userLogRes.data.message == 'getUserLog_success') {
|
||||
const userLogDataArr = Object.values(userLogRes.data.logData);
|
||||
|
||||
// user_id -> user_nm 매핑 (중복 제거)
|
||||
const userMap = new Map();
|
||||
userLogDataArr.forEach(data => {
|
||||
if(!userMap.has(data.user_id)) {
|
||||
userMap.set(data.user_id, data.user_nm);
|
||||
}
|
||||
});
|
||||
|
||||
// selectbox 사용자 리스트 추가
|
||||
userSelect.innerHTML = '';
|
||||
userSelect.insertAdjacentHTML('beforeend', '<option value="allUser">모든 사용자</option>');
|
||||
|
||||
// Map -> Array로 변환 후 user_id로 정렬
|
||||
const sortedUsers = [...userMap.entries()].sort((a, b) => {
|
||||
const userIdA = a[0];
|
||||
const userIdB = b[0];
|
||||
return userIdA.localeCompare(userIdB, 'ko-KR');
|
||||
});
|
||||
|
||||
// 정렬된 사용자 추가
|
||||
sortedUsers.forEach(([userId, userNm]) => {
|
||||
let option = document.createElement('option');
|
||||
option.value = userId;
|
||||
option.textContent = `${userId} (${userNm})`;
|
||||
userSelect.appendChild(option);
|
||||
});
|
||||
|
||||
// custom select 리스트에도 반영
|
||||
customList.innerHTML = '';
|
||||
Array.from(userSelect.options).forEach(opt => {
|
||||
const li = document.createElement('li');
|
||||
li.dataset.value = opt.value;
|
||||
li.textContent = opt.textContent;
|
||||
customList.appendChild(li);
|
||||
});
|
||||
|
||||
// 기본값으로 설정
|
||||
customDisplay.textContent = "모든 사용자";
|
||||
userSelect.value = "allUser";
|
||||
customList.style.display = "none"; // 혹시 열려있으면 닫기
|
||||
}
|
||||
|
||||
// 활동유형 초기화
|
||||
let activityCheckbox = modal.querySelectorAll('.body .log-activity label input[type="checkbox"]');
|
||||
activityCheckbox.forEach(checkbox => {
|
||||
checkbox.checked = true;
|
||||
})
|
||||
|
||||
// 활동로그에 존재하는 '오늘 ~ 일주일' 간의 로그 기록
|
||||
let logRes = await axios.get(`${vars.path_name}/getLog`);
|
||||
if (logRes.data.message == 'getLog_success') {
|
||||
const logDataArr = Object.values(logRes.data.logData);
|
||||
|
||||
// 기본 활동시간 - 오늘 ~ 일주일로 설정
|
||||
const dateInputs = modal.querySelectorAll('.log-date .log-date-wrap input[type="date"]');
|
||||
|
||||
const toDateStr = (d) => d.toISOString().slice(0, 10);
|
||||
|
||||
const today = new Date();
|
||||
const weekAgo = new Date(today)
|
||||
weekAgo.setDate(today.getDate() - 7);
|
||||
|
||||
let startDate = toDateStr(weekAgo);
|
||||
let endDate = toDateStr(today);
|
||||
|
||||
// 달력창 UI
|
||||
dateInputs[0].value = startDate;
|
||||
dateInputs[1].value = endDate;
|
||||
|
||||
return { logDataArr }
|
||||
}
|
||||
}
|
||||
|
||||
// custom select 동작
|
||||
customDisplay.addEventListener('click', () => {
|
||||
customList.style.display = customList.style.display === 'flex' ? 'none': 'flex';
|
||||
});
|
||||
|
||||
// 항목 선택 시
|
||||
customList.addEventListener('click', (e) => {
|
||||
if (e.target.tagName === 'LI') {
|
||||
const value = e.target.dataset.value;
|
||||
const text = e.target.textContent;
|
||||
|
||||
// 표시 UI 업데이트
|
||||
customDisplay.textContent = text;
|
||||
|
||||
// 실제 select 값도 변경
|
||||
userSelect.value = value;
|
||||
|
||||
// 리스트 닫기
|
||||
customList.style.display = 'none';
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
// 로그필터 저장 버튼 클릭 이벤트
|
||||
modal.querySelector('.foot ._button-medium').addEventListener('click', async() => {
|
||||
// date 불러오기
|
||||
let { startDate, endDate } = getLogFilterDate();
|
||||
|
||||
// user 불러오기
|
||||
let { user } = getLogFilterUser();
|
||||
|
||||
// activity 불러오기
|
||||
let { activity } = getLogFilterActivity();
|
||||
|
||||
// 서버로 전달한 파라미터
|
||||
const logFilterParams = {
|
||||
startDate: startDate,
|
||||
endDate: endDate,
|
||||
user: user,
|
||||
activity: activity
|
||||
};
|
||||
|
||||
let filterLogRes = await axios.get(`${vars.path_name}/getFilterLog`, { params: logFilterParams });
|
||||
if (filterLogRes.data.message == 'getFilterLog_success') {
|
||||
const isInit = true;
|
||||
const addFooterLog = false;
|
||||
|
||||
const archiveModalLogList = document.querySelector('.archive-modal > .wrap .modal-wrap .modal-body .log-wrap .log-item-wrap.log-body');
|
||||
archiveModalLogList.scrollTop = 0;
|
||||
|
||||
renderLog(isInit, addFooterLog, filterLogRes.data.logData)
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
|
||||
document.querySelector('.archive-modal').addEventListener('click', async(e) => {
|
||||
if (modal.style.display == 'flex') {
|
||||
// 로그 필터 켜져있을 때에만 이벤트 작동
|
||||
if (e.target.classList.contains('custom-select-display') || e.target.classList.contains('custom-select-list') || e.target.closest('.custom-select-list')) {
|
||||
// 사용자 셀렉트 박스 또는 옵션 영역 클릭했을 때
|
||||
} else {
|
||||
// 사용자 셀렉트 박스 또는 옵션 영역을 제외한 나머지 모달 영역 클릭했을 때
|
||||
customList.style.display = 'none';
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// date 불러오기
|
||||
function getLogFilterDate() {
|
||||
// 날짜 선택 (start, end)
|
||||
const dateInputs = modal.querySelectorAll('.log-date .log-date-wrap input[type="date"]');
|
||||
let startDate = dateInputs[0].value; // 시작일
|
||||
let endDate = dateInputs[1].value; // 종료일
|
||||
|
||||
return { startDate, endDate };
|
||||
}
|
||||
|
||||
// user 불러오기
|
||||
function getLogFilterUser() {
|
||||
// 선택한 사용자 user에 추가
|
||||
const userSelect = modal.querySelector('.body .log-user select');
|
||||
const selectValue = userSelect.value;
|
||||
let user = [];
|
||||
|
||||
if(selectValue == 'allUser') {
|
||||
// 전체 사용자 선택
|
||||
user = Array.from(userSelect.options)
|
||||
.map(opt => opt.value)
|
||||
.filter(value => value !== 'allUser' && value !== '');
|
||||
} else {
|
||||
user.push(selectValue);
|
||||
}
|
||||
|
||||
|
||||
return { user };
|
||||
}
|
||||
|
||||
// activity 불러오기
|
||||
function getLogFilterActivity() {
|
||||
// 체크한 활동유형 activity에 추가
|
||||
const activityMapping = {
|
||||
'uploadData_file' : ['uploadData_file', 'addOn_version', 'addOn_attachment'],
|
||||
'renameTarget' : ['renameTarget_folder', 'renameTarget_file'],
|
||||
'removeTarget' : ['removeTarget_folder', 'removeTarget_file', 'removeTarget_folder_expired'],
|
||||
'downloadTarget': ['downloadTarget_folder', 'downloadTarget_file'],
|
||||
'addPermission': ['addPermission_worker', 'addPermission_viewer', 'addPermission_subMaster', 'addPermission_securityWorker'],
|
||||
'deletePermission': ['deletePermission_worker', 'deletePermission_viewer', 'deletePermission_subMaster', 'deletePermission_securityWorker']
|
||||
}
|
||||
|
||||
const activity = Array.from(modal.querySelectorAll('.body .log-activity label input[type="checkbox"]:checked')).flatMap(check => activityMapping[check.value] || [check.value]);
|
||||
|
||||
return { activity };
|
||||
}
|
||||
|
||||
// 초기화 / 전체해제 버튼 클릭 이벤트
|
||||
const logFilterBtn = modal.querySelectorAll('._button-xsmall');
|
||||
logFilterBtn.forEach(btn => {
|
||||
btn.addEventListener('click', (e) => {
|
||||
if(e.target.classList.contains('reset')) {
|
||||
// 활동시간 초기화- '오늘~일주일'
|
||||
const dateInputs = modal.querySelectorAll('.log-date .log-date-wrap input[type="date"]');
|
||||
const toDateStr = (d) => d.toISOString().slice(0, 10);
|
||||
|
||||
const today = new Date();
|
||||
const weekAgo = new Date(today);
|
||||
weekAgo.setDate(today.getDate() - 7);
|
||||
|
||||
let startDate = toDateStr(weekAgo);
|
||||
let endDate = toDateStr(today);
|
||||
|
||||
dateInputs[0].value = startDate;
|
||||
dateInputs[1].value = endDate;
|
||||
|
||||
// 사용자 초기화 - '모든 사용자'
|
||||
customDisplay.textContent = '모든 사용자';
|
||||
userSelect.value = 'allUser';
|
||||
customList.style.display = 'none';
|
||||
}
|
||||
|
||||
const inputs = modal.querySelectorAll('.body .log-activity label input[type="checkbox"]');
|
||||
|
||||
inputs.forEach(input => {
|
||||
if(e.target.classList.contains('reset')) {
|
||||
input.checked = true;
|
||||
} else if (e.target.classList.contains('select-all')) {
|
||||
input.checked = true;
|
||||
} else if (e.target.classList.contains('clear-all')) {
|
||||
input.checked = false;
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
32
views/main/jsm/archive/managementFunctions.js
Normal file
32
views/main/jsm/archive/managementFunctions.js
Normal file
@@ -0,0 +1,32 @@
|
||||
import { vars } from './variable.js';
|
||||
import { toggleContextmenu, toggleContextFocusBox } from './eventManager.js'
|
||||
|
||||
export async function mgmtFunc_changeProjectState(toggleParams) {
|
||||
if (toggleParams.type == 'changeProjectState') toggleParams.targetColumn = 'is_active';
|
||||
await axios.post('/common/mgmtFunc_updateProject', { params: toggleParams });
|
||||
}
|
||||
|
||||
export async function mgmtFunc_changeBannerNotice(toggleParams) {
|
||||
if (toggleParams.type == 'changeBannerNotice') toggleParams.targetColumn = 'banner_notice';
|
||||
await axios.post('/common/mgmtFunc_updateProject', { params: toggleParams });
|
||||
}
|
||||
|
||||
export async function mgmtFunc_resetConvert(resourcePath, dataId) {
|
||||
let resetConvertParams = {
|
||||
resourcePath: resourcePath,
|
||||
dataId: dataId
|
||||
}
|
||||
|
||||
let resetConvertRes = await axios.post(`${vars.path_name}/mgmtFunc_resetConvert`, { params: resetConvertParams });
|
||||
if (resetConvertRes.data.message == 'mgmtFunc_resetConvert_success') {
|
||||
toggleContextmenu(false);
|
||||
toggleContextFocusBox(false);
|
||||
}
|
||||
}
|
||||
|
||||
export async function mgmtFunc_addClickLog(params) {
|
||||
// params.dataId -> 컨트롤러에서 숫자형으로 형변환해서 db추가
|
||||
// project_id, activity, user_id, user_ip는 컨트롤러에서 params에 추가 (user_id는 params.userInfoString 사용)
|
||||
let addClickLogRes = await axios.post(`${vars.path_name}/mgmtFunc_addClickLog`, { params: params });
|
||||
// console.log(addClickLogRes.data.message);
|
||||
}
|
||||
72
views/main/jsm/archive/manualSwiper.js
Normal file
72
views/main/jsm/archive/manualSwiper.js
Normal file
@@ -0,0 +1,72 @@
|
||||
let archiveManualSwiper = undefined;
|
||||
export async function renderManual() {
|
||||
await createManualSwiperArea();
|
||||
|
||||
archiveManualSwiper = new Swiper('.modal-body > .manual-wrap .manual-swiper', {
|
||||
slidesPerView: 1,
|
||||
spaceBetween: 0,
|
||||
centeredSlides: true,
|
||||
speed: 400,
|
||||
loop: true,
|
||||
navigation: {
|
||||
nextEl: '.swiper.manual-swiper .button-next',
|
||||
prevEl: '.swiper.manual-swiper .button-prev',
|
||||
},
|
||||
pagination: {
|
||||
el: '.swiper.manual-swiper .swiper-pagination',
|
||||
clickable: true,
|
||||
renderBullet: function (index, className) {
|
||||
return '<span class="' + className + '">' + (index + 1) + "</span>";
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/** swiper area
|
||||
* [div] manual-wrap
|
||||
* [div] manual-swiper (v) <-- swiper
|
||||
* [ul] manual-swiper-wrap <-- swiper-wrapper
|
||||
* [li] manual-swiper-item <-- swiper-slide
|
||||
* [img] manual-img
|
||||
* [div] button-prev
|
||||
* [div] button-next
|
||||
* [div] swiper-pagination
|
||||
*/
|
||||
|
||||
export async function createManualSwiperArea() {
|
||||
const manualWrap = document.querySelector('.modal-body > .manual-wrap');
|
||||
manualWrap.innerHTML = ''; // swiper 초기화
|
||||
|
||||
const manualSwiper = document.createElement('div');
|
||||
manualSwiper.classList.add('swiper', 'manual-swiper');
|
||||
const manualSwiperWrap = document.createElement('ul');
|
||||
manualSwiperWrap.classList.add('swiper-wrapper', 'manual-swiper-wrap');
|
||||
manualSwiper.appendChild(manualSwiperWrap);
|
||||
manualWrap.appendChild(manualSwiper);
|
||||
|
||||
// swiper 동적 생성
|
||||
for (let i = 0; i < 5; i++) {
|
||||
const manualSwiperItem = document.createElement('li');
|
||||
manualSwiperItem.classList.add('swiper-slide', 'manual-swiper-item');
|
||||
manualSwiperItem.dataset.save = true;
|
||||
manualSwiperItem.dataset.fileName = `${[i+1]}.jpg`;
|
||||
|
||||
const manualImg = document.createElement('img');
|
||||
manualImg.classList.add('manual-img');
|
||||
manualImg.src = `/main/img/manual/${[i+1]}.jpg`;
|
||||
|
||||
manualSwiperItem.appendChild(manualImg);
|
||||
manualSwiperWrap.appendChild(manualSwiperItem);
|
||||
}
|
||||
|
||||
// 이전버튼, 다음버튼, 페이지네이션버튼 생성
|
||||
const swiperButtonPrev = document.createElement('div');
|
||||
swiperButtonPrev.classList.add('button-prev');
|
||||
const swiperButtonNext = document.createElement('div');
|
||||
swiperButtonNext.classList.add('button-next');
|
||||
const swiperPagination = document.createElement('div');
|
||||
swiperPagination.classList.add('swiper-pagination');
|
||||
manualSwiper.appendChild(swiperButtonPrev);
|
||||
manualSwiper.appendChild(swiperButtonNext);
|
||||
manualSwiper.appendChild(swiperPagination);
|
||||
}
|
||||
1314
views/main/jsm/archive/modalManager.js
Normal file
1314
views/main/jsm/archive/modalManager.js
Normal file
File diff suppressed because it is too large
Load Diff
754
views/main/jsm/archive/olPmtiles.js
Normal file
754
views/main/jsm/archive/olPmtiles.js
Normal file
@@ -0,0 +1,754 @@
|
||||
import { PMTiles } from 'https://esm.sh/pmtiles@3.0.7';
|
||||
|
||||
const MAX_LEVEL = 20;
|
||||
const CLICK_HIGHLIGHT_OUTER_WIDTH = 10; // 바깥쪽 흰색 테두리 두께
|
||||
const CLICK_HIGHLIGHT_INNER_WIDTH = 5; // 안쪽 선 두께
|
||||
const HOVER_HIGHLIGHT_OUTER_WIDTH = 6; // 바깥쪽 흰색 테두리 두께
|
||||
const HOVER_HIGHLIGHT_INNER_WIDTH = 2; // 안쪽 선 두께
|
||||
|
||||
// 선택/호버 상태 추적
|
||||
let selectedFeature = null;
|
||||
let hoveredFeature = null;
|
||||
let selectLayer = null;
|
||||
let hoverLayer = null;
|
||||
// 현재 선택된 feature 저장
|
||||
let currentSelectedFeature = null;
|
||||
// 선택된 feature의 레이어 키 추가
|
||||
let currentSelectedLayerKey = null;
|
||||
|
||||
// ---- OpenLayers VectorTile Layer ----
|
||||
class OLVectorLayer {
|
||||
constructor(olMap, layerId) {
|
||||
this.olMap = olMap;
|
||||
this.layerId = layerId;
|
||||
this.type = null;
|
||||
this.templateUrl = null;
|
||||
this.pmtilesInstance = null;
|
||||
this.maxLevel = MAX_LEVEL;
|
||||
this.vectorLayer = null;
|
||||
}
|
||||
|
||||
async setTileSource(source, type, zoomRange = null) {
|
||||
this.type = type;
|
||||
|
||||
if (type === 'pbf') {
|
||||
this.templateUrl = source;
|
||||
|
||||
try {
|
||||
const baseUrl = source.replace('/{z}/{x}/{y}.pbf', '');
|
||||
const metadataUrl = `${baseUrl}/metadata.json`;
|
||||
const response = await fetch(metadataUrl);
|
||||
const metadata = await response.json();
|
||||
|
||||
if (metadata.maxzoom) {
|
||||
this.maxLevel = parseInt(metadata.maxzoom);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`[${this.layerId}] Could not read metadata.json`);
|
||||
}
|
||||
|
||||
this.vectorLayer = new ol.layer.VectorTile({
|
||||
source: new ol.source.VectorTile({
|
||||
format: new ol.format.MVT(),
|
||||
url: source,
|
||||
maxZoom: this.maxLevel
|
||||
}),
|
||||
style: styleFunction,
|
||||
renderMode: 'vector',
|
||||
zIndex: 1,
|
||||
// 줌 범위 설정
|
||||
minZoom: zoomRange ? zoomRange.min : undefined,
|
||||
maxZoom: zoomRange ? zoomRange.max : undefined
|
||||
});
|
||||
|
||||
this.olMap.addLayer(this.vectorLayer);
|
||||
const zoomInfo = zoomRange ? ` (zoom ${zoomRange.min}-${zoomRange.max})` : '';
|
||||
console.log(`[${this.layerId}] PBF Vector layer initialized${zoomInfo}`);
|
||||
|
||||
} else if (type === 'pmtiles') {
|
||||
this.pmtilesInstance = source;
|
||||
|
||||
try {
|
||||
const metadata = await this.pmtilesInstance.getMetadata();
|
||||
if (metadata.maxzoom) {
|
||||
this.maxLevel = parseInt(metadata.maxzoom);
|
||||
}
|
||||
// console.log(`[${this.layerId}] PMTiles metadata:`, metadata);
|
||||
} catch (error) {
|
||||
console.warn(`[${this.layerId}] Could not read PMTiles metadata`);
|
||||
}
|
||||
|
||||
this.vectorLayer = new ol.layer.VectorTile({
|
||||
source: new ol.source.VectorTile({
|
||||
format: new ol.format.MVT(),
|
||||
tileUrlFunction: (tileCoord) => {
|
||||
const z = tileCoord[0];
|
||||
const x = tileCoord[1];
|
||||
const y = tileCoord[2];
|
||||
return `pmtiles://${this.layerId}/${z}/${x}/${y}`;
|
||||
},
|
||||
tileLoadFunction: async (tile, url) => {
|
||||
const urlParts = url.split('/');
|
||||
const z = parseInt(urlParts[urlParts.length - 3]);
|
||||
const x = parseInt(urlParts[urlParts.length - 2]);
|
||||
const y = parseInt(urlParts[urlParts.length - 1]);
|
||||
|
||||
// console.log(`Loading PMTiles tile: ${z}/${x}/${y}`);
|
||||
|
||||
try {
|
||||
const tileData = await this.pmtilesInstance.getZxy(z, x, y);
|
||||
if (tileData && tileData.data) {
|
||||
// console.log(`PMTiles tile ${z}/${x}/${y} loaded, size: ${tileData.data.byteLength} bytes`);
|
||||
|
||||
const format = new ol.format.MVT();
|
||||
|
||||
const tileGrid = ol.tilegrid.createXYZ({maxZoom: this.maxLevel});
|
||||
const tileExtent = tileGrid.getTileCoordExtent([z, x, y]);
|
||||
|
||||
const features = format.readFeatures(tileData.data, {
|
||||
extent: tileExtent,
|
||||
featureProjection: 'EPSG:3857'
|
||||
});
|
||||
|
||||
// console.log(`PMTiles tile ${z}/${x}/${y} features: ${features.length}`);
|
||||
|
||||
if (tile.setFeatures) {
|
||||
tile.setFeatures(features);
|
||||
} else {
|
||||
tile.setLoader(() => Promise.resolve(features));
|
||||
}
|
||||
} else {
|
||||
// console.log(`PMTiles tile ${z}/${x}/${y} - no data`);
|
||||
if (tile.setFeatures) {
|
||||
tile.setFeatures([]);
|
||||
} else {
|
||||
tile.setLoader(() => Promise.resolve([]));
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// console.error(`Failed to load PMTiles tile ${z}/${x}/${y}:`, error);
|
||||
if (tile.setFeatures) {
|
||||
tile.setFeatures([]);
|
||||
} else {
|
||||
tile.setLoader(() => Promise.resolve([]));
|
||||
}
|
||||
}
|
||||
},
|
||||
maxZoom: this.maxLevel
|
||||
}),
|
||||
style: styleFunction,
|
||||
zIndex: 1,
|
||||
updateWhileAnimating: false,
|
||||
updateWhileInteracting: false,
|
||||
renderBuffer: 50,
|
||||
declutter: false,
|
||||
// 줌 범위 설정
|
||||
minZoom: zoomRange ? zoomRange.min : undefined,
|
||||
maxZoom: zoomRange ? zoomRange.max : undefined
|
||||
});
|
||||
|
||||
this.olMap.addLayer(this.vectorLayer);
|
||||
|
||||
|
||||
|
||||
// this.pmtilesInstance = source;
|
||||
|
||||
// try {
|
||||
// const metadata = await this.pmtilesInstance.getMetadata();
|
||||
// if (metadata.maxzoom) this.maxLevel = parseInt(metadata.maxzoom, 10);
|
||||
// } catch (e) {
|
||||
// console.warn(`[${this.layerId}] Could not read PMTiles metadata`);
|
||||
// }
|
||||
|
||||
// // PMTiles가 512 기준이면 512 권장
|
||||
// const tileSize = 256;
|
||||
// const tileGrid = ol.tilegrid.createXYZ({
|
||||
// maxZoom: this.maxLevel,
|
||||
// tileSize
|
||||
// });
|
||||
|
||||
// // 기존 styleFunction 그대로 사용
|
||||
// const styleFn = styleFunction;
|
||||
|
||||
// // 벡터 → 래스터 변환용 레이어
|
||||
// this.rasterLayer = new ol.layer.Tile({
|
||||
// source: new ol.source.TileImage({
|
||||
// tileGrid,
|
||||
// tileUrlFunction: (tileCoord) => {
|
||||
// const [z, x, y] = tileCoord;
|
||||
// return `pmtiles://${this.layerId}/${z}/${x}/${y}`;
|
||||
// },
|
||||
// tileLoadFunction: async (imageTile, url) => {
|
||||
// const parts = url.split('/');
|
||||
// const z = parseInt(parts[parts.length - 3], 10);
|
||||
// const x = parseInt(parts[parts.length - 2], 10);
|
||||
// const y = parseInt(parts[parts.length - 1], 10);
|
||||
|
||||
// // 타일 extent (EPSG:3857)
|
||||
// const tileExtent = tileGrid.getTileCoordExtent([z, x, y]);
|
||||
// const resolution = tileGrid.getResolution(z);
|
||||
|
||||
// // 해상도/부하 트레이드오프
|
||||
// const pixelRatio = 1; // 필요시 window.devicePixelRatio
|
||||
|
||||
// const canvas = document.createElement('canvas');
|
||||
// canvas.width = tileSize * pixelRatio;
|
||||
// canvas.height = tileSize * pixelRatio;
|
||||
// const ctx = canvas.getContext('2d');
|
||||
|
||||
// // ★ extent 반영하여 벡터 컨텍스트 생성
|
||||
// const vectorCtx = ol.render.toContext(ctx, {
|
||||
// size: [tileSize * pixelRatio, tileSize * pixelRatio],
|
||||
// pixelRatio,
|
||||
// extent: tileExtent // ← 중요: 지도좌표 → 픽셀 변환 기준
|
||||
// });
|
||||
|
||||
// try {
|
||||
// const tileData = await this.pmtilesInstance.getZxy(z, x, y);
|
||||
|
||||
// if (tileData && tileData.data) {
|
||||
// // MVT 파싱 (extent는 featureProjection과 동일 좌표계여야 함)
|
||||
// const format = new ol.format.MVT();
|
||||
// const features = format.readFeatures(tileData.data, {
|
||||
// extent: tileExtent, // EPSG:3857
|
||||
// featureProjection: 'EPSG:3857'
|
||||
// });
|
||||
|
||||
// // zIndex 반영을 위해 (feature, style) 쌍 정렬
|
||||
// const drawQueue = [];
|
||||
// for (const f of features) {
|
||||
// let s = styleFn ? styleFn(f, resolution) : null;
|
||||
// if (!s) continue;
|
||||
// const styles = Array.isArray(s) ? s : [s];
|
||||
// for (const one of styles) {
|
||||
// if (one && typeof one.getZIndex === 'function') {
|
||||
// const zi = one.getZIndex() ?? 0;
|
||||
// drawQueue.push({ f, style: one, z: zi });
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// drawQueue.sort((a, b) => a.z - b.z);
|
||||
// for (const { f, style } of drawQueue) {
|
||||
// vectorCtx.drawFeature(f, style);
|
||||
// }
|
||||
// } else {
|
||||
// // 데이터 없음 → 투명 타일
|
||||
// ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
// }
|
||||
|
||||
// imageTile.getImage().src = canvas.toDataURL('image/png');
|
||||
// } catch (err) {
|
||||
// console.warn(`PMTiles raster tile load failed ${z}/${x}/${y}`, err);
|
||||
// const empty = document.createElement('canvas');
|
||||
// empty.width = tileSize;
|
||||
// empty.height = tileSize;
|
||||
// imageTile.getImage().src = empty.toDataURL();
|
||||
// }
|
||||
// },
|
||||
// // crossOrigin: 'anonymous',
|
||||
// }),
|
||||
// zIndex: 100000,
|
||||
// minZoom: zoomRange ? zoomRange.min : undefined,
|
||||
// maxZoom: zoomRange ? zoomRange.max : undefined
|
||||
// });
|
||||
|
||||
// this.olMap.addLayer(this.rasterLayer);
|
||||
|
||||
|
||||
const zoomInfo = zoomRange ? ` (zoom ${zoomRange.min}-${zoomRange.max})` : '';
|
||||
console.log(`[${this.layerId}] PMTiles Vector layer initialized${zoomInfo}`);
|
||||
|
||||
try {
|
||||
const metadata = await this.pmtilesInstance.getMetadata();
|
||||
// PMTiles metadata에서 bounds 정보 확인
|
||||
if (metadata && (metadata.bounds || metadata.antimeridian_adjusted_bounds || metadata.center)) {
|
||||
let extent;
|
||||
let bounds;
|
||||
if (metadata.bounds) bounds = metadata.bounds;
|
||||
if (metadata.antimeridian_adjusted_bounds) bounds = metadata.antimeridian_adjusted_bounds.split(',');
|
||||
|
||||
if (bounds) {
|
||||
// bounds: [minLon, minLat, maxLon, maxLat]
|
||||
const [minLon, minLat, maxLon, maxLat] = bounds;
|
||||
const bottomLeft = ol.proj.fromLonLat([minLon, minLat]);
|
||||
const topRight = ol.proj.fromLonLat([maxLon, maxLat]);
|
||||
extent = [bottomLeft[0], bottomLeft[1], topRight[0], topRight[1]];
|
||||
} else if (metadata.center) {
|
||||
// center와 zoom을 이용한 대략적인 영역 계산
|
||||
const [centerLon, centerLat, centerZoom] = metadata.center;
|
||||
const centerPoint = ol.proj.fromLonLat([centerLon, centerLat]);
|
||||
const resolution = this.olMap.getView().getResolutionForZoom(centerZoom || 10);
|
||||
const size = 1000; // 대략적인 크기
|
||||
extent = [
|
||||
centerPoint[0] - size * resolution,
|
||||
centerPoint[1] - size * resolution,
|
||||
centerPoint[0] + size * resolution,
|
||||
centerPoint[1] + size * resolution
|
||||
];
|
||||
}
|
||||
|
||||
let pmtilesFitBtn = document.querySelector('.map-container .control-btn-wrap .pmtiles-fit-btn');
|
||||
if (extent) {
|
||||
if (ol.map.getOverlays().getLength() == 0) {
|
||||
this.olMap.getView().fit(extent, {
|
||||
padding: [50, 50, 50, 50],
|
||||
// duration: 500,
|
||||
// constrainResolution: true,
|
||||
maxZoom: 18
|
||||
});
|
||||
// console.log(`[${this.layerId}] PMTiles layer fitted to bounds`);
|
||||
}
|
||||
|
||||
pmtilesFitBtn.style.display = 'flex';
|
||||
pmtilesFitBtn.addEventListener('click', async() => {
|
||||
this.olMap.getView().fit(extent, {
|
||||
padding: [50, 50, 50, 50],
|
||||
// duration: 500,
|
||||
// constrainResolution: true,
|
||||
maxZoom: 18
|
||||
});
|
||||
})
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`[${this.layerId}] Could not fit PMTiles bounds:`, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GeoJSON/KML용 벡터 소스 설정 메서드
|
||||
async setVectorSource(url, type, zoomRange = null) {
|
||||
this.type = type;
|
||||
|
||||
if (type === 'geojson') {
|
||||
console.log(`[${this.layerId}] Loading GeoJSON from: ${url}`);
|
||||
|
||||
this.vectorLayer = new ol.layer.Vector({
|
||||
source: new ol.source.Vector({
|
||||
url: url,
|
||||
format: new ol.format.GeoJSON()
|
||||
}),
|
||||
style: styleFunction,
|
||||
zIndex: 1,
|
||||
updateWhileAnimating: false,
|
||||
updateWhileInteracting: false,
|
||||
renderBuffer: 50,
|
||||
declutter: false,
|
||||
// 줌 범위 설정
|
||||
minZoom: zoomRange ? zoomRange.min : undefined,
|
||||
maxZoom: zoomRange ? zoomRange.max : undefined
|
||||
});
|
||||
|
||||
this.olMap.addLayer(this.vectorLayer);
|
||||
const zoomInfo = zoomRange ? ` (zoom ${zoomRange.min}-${zoomRange.max})` : '';
|
||||
console.log(`[${this.layerId}] GeoJSON Vector layer initialized${zoomInfo}`);
|
||||
|
||||
// GeoJSON 로드 완료 이벤트 처리
|
||||
const source = this.vectorLayer.getSource();
|
||||
source.once('change', () => {
|
||||
if (source.getState() === 'ready') {
|
||||
const features = source.getFeatures();
|
||||
const featureCount = features.length;
|
||||
console.log(`[${this.layerId}] GeoJSON loaded: ${featureCount} features`);
|
||||
|
||||
// feature 수에 따른 경고
|
||||
if (featureCount > 10000) {
|
||||
console.warn(`⚠️ Large dataset detected (${featureCount} features). Performance may be affected.`);
|
||||
}
|
||||
|
||||
// 로드된 features가 있으면 해당 영역으로 자동 줌 (단독 레이어인 경우만)
|
||||
if (features.length > 0 && !zoomRange) {
|
||||
const extent = source.getExtent();
|
||||
this.olMap.getView().fit(extent, {
|
||||
padding: [50, 50, 50, 50],
|
||||
maxZoom: 18
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (type === 'kml') {
|
||||
console.log(`[${this.layerId}] Loading KML from: ${url}`);
|
||||
|
||||
this.vectorLayer = new ol.layer.Vector({
|
||||
source: new ol.source.Vector({
|
||||
url: url,
|
||||
format: new ol.format.KML({
|
||||
extractStyles: false,
|
||||
showPointNames: false
|
||||
})
|
||||
}),
|
||||
style: styleFunction,
|
||||
zIndex: 1,
|
||||
updateWhileAnimating: false,
|
||||
updateWhileInteracting: false,
|
||||
renderBuffer: 50,
|
||||
declutter: false,
|
||||
// 줌 범위 설정
|
||||
minZoom: zoomRange ? zoomRange.min : undefined,
|
||||
maxZoom: zoomRange ? zoomRange.max : undefined
|
||||
});
|
||||
|
||||
this.olMap.addLayer(this.vectorLayer);
|
||||
const zoomInfo = zoomRange ? ` (zoom ${zoomRange.min}-${zoomRange.max})` : '';
|
||||
console.log(`[${this.layerId}] KML Vector layer initialized${zoomInfo}`);
|
||||
|
||||
// KML 로드 완료 이벤트 처리
|
||||
const source = this.vectorLayer.getSource();
|
||||
source.once('change', () => {
|
||||
if (source.getState() === 'ready') {
|
||||
const features = source.getFeatures();
|
||||
console.log(`[${this.layerId}] KML loaded: ${features.length} features`);
|
||||
|
||||
if (features.length > 0 && !zoomRange) {
|
||||
const extent = source.getExtent();
|
||||
this.olMap.getView().fit(extent, {
|
||||
padding: [50, 50, 50, 50],
|
||||
maxZoom: 18
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
clear() {
|
||||
if (this.vectorLayer) {
|
||||
this.olMap.removeLayer(this.vectorLayer);
|
||||
this.vectorLayer = null;
|
||||
console.log(`[${this.layerId}] Layer cleared`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---- 통합 멀티 레이어 관리자 (OpenLayers) ----
|
||||
export class OLMultiLayerManager {
|
||||
constructor(olMap) {
|
||||
this.olMap = olMap;
|
||||
this.layerMap = new Map();
|
||||
this.pmtilesInstances = new Map();
|
||||
|
||||
console.log('OLMultiLayerManager initialized');
|
||||
}
|
||||
|
||||
async toggleLayer(layerId, format) {
|
||||
const key = `${format}_${layerId}`;
|
||||
|
||||
if (this.layerMap.has(key)) {
|
||||
// 레이어 제거
|
||||
// PMTiles 제거 시 연결된 detail GeoJSON도 함께 제거
|
||||
|
||||
const layer = this.layerMap.get(key);
|
||||
layer.clear();
|
||||
this.layerMap.delete(key);
|
||||
|
||||
if (this.pmtilesInstances.has(key)) {
|
||||
this.pmtilesInstances.delete(key);
|
||||
}
|
||||
|
||||
console.log(`[${key}] Layer removed`);
|
||||
return false;
|
||||
} else {
|
||||
// 레이어 추가 로직
|
||||
if (format === 'pbf') {
|
||||
const layer = new OLVectorLayer(this.olMap, key);
|
||||
const templateUrl = `http://172.16.41.52:3003/vector_tile_pbf/${layerId}/{z}/{x}/{y}.pbf`;
|
||||
await layer.setTileSource(templateUrl, 'pbf');
|
||||
this.layerMap.set(key, layer);
|
||||
|
||||
} else if (format === 'pmtiles') {
|
||||
// console.log(`🚀 Creating hybrid PMTiles + GeoJSON layers for: ${layerId}`);
|
||||
|
||||
// // 1. PMTiles 레이어 추가 (zoom 0-17만 표시)
|
||||
// const pmtilesLayer = new OLVectorLayer(this.olMap, key);
|
||||
// const pmtilesPath = `http://172.16.41.52:3003/vector_tile_pmtiles/${layerId}.pmtiles`;
|
||||
// const pmtiles = new PMTiles(pmtilesPath);
|
||||
// this.pmtilesInstances.set(key, pmtiles);
|
||||
// await pmtilesLayer.setTileSource(pmtiles, 'pmtiles', { min: 0, max: 18.1 });
|
||||
// this.layerMap.set(key, pmtilesLayer);
|
||||
// console.log(`✅ [${key}] PMTiles layer added (zoom 0-17)`);
|
||||
|
||||
// // 2. GeoJSON 레이어 추가 (zoom 18-24만 표시)
|
||||
// const detailGeojsonKey = `geojson_detail_${layerId}`;
|
||||
// const detailGeojsonLayer = new OLVectorLayer(this.olMap, detailGeojsonKey);
|
||||
// const geojsonUrl = `http://172.16.41.52:3003/vector_tile_geojson/${layerId}.geojson`;
|
||||
// await detailGeojsonLayer.setVectorSource(geojsonUrl, 'geojson', { min: 17.9, max: 24 });
|
||||
// this.layerMap.set(detailGeojsonKey, detailGeojsonLayer);
|
||||
// console.log(`✅ [${detailGeojsonKey}] Detail GeoJSON layer added (zoom 18-24)`);
|
||||
|
||||
// console.log(`🎯 Hybrid layer setup complete. PMTiles will show at zoom 0-17, GeoJSON at zoom 18+`);
|
||||
|
||||
|
||||
const pmtilesLayer = new OLVectorLayer(this.olMap, key);
|
||||
|
||||
// const pmtilesPath = `http://172.16.41.52:3003/vector_tile_pmtiles/${layerId}.pmtiles`;
|
||||
let pmtilesPath = `https://gsim-model.digitalarchive.work/pmtiles/vector/${layerId}.pmtiles`;
|
||||
if (layerId == 'testbim') pmtilesPath = `https://gsim-model.digitalarchive.work/pmtiles/vector/dsdj2.pmtiles`;
|
||||
|
||||
// let mapName = layerId;
|
||||
// if (layerId == 'testbim') mapName = 'dsdj2';
|
||||
// let pmtilesPath = `https://gsim-model.digitalarchive.work/pmtiles/vector/${mapName}.pmtiles`;
|
||||
|
||||
let checkUrlExistsResult = await checkUrlExists(pmtilesPath);
|
||||
if (checkUrlExistsResult == true) {
|
||||
const pmtiles = new PMTiles(pmtilesPath);
|
||||
this.pmtilesInstances.set(key, pmtiles);
|
||||
await pmtilesLayer.setTileSource(pmtiles, 'pmtiles');
|
||||
this.layerMap.set(key, pmtilesLayer);
|
||||
} else {
|
||||
let pmtilesFitBtn = document.querySelector('.map-container .control-btn-wrap .pmtiles-fit-btn');
|
||||
pmtilesFitBtn.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
} else if (format === 'geojson') {
|
||||
const layer = new OLVectorLayer(this.olMap, key);
|
||||
const geojsonUrl = `http://172.16.41.52:3003/vector_tile_geojson/${layerId}.geojson`;
|
||||
await layer.setVectorSource(geojsonUrl, 'geojson');
|
||||
this.layerMap.set(key, layer);
|
||||
|
||||
} else if (format === 'kml') {
|
||||
const layer = new OLVectorLayer(this.olMap, key);
|
||||
const kmlUrl = `http://172.16.41.52:3003/vector_tile_kml/${layerId}.kml`;
|
||||
await layer.setVectorSource(kmlUrl, 'kml');
|
||||
this.layerMap.set(key, layer);
|
||||
}
|
||||
|
||||
// console.log(`[${key}] Layer added`);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
clearAll() {
|
||||
this.layerMap.forEach(layer => layer.clear());
|
||||
this.layerMap.clear();
|
||||
this.pmtilesInstances.clear();
|
||||
console.log('All OpenLayers layers cleared');
|
||||
}
|
||||
|
||||
getActiveLayers() {
|
||||
return Array.from(this.layerMap.keys());
|
||||
}
|
||||
}
|
||||
|
||||
// ---- 피처별 스타일 함수 ----
|
||||
function styleFunction(feature, resolution) {
|
||||
const props = feature.getProperties();
|
||||
|
||||
const entityType = props?.EntityType;
|
||||
let color = props.Color || '#ff0000';
|
||||
const constWidth = props.constWidth || 0;
|
||||
const ltscale = props.ltscale || 1.0;
|
||||
|
||||
// let dashPattern = props.dashPattern;
|
||||
// if (dashPattern && typeof dashPattern === 'string') {
|
||||
// try {
|
||||
// // "[0.5,-0.25]" → [0.5, -0.25]
|
||||
// dashPattern = JSON.parse(dashPattern);
|
||||
// } catch (e) {
|
||||
// console.warn('dashPattern 파싱 실패:', dashPattern, e);
|
||||
// dashPattern = null;
|
||||
// }
|
||||
// }
|
||||
|
||||
// ⭐ constWidth에 따른 선 두께 결정
|
||||
let strokeWidth = 1; // 기본값
|
||||
// if (constWidth === 0) {
|
||||
// strokeWidth = 1;
|
||||
// } else if (constWidth === 1) {
|
||||
// strokeWidth = 5;
|
||||
// } else {
|
||||
// // 향후 다른 값 대비
|
||||
// // strokeWidth = 2 + (constWidth * 2);
|
||||
// // strokeWidth = 1 + (constWidth * 5);
|
||||
// strokeWidth = constWidth * 5;
|
||||
// }
|
||||
|
||||
// if (constWidth === 0) {
|
||||
// strokeWidth = 1;
|
||||
// } else {
|
||||
// // 향후 다른 값 대비
|
||||
// // strokeWidth = 2 + (constWidth * 2);
|
||||
// // strokeWidth = 1 + (constWidth * 5);
|
||||
// strokeWidth = constWidth * 10;
|
||||
// }
|
||||
|
||||
// if (constWidth === 0) {
|
||||
// strokeWidth = 1;
|
||||
// } else if (constWidth > 0 && constWidth <= 1) {
|
||||
// strokeWidth = constWidth * 10;
|
||||
// } else {
|
||||
// strokeWidth = constWidth * 5;
|
||||
// }
|
||||
|
||||
// if (constWidth === 0) {
|
||||
// strokeWidth = 1;
|
||||
// } else {
|
||||
// console.log(constWidth);
|
||||
// strokeWidth = constWidth*2;
|
||||
// }
|
||||
|
||||
// // ⭐ dashPattern을 OpenLayers lineDash로 변환
|
||||
// let lineDash = null;
|
||||
// if (dashPattern && Array.isArray(dashPattern) && dashPattern.length > 0) {
|
||||
// // const zoomFactor = Math.min(1 / resolution, 1.0);
|
||||
// const scaleFactor = 10; // 조정 가능 (0.3 ~ 1.0 추천)
|
||||
// lineDash = dashPattern.map(value =>
|
||||
// Math.abs(value) * ltscale * scaleFactor
|
||||
// );
|
||||
// }
|
||||
|
||||
// 투명도 처리
|
||||
const hexToRgba = (hex, alpha = 1) => {
|
||||
if (!hex || hex.length < 7) return `rgba(255, 0, 0, ${alpha})`;
|
||||
const r = parseInt(hex.slice(1, 3), 16);
|
||||
const g = parseInt(hex.slice(3, 5), 16);
|
||||
const b = parseInt(hex.slice(5, 7), 16);
|
||||
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
|
||||
};
|
||||
|
||||
const geomType = feature.getGeometry().getType();
|
||||
if (entityType) {
|
||||
if (entityType === 'TEXT_POLYGON' || entityType === 'LINETYPE_PATTERN_TEXT') {
|
||||
return new ol.style.Style({
|
||||
fill: new ol.style.Fill({
|
||||
color: hexToRgba(color, 1.0)
|
||||
}),
|
||||
zIndex: 40
|
||||
});
|
||||
} else if (entityType === 'LINETYPE_PATTERN_SHAPE') {
|
||||
return new ol.style.Style({
|
||||
fill: new ol.style.Fill({
|
||||
color: hexToRgba(color, 0.4)
|
||||
}),
|
||||
stroke: new ol.style.Stroke({
|
||||
color: hexToRgba(color, 1.0),
|
||||
width: strokeWidth,
|
||||
// lineDash: lineDash,
|
||||
lineCap: 'butt',
|
||||
lineJoin: 'miter'
|
||||
}),
|
||||
zIndex: 20
|
||||
});
|
||||
} else if (entityType === 'HATCH') {
|
||||
return new ol.style.Style({
|
||||
fill: new ol.style.Fill({
|
||||
color: hexToRgba(color, 0.4)
|
||||
}),
|
||||
stroke: new ol.style.Stroke({
|
||||
color: hexToRgba(color, 1.0),
|
||||
width: strokeWidth,
|
||||
// lineDash: lineDash,
|
||||
lineCap: 'butt',
|
||||
lineJoin: 'miter'
|
||||
}),
|
||||
zIndex: 10
|
||||
});
|
||||
} else if (entityType === 'SOLID_LINE_POLYGON' || entityType === 'DASHED_LINE_POLYGON') {
|
||||
return new ol.style.Style({
|
||||
fill: new ol.style.Fill({
|
||||
color: hexToRgba(color, 1)
|
||||
}),
|
||||
stroke: new ol.style.Stroke({
|
||||
color: hexToRgba(color, 1.0),
|
||||
width: strokeWidth,
|
||||
// lineDash: lineDash,
|
||||
lineCap: 'butt',
|
||||
lineJoin: 'miter'
|
||||
}),
|
||||
zIndex: 10
|
||||
});
|
||||
} else if (entityType === 'LINETYPE_PATTERN_SHAPE_LINE') {
|
||||
return new ol.style.Style({
|
||||
stroke: new ol.style.Stroke({
|
||||
color: hexToRgba(color, 1.0),
|
||||
width: strokeWidth,
|
||||
// lineDash: lineDash,
|
||||
lineCap: 'butt',
|
||||
lineJoin: 'miter'
|
||||
}),
|
||||
zIndex: 30
|
||||
});
|
||||
} else if (entityType === 'LWPOLYLINE' || entityType === 'LINE') {
|
||||
return new ol.style.Style({
|
||||
stroke: new ol.style.Stroke({
|
||||
color: hexToRgba(color, 1.0),
|
||||
width: strokeWidth,
|
||||
// lineDash: lineDash,
|
||||
lineCap: 'butt',
|
||||
lineJoin: 'miter'
|
||||
}),
|
||||
zIndex: 30
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (geomType === 'Polygon' || geomType === 'MultiPolygon') {
|
||||
return new ol.style.Style({
|
||||
fill: new ol.style.Fill({
|
||||
color: hexToRgba(color, 0.4)
|
||||
}),
|
||||
stroke: new ol.style.Stroke({
|
||||
color: hexToRgba(color, 1.0),
|
||||
width: strokeWidth,
|
||||
// lineDash: lineDash,
|
||||
lineCap: 'butt',
|
||||
lineJoin: 'miter'
|
||||
}),
|
||||
zIndex: 20
|
||||
});
|
||||
} else if (geomType === 'LineString' || geomType === 'MultiLineString') {
|
||||
// let strokeWidth, color;
|
||||
// if (props.DIVI == '주곡선') {
|
||||
// strokeWidth = 2;
|
||||
// color = '#cccccc';
|
||||
// } else {
|
||||
// strokeWidth = 1;
|
||||
// color = '#777777';
|
||||
// }
|
||||
return new ol.style.Style({
|
||||
stroke: new ol.style.Stroke({
|
||||
color: hexToRgba(color, 1.0),
|
||||
width: strokeWidth,
|
||||
// lineDash: lineDash,
|
||||
lineCap: 'butt',
|
||||
lineJoin: 'miter'
|
||||
}),
|
||||
zIndex: 30
|
||||
});
|
||||
} else if (geomType === 'Point' || geomType === 'MultiPoint') {
|
||||
return new ol.style.Style({
|
||||
image: new ol.style.Circle({
|
||||
radius: 4,
|
||||
fill: new ol.style.Fill({
|
||||
color: hexToRgba(color, 1.0)
|
||||
})
|
||||
}),
|
||||
zIndex: 30
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// async function checkUrlExists(url) {
|
||||
// try {
|
||||
// const res = await fetch(url, { method: "HEAD" });
|
||||
|
||||
// if (res.ok) {
|
||||
// return true; // 200~299 → 정상 URL
|
||||
// }
|
||||
// return false; // 404, 403 등
|
||||
// } catch (e) {
|
||||
// return false; // 네트워크 오류, CORS 오류 포함
|
||||
// }
|
||||
// }
|
||||
|
||||
async function checkUrlExists(url) {
|
||||
try {
|
||||
const res = await fetch(url, { method: "HEAD" });
|
||||
|
||||
// 200~299
|
||||
return res.ok;
|
||||
} catch (e) {
|
||||
// 여기서 swallow(삼키기)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
7548
views/main/jsm/archive/pageRenderer.js
Normal file
7548
views/main/jsm/archive/pageRenderer.js
Normal file
File diff suppressed because it is too large
Load Diff
710
views/main/jsm/archive/projectSetting.js
Normal file
710
views/main/jsm/archive/projectSetting.js
Normal file
@@ -0,0 +1,710 @@
|
||||
import { typeStep2Kor } from '../main.js';
|
||||
|
||||
let projectTypeBtn;
|
||||
let projectTypeCapsule;
|
||||
let projectTypeList = null;
|
||||
let projectStepList = null;
|
||||
let mainLayerState = null; // 메인 레이어 상태
|
||||
|
||||
// 프로젝트 설정 저장 버튼 클릭 이벤트
|
||||
document.getElementById('project-save-btn').addEventListener('click', async () => {
|
||||
const projectSaveBtn = document.getElementById('project-save-btn');
|
||||
const projectStepBtn = document.getElementById('project-step-btn');
|
||||
const projectNameInput = document.getElementById('project-name-input');
|
||||
|
||||
if (projectSaveBtn.textContent === '저장') {
|
||||
// type
|
||||
let typeVal = projectTypeBtn.children[0].textContent;
|
||||
let typeValResult = stepTypeEng(typeVal);
|
||||
|
||||
// step
|
||||
let stepVal = projectStepBtn.children[0].textContent;
|
||||
let stepValResult = stepTypeEng(stepVal);
|
||||
|
||||
// project_name
|
||||
let nameVal = projectNameInput.value;
|
||||
|
||||
let projectInfoParams = {
|
||||
projectId : vars.project.project_id,
|
||||
category : vars.project.category,
|
||||
project_type : typeValResult,
|
||||
step : stepValResult,
|
||||
project_nm : nameVal,
|
||||
}
|
||||
|
||||
let res = await axios.post('/common/updateProjectInfo', {params: projectInfoParams});
|
||||
if (res.data.message == 'updateProjectInfo_success') {
|
||||
// 수정된 프로젝트 정보 가져와서 전역변수에 저장
|
||||
let projectRes = await axios.get('/common/getProject');
|
||||
for (let project of projectRes.data.data) {
|
||||
vars.allProject[project.project_id] = project;
|
||||
}
|
||||
vars.project = vars.allProject[vars.project_id];
|
||||
|
||||
setReadOnlyMode(vars.project.category);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 프로젝트 설정 취소 클릭 이벤트
|
||||
document.querySelector('#project-cancel-btn').addEventListener('click', () => {
|
||||
const projectSaveBtn = document.getElementById('project-save-btn');
|
||||
if (projectSaveBtn.textContent === '저장') projectSaveBtn.textContent = '수정';
|
||||
|
||||
const list = document.querySelector('#project-type-wrap-overseas .project-type__list');
|
||||
list.classList.remove('--project-list__open');
|
||||
|
||||
setReadOnlyMode(vars.project.category);
|
||||
});
|
||||
|
||||
export function initProjectSetting(param) {
|
||||
const projectSaveBtn = document.getElementById('project-save-btn');
|
||||
const projectCancelBtn = document.getElementById('project-cancel-btn');
|
||||
const projectLocationBtn = document.getElementById('project-location-btn');
|
||||
let projectTypeWrap = document.getElementById('project-type-wrap');
|
||||
let projectTypeWrapOverseas = document.getElementById('project-type-wrap-overseas');
|
||||
|
||||
if (param === undefined) {
|
||||
// overseas, bim 아닌 경우
|
||||
projectTypeWrap.style.display = 'none';
|
||||
projectTypeWrapOverseas.style.display = 'none';
|
||||
|
||||
projectTypeBtn = document.getElementById('project-type-btn');
|
||||
projectTypeCapsule = document.getElementById('project-type-capsule');
|
||||
|
||||
} else {
|
||||
if (param === 'overseas') {
|
||||
// overseas
|
||||
projectTypeWrap.style.display = 'none';
|
||||
projectTypeWrapOverseas.style.display = 'flex';
|
||||
|
||||
projectTypeBtn = document.getElementById('project-type-btn-overseas');
|
||||
projectTypeCapsule = document.getElementById('project-type-capsule-overseas');
|
||||
|
||||
} else {
|
||||
//bim
|
||||
projectTypeWrap.style.display = 'flex';
|
||||
projectTypeWrapOverseas.style.display = 'none';
|
||||
|
||||
projectTypeBtn = document.getElementById('project-type-btn');
|
||||
projectTypeCapsule = document.getElementById('project-type-capsule');
|
||||
}
|
||||
|
||||
adjustSelectBoxWidth(projectTypeBtn);
|
||||
}
|
||||
|
||||
setReadOnlyMode(param);
|
||||
|
||||
// 권한이 있는 경우 저장 버튼 이벤트 추가
|
||||
if (vars.permission.checkPermission('change-project-btn')) {
|
||||
// 기존 리스너 제거 후 추가 (중복 방지)
|
||||
projectSaveBtn.textContent = '수정';
|
||||
projectSaveBtn.removeEventListener('click', handleProjectSaveClick);
|
||||
projectSaveBtn.addEventListener('click', () => handleProjectSaveClick(param));
|
||||
} else {
|
||||
// 권한 없으면 버튼 제거
|
||||
projectSaveBtn.remove();
|
||||
projectCancelBtn.remove();
|
||||
projectLocationBtn.remove();
|
||||
}
|
||||
|
||||
// 이벤트 리스너 초기화
|
||||
initEventListeners();
|
||||
}
|
||||
|
||||
function handleProjectSaveClick(param) {
|
||||
const projectSaveBtn = document.getElementById('project-save-btn');
|
||||
|
||||
if (projectSaveBtn.textContent === '저장') {
|
||||
// 저장 버튼 클릭 : 읽기 모드로 전환
|
||||
projectSaveBtn.textContent = '수정';
|
||||
setReadOnlyMode(param);
|
||||
} else {
|
||||
// 수정 버튼 클릭: 편집 모드로 전환
|
||||
projectSaveBtn.textContent = '저장';
|
||||
setEditMode(param);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 읽기 모드 : 관리자 이상이면서 읽기모드 or 일반유저
|
||||
function setReadOnlyMode(param) {
|
||||
|
||||
const projectStepBtn = document.getElementById('project-step-btn');
|
||||
const projectStepCapsule = document.getElementById('project-step-capsule');
|
||||
const projectNameView = document.getElementById('project-name-view');
|
||||
const projectNameInput = document.getElementById('project-name-input');
|
||||
const projectSaveBtn = document.getElementById('project-save-btn');
|
||||
const projectCancelBtn = document.getElementById('project-cancel-btn');
|
||||
|
||||
// step - 버튼 숨기고 캡슐 표시
|
||||
projectStepBtn.style.display = 'none';
|
||||
projectStepCapsule.style.display = 'flex';
|
||||
projectStepCapsule.innerHTML = typeStep2Kor(vars.project.step);
|
||||
projectStepCapsule.className = `project-step-capsule --step-capsule__${vars.project.step}`;
|
||||
|
||||
// type - 버튼 숨기고 캡슐 표시
|
||||
projectTypeBtn.style.display = 'none';
|
||||
projectTypeCapsule.style.display = 'flex';
|
||||
projectTypeCapsule.innerHTML = typeStep2Kor(vars.project.project_type);
|
||||
|
||||
// name - input 숨기고 view 표시
|
||||
projectNameInput.style.display = 'none';
|
||||
projectNameView.style.display = 'flex';
|
||||
projectNameView.innerHTML = vars.project.short_nm;
|
||||
|
||||
if(param !== 'overseas' && param !== 'bimproject') {
|
||||
projectStepCapsule.style.display = 'none';
|
||||
projectTypeCapsule.style.display = 'none';
|
||||
|
||||
projectNameView.innerHTML = vars.project.project_nm;
|
||||
projectSaveBtn.style.display = 'none';
|
||||
projectCancelBtn.style.display = 'none';
|
||||
}
|
||||
|
||||
// location - 관리자이상:버튼 o , 관리자이하: 버튼x
|
||||
if (vars.permission.checkPermission('change-project-btn')) {
|
||||
document.querySelector('#project-location-btn').style.display = 'flex';
|
||||
}
|
||||
const lat = document.querySelector('.archive-modal .wrap .modal-wrap .modal-body .project-setting-wrap .project-location-wrap .project-location-lat');
|
||||
lat.innerHTML =`<div class="project-location-lat">위도 ${vars.project.lat}</div>`
|
||||
const lon = document.querySelector('.archive-modal .wrap .modal-wrap .modal-body .project-setting-wrap .project-location-wrap .project-location-lon');
|
||||
lon.innerHTML =`<div class="project-location-lon">경도 ${vars.project.lon}</div>`
|
||||
}
|
||||
|
||||
// 편집모드 : 관리자 이상이면서 편집모드
|
||||
function setEditMode(param) {
|
||||
|
||||
const projectStepBtn = document.getElementById('project-step-btn');
|
||||
const projectStepCapsule = document.getElementById('project-step-capsule');
|
||||
const projectNameView = document.getElementById('project-name-view');
|
||||
const projectNameInput = document.getElementById('project-name-input');
|
||||
|
||||
// step - 캡슐 숨기고 버튼 표시
|
||||
projectStepCapsule.style.display = 'none';
|
||||
projectStepBtn.style.display = 'flex';
|
||||
projectStepBtn.innerHTML = `
|
||||
<h5 class="project-step__label --step__${vars.project.step}">${typeStep2Kor(vars.project.step)}</h5>
|
||||
<i class="project-step__icon"></i>
|
||||
`;
|
||||
|
||||
// type - 캡슐 숨기고 버튼 표시
|
||||
projectTypeCapsule.style.display = 'none';
|
||||
projectTypeBtn.style.display = 'flex';
|
||||
projectTypeBtn.innerHTML = `
|
||||
<h5 class="project-type__label --type__${vars.project.project_type}">${typeStep2Kor(vars.project.project_type)}</h5>
|
||||
<i class="project-type__icon"></i>
|
||||
`;
|
||||
|
||||
// name - view 숨기고 input 표시
|
||||
projectNameView.style.display = 'none';
|
||||
projectNameInput.style.display = 'flex';
|
||||
projectNameInput.value = projectNameView.textContent;
|
||||
|
||||
if(param !== 'overseas' && param !== 'bimproject') {
|
||||
projectStepBtn.style.display = 'none';
|
||||
projectTypeBtn.style.display = 'none';
|
||||
}
|
||||
|
||||
// location - 수정 버튼, 위경도 위치 표시
|
||||
document.querySelector('#project-location-btn').style.display = 'flex';
|
||||
const lat = document.querySelector('.archive-modal .wrap .modal-wrap .modal-body .project-setting-wrap .project-location-wrap .project-location-lat');
|
||||
lat.innerHTML =`<div class="project-location-lat">위도 ${vars.project.lat}</div>`
|
||||
const lon = document.querySelector('.archive-modal .wrap .modal-wrap .modal-body .project-setting-wrap .project-location-wrap .project-location-lon');
|
||||
lon.innerHTML =`<div class="project-location-lon">경도 ${vars.project.lon}</div>`
|
||||
}
|
||||
|
||||
// 접속 인원 모달 - 프로젝트 type, step 변경 이벤트
|
||||
function initEventListeners() {
|
||||
// project-type-btn 클릭 이벤트
|
||||
if (projectTypeBtn) {
|
||||
// 기존 이벤트 제거 후 추가 (중복 방지)
|
||||
const newBtn = projectTypeBtn.cloneNode(true);
|
||||
projectTypeBtn.parentNode.replaceChild(newBtn, projectTypeBtn);
|
||||
projectTypeBtn = newBtn;
|
||||
|
||||
projectTypeList = projectTypeBtn.parentElement.querySelector('.project-type__list');
|
||||
|
||||
projectTypeBtn.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
projectTypeList?.classList.toggle('--project-list__open');
|
||||
});
|
||||
}
|
||||
|
||||
// project-step-btn 클릭 이벤트
|
||||
const projectStepBtn = document.getElementById('project-step-btn');
|
||||
if (projectStepBtn) {
|
||||
projectStepList = document.querySelector('.project-step__list');
|
||||
|
||||
projectStepBtn.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
projectStepList?.classList.toggle('--project-list__open');
|
||||
});
|
||||
}
|
||||
|
||||
// modal-wrap 클릭 이벤트
|
||||
if (projectTypeBtn) {
|
||||
const modalWrap = projectTypeBtn.closest('.modal-wrap');
|
||||
if (modalWrap) {
|
||||
modalWrap.addEventListener('click', (e) => {
|
||||
projectTypeList?.classList.remove('--project-list__open');
|
||||
projectStepList?.classList.remove('--project-list__open');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// project-type__list_item 클릭 이벤트
|
||||
if (projectTypeList) {
|
||||
projectTypeList.querySelectorAll('.project-type__list_item').forEach(item => {
|
||||
item.addEventListener('click', async(e) => {
|
||||
e.stopPropagation();
|
||||
projectTypeList.classList.remove('--project-list__open');
|
||||
let type = e.target.classList[1].split('__')[1];
|
||||
let kor = e.target.textContent;
|
||||
projectTypeBtn.innerHTML = `
|
||||
<h5 class="project-type__label --type__${type}">${kor}</h5>
|
||||
<i class="project-type__icon"></i>`;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// project-step__list_item 클릭 이벤트
|
||||
if (projectStepList) {
|
||||
projectStepList.querySelectorAll('.project-step__list_item').forEach(item => {
|
||||
item.addEventListener('click', async(e) => {
|
||||
e.stopPropagation();
|
||||
projectStepList.classList.remove('--project-list__open');
|
||||
let step = e.target.classList[1].split('__')[1];
|
||||
let btn = document.getElementById('project-step-btn');
|
||||
let kor = typeStep2Kor(step);
|
||||
if (btn) {
|
||||
btn.innerHTML = `
|
||||
<h5 class="project-step__label --step__${step}">${kor}</h5>
|
||||
<i class="project-step__icon"></i>`;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function stepTypeEng(param) {
|
||||
let result = param;
|
||||
switch(param) {
|
||||
case '완료':
|
||||
result = 'done';
|
||||
break;
|
||||
case '진행':
|
||||
result = 'active';
|
||||
break;
|
||||
case '중지':
|
||||
result = 'stop';
|
||||
break;
|
||||
case '대기':
|
||||
result = 'wait';
|
||||
break;
|
||||
|
||||
case '시공':
|
||||
result = 'construction';
|
||||
break;
|
||||
case '설계':
|
||||
result = 'design';
|
||||
break;
|
||||
case '제안':
|
||||
result = 'surgest';
|
||||
break;
|
||||
case '연구':
|
||||
result = 'research';
|
||||
break;
|
||||
case '지원':
|
||||
result = 'support';
|
||||
break;
|
||||
case '센터':
|
||||
result = 'center';
|
||||
break;
|
||||
case '측량':
|
||||
result = 'survey';
|
||||
break;
|
||||
|
||||
case 'MP (기본계획)':
|
||||
result = 'MP';
|
||||
break;
|
||||
case 'DD (실시설계)':
|
||||
result = 'DD';
|
||||
break;
|
||||
case 'FS (타당성조사)':
|
||||
result = 'FS';
|
||||
break;
|
||||
case 'PD (기본설계)':
|
||||
result = 'PD';
|
||||
break;
|
||||
case 'DS (설계감리)':
|
||||
result = 'DS';
|
||||
break;
|
||||
case 'CS (시공감리)':
|
||||
result = 'CS';
|
||||
break;
|
||||
case 'PMC (실시설계)':
|
||||
result = 'PMC';
|
||||
break;
|
||||
case 'IDC (타당성조사)':
|
||||
result = 'IDC';
|
||||
break;
|
||||
case 'DR (설계검토)':
|
||||
result = 'DR';
|
||||
break;
|
||||
case 'BD (수주영업)':
|
||||
result = 'BD';
|
||||
break;
|
||||
case 'ETC (기타)':
|
||||
result = 'ETC';
|
||||
break;
|
||||
|
||||
default:
|
||||
result = '없음';
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 셀렉트박스 크기 조정
|
||||
function adjustSelectBoxWidth(button) {
|
||||
if (!button) return;
|
||||
|
||||
const list = button.parentElement.querySelector('.project-type__list');
|
||||
if (!list) return;
|
||||
|
||||
// canvas를 사용한 텍스트 너비 측정
|
||||
const canvas = document.createElement('canvas');
|
||||
const context = canvas.getContext('2d');
|
||||
|
||||
// 현재 폰트 스타일 가져오기
|
||||
const computedStyle = window.getComputedStyle(button);
|
||||
context.font =`${computedStyle.fontSize} ${computedStyle.fontFamily}`;
|
||||
|
||||
let maxWidth = 0;
|
||||
const items = list.querySelectorAll('.project-type__list_item');
|
||||
|
||||
items.forEach(item => {
|
||||
const text = item.textContent.trim();
|
||||
const metrics = context.measureText(text);
|
||||
const width = metrics.width;
|
||||
|
||||
if (width > maxWidth) {
|
||||
maxWidth = width;
|
||||
}
|
||||
});
|
||||
|
||||
// 적용
|
||||
if (maxWidth > 0) {
|
||||
const finalWidth = maxWidth + 25;
|
||||
|
||||
// 버튼에 적용
|
||||
button.style.minWidth = `${finalWidth}px`;
|
||||
button.style.textAlign = 'center'; // 가운데 정렬
|
||||
button.style.justifyContent = 'center'; // flex인 경우
|
||||
button.style.display = 'flex'; // flex 적용
|
||||
button.style.alignItems = 'center'; // 세로 가운데 정렬
|
||||
|
||||
// 캡슐에 적용
|
||||
const capsule = button.parentElement.querySelector('.--type-capsule');
|
||||
if (capsule) {
|
||||
capsule.style.minWidth = `${finalWidth}px`;
|
||||
capsule.style.textAlign = 'center'; // 가운데 정렬
|
||||
capsule.style.justifyContent = 'center'; // flex인 경우
|
||||
capsule.style.display = 'flex'; // flex 적용
|
||||
capsule.style.alignItems = 'center'; // 세로 가운데 정렬
|
||||
|
||||
}
|
||||
|
||||
// 리스트에 적용
|
||||
list.style.minWidth = `${finalWidth}px`;
|
||||
|
||||
// 리스트 글씨 정렬
|
||||
items.forEach(item => {
|
||||
item.style.paddingLeft = '5px';
|
||||
item.style.paddingRight = '5px';
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// 프로젝트 위치 수정 버튼 클릭 이벤트
|
||||
document.getElementById('project-location-btn').addEventListener('click', () => {
|
||||
document.querySelector('.project-location-modal').style.display = 'flex';
|
||||
openLocationModal();
|
||||
})
|
||||
|
||||
// 지도 모달창 닫기 버튼, 취소 버튼 클릭 이벤트
|
||||
document.querySelector('.project-location-modal .modal-wrap .modal-head .close').addEventListener('click', () => {
|
||||
closeLocationModal();
|
||||
});
|
||||
document.querySelector('.project-location-modal .modal-wrap .modal-foot .button').addEventListener('click', () => {
|
||||
closeLocationModal();
|
||||
});
|
||||
|
||||
// 기본 지도 모달창 버튼 클릭 이벤트
|
||||
document.querySelector('.project-location-modal .modal-wrap .modal-body .map-wrap .xs-button').addEventListener('click', () => {
|
||||
document.querySelector('.project-location-modal .modal-wrap .modal-body .map-wrap .base-map').style.display = 'flex';
|
||||
});
|
||||
// 기본 지도 모달창 닫기 버튼 클릭 이벤트
|
||||
document.querySelector('.project-location-modal .modal-wrap .modal-body .map-wrap .base-map .text-wrap .close').addEventListener('click', () => {
|
||||
document.querySelector('.project-location-modal .modal-wrap .modal-body .map-wrap .base-map').style.display = 'none';
|
||||
});
|
||||
|
||||
// 지도 모달창 저장 버튼 클릭 이벤트
|
||||
document.querySelector('.project-location-modal .modal-wrap .modal-foot .primary-button').addEventListener('click', async() => {
|
||||
if(!ol.locationMap || !ol.locationMap.clickCoord) {
|
||||
alert('위치를 선택해주세요');
|
||||
return;
|
||||
}
|
||||
|
||||
let updateLocationParams = {
|
||||
projectId: vars.project.project_id,
|
||||
category: vars.project.category,
|
||||
project_nm: vars.project.project_nm,
|
||||
lon: ol.locationMap.clickCoord.lon,
|
||||
lat: ol.locationMap.clickCoord.lat
|
||||
};
|
||||
|
||||
let res = await axios.post('/common/updateLocationInfo', {params: updateLocationParams});
|
||||
if (res.data.message == 'updateLocationInfo_success') {
|
||||
vars.project.lon = res.data.data.lon;
|
||||
vars.project.lat = res.data.data.lat;
|
||||
|
||||
closeLocationModal();
|
||||
|
||||
const lat = document.querySelector('.archive-modal .wrap .modal-wrap .modal-body .project-setting-wrap .project-location-wrap .project-location-lat');
|
||||
lat.innerHTML = `<div class="project-location-lat">위도 ${res.data.data.lat}</div>`;
|
||||
|
||||
const lon = document.querySelector('.archive-modal .wrap .modal-wrap .modal-body .project-setting-wrap .project-location-wrap .project-location-lon');
|
||||
lon.innerHTML = `<div class="project-location-lon">경도 ${res.data.data.lon}</div>`;
|
||||
}
|
||||
})
|
||||
|
||||
// 프로젝트 위치 수정
|
||||
function openLocationModal() {
|
||||
// 메인 지도 레이어 상태 백업
|
||||
if (vars.roadLayer && vars.satelliteLayer && vars.hybridLayer) {
|
||||
mainLayerState = {
|
||||
road: vars.roadLayer.getVisible(),
|
||||
satellite: vars.satelliteLayer.getVisible(),
|
||||
hybrid: vars.hybridLayer.getVisible()
|
||||
};
|
||||
}
|
||||
|
||||
initLocationMap();
|
||||
|
||||
// 기존 좌표가 있을 때만 블루 마커 생성
|
||||
if (vars.project.lon && vars.project.lat) {
|
||||
if (!ol.locationMap.blueMarkerOverlay) {
|
||||
// 블루 마커 (저장된 위치)
|
||||
let blueMarker = document.createElement('div');
|
||||
blueMarker.style.width = '1rem';
|
||||
blueMarker.style.height = '1rem';
|
||||
blueMarker.style.backgroundColor = '#0D8DF2';
|
||||
blueMarker.style.borderRadius = '50%';
|
||||
blueMarker.style.border = '0.1875rem solid #fff';
|
||||
blueMarker.style.boxShadow = '0 0.25rem 0.5rem rgba(0, 0, 0, 0.05)';
|
||||
|
||||
ol.locationMap.blueMarkerOverlay = new ol.Overlay({
|
||||
element: blueMarker,
|
||||
positioning: 'center-center',
|
||||
stopEvent: false,
|
||||
zIndex: 9998
|
||||
});
|
||||
ol.locationMap.addOverlay(ol.locationMap.blueMarkerOverlay);
|
||||
}
|
||||
|
||||
if (!ol.locationMap.blueLabelOverlay) {
|
||||
let blueLabel = document.createElement('div');
|
||||
|
||||
ol.locationMap.blueLabelOverlay = new ol.Overlay({
|
||||
element: blueLabel,
|
||||
positioning: 'bottom-center',
|
||||
offset: [0, -10],
|
||||
stopEvent: false,
|
||||
zIndex: 9998,
|
||||
});
|
||||
ol.locationMap.addOverlay(ol.locationMap.blueLabelOverlay);
|
||||
}
|
||||
|
||||
const position3857 = ol.proj.fromLonLat([vars.project.lon, vars.project.lat]);
|
||||
|
||||
ol.locationMap.blueMarkerOverlay.setPosition(position3857);
|
||||
ol.locationMap.blueLabelOverlay.setPosition(position3857);
|
||||
ol.locationMap.blueLabelOverlay.element.innerHTML = `
|
||||
<div style="background: #fff; color: #111; padding: 0.25rem 0.5rem; border-radius: 0.25rem; border: 0.1875rem solid #0D8DF2; box-shadow: 0 0.25rem 0.5rem rgba(0, 0, 0, 0.05); font-size: 0.875rem; font-weight: 500; line-height: 1.25rem; letter-spacing: -0.0175rem;">
|
||||
저장된 위치 <br>
|
||||
경도 : ${vars.project.lon}<br>
|
||||
위도 : ${vars.project.lat}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
// 지도 초기화
|
||||
function initLocationMap() {
|
||||
// 위치 모달 지도만 삭제
|
||||
if (ol.locationMap) {
|
||||
ol.locationMap.getOverlays()?.clear();
|
||||
ol.locationMap.getLayers()?.forEach(l => {
|
||||
const src = l.getSource?.();
|
||||
if (src && src instanceof ol.source.Vector) src.clear(true);
|
||||
});
|
||||
ol.locationMap.getLayers()?.clear();
|
||||
if (typeof ol.locationMap.dispose === 'function') {
|
||||
ol.locationMap.dispose();
|
||||
}
|
||||
ol.locationMap = null;
|
||||
}
|
||||
|
||||
// 위치 설정 모달용 임시 레이어 생성
|
||||
const tempRoadLayer = new ol.layer.Tile({
|
||||
source: new ol.source.XYZ({
|
||||
url: vars.roadLayer.getSource().getUrls()[0]
|
||||
}),
|
||||
visible: mainLayerState?.road ?? true
|
||||
});
|
||||
const tempSatelliteLayer = new ol.layer.Tile({
|
||||
source: new ol.source.XYZ({
|
||||
url: vars.satelliteLayer.getSource().getUrls()[0]
|
||||
}),
|
||||
visible: mainLayerState?.satellite ?? false
|
||||
});
|
||||
const tempHybridLayer = new ol.layer.Tile({
|
||||
source: new ol.source.XYZ({
|
||||
url: vars.hybridLayer.getSource().getUrls()[0]
|
||||
}),
|
||||
visible: mainLayerState?.hybrid ?? false
|
||||
});
|
||||
|
||||
ol.locationMap = new ol.Map({
|
||||
target: 'map-location',
|
||||
layers: [tempRoadLayer, tempSatelliteLayer, tempHybridLayer],
|
||||
view: new ol.View({
|
||||
projection: 'EPSG:3857',
|
||||
center: ol.proj.fromLonLat([127.8, 35.9]),
|
||||
zoom: 7,
|
||||
constrainResolution: true,
|
||||
smoothResolutionConstraint: true
|
||||
})
|
||||
});
|
||||
|
||||
// 임시 레이어 참조 저장
|
||||
ol.locationMap.tempRoadLayer = tempRoadLayer;
|
||||
ol.locationMap.tempSatelliteLayer = tempSatelliteLayer;
|
||||
ol.locationMap.tempHybridLayer = tempHybridLayer;
|
||||
|
||||
// proj4 설정
|
||||
proj4.defs([
|
||||
['EPSG:5185', '+proj=tmerc +lat_0=38 +lon_0=125 +k=1 +x_0=200000 +y_0=600000 +ellps=GRS80 +units=m +no_defs'],
|
||||
['EPSG:5186', '+proj=tmerc +lat_0=38 +lon_0=127 +k=1 +x_0=200000 +y_0=600000 +ellps=GRS80 +units=m +no_defs'],
|
||||
['EPSG:5187', '+proj=tmerc +lat_0=38 +lon_0=129 +k=1 +x_0=200000 +y_0=600000 +ellps=GRS80 +units=m +no_defs'],
|
||||
['EPSG:5188', '+proj=tmerc +lat_0=38 +lon_0=131 +k=1 +x_0=200000 +y_0=600000 +ellps=GRS80 +units=m +no_defs'],
|
||||
['EPSG:4978', '+proj=geocent +datum=WGS84 +units=m +no_defs'],
|
||||
['EPSG:4326', '+proj=longlat +datum=WGS84 +no_defs'],
|
||||
]);
|
||||
ol.proj.proj4.register(proj4);
|
||||
|
||||
// 레드 마커 생성
|
||||
let marker = document.createElement('div');
|
||||
marker.classList.add('marker');
|
||||
marker.style.width = '1rem';
|
||||
marker.style.height = '1rem';
|
||||
marker.style.backgroundColor = '#F21D0D';
|
||||
marker.style.borderRadius = '50%';
|
||||
marker.style.border = '0.1875rem solid #fff';
|
||||
marker.style.boxShadow = '0 0.25rem 0.5rem rgba(0, 0, 0, 0.05)';
|
||||
|
||||
ol.locationMap.redMarkerOverlay = new ol.Overlay({
|
||||
element: marker,
|
||||
positioning: 'center-center',
|
||||
stopEvent: false,
|
||||
zIndex: 9999
|
||||
});
|
||||
ol.locationMap.addOverlay(ol.locationMap.redMarkerOverlay);
|
||||
|
||||
let label = document.createElement('div');
|
||||
|
||||
ol.locationMap.redLabelOverlay = new ol.Overlay({
|
||||
element: label,
|
||||
positioning: 'bottom-center',
|
||||
offset: [0, -10],
|
||||
stopEvent: false,
|
||||
zIndex: 9999,
|
||||
});
|
||||
ol.locationMap.addOverlay(ol.locationMap.redLabelOverlay);
|
||||
|
||||
// 클릭 이벤트
|
||||
ol.locationMap.on('click', function(e) {
|
||||
const lonLat = ol.proj.toLonLat(e.coordinate);
|
||||
const lon = lonLat[0].toFixed(6);
|
||||
const lat = lonLat[1].toFixed(6);
|
||||
|
||||
ol.locationMap.clickCoord = {
|
||||
lon: Number(lon),
|
||||
lat: Number(lat),
|
||||
coordinate3857: e.coordinate
|
||||
};
|
||||
|
||||
ol.locationMap.redMarkerOverlay.setPosition(e.coordinate);
|
||||
|
||||
ol.locationMap.redLabelOverlay.element.innerHTML = `
|
||||
<div style="background: #fff; color: #111; padding: 0.25rem 0.5rem; border-radius: 0.25rem; border: 0.1875rem solid #F21D0D; box-shadow: 0 0.25rem 0.5rem rgba(0, 0, 0, 0.05); font-size: 0.875rem; font-weight:500; line-height: 1.25rem; letter-spacing: -0.0175rem;">
|
||||
선택한 위치 <br>
|
||||
경도 : ${lon} <br>
|
||||
위도 : ${lat}
|
||||
</div>
|
||||
`;
|
||||
|
||||
ol.locationMap.redLabelOverlay.setPosition(e.coordinate);
|
||||
});
|
||||
}
|
||||
|
||||
// 기본지도 타입 라디오 버튼 클릭 이벤트
|
||||
document.querySelectorAll('.project-location-modal input[name="location-base-map"]').forEach(radio => {
|
||||
radio.addEventListener('change', function() {
|
||||
if (!ol.locationMap) return;
|
||||
|
||||
const type = this.value;
|
||||
|
||||
// 위치 설정 모달의 임시 레이어만 변경
|
||||
ol.locationMap.tempRoadLayer.setVisible(false);
|
||||
ol.locationMap.tempSatelliteLayer.setVisible(false);
|
||||
ol.locationMap.tempHybridLayer.setVisible(false);
|
||||
|
||||
switch(type) {
|
||||
case 'road':
|
||||
ol.locationMap.tempRoadLayer.setVisible(true);
|
||||
break;
|
||||
case 'hybrid':
|
||||
ol.locationMap.tempHybridLayer.setVisible(true);
|
||||
break;
|
||||
case 'satellite':
|
||||
ol.locationMap.tempSatelliteLayer.setVisible(true);
|
||||
break;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 모달 닫기 함수
|
||||
function closeLocationModal() {
|
||||
document.querySelector('.project-location-modal').style.display = 'none';
|
||||
|
||||
if (ol.locationMap) {
|
||||
ol.locationMap.setTarget(null);
|
||||
if (typeof ol.locationMap.dispose === 'function') {
|
||||
ol.locationMap.dispose();
|
||||
}
|
||||
ol.locationMap = null;
|
||||
}
|
||||
|
||||
// 메인 지도 레이어 상태 복원
|
||||
if (mainLayerState) {
|
||||
vars.roadLayer.setVisible(mainLayerState.road);
|
||||
vars.satelliteLayer.setVisible(mainLayerState.satellite);
|
||||
vars.hybridLayer.setVisible(mainLayerState.hybrid);
|
||||
mainLayerState = null;
|
||||
}
|
||||
}
|
||||
|
||||
2119
views/main/jsm/archive/socketManager.js
Normal file
2119
views/main/jsm/archive/socketManager.js
Normal file
File diff suppressed because it is too large
Load Diff
663
views/main/jsm/archive/userPermission.js
Normal file
663
views/main/jsm/archive/userPermission.js
Normal file
@@ -0,0 +1,663 @@
|
||||
import { vars } from './variable.js';
|
||||
|
||||
//설정창 close 25-08-18 권한 설정 모달 사용 중에 모달이 꺼지는일이 많아 주석처리로 꺼지는 기능을 막음
|
||||
// document.querySelector('.permission-modal').addEventListener('click', (e) => {
|
||||
// if (e.target.className.includes('permission-modal')) document.querySelector('.permission-modal').style.display = 'none';
|
||||
// })
|
||||
document.querySelector('.permission-modal img[alt="닫기"]').addEventListener('click', () => {
|
||||
// 이름 검색 결과창 초기화
|
||||
searchResultInit();
|
||||
document.querySelector('.permission-modal').style.display = 'none';
|
||||
})
|
||||
|
||||
//설정창 저장
|
||||
document.getElementById('permission-submit').addEventListener('click', () => {
|
||||
if(vars.permissionList.changed && Object.keys(vars.permissionList.changed).length > 0){
|
||||
if (confirm(`${Object.keys(vars.permissionList.changed).length}개의 변경사항을 저장하시겠습니까?`)) {
|
||||
upsertAuthList();
|
||||
}
|
||||
} else {
|
||||
searchResultInit();
|
||||
document.querySelector('.permission-modal').style.display = 'none';
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
//설정창 취소
|
||||
document.getElementById('permission-cancel').addEventListener('click', () => {
|
||||
if (vars.permissionList.changed && Object.keys(vars.permissionList.changed).length > 0) {
|
||||
if (!confirm(`${Object.keys(vars.permissionList.changed).length}개의 변경사항을 저장하지 않고 취소하시겠습니까?`)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// 이름 검색 결과창 초기화
|
||||
searchResultInit();
|
||||
document.querySelector('.permission-modal').style.display = 'none';
|
||||
});
|
||||
|
||||
//tab 변경
|
||||
document.querySelectorAll('.permission-modal input[type="radio"]').forEach(ele => {
|
||||
ele.addEventListener('click', (e) => {
|
||||
document.querySelector('.permission-modal .modal-wrap .left .search-result-container').style.display = 'none';
|
||||
document.querySelector('.permission-modal .modal-wrap .left ul').style.display = 'block';
|
||||
|
||||
let id = e.target.id;
|
||||
let tab = document.querySelector('.permission-modal .select-list-' + id);
|
||||
|
||||
drawUserPermissionList(vars.permissionList, id);
|
||||
|
||||
[...tab.parentElement.children].forEach(element => {
|
||||
if (element.classList.contains('select-list')) element.style.display = 'none';
|
||||
});
|
||||
tab.style.display = 'block';
|
||||
})
|
||||
});
|
||||
|
||||
export async function getList() {
|
||||
let param = {
|
||||
// project_id : JSON.parse(localStorage.getItem('data')).id
|
||||
project_id: vars.project_id
|
||||
}
|
||||
let res = await axios.get('/auth/getMemberList', { params: param });
|
||||
return res.data;
|
||||
}
|
||||
|
||||
// 유저권한설정창
|
||||
export async function drawUserPermissionList(list, rightId = 'sub-master') {
|
||||
document.querySelector('.permission-modal .left ul').innerHTML = '';
|
||||
document.querySelectorAll(`.permission-modal .select-list ul`).forEach(ul => ul.innerHTML = '');
|
||||
|
||||
// DB에 저장횐 company-list가 6개 외에 더 있어서 회사 리스트 고정
|
||||
const companyList = [ '한맥기술', '삼안', '장헌산업', `(주)장헌` , '피티씨' , '한라산업개발', '바론컨설턴트' , '기타'];
|
||||
// 기존 권한을 가진 인원(key-value)들을 배열화
|
||||
const permission = Object.values(list.changePermission);
|
||||
|
||||
try {
|
||||
// changePermission에는 user_id - lev만 있기때문에 배열을 DB로 보내 유저 정보를 가져온다
|
||||
const usersInfoRes = await axios.post('/auth/getPermissionUserInfo', {permission : permission});
|
||||
const userInfoArr = usersInfoRes.data.result;
|
||||
const rightUl = document.querySelector(`.permission-modal .select-list.select-list-${rightId} ul`);
|
||||
|
||||
userInfoArr.forEach(user => {
|
||||
if (findPermission(user.user_id, permission) !== rightId) return;
|
||||
|
||||
if (rightUl.querySelector(`#_${user.user_id}`)) return;
|
||||
// 유저 리스트 DOM 생성
|
||||
const userLi = drawUserList(user, rightId, permission, true);
|
||||
rightUl.appendChild(userLi);
|
||||
// 클릭 이벤트
|
||||
bindUserLiClick(userLi, user, rightId, permission, true);
|
||||
});
|
||||
// 유저 수 체크
|
||||
checkPermissionUserNum(rightId);
|
||||
} catch (err) {
|
||||
console.error('getPermissionUserInfo', err);
|
||||
}
|
||||
|
||||
// 회사 리스트 생성
|
||||
companyList.forEach(company => {
|
||||
const companyTitle = drawCompanyTitle(company);
|
||||
const companyToggleImg = companyTitle.querySelector('.company-toggle img');
|
||||
|
||||
// 회사 리스트 클릭시 부서명 생성
|
||||
companyTitle.addEventListener('click', async () => {
|
||||
let deptTitle = companyTitle.nextElementSibling;
|
||||
|
||||
// 부서명 최초 1회 DOM 생성 이후에 토글
|
||||
if (!deptTitle || !deptTitle.classList.contains('dept-container')) {
|
||||
companyToggleImg.src = '/main/img/permission/down.svg';
|
||||
deptTitle = document.createElement('ul');
|
||||
deptTitle.className = 'dept-container _scrollbar';
|
||||
deptTitle.dataset.company = company;
|
||||
companyTitle.insertAdjacentElement('afterend', deptTitle);
|
||||
|
||||
try {
|
||||
// 회사명을 통해 부서리스트 조회
|
||||
const deptRes = await axios.get('/auth/getDeptList', { params: { company } });
|
||||
if (deptRes.data.message === '200') {
|
||||
const deptList = deptRes.data.result;
|
||||
|
||||
// 한글 정렬
|
||||
deptList.sort((a, b) => {
|
||||
if (!a.dept || !b.dept) return 0;
|
||||
return a.dept.localeCompare(b.dept, 'ko');
|
||||
});
|
||||
|
||||
// 부서 리스트 생성
|
||||
for (const depts of deptList) {
|
||||
if (!depts.dept) continue;
|
||||
|
||||
const deptLi = drawDeptTitle(company, depts.dept);
|
||||
const deptToggleImg = deptLi.querySelector('.dept-toggle img');
|
||||
const deptCheckBox = deptLi.querySelector('input[type="checkbox"]');
|
||||
const deptToggelWrap = deptLi.querySelector('.toggle-wrap');
|
||||
|
||||
// 부서 개별 체크 박스 change 이벤트
|
||||
deptCheckBox.addEventListener('change', async (e) => {
|
||||
const isChecked = deptCheckBox.checked;
|
||||
const rightUl = document.querySelector(`.permission-modal .right .select-list.select-list-${rightId} ul`);
|
||||
try {
|
||||
// 회사명 - 부서를 통해 부서의 유저를 전부 조회
|
||||
const userRes = await axios.get('/auth/getUserList', { params: { company, dept: depts.dept }});
|
||||
|
||||
const userList = userRes.data.result;
|
||||
// 부서 전체 선택시에 이미 권한이 있어 권한을 부여할 수 없는 인원 수를 체크하는 배열
|
||||
const diffGroupArr = [];
|
||||
|
||||
// 유저 리스트 생성
|
||||
for (let user of userList) {
|
||||
const existingRight = rightUl.querySelector(`#_${user.user_id}`);
|
||||
const existingLeft = document.querySelector(`.permission-modal .left ul li#_${user.user_id} .user-wrap`);
|
||||
const lev = findPermission(user.user_id, Object.values(vars.permissionList.changePermission));
|
||||
// 전체 선택시
|
||||
if (isChecked) {
|
||||
// 이미 오른쪽(권한이 있던)에 있는 인원과 권한이 다른 인원은 continue로 건너뛰기
|
||||
if (existingRight) continue;
|
||||
if (lev && lev !== rightId){
|
||||
diffGroupArr.push(user);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 부관리자 10명 제한
|
||||
if (rightId === 'sub-master' && rightUl.children.length >= 10) {
|
||||
updateCheckboxState(user.company, user.dept);
|
||||
return alert('부관리자는 최대 10명까지 선택 가능합니다.');
|
||||
}
|
||||
// 전체선택으로 권한 변경된 유저 권한 변경
|
||||
changePermission(user.user_id, rightId, undefined);
|
||||
// 오른쪽 리스트에 생성
|
||||
const copy = drawUserList(user, rightId, permission, true);
|
||||
rightUl.appendChild(copy);
|
||||
// 클릭이벤트 추카
|
||||
bindUserLiClick(copy, user, rightId, permission, true);
|
||||
|
||||
// 왼쪽 유저리스트에 권한 뱃지 생성
|
||||
if (existingLeft) {
|
||||
const roleDiv = createRoleDiv(rightId);
|
||||
existingLeft.insertAdjacentHTML('beforeend', roleDiv);
|
||||
existingLeft.classList.add('selected_' + rightId);
|
||||
existingLeft.style.display = 'flex';
|
||||
}
|
||||
} else {
|
||||
// 전체 선택해제
|
||||
if (existingRight) {
|
||||
// 오른쪽 DOM 삭제와 권한 해제
|
||||
existingRight.remove();
|
||||
changePermission(user.user_id, undefined, rightId);
|
||||
}
|
||||
// 왼쪽 유저 리스트에 권한 뱃지 삭제
|
||||
existingLeft?.querySelector(`.user-permission-${rightId}`)?.remove();
|
||||
existingLeft?.classList.remove(`selected_${rightId}`);
|
||||
}
|
||||
}
|
||||
// 체크 박스 상태 변화 감지
|
||||
updateCheckboxState(company, depts.dept);
|
||||
if (diffGroupArr.length > 0){
|
||||
alert(`이미 권한을 가진 ${diffGroupArr.length}명은 ${{'sub-master' : '부관리자', 'security-worker' : '보안참여자', 'worker' : '일반참여자', 'viewer' : '참관자'}[rightId]} 권한을 부여할 수 없습니다.`);
|
||||
deptCheckBox.checked = true;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('getUserList Error (checkbox): ', err);
|
||||
}
|
||||
});
|
||||
|
||||
// 체크 박스 상태 변화 감지
|
||||
updateCheckboxState(company, depts.dept);
|
||||
|
||||
// 부서명 클릭 이벤트
|
||||
deptToggelWrap.addEventListener('click', async () => {
|
||||
let userTitle = deptLi.nextElementSibling;
|
||||
// 유저 최초 1회 생성 이후에 토글
|
||||
if (!userTitle || !userTitle.classList.contains('user-container')) {
|
||||
deptToggleImg.src = '/main/img/permission/down.svg';
|
||||
userTitle = document.createElement('ul');
|
||||
userTitle.className = 'user-container _scrollbar';
|
||||
deptLi.insertAdjacentElement('afterend', userTitle);
|
||||
|
||||
// 회사명,부서명으로 유저리스트 조회
|
||||
const userRes = await axios.get('/auth/getUserList', { params: { company, dept: depts.dept }});
|
||||
|
||||
if (userRes.data.message === '200') {
|
||||
const userList = userRes.data.result;
|
||||
// 유저리스트 생성
|
||||
userList.forEach(user => {
|
||||
// 유저 DOM 생성
|
||||
const userLi = drawUserList(user, rightId, permission);
|
||||
userTitle.appendChild(userLi);
|
||||
// 유저 클릭 이벤트
|
||||
bindUserLiClick(userLi, user, rightId, permission);
|
||||
});
|
||||
}
|
||||
}
|
||||
// 유저 토글 처리
|
||||
userTitle.style.display = userTitle.style.display === 'block' ? 'none' : 'block';
|
||||
deptToggleImg.src = userTitle.style.display === 'block' ? '/main/img/permission/down.svg' : '/main/img/permission/up.svg';
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('getDeptList Error', error);
|
||||
}
|
||||
}
|
||||
// 부서 토글처리
|
||||
deptTitle.style.display = deptTitle.style.display === 'block' ? 'none' : 'block';
|
||||
companyToggleImg.src = deptTitle.style.display === 'block' ? '/main/img/permission/down.svg' : '/main/img/permission/up.svg';
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 유저 검색 기능 엔터 이벤트
|
||||
document.getElementById('permission-search').addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
// input 값 추출 이후 DOM 생성
|
||||
const keyword = e.target.value.trim().toLowerCase();
|
||||
searchResultUser(keyword);
|
||||
}
|
||||
});
|
||||
|
||||
// 유저 검색 기능 클릭 이벤트
|
||||
document.querySelector('.permission-modal .modal-wrap .left .search-box .wrap .search-image').addEventListener('click', () => {
|
||||
const keyword = document.querySelector('.permission-modal .modal-wrap .left .search-input').value.trim().toLowerCase();
|
||||
searchResultUser(keyword);
|
||||
});
|
||||
|
||||
// 유저 검색 기능(엔터, 검색바 클릭)
|
||||
function searchResultUser(keyword) {
|
||||
// 프로젝트 관리자를 제외하고 검색하기 위해 필터링
|
||||
const users = vars.permissionList.all.filter(user => user.user_id !== vars.project.user_id);
|
||||
const searchResult = document.querySelector('.permission-modal .modal-wrap .left .search-result-container');
|
||||
const permissionLeftUl = document.querySelector('.permission-modal .modal-wrap .left ul');
|
||||
const permission = Object.values(vars.permissionList.changePermission);
|
||||
const rightId = document.querySelector('.permission-modal .modal-wrap .tab-input:checked').id;
|
||||
const positionMap = {'회장': 1, '부회장':2, '사장':3, '상임고문':4, '기술위원':5, '부사장':6, '고문':7, '전무이사':8, '수석연구원':9, '상무이사':10, '이사':11, '책임연구원':12, '부장':13, '차장':14, '선임연구원':15, '과장':16, '연구원':17, '대리':18, '사원':19};
|
||||
if (keyword !== '') {
|
||||
// 이름으로 필터링 이후 직급으로 정렬
|
||||
const result = users.filter(user => user.user_nm?.toLowerCase().includes(keyword)).sort((a,b) => {
|
||||
const aPosition = positionMap[a.position] ?? 99;
|
||||
const bPosition = positionMap[b.position] ?? 99;
|
||||
|
||||
if(aPosition !== bPosition) return aPosition - bPosition;
|
||||
return a.user_id.localeCompare(b.user_id);
|
||||
})
|
||||
|
||||
if (result.length === 0) {
|
||||
searchResult.style.display = 'none';
|
||||
permissionLeftUl.style.display = 'block';
|
||||
return alert('일치하는 사용자가 없습니다.');
|
||||
}
|
||||
|
||||
searchResult.innerHTML = '';
|
||||
// 조회된 유저 있을때
|
||||
result.forEach(user => {
|
||||
//유저 DOM 생성
|
||||
const userLi = drawUserList(user, rightId, permission);
|
||||
searchResult.appendChild(userLi);
|
||||
//클릭이벤트
|
||||
bindUserLiClick(userLi, user, rightId, permission, false);
|
||||
});
|
||||
// 검색결과창 띄우기
|
||||
searchResult.style.display = 'block';
|
||||
permissionLeftUl.style.display = 'none';
|
||||
} else {
|
||||
// 빈 값 입력시 검색결과창 내리기
|
||||
searchResult.style.display = 'none';
|
||||
permissionLeftUl.style.display = 'block';
|
||||
}
|
||||
}
|
||||
|
||||
// 유저 검색결과 초기화(닫기버튼, X버튼 클릭시 실행)
|
||||
function searchResultInit(){
|
||||
const searchResult = document.querySelector('.search-result-container');
|
||||
searchResult.innerHTML = '';
|
||||
searchResult.style.display = 'none';
|
||||
document.querySelector('.permission-modal .modal-wrap .left ul').style.display = 'block';
|
||||
document.querySelector('.permission-modal .left .search-input').value = '';
|
||||
}
|
||||
|
||||
// 회사명 DOM 생성
|
||||
function drawCompanyTitle(company){
|
||||
if(company == '(주)장헌') company = '장헌';
|
||||
if(company == '피티씨') company = 'PTC';
|
||||
|
||||
const companyTitle = document.createElement('li');
|
||||
companyTitle.className = 'company-title';
|
||||
companyTitle.innerHTML += `
|
||||
<img class="" src="/main/img/permission/s-icon__${company}-large-logo.svg" alt="" />
|
||||
<div class="toggle-wrap">
|
||||
<span class="company-name">${(company == '기타')? '' :"(주)"}${company}</span>
|
||||
<div class="company-toggle">
|
||||
<img src="/main/img/permission/up.svg" alt="방향표">
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
document.querySelector('.permission-modal .left ul').append(companyTitle);
|
||||
|
||||
return companyTitle;
|
||||
}
|
||||
|
||||
// 부서명 DOM 생성
|
||||
function drawDeptTitle(company, dept){
|
||||
const deptLi = document.createElement('li');
|
||||
deptLi.className = 'dept-title';
|
||||
deptLi.dataset.company = company;
|
||||
deptLi.dataset.dept = dept;
|
||||
deptLi.innerHTML = `
|
||||
<label class="custom-checkbox">
|
||||
<input type="checkbox">
|
||||
<span class="checkmark"></span>
|
||||
</label>
|
||||
<div class="toggle-wrap">
|
||||
<span class="dept-name">${dept}</span>
|
||||
<div class="dept-toggle">
|
||||
<img src="/main/img/permission/up.svg" alt="방향표">
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
document.querySelector(`.permission-modal .left ul .dept-container[data-company="${company}"]`).appendChild(deptLi)
|
||||
|
||||
return deptLi;
|
||||
}
|
||||
// 유저리스트 DOM 생성
|
||||
function drawUserList(user, rightId, permissionList, isRightSide = false) {
|
||||
const li = document.createElement('li');
|
||||
li.className = 'user-li';
|
||||
li.id = `_${user.user_id}`;
|
||||
li.dataset.company = user.company;
|
||||
li.dataset.dept = user.dept;
|
||||
|
||||
const userWrap = document.createElement('div');
|
||||
userWrap.className = `user-wrap${isRightSide ? ` selected_${rightId} _scrollbar` : ''}`;
|
||||
|
||||
|
||||
userWrap.innerHTML += `
|
||||
<div class="user-img">
|
||||
<img draggable="false" src="http://erp.${(user.company === '장헌산업' ? 'jangheon' : 'hanmaceng')}.co.kr/erpphoto/${user.user_id}.jpg" alt="이미지" onerror="this.onerror=null; this.src='/main/img/archive/empty-profile.svg'">
|
||||
</div>
|
||||
<div class="user-info">
|
||||
<div class="user-path">
|
||||
<img class="user-logo" src="/main/img/permission/s-icon__${(user.company == '피티씨')?'PTC':user.company}-small-logo.svg" alt="" />
|
||||
<span class="user-team">${user.dept}</span>
|
||||
</div>
|
||||
<div class="user-top">
|
||||
<span class="user-name">${user.user_nm}</span>
|
||||
<span class="user-position">${user.position ?? ''}</span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
// 유저 권한 확인
|
||||
const lev = findPermission(user.user_id, Object.values(vars.permissionList.changePermission));
|
||||
const role = isRightSide ? rightId : lev;
|
||||
// 유저권한 생성
|
||||
if (role) {
|
||||
const roleDiv = createRoleDiv(role);
|
||||
userWrap.insertAdjacentHTML('beforeend', roleDiv);
|
||||
if (!isRightSide && lev !== rightId) userWrap.classList.add('disabled');
|
||||
if (!isRightSide) userWrap.classList.add(`selected_${lev}`);
|
||||
}
|
||||
|
||||
li.appendChild(userWrap);
|
||||
return li;
|
||||
}
|
||||
|
||||
// 유저 권한 뱃지 생성
|
||||
function createRoleDiv(role){
|
||||
return `<div class="user-permission-${role}">
|
||||
<h6>${{'sub-master' : '부관리', 'security-worker' : '보안', 'worker' : '일반', 'viewer' : '참관'}[role]}</h6>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
// 유저 클릭이벤트 바인딩
|
||||
function bindUserLiClick(userLi, user, rightId, permissionList, isRightSide = false) {
|
||||
const userWrap = userLi.querySelector('.user-wrap');
|
||||
const rightUl = document.querySelector(`.permission-modal .select-list.select-list-${rightId} ul`);
|
||||
|
||||
userLi.addEventListener('click', () => {
|
||||
const rightUser = rightUl.querySelector(`li#_${user.user_id}`);
|
||||
const leftUserWrap = document.querySelector(`.permission-modal .left ul li#_${user.user_id} .user-wrap`);
|
||||
const leftUserPermission = leftUserWrap?.querySelector(`.user-permission-${rightId}`);
|
||||
|
||||
if (userWrap.classList.contains('disabled')) return alert('다른 그룹에 등록된 인원입니다.');
|
||||
|
||||
const alreadySelected = userWrap.classList.contains(`selected_${rightId}`);
|
||||
// 이미 권한을 가진 인원 클릭했을때 해제 이벤트
|
||||
if (alreadySelected) {
|
||||
changePermission(user.user_id, undefined, rightId);
|
||||
// 오른쪽 유저 삭제(오른쪽 리스트 유저 삭제)
|
||||
rightUser.remove();
|
||||
// 왼쪽 유저 삭제(뱃지삭제 -> userWrap 클래스 삭제) 리스트가 펼쳐지지않아 아직 DOM 생성전에는 삭제가 불가능하기 때문에 옵셔널체이닝 추가
|
||||
leftUserPermission?.remove();
|
||||
leftUserWrap?.classList.remove(`selected_${rightId}`);
|
||||
} else {
|
||||
// 권한 부여 이벤트
|
||||
if (rightId === 'sub-master' && rightUl.children.length >= 10) return alert('부관리자는 최대 10명까지 선택 가능합니다.');
|
||||
|
||||
changePermission(user.user_id, rightId, undefined);
|
||||
userWrap.classList.add(`selected_${rightId}`);
|
||||
userWrap.insertAdjacentHTML('beforeend', createRoleDiv(rightId));
|
||||
|
||||
// 왼쪽 유저리스트 클릭시 오른쪽에 복사하여 생성+이벤트 추가
|
||||
const copy = drawUserList(user, rightId, permissionList, true);
|
||||
rightUl.appendChild(copy);
|
||||
bindUserLiClick(copy, user, rightId, permissionList, true);
|
||||
}
|
||||
updateCheckboxState(user.company, user.dept);
|
||||
// 유저 수 조회 이후 갱신
|
||||
checkPermissionUserNum(rightId);
|
||||
});
|
||||
}
|
||||
|
||||
document.querySelector('.permission-modal #help-btn').addEventListener('click', () => toggleHelp(true));
|
||||
|
||||
document.querySelector('.permission-modal .modal-permission-help .modal-permission-help__head img').addEventListener('click', ()=>toggleHelp(false));
|
||||
|
||||
function toggleHelp(show){
|
||||
const helpModal = document.querySelector('.permission-modal .modal-permission-help');
|
||||
const modalBack = document.querySelector('.permission-modal .modal-background');
|
||||
|
||||
helpModal.style.display = show ? 'block' : 'none';
|
||||
modalBack.style.display = show ? 'block' : 'none';
|
||||
}
|
||||
|
||||
// 권한을 통해 유저 수 체크
|
||||
function checkPermissionUserNum(rightId){
|
||||
// 오른쪽 리스트에 몇명이 있는지 체크
|
||||
const userNum = document.querySelector(`.permission-modal .modal-wrap .right .select-list.select-list-${rightId} ul`).children.length;
|
||||
// 유저 수를 나타낼 dom
|
||||
const headerUserNum = document.querySelector('.permission-modal .modal-header .header-user-num-wrap .user-num');
|
||||
// 부관리자 10명이 넘을때 강조효과
|
||||
let text = `현재 ${userNum}명`;
|
||||
let color = '#111';
|
||||
|
||||
if(rightId === 'sub-master'){
|
||||
text += ' (최대 10명)';
|
||||
if(userNum >= 10) color = '#F21D0D';
|
||||
}
|
||||
|
||||
headerUserNum.innerText = text;
|
||||
headerUserNum.style.color = color;
|
||||
}
|
||||
|
||||
// 체크박스 DOM, 회사명, 부서를 통해 체크박스 상태 반영 함수
|
||||
async function updateCheckboxState(company, dept){
|
||||
const deptUsers = document.querySelectorAll(`.permission-modal .modal-wrap .right ul li[data-company="${company}"][data-dept="${dept}"]`);
|
||||
const checkbox = document.querySelector(`.permission-modal .modal-wrap .left li[data-company="${company}"][data-dept="${dept}"] input[type=checkbox]`);
|
||||
const checkmark = document.querySelector(`.permission-modal .modal-wrap .left li[data-company="${company}"][data-dept="${dept}"] label .checkmark`);
|
||||
const rightId = document.querySelector('.permission-modal .modal-wrap .tab-input:checked').id;
|
||||
const key = `${company}_${dept}`;
|
||||
//permissionList.deptUserCount와 현재 권한 부여된 유저의 길이를 비교해 체크박스 상태변화
|
||||
if(vars.permissionList.deptUserCount[key]){
|
||||
if(!checkbox) return;
|
||||
|
||||
if(deptUsers.length === 0){
|
||||
checkbox.checked = false;
|
||||
checkmark.classList.remove('indeterminate');
|
||||
} else if(deptUsers.length === vars.permissionList.deptUserCount[key]) {
|
||||
checkbox.checked = true;
|
||||
checkmark.classList.remove('indeterminate');
|
||||
} else {
|
||||
checkbox.checked = false;
|
||||
checkmark.classList.add('indeterminate');
|
||||
}
|
||||
}
|
||||
checkPermissionUserNum(rightId);
|
||||
}
|
||||
|
||||
function findPermission(id, permissionList) {
|
||||
for (let i = 0; i < permissionList.length; i++) {
|
||||
if (permissionList[i].user_id.toUpperCase() == id.toUpperCase()) return permissionList[i].lev;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getUserInfo(id) {
|
||||
for (let i = 0; i < vars.permissionList.all.length; i++) {
|
||||
if (vars.permissionList.all[i].user_id == id) return vars.permissionList.all[i];
|
||||
}
|
||||
}
|
||||
|
||||
function changePermission(id, group, rightId) {
|
||||
if (!vars.permissionList.changed) vars.permissionList.changed = [];
|
||||
if (!vars.permissionList.changePermission) vars.permissionList.changePermission = {};
|
||||
id = id.toUpperCase();
|
||||
/*
|
||||
vars.permissionList.changed => 변화객체
|
||||
vars.permissionList.permission => 원본 permission
|
||||
vars.permissionList.changePermission => 변화후 permission
|
||||
*/
|
||||
if (group) {
|
||||
//그룹에 넣을때 key, value 형태로 저장
|
||||
vars.permissionList.changePermission[id] = {user_id : id, lev : group}
|
||||
} else {
|
||||
//그룹에서 제외
|
||||
delete vars.permissionList.changePermission[id];
|
||||
}
|
||||
|
||||
// 기존 permission에서 이전 Lev 가져오기
|
||||
const original = vars.permissionList.permission?.find(p => p.user_id.toUpperCase() === id);
|
||||
const originalLev = original?.lev;
|
||||
|
||||
// 기존 permission과 새로운 permission을 비교해 변화 감지
|
||||
if(group === originalLev || (!group && !originalLev)){
|
||||
delete vars.permissionList.changed[id];
|
||||
return;
|
||||
}
|
||||
|
||||
// 변화감지하여 변화객체 저장 (중복성 피하기 위해)
|
||||
vars.permissionList.changed[id] = {user_id : id, lev : group, before : originalLev, user_nm : getUserInfo(id)?.user_nm}
|
||||
}
|
||||
|
||||
async function upsertAuthList() {
|
||||
//lev undefined는 deleteAuthList
|
||||
let upsertArr = [];
|
||||
let deleteArr = [];
|
||||
|
||||
//key-value 형태로 만들었던 changed를 배열로 전환
|
||||
const changedArr = Object.values(vars.permissionList.changed);
|
||||
if (changedArr.length > 0) {
|
||||
for (let i = 0; i < changedArr.length; i++) {
|
||||
if (changedArr[i].lev == undefined) {
|
||||
deleteArr.push(changedArr[i]);
|
||||
} else {
|
||||
upsertArr.push(changedArr[i]);
|
||||
}
|
||||
}
|
||||
|
||||
let upsertMessage = (upsertArr.length > 0) ? false : true;
|
||||
let deleteMessage = (deleteArr.length > 0) ? false : true;
|
||||
console.log(upsertArr);
|
||||
console.log(deleteArr);
|
||||
|
||||
if (upsertArr.length > 0) {
|
||||
let upsertRes = await axios.post('/auth/upsertPermission', { project_id: vars.project_id, targetArr: upsertArr, userInfoString: vars.userInfoString });
|
||||
if (upsertRes.data.message == 'upsert success'){
|
||||
const logs = upsertRes.data.logs;
|
||||
if(logs.length > 0){
|
||||
try{ // upsert 이후 로그 추가
|
||||
const logRes = await axios.post(`${vars.path_name}/addPermissionLog`, {logs});
|
||||
if(logRes.data.message === 'addPermissionLog success') upsertMessage = true;
|
||||
} catch(err) {
|
||||
console.error('upsert addPermissionLog Error', err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (deleteArr.length > 0) {
|
||||
let deleteRes = await axios.post('/auth/deletePermission', { project_id: vars.project_id, targetArr: deleteArr, userInfoString: vars.userInfoString });
|
||||
if (deleteRes.data.message == 'delete success'){
|
||||
const logs = deleteRes.data.logs;
|
||||
if(logs.length > 0){
|
||||
try{ // delete 이후 로그 추가
|
||||
const logRes = await axios.post(`${vars.path_name}/addPermissionLog`, {logs});
|
||||
if(logRes.data.message === 'addPermissionLog success') deleteMessage = true;
|
||||
} catch(err) {
|
||||
console.error('delete addPermssionLog Error', err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (upsertMessage && deleteMessage) {
|
||||
alert(`${Object.keys(vars.permissionList.changed).length}개의 변경사항이 저장되었습니다.`);
|
||||
}
|
||||
}
|
||||
searchResultInit();
|
||||
document.querySelector('.permission-modal').style.display = 'none';
|
||||
}
|
||||
|
||||
export class Permission {
|
||||
constructor(){
|
||||
//permission 정의
|
||||
this.permissionDef = {
|
||||
1535 : '개발자',
|
||||
255 : '관리자',
|
||||
191 : '부관리자',
|
||||
15 : '보안참여자',
|
||||
7 : '일반참여자',
|
||||
1 : '참관자'
|
||||
}
|
||||
//기능 정의 -'기능명': 허용 권한 code -> ex) 'headerPlusBtn' : 191
|
||||
this.funcDef = {
|
||||
'dev-menu' : 1535,
|
||||
'permission-btn' : 191,
|
||||
'overview-left-edit' : 191,
|
||||
'overview-middle-edit' : 191,
|
||||
'overview-task-history-add' : 191,
|
||||
'overview-task-history-save' : 191,
|
||||
'overview-task-history-delete' : 191,
|
||||
'overview-schedule-add' : 7,
|
||||
'overview-schedule-edit' : 7,
|
||||
'overview-issue-edit' : 191,
|
||||
'header-menu-add' : 191,
|
||||
'memo-ai' : 7,
|
||||
'memo-edit' : 7,
|
||||
'memo-text' : 7,
|
||||
'set-user-permission' : 191,
|
||||
'context-menu-viewer' : 7,
|
||||
'context-menu-sub-master' : 191,
|
||||
'add-badge-master' : 255,
|
||||
'add-badge-sub-master' : 191,
|
||||
'add-badge-security-worker': 15,
|
||||
'add-badge-worker': 7,
|
||||
'add-badge-viewer': 1,
|
||||
'convert-btn-viewer': 7,
|
||||
'project-inactive-sign' : 1535,
|
||||
'createFolder-gallery' : 1535,
|
||||
'download-folder' : 255,
|
||||
'change-project-btn' : 255,
|
||||
|
||||
}
|
||||
|
||||
|
||||
this.user = (vars.userInfoString)?JSON.parse(vars.userInfoString) : undefined;
|
||||
this.permission = this.user.permission;
|
||||
this.permissionString = this._toPermissionString();
|
||||
}
|
||||
|
||||
_toPermissionString(){
|
||||
return this.permissionDef[`${this.permission}`];
|
||||
}
|
||||
|
||||
checkPermission(func){
|
||||
return this.permission >= this.funcDef[func];
|
||||
}
|
||||
}
|
||||
213
views/main/jsm/archive/variable.js
Normal file
213
views/main/jsm/archive/variable.js
Normal file
@@ -0,0 +1,213 @@
|
||||
let path_name = decodeURIComponent(window.location.pathname);
|
||||
let project_id = path_name.split('/')[1];
|
||||
|
||||
//////// pm-bcmf 연결용 테스트 코드 - project_id가 pm-bcmf일 때 project_id는 dsdj2, path_name은 /dsdj2/archive로 변경
|
||||
if (project_id == 'pm-bcmf') {
|
||||
project_id = 'dsdj2';
|
||||
path_name = '/dsdj2/archive';
|
||||
}
|
||||
|
||||
//세종안성(sjas)(gtb.) and 청용천교(cheongyong)(cheongyong.) 연결용 테스트 코드
|
||||
if(project_id == '' || project_id == undefined) {
|
||||
if(window.location.origin.includes('gtb.')){
|
||||
project_id = 'sjas';
|
||||
path_name = '/sjas/archive';
|
||||
}/* else if(window.location.origin.includes('cheongyong.')){
|
||||
project_id = 'cheongyong';
|
||||
path_name = '/cheongyong/archive';
|
||||
} */
|
||||
}
|
||||
|
||||
export const vars = {
|
||||
// 아카이브 라우터 연결을 위한 변수
|
||||
path_name: path_name,
|
||||
|
||||
// 프로젝트 id
|
||||
project_id: project_id,
|
||||
|
||||
// 오브젝트 스토리지 타입 (ONPREMISE / CLOUD)
|
||||
storageType: undefined,
|
||||
|
||||
// 클라우드 타입 (ONPREMISE / CLOUD)
|
||||
cloudType: undefined,
|
||||
|
||||
// 버킷 (프로젝트 id와 동일)
|
||||
bucket: project_id,
|
||||
|
||||
// 현재 로그인 한 유저 정보
|
||||
userInfoString: undefined,
|
||||
|
||||
// 현재 로그인 한 유저의 permission 관리 객체
|
||||
permission: undefined,
|
||||
|
||||
// 현재 로그인 한 유저의 마우스커서 정보를 담는 객체
|
||||
cursors: {},
|
||||
|
||||
// 전체 프로젝트 정보
|
||||
allProject: {},
|
||||
|
||||
// 현재 프로젝트 정보
|
||||
project: undefined,
|
||||
|
||||
// 최상위 폴더 사용 용량 / 남은 용량 / 저장 가능한 최대 용량 (50 GB) -> DB에서 가져오기 / 폴더별 사이즈 (아카이브, 공문, 과업개요, GSIM 모델)
|
||||
bucketSize: undefined, remainingSize: undefined, bucketMaxSize: 50*1024*1024*1024, foldersSize: undefined,
|
||||
|
||||
// 최상위 폴더 아래에 있는 모든 폴더/파일 데이터
|
||||
allTreeObject: undefined,
|
||||
|
||||
// 현재 선택된 폴더 아래에 있는 모든 폴더/파일 데이터
|
||||
currentTreeObject: undefined,
|
||||
|
||||
// 휴지통에 있는 모든 파일 데이터
|
||||
recycleBinObject: undefined,
|
||||
|
||||
// 마지막으로 선택한 header-btn/tree-item/list-item -> 스타일 변경에 사용 - changeListItemStyle(listItem)
|
||||
lastHeaderBtn: undefined, lastMainTreeItem: undefined, lastModalTreeItem: undefined, lastListItem: undefined, lastListGroupTarget: undefined,
|
||||
// lastMainTreeDepth2Item: undefined, lastModalTreeDepth2Item: undefined,
|
||||
// lastMainTreeDepth3Item: undefined, lastModalTreeDepth3Item: undefined,
|
||||
|
||||
// 마지막으로 우클릭한 타겟 -> 컨텍스트 메뉴에 사용
|
||||
lastContextTarget: undefined,
|
||||
|
||||
// 마지막으로 선택한 header/tree/list item
|
||||
lastSelectTarget: undefined,
|
||||
|
||||
// 드래그가 시작된 list-item
|
||||
dragStartListItem: undefined,
|
||||
|
||||
// 클릭, 더블클릭 구분을 위한 시간
|
||||
clickDuration: 500,
|
||||
|
||||
// 업로드, 다운로드 완료 후 프로그레스 종료 전 완료 후 잠깐 대기하는 시간
|
||||
progressDuration: 500,
|
||||
|
||||
// 현재 변환중인 파일 정보를 저장하는 배열
|
||||
convertingDataArr: [],
|
||||
|
||||
// 현재 요약중인 파일 정보를 저장하는 배열
|
||||
summarizeAiDataArr: [],
|
||||
|
||||
// file area list에서 다중 선택된 item 배열
|
||||
multiSelectListItemArr: [],
|
||||
|
||||
// 휴지통에서 사용할 lastListItem, lastListGroupTarget, lastContextTarget, lastSelectTarget, multiSelectListItemArr
|
||||
// 휴지통에서 다중 선택된 item 배열
|
||||
lastListItem_bin: undefined,
|
||||
lastListGroupTarget_bin: undefined,
|
||||
lastContextTarget_bin: undefined,
|
||||
lastSelectTarget_bin: undefined,
|
||||
multiSelectListItemArr_bin: [],
|
||||
|
||||
// 로그 데이터 -> 활동 정보 activity-info-item(로그 데이터)에 사용 (활동 정보 아이템 클릭 시 상세 정보 표시에 사용)
|
||||
logData: undefined,
|
||||
|
||||
// 로그 모달창이 열렸는지 안열렸는지 판단하는 변수 -> 최초에만 false이고 한 번이라도 열리면 true로 유지
|
||||
isLogModalOpened: false,
|
||||
|
||||
// 새로 추가되는 로그 아이템을 저장하는 배열
|
||||
newLogItemArr: [],
|
||||
|
||||
// 일반아이템리스트/휴지통아이템리스트 구분에 사용
|
||||
listItemWrap: document.querySelector('.archive-main-center .list-container .list-body .list-item-wrap'),
|
||||
|
||||
// 미리보기 뷰어
|
||||
viewer: undefined,
|
||||
|
||||
// 메모 임시 저장
|
||||
tempMemo: [],
|
||||
|
||||
// 파일 리스트 sort용 현재 컬럼, 현재 정렬순
|
||||
curSortCol : 'name',
|
||||
curSortOrder : 'asc',
|
||||
|
||||
// 휴지통 모달창 sort용 현재 컬럼, 현재 정렬순
|
||||
curSortCol_bin : 'remove-date',
|
||||
curSortOrder_bin : 'desc',
|
||||
|
||||
// 리스트/그리드 파일 목록에서 파일 선택했을 때 뷰어에 연결중 표시에 사용할 시간
|
||||
viewerConnectingTime: 0, viewerConnectingTimeValue: 700,
|
||||
|
||||
// 트리 영역에서 마지막으로 선택한 폴더 타입
|
||||
lastFolderType: undefined,
|
||||
|
||||
// 컨트롤 박스에서 마지막으로 선택한 파일 영역 모드
|
||||
// lastFileAreaMode: undefined,
|
||||
lastFileAreaMode: 'list',
|
||||
|
||||
// 지도 모드 (normal <-> edit)
|
||||
mapMode: 'normal',
|
||||
|
||||
// 배경지도 url (일반, 위성, 하이브리드)
|
||||
road: 'https://mt1.google.com/vt/lyrs=m&x={x}&y={y}&z={z}&hl=ko&&apistyle=s.e:l.i|p.v:off',
|
||||
satellite: 'https://mt1.google.com/vt/lyrs=s&x={x}&y={y}&z={z}&hl=ko&apistyle=s.e:l.i|p.v:off',
|
||||
hybrid: 'https://mt1.google.com/vt/lyrs=y&x={x}&y={y}&z={z}&hl=ko&apistyle=s.e:l.i|p.v:off',
|
||||
|
||||
// 클러스터 거리 (픽셀 기준)
|
||||
clusterDistance: 1,
|
||||
|
||||
// 마지막으로 선택한 클러스터
|
||||
lastSelectCluster: undefined,
|
||||
|
||||
// 모든 오버레이를 표시하는 줌 레벨
|
||||
allOverlayVisibleZoom: 19,
|
||||
|
||||
// 클러스터 레이어, 포인트 레이어
|
||||
olClusterLayer: undefined,
|
||||
olPointLayer: undefined,
|
||||
|
||||
listOrder: [],
|
||||
|
||||
countdownTimer: undefined,
|
||||
}
|
||||
|
||||
// 오픈레이어스 타일 레이어
|
||||
vars.baseLayer = new ol.layer.Tile({
|
||||
source: new ol.source.XYZ({
|
||||
url: vars.road,
|
||||
crossOrigin: 'anonymous',
|
||||
transition: 0
|
||||
})
|
||||
});
|
||||
|
||||
vars.roadLayer = new ol.layer.Tile({
|
||||
source: new ol.source.XYZ({
|
||||
url: vars.road,
|
||||
crossOrigin: 'anonymous',
|
||||
transition: 0
|
||||
}),
|
||||
visible: true
|
||||
});
|
||||
vars.satelliteLayer = new ol.layer.Tile({
|
||||
source: new ol.source.XYZ({
|
||||
url: vars.satellite,
|
||||
crossOrigin: 'anonymous',
|
||||
transition: 0
|
||||
}),
|
||||
visible: false
|
||||
});
|
||||
vars.hybridLayer = new ol.layer.Tile({
|
||||
source: new ol.source.XYZ({
|
||||
url: vars.hybrid,
|
||||
crossOrigin: 'anonymous',
|
||||
transition: 0
|
||||
}),
|
||||
visible: false
|
||||
});
|
||||
|
||||
// 구성 popup ver 위한 전역 함수
|
||||
// vars.navigateToPath = function(path) {
|
||||
// const resourcePath = `/${path}`;
|
||||
|
||||
// // 헤더 버튼 클릭 (탭 선택)
|
||||
// const tabPath = resourcePath.split('/')[1]; // '모델뷰어테스트/모델/3dm' -> '모델뷰어테스트'
|
||||
// const headerBtn = document.querySelector(`[data-resource-path="/${tabPath}"]`);
|
||||
// if (headerBtn) headerBtn.click();
|
||||
|
||||
// // 트리 아이템 클릭 (폴더 선택)
|
||||
// setTimeout(() => {
|
||||
// const treeItem = document.querySelector(`.tree-item-wrap[data-resource-path="${resourcePath}"] .tree-item`);
|
||||
// if (treeItem) treeItem.click();
|
||||
// }, 500);
|
||||
// }
|
||||
|
||||
window.vars = vars;
|
||||
Reference in New Issue
Block a user