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

1095 lines
47 KiB
JavaScript

import { vars } from '../archive/variable.js';
import { docVars } from './docVariable.js';
import { checkProjectInactive } from '../main.js';
import { getAiUsed, syncDocInfo, getDocDataBySelected, getGroupCompanyData, uploadDocFile, upsertDocData } from './docDataManager.js';
import { initCustomSelectBoxes, renderDocViewer, itemBoxSelected } from './docPageRenderer.js';
let fileArr = [];
let originCompanyList = [];
let timeoutId;
function debounce(func, delay) {
return function(...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
func.apply(this, args);
}, delay);
};
}
/*****************************************************************************************************************************/
/**************************************************************************************************************** AI 선택 모달 */
/*****************************************************************************************************************************/
let aiChoiceConfirmCallback = null;
export async function openDocAiChoiceModal(onConfirm) {
if (checkProjectInactive()) return;
// 체크 초기화
// const radioButtons = document.querySelectorAll('input[name="aiChoice"]');
// radioButtons.forEach(radio => {
// radio.checked = false;
// });
aiChoiceConfirmCallback = onConfirm; // 콜백 저장
const modalBackground = document.querySelector('.official-doc-modal-background');
const modalContainer = document.querySelector('.official-doc-modal.official-doc-ai-choice-modal');
modalBackground.style.display = 'flex';
modalContainer.style.display = 'flex';
}
function clickDocAiChoiceModalConfirm() {
if (checkProjectInactive()) return;
const confirmBtn = document.querySelector('.official-doc-modal.official-doc-ai-choice-modal .official-doc-positive-btn');
confirmBtn.addEventListener('click', async () => {
const selectedRadio = document.querySelector('input[name="aiChoice"]:checked');
if (!selectedRadio) {
alert('사용할 AI를 선택해주세요.');
return;
}
const selectedValue = selectedRadio?.value;
// 모달 닫기
document.querySelector('.official-doc-modal-background').style.display = 'none';
document.querySelector('.official-doc-modal.official-doc-ai-choice-modal').style.display = 'none';
// 저장된 콜백 실행
if (aiChoiceConfirmCallback) {
aiChoiceConfirmCallback(selectedValue);
aiChoiceConfirmCallback = null; // 재사용 대비 초기화
}
});
}
clickDocAiChoiceModalConfirm();
function clickDocAiChoiceModalCancel() {
if (checkProjectInactive()) return;
const modalBackground = document.querySelector('.official-doc-modal-background');
const modal = document.querySelector('.official-doc-modal.official-doc-ai-choice-modal');
const cancelBtn = document.querySelector('.official-doc-modal.official-doc-ai-choice-modal .official-doc-negative-btn');
if (!cancelBtn || !modal) return;
cancelBtn.addEventListener('click', () => {
modalBackground.style.display = 'none';
modal.style.display = 'none';
});
}
clickDocAiChoiceModalCancel();
/*****************************************************************************************************************************/
/************************************************************************************** 추가모달 수정모달 사용자입력모달 업서트모달 */
/*****************************************************************************************************************************/
export async function openDocUpsertModal(data, droppedFiles, fileName, mode = 'add') {
const modalBackground = document.querySelector('.official-doc-modal-background');
const modalContainer = document.querySelector('.official-doc-modal.official-doc-upsert-modal');
modalBackground.style.display = 'flex';
modalContainer.style.display = 'flex';
modalContainer.dataset.originFileName = mode === 'edit' ? data?.file_path?.split('/')?.pop() || '' : fileName || '';
modalContainer.dataset.mode = mode;
/***** 업서트모달 헤더 */
const realFileName = mode === 'edit' ? data?.file_path?.split('/')?.pop() || '수정' : fileName || '추가';
createDocModalHeader(realFileName);
/***** 업서트모달 바디 */
await createDocModalBody(modalContainer, data, mode, droppedFiles);
/***** 업서트모달 프리뷰 */
handleDocModalPreview(modalContainer, data, mode, droppedFiles);
// 모달 백그라운드 누르면 모달 초기화 + flex none 되기
// modalBackground.addEventListener('pointerdown', () => {
// modalContainer.style.display = 'none';
// modalHeader.innerHTML = '';
// modalBody.innerHTML = '';
// docModalPreview.innerHTML = '';
// modalBackground.style.display = 'none';
// droppedFiles = [];
// });
}
// 업서트모달 헤더 만들기
function createDocModalHeader(realFileName) {
const modalHeader = document.querySelector('.official-doc-modal.official-doc-upsert-modal .official-doc-modal-header');
const icon = document.createElement('img');
icon.classList.add('icon');
icon.src = '/main/img/officialDoc/Ficon-pdf.svg';
icon.alt = 'Ficon-pdf';
const title = document.createElement('h3');
title.classList.add('text-green');
title.textContent = realFileName;
modalHeader.appendChild(icon);
modalHeader.appendChild(title);
return modalHeader;
}
// 업서트모달 바디
const createDocModalBody = async(modalContainer, data, mode, droppedFiles) => {
const modalBody = modalContainer.querySelector('.official-doc-modal-body');
modalBody.innerHTML = ''; // 기존 내용 초기화
// 추가모드일 때만 필요한 파일과 아이디
if (mode === 'add' || mode === 'input') {
fileArr = droppedFiles;
docVars.currentGroupId = await getDocSeq();
}
// 입력 필드 만들기
const inputFields = getInputFields(data, mode);
inputFields.forEach(({ label, type = 'text', name, value, placeholder, attrs = {}, extra = null }) => {
const input = createInputField(type, name, value, placeholder, attrs);
modalBody.appendChild(type === 'hidden' ? input : createInputWrap(label, input, extra));
});
// 수신처에 따른 수/발신 영역 자동으로 채우기
populateCompanySelectOption(modalBody);
// 공문종류 셀렉트박스 옵션 채우기
populateCategorySelectOption(modalBody);
//
addSelectOptionListener(modalBody);
// 수정모드에서 구분(발주처/외) 채우기
if (mode === 'edit') populateCompanyType();
};
// 업서트모달 바디 : 입력필드
function getInputFields(data, mode) {
const safeData = typeof data === 'object' && data !== null ? data : {};
return [
{ label: 'group_id', type: 'hidden', name: 'group_id', value: `${docVars.currentGroupId}` },
{
label: '수신처 *',
type: 'text',
name: 'recipient_org',
value: safeData.수신처 ?? '',
attrs: { readonly: true, id: 'recipient_org' },
extra: createDocCustomSelectbox('recipient', safeData.수신처약자 ?? '수신처'),
placeholder: '오른쪽 셀렉트박스에서 회사를 선택해주세요.' ?? '',
},
{ label: '수신자', type: 'text', name: 'recipient_name', value: safeData.수신자 ?? '' },
{ label: '수신자 약자 *', type: 'text', name: 'recipient_name_abbr', value: safeData.수신자약자 ?? '' },
{
label: '발신처 *',
type: 'text',
name: 'sender_org',
value: safeData.발신처 ?? '',
attrs: { readonly: true, id: 'sender_org' },
extra: createDocCustomSelectbox('sender', safeData.발신처약자 ?? '발신처'),
placeholder: '오른쪽 셀렉트박스에서 회사를 선택해주세요.' ?? '',
},
{ label: '발신자', type: 'text', name: 'sender_name', value: safeData.발신자 ?? '' },
{ label: '발신자 약자 *', type: 'text', name: 'sender_name_abbr', value: safeData.발신자약자 ?? '' },
{
label: '수신 / 발신',
type: 'text',
name: 'doc_direction',
value: safeData.doc_direction ?? '',
attrs: { readonly: true, id: 'doc-direction' },
placeholder: '수신처를 선택하시면 자동으로 입력됩니다.' ?? '',
},
{
label: '구분(발주처/외)',
type: 'text',
name: 'company_type',
value: '',
attrs: { readonly: true, id: 'company-type' },
placeholder: '수신처를 선택하시면 자동으로 입력됩니다.' ?? '',
},
{ label: '공문번호 *', type: 'text', name: 'doc_number', value: safeData.공문번호 ?? '' },
{ label: '유형', type: 'text', name: 'doc_type', value: safeData.공문유형 ?? '' },
{
label: '종류 *',
type: 'text',
name: 'category',
value: safeData.공문종류 ?? '',
attrs: { readonly: true, id: 'doc-category' },
extra: createDocCustomSelectbox('category', safeData.공문종류 ?? ''),
},
{ label: '제목 *', type: 'text', name: 'doc_title', value: safeData.공문제목 ?? '' },
{ label: '제목요약 *', type: 'text', name: 'doc_title_summary', value: safeData.공문제목요약 ?? '' },
{ label: '연관공문', type: 'text', name: 'doc_related_docs', value: safeData.공문간연계 ?? '' },
{ label: '사내담당', type: 'text', name: 'doc_manager', value: safeData.doc_manager ?? '' },
{ label: '공문일자 *', type: 'date', name: 'doc_date', value: safeData.공문일자 ?? '' },
{ label: '내용요약', type: 'textarea', name: 'doc_content_summary', value: safeData.공문내용요약 ?? '' },
{ label: '특이사항', type: 'textarea', name: 'doc_memo', value: safeData.doc_memo ?? '' },
{ label: '첨부문서수', type: 'text', name: 'attachment_count', value: safeData.첨부문서수 ?? '' },
{ label: '첨부문서제목', type: 'hidden', name: 'attachment_title', value: safeData.첨부문서제목 ?? '' },
];
}
// 업서트모달 바디 : 입력필드 wrapper 만들기
const createInputWrap = (labelText, inputElement, extraElement = null) => {
const wrap = document.createElement('div');
wrap.className = 'official-doc-type-wrap';
const label = document.createElement('p');
// 필수입력 * 빨간색으로 표시
if (labelText.includes('*')) {
const parts = labelText.split('*');
label.innerHTML = `${parts[0]}<span class="doc-required-star">*</span>`;
} else {
label.textContent = labelText;
}
wrap.appendChild(label);
if (labelText != '종류') wrap.appendChild(inputElement);
if (extraElement) wrap.appendChild(extraElement);
return wrap;
};
// 업서트모달 바디 : 입력필드 만들기
const createInputField = (type, name, value, placeholder, attrs = {}) => {
let inputElement;
if (type === 'textarea') {
inputElement = document.createElement('textarea');
} else {
inputElement = document.createElement('input');
inputElement.type = type;
}
inputElement.name = name;
inputElement.value = value;
if (placeholder !== undefined) inputElement.placeholder = placeholder;
Object.entries(attrs).forEach(([key, val]) => inputElement.setAttribute(key, val));
return inputElement;
};
// 업서트모달 바디 : 입력필드 셀렉트박스 만들기
function createDocCustomSelectbox(id, selectedText) {
const wrap = document.createElement('div');
wrap.classList.add('doc-modal-custom-select-wrap');
wrap.id = `doc-modal-select-${id}`;
const select = document.createElement('div');
select.classList.add('doc-modal-custom-select');
select.id = `doc-modal-custom-select-${id}`;
select.setAttribute('tabindex', '0');
const div1 = document.createElement('div');
div1.className = 'doc-modal-custom-select-div';
const span = document.createElement('span');
span.className = 'selected';
if (id === 'category') {
if (['행정/일반', '기술/성과물', '회의/기타'].includes(selectedText)) {
span.textContent = selectedText;
} else {
span.textContent = '종류를 선택해주세요.';
}
} else {
span.textContent = selectedText;
}
div1.appendChild(span);
select.appendChild(div1);
const div2 = document.createElement('div');
div2.className = 'doc-select-icon-wrap';
const img = document.createElement('img');
img.className = 'doc-select-icon';
img.src = '/main/img/officialDoc/icon-down-sign-aaa.svg';
img.alt = 'icon-down-sign-aaa';
div2.appendChild(img);
select.appendChild(div2);
const optionsDiv = document.createElement('div');
optionsDiv.className = 'options';
select.appendChild(optionsDiv);
wrap.appendChild(select);
return wrap;
}
// 업서트모달 바디 : 입력필드 수신처, 발신처에 따른 수/발신 값 자동으로 채우기
function populateCompanySelectOption(modalBody) {
const recipientWrapper = document.querySelector('#doc-modal-select-recipient'); // 수신처
const senderWrapper = document.querySelector('#doc-modal-select-sender'); // 발신처
const directioninput = document.querySelector('#doc-direction'); // 수/발신
const rolePairMap = {
기준: '상대기관',
상대기관: '기준',
발주처: '수신처',
수신처: '발주처',
};
// 옵션 생성 함수
const createOption = (wrapper, role, type, name, id) => {
const option = document.createElement('div');
option.classList.add('option');
option.textContent = `${name} (${type}, ${role})`;
option.dataset.companyRole = role;
option.dataset.companyType = type;
option.dataset.companyId = id;
option.dataset.companyName = name;
option.addEventListener('click', () => {
wrapper.querySelector('.selected').textContent = name;
if (wrapper.id === 'doc-modal-select-recipient') {
const recipientRole = role;
const companyType = type;
const counterpartRole = rolePairMap[recipientRole];
directioninput.value = recipientRole === '기준' || recipientRole === '발주처' ? '수신' : '발신';
document.querySelector('#recipient_org').value = name;
document.querySelector('#company-type').value = companyType;
const senderOptionsWrapper = senderWrapper.querySelector('.options');
senderOptionsWrapper.innerHTML = '';
const counterpartList = docVars.groupCompanyData[companyType][counterpartRole] || [];
counterpartList.forEach(({ name: counterName, id: counterId }, idx) => {
const counterOption = document.createElement('div');
counterOption.classList.add('option');
counterOption.textContent = counterName;
counterOption.dataset.companyRole = counterpartRole;
counterOption.dataset.companyType = companyType;
counterOption.dataset.companyId = counterId;
counterOption.addEventListener('click', () => {
senderWrapper.querySelector('.selected').textContent = counterName;
document.querySelector('#sender_org').value = counterName;
});
senderOptionsWrapper.appendChild(counterOption);
if (idx === 0) {
senderWrapper.querySelector('.selected').textContent = counterName;
document.querySelector('#sender_org').value = counterName;
}
});
}
if (wrapper.id === 'doc-modal-select-sender') {
document.querySelector('#sender_org').value = name;
}
});
wrapper.querySelector('.options').appendChild(option);
};
// 수/발신 박스에 옵션 각각 생성
for (const type in docVars.groupCompanyData) {
const roleGroups = docVars.groupCompanyData[type];
for (const role in roleGroups) {
const companies = roleGroups[role];
companies.forEach(({ name, id }) => {
createOption(recipientWrapper, role, type, name, id);
createOption(senderWrapper, role, type, name, id);
});
}
}
}
function addSelectOptionListener(modalBody) {
if (!modalBody.dataset.listenerAttached) {
modalBody.addEventListener('click', (e) => {
const select = e.target.closest('.doc-modal-custom-select');
const option = e.target.closest('.option');
if (option && select) {
e.stopPropagation();
const selected = select.querySelector('.selected');
selected.innerText = option.innerText.trim();
select.classList.remove('open');
} else if (select) {
e.stopPropagation();
document.querySelectorAll('.doc-modal-custom-select').forEach((s) => {
if (s !== select) s.classList.remove('open');
});
select.classList.toggle('open');
} else {
document.querySelectorAll('.doc-modal-custom-select').forEach((s) => s.classList.remove('open'));
}
const selectWrapper = e.target.closest('.doc-modal-custom-select-wrap');
const selectedText = option?.innerText;
if (selectWrapper && selectWrapper.id === 'doc-modal-select-recipient' && option) {
const selectedName = option.dataset.companyName;
const selectedType = option.dataset.companyType;
let direction = '';
let companyType = '';
for (const type in docVars.groupCompanyData) {
const base = docVars.groupCompanyData[type]['기준'] || [];
const target = docVars.groupCompanyData[type]['상대기관'] || [];
const buyer = docVars.groupCompanyData[type]['발주처'] || [];
const receiver = docVars.groupCompanyData[type]['수신처'] || [];
const isInBaseOrBuyer = base.some((item) => item.name === selectedName && type === selectedType) || buyer.some((item) => item.name === selectedName && type === selectedType);
const isInTargetOrReceiver = target.some((item) => item.name === selectedName && type === selectedType) || receiver.some((item) => item.name === selectedName && type === selectedType);
if (isInBaseOrBuyer) {
direction = '수신';
companyType = type;
break;
} else if (isInTargetOrReceiver) {
direction = '발신';
companyType = type;
break;
}
}
document.querySelector('#doc-direction').value = direction;
document.querySelector('#company-type').value = companyType;
document.querySelector('#recipient_org').value = selectedName;
}
if (selectWrapper && selectWrapper.id === 'doc-modal-select-sender' && option) {
document.querySelector('#sender_org').value = selectedText;
}
});
// 중복 리스너 방지
modalBody.dataset.listenerAttached = 'true';
}
}
// 업서트모달 바디 : 수정모드에서 구분(발주처/외) 채우기
const populateCompanyType = () => {
const selectedText = document.querySelector('.official-doc-modal-body .doc-modal-custom-select-wrap .doc-modal-custom-select-div .selected').innerHTML; // 선택한 값
const options = document.querySelectorAll('.official-doc-modal-body .doc-modal-custom-select-wrap .options .option'); // 모든 옵션
options.forEach(option => {
let pureCompanyName = getPureCompanyName(option.textContent)
if (pureCompanyName.trim() === selectedText.trim()) { // selectedText와 옵션의 텍스트가 일치하는지
// 일치하는 옵션을 찾았으면 그 옵션의 data-company-type을 입력 필드에 채워줌
const companyType = option.dataset.companyType; // data-company-type 값 가져오기
const companyTypeInput = document.querySelector('#company-type'); // 입력 필드
companyTypeInput.value = companyType;
}
});
};
// 업서트모달 바디 : 종류 셀렉트박스 옵션
function populateCategorySelectOption(modalBody) {
const categoryWrapper = document.querySelector('#doc-modal-custom-select-category');
const optionsWrapper = categoryWrapper.querySelector('.options');
const categoryInput = document.querySelector('.official-doc-modal .official-doc-modal-block .official-doc-modal-body .official-doc-type-wrap #doc-category');
const validOptions = ['행정/일반', '기술/성과물', '회의/기타'];
const createOption = (wrapper) => {
const options = validOptions;
options.forEach(optionText => {
const optionDiv = document.createElement('div');
optionDiv.className = 'option';
optionDiv.textContent = optionText;
optionDiv.addEventListener('click', () => {
if (validOptions.includes(optionText)) {
categoryWrapper.querySelector('.selected').textContent = optionText;
categoryInput.value = optionText;
}
categoryWrapper.classList.remove('open');
});
wrapper.appendChild(optionDiv);
});
};
createOption(optionsWrapper);
}
// 업서트모달 프리뷰 만들기
async function handleDocModalPreview(modalContainer, data, mode, droppedFiles) {
const docModalPreview = modalContainer.querySelector('.official-doc-modal-preview');
docModalPreview.innerHTML = '';
docModalPreview.style.display = 'flex';
if (mode === 'edit') {
// 수정모드
let PresignedUrl = undefined;
const resourcePath = data.file_path;
await syncDocInfo(['official', 'attach', null]);
let objectKey = docVars.allDocData?.find((doc) => doc.doc_id === data.공문아이디)?.preview_key;
let generateDownloadUrlParams = {
objectKey: objectKey,
resourcePath: resourcePath,
};
let generateDownloadUrlRes = await axios.post(`${docVars.path_name}/generateDownloadDocUrl`, generateDownloadUrlParams);
if (generateDownloadUrlRes.data.message == 'generateDownloadDocUrl_success') {
PresignedUrl = generateDownloadUrlRes.data.url;
}
if (PresignedUrl) {
const response = await fetch(PresignedUrl);
const blob = await response.blob();
const blobUrl = URL.createObjectURL(blob);
const iframe = document.createElement('iframe');
iframe.src = `/libs/pdfViewer/web/viewer.html`;
iframe.addEventListener('load', () => {
const app = iframe.contentWindow.PDFViewerApplication;
app.pdfCursorTools._handTool.activate();
app.open({ url: blobUrl });
app.appConfig.mainContainer.classList.add('scrollbar');
app.appConfig.mainContainer.style.marginLeft = '4px';
});
docModalPreview.appendChild(iframe);
}
} else if (mode === 'add' || mode === 'input') {
// 추가모드
const file = droppedFiles[0];
const fileURL = URL.createObjectURL(file);
const iframe = document.createElement('iframe');
iframe.src = `/libs/pdfViewer/web/viewer.html`;
iframe.addEventListener('load', () => {
const app = iframe.contentWindow.PDFViewerApplication;
app.pdfCursorTools._handTool.activate();
app.open({ url: fileURL });
});
docModalPreview.appendChild(iframe);
}
}
// 업서트모달 취소버튼
function clickDocUpsertModalCancel() {
if (checkProjectInactive()) return;
const modalBackground = document.querySelector('.official-doc-modal-background');
const modal = document.querySelector('.official-doc-modal.official-doc-upsert-modal');
const modalHeader = modal?.querySelector('.official-doc-modal-header');
const modalBody = modal?.querySelector('.official-doc-modal-body');
const docModalPreview = modal?.querySelector('.official-doc-modal-preview');
const cancelBtn = document.querySelector('.official-doc-modal.official-doc-upsert-modal .official-doc-modal-block .official-doc-modal-footer .official-doc-negative-btn');
if (!cancelBtn || !modal) return;
cancelBtn.addEventListener('click', () => {
modalBackground.style.display = 'none';
modal.style.display = 'none';
modal.dataset.originFileName = '';
modal.dataset.mode = '';
modalHeader.innerHTML = '';
modalBody.innerHTML = '';
docModalPreview.innerHTML = '';
});
}
clickDocUpsertModalCancel();
// 업서트모달 필수입력 유효성 검사
function validateDocModalInput(modalContainer) {
const inputWraps = modalContainer.querySelectorAll('.official-doc-type-wrap');
let isValid = true;
inputWraps.forEach((wrap) => {
const label = wrap.querySelector('p');
const input = wrap.querySelector('input, textarea');
// label에 *이 있으면 필수입력
if (label && label.innerText.includes('*')) {
const value = input?.value.trim();
if (!value) {
isValid = false;
}
}
});
return isValid;
}
// 업서트모달 확인버튼
const confirmBtn = document.querySelector('.official-doc-upsert-modal .official-doc-modal-block .official-doc-modal-footer .official-doc-positive-btn');
const clickDocUpsertModalConfirm = debounce(async() => {
if (checkProjectInactive()) return;
// 필수입력 유효성 검사
const modalContainer = document.querySelector('.official-doc-modal.official-doc-upsert-modal');
const isValid = validateDocModalInput(modalContainer);
if (!isValid) {
alert('필수 항목을 모두 입력해주세요.');
return;
}
// textarea 엔터 제거
const rawSummary = document.querySelector('textarea[name="doc_content_summary"]').value.trim();
const cleanSummary = rawSummary.replace(/\r?\n/g, ' ');
const rawMemo = document.querySelector('textarea[name="doc_memo"]').value.trim();
const cleanMemo = rawMemo.replace(/\r?\n/g, ' ');
// 회사명에서 괄호 뒤에 글자 빼기 > (발주처, 기준)
const recipientOrgFullText = document.querySelector('#doc-modal-select-recipient .selected').innerText;
const senderOrgFullText = document.querySelector('#doc-modal-select-sender .selected').innerText;
const categoryText = document.querySelector('#doc-modal-custom-select-category .selected').innerText;
const mode = modalContainer.dataset.mode;
const existingFileName = modalContainer.dataset.originFileName || '';
const droppedFile = fileArr?.[0];
const fileName = droppedFile ? droppedFile.name : existingFileName;
const formData = {
group_id: document.querySelector('input[name="group_id"]').value.trim(),
recipient_org: document.querySelector('input[name="recipient_org"]').value.trim(),
recipient_org_abbr: getPureCompanyName(recipientOrgFullText),
recipient_name: document.querySelector('input[name="recipient_name"]').value.trim(),
recipient_name_abbr: document.querySelector('input[name="recipient_name_abbr"]').value.trim(),
sender_org: document.querySelector('input[name="sender_org"]').value.trim(),
sender_org_abbr: getPureCompanyName(senderOrgFullText),
sender_name: document.querySelector('input[name="sender_name"]').value.trim(),
sender_name_abbr: document.querySelector('input[name="sender_name_abbr"]').value.trim(),
doc_direction: document.querySelector('input[name="doc_direction"]').value.trim(),
doc_number: document.querySelector('input[name="doc_number"]').value.trim(),
doc_type: document.querySelector('input[name="doc_type"]').value.trim(),
doc_category: categoryText,
doc_title: document.querySelector('input[name="doc_title"]').value.trim(),
doc_title_summary: document.querySelector('input[name="doc_title_summary"]').value.trim(),
doc_related_docs: document.querySelector('input[name="doc_related_docs"]').value.trim(),
doc_date: document.querySelector('input[name="doc_date"]').value.trim(),
doc_content_summary: cleanSummary,
attachment_title: document.querySelector('input[name="attachment_title"]').value.trim(),
attachment_count: document.querySelector('input[name="attachment_count"]').value.trim(),
doc_manager: document.querySelector('input[name="doc_manager"]').value.trim(),
doc_memo: cleanMemo,
doc_label: 'official',
file_name: fileName,
is_aiused: getAiUsed(),
};
// 수신/발신 선택 확인
const recipientInnerText = document.querySelector('#doc-modal-select-recipient .selected').innerText;
const senderInnerText = document.querySelector('#doc-modal-select-sender .selected').innerText;
if (recipientInnerText === '수신처' || senderInnerText === '발신처') {
alert('수신처 또는 발신처를 선택해주세요.');
return;
}
try {
if (mode === 'add' || mode === 'input') {
const uploadResult = await uploadDocFile(fileArr, 'modal_add');
if (!uploadResult.success) {
alert('파일 업로드에 실패했습니다.');
return;
}
formData.file_name = fileName;
formData.userInfoString = vars.userInfoString;
formData.uploadParams = uploadResult.uploadParams;
formData.uploadSuccessFileArr = uploadResult.uploadSuccessFileArr;
formData.uploadFailedFileArr = uploadResult.uploadFailedFileArr;
// 메타데이터 DB에 저장
await upsertDocData(formData, 'modal_add');
} else {
// 수정 모드일 경우 DB에 내용만 업데이트
await upsertDocData(formData, 'modal_edit');
}
// 모달 닫기 및 초기화
document.querySelector('.official-doc-modal-background').style.display = 'none';
const modal = document.querySelector('.official-doc-modal.official-doc-upsert-modal');
modal.style.display = 'none';
modal.querySelector('.official-doc-modal-header').innerHTML = '';
modal.querySelector('.official-doc-modal-body').innerHTML = '';
alert('저장이 완료되었습니다.');
await syncDocInfo(['official', 'attach', null]);
await getDocDataBySelected();
let docId = docVars.selectedDoc?.doc_id;
let newLi = document.querySelector(`li[data-doc-id="${docId}"]`);
if (newLi) {
itemBoxSelected(newLi);
// await renderDocViewer(newLi.dataset.resourcePath, newLi.dataset.docId);
if (mode === 'edit') {
await renderDocViewer(newLi.dataset.resourcePath, newLi.dataset.docId);
}
}
} catch (err) {
console.error('❌ 저장 중 오류 발생:', err);
alert('저장 중 오류가 발생했습니다.');
}
}, 500);
confirmBtn.addEventListener('click', clickDocUpsertModalConfirm);
// 마지막 시퀀스 가져오기 // 공문 아이디 저장용
async function getDocSeq() {
try {
const res = await axios.get(`${docVars.path_name}/getDocSeq`);
let currNum = parseInt(res.data.data);
return currNum;
} catch (error) {
console.error('getDocSeq error', error);
}
}
/******************************************************************************************************************************/
/***************************************************************************************************************** 세팅모달 설정모달 */
/******************************************************************************************************************************/
async function openDocSettingModal() {
if (checkProjectInactive()) return;
const settingBtn = document.querySelector('.official-doc-setting-button');
const settingModalContainer = document.querySelector('.official-doc-modal.official-doc-setting-modal');
const modalBackground = document.querySelector('.official-doc-modal-background');
const orderBaseContainer = document.querySelector('.order-base');
const orderTargetContainer = document.querySelector('.order-target');
const otherOrderBaseContainer = document.querySelector('.other-order-base');
const otherOrderTargetContainer = document.querySelector('.other-order-target');
// input 템플릿 함수
const getInputHTML = (name, companyId = '') => `
<div class="official-doc-inline">
<input type="text" value="${name}" data-company-id="${companyId}" placeholder="내용을 입력해주세요.">
</div>
`;
// button 템플릿 함수
function getButtonHTML(companyName, companyId = '') {
return `
<div class="official-doc-inline">
<input type="text" value="${companyName}" data-company-id="${companyId}" placeholder="내용을 입력해주세요.">
<button class="xs-btn">
<img class="icon" src="/main/img/officialDoc/icon-close.svg" alt="icon-close">
</button>
</div>
`;
}
// 모달 버튼 클릭 시
settingBtn.addEventListener('click', async () => {
settingModalContainer.style.display = 'flex';
modalBackground.style.display = 'flex';
// 사용자 설정 DB에서 가져오기
getUserSettingsInDB();
// 인풋창 최소 하나씩은 가지고있게 만들기(데이터가 없을 때)
if (!orderBaseContainer.querySelector('input')) {
orderBaseContainer.innerHTML = getInputHTML('', '');
}
if (!orderTargetContainer.querySelector('input')) {
orderTargetContainer.innerHTML = getInputHTML('', '');
}
if (!otherOrderBaseContainer.querySelector('input')) {
otherOrderBaseContainer.innerHTML = getInputHTML('', '');
}
if (!otherOrderTargetContainer.querySelector('input')) {
otherOrderTargetContainer.innerHTML = getButtonHTML('', '');
}
try {
const res = await axios.get(`${docVars.path_name}/getCompanyList`, docVars.project_id);
if (res.data.success) {
const companyList = res.data.data;
originCompanyList = companyList.map((item) => ({
company_id: item.company_id,
company_name: item.company_name.trim(),
}));
companyList.forEach((item) => {
if (item.company_type === '발주처' && item.company_role === '기준') {
const input = orderBaseContainer.querySelector('input');
if (input) {
input.value = item.company_name;
input.dataset.companyId = item.company_id;
}
} else if (item.company_type === '발주처' && item.company_role === '상대기관') {
const input = orderTargetContainer.querySelector('input');
if (input) {
input.value = item.company_name;
input.dataset.companyId = item.company_id;
}
} else if (item.company_type === '발주처외' && item.company_role === '기준') {
const input = otherOrderBaseContainer.querySelector('input');
if (input) {
input.value = item.company_name;
input.dataset.companyId = item.company_id;
}
} else if (item.company_type === '발주처외' && item.company_role === '상대기관') {
const allInputs = otherOrderTargetContainer.querySelectorAll('input');
allInputs.forEach((input) => {
if (!input.value) {
input.closest('.official-doc-inline')?.remove();
}
});
const existingValues = [...otherOrderTargetContainer.querySelectorAll('input')].map((input) => input.value.trim());
if (!existingValues.includes(item.company_name.trim())) {
const wrapper = document.createElement('div');
wrapper.innerHTML = getButtonHTML(item.company_name, item.company_id);
otherOrderTargetContainer.appendChild(wrapper.firstElementChild);
}
}
});
}
} catch (error) {
console.error('getCompanyList err:', error);
}
});
// 추가 버튼
const addBtn = document.querySelector('.other-order-target-btn');
if (addBtn && !addBtn.dataset.listener) {
addBtn.dataset.listener = 'true'; // 중복 방지 플래그
addBtn.addEventListener('click', () => {
const wrapper = document.createElement('div');
wrapper.innerHTML = getButtonHTML('');
otherOrderTargetContainer.appendChild(wrapper.firstElementChild);
});
}
// 모달 백그라운드 누르면 모달 초기화 + flex none 되기
// modalBackground.addEventListener('pointerdown', () => {
// settingModalContainer.style.display = 'none';
// modalBackground.style.display = 'none';
// orderBaseContainer.innerHTML = '';
// orderTargetContainer.innerHTML = '';
// otherOrderBaseContainer.innerHTML = '';
// otherOrderTargetContainer.innerHTML = '';
// });
// X버튼 삭제
document.querySelector('.official-doc-modal.official-doc-setting-modal').addEventListener('click', (e) => {
if (e.target.closest('.xs-btn')) {
e.target.closest('.official-doc-inline')?.remove();
}
});
}
openDocSettingModal();
// 사용자 설정 불러오기 함수
export async function getUserSettingsInDB() {
try {
const res = await axios.get(`${docVars.path_name}/getUserSetting`, {
params: { userInfoString: vars.userInfoString },
});
if (res.data.success) {
if (!res.data.data) {
// res.data.data가 없으면 기본값으로 설정
setDefaultSettings();
return;
}
const { doc_option_instructions } = res.data.data;
// 서버에서 받은 설정 값에 따라 체크박스 업데이트
document.querySelector('#doc-instructions').checked = doc_option_instructions === 'true';
isInstructionsChecked = doc_option_instructions === 'true';
return { doc_option_instructions };
} else {
setDefaultSettings();
return { doc_option_instructions: 'true' };
}
} catch (error) {
console.error('getUserSettingsInDB: ', error);
setDefaultSettings();
return { doc_option_instructions: 'true' };
}
}
// 디폴트 설정 함수 // true
function setDefaultSettings() {
const instructionsCheckbox = document.querySelector('#doc-instructions');
if (instructionsCheckbox) {
instructionsCheckbox.checked = true;
isInstructionsChecked = true;
}
}
// 세팅모달 저장버튼
document.querySelector('.official-doc-modal.official-doc-setting-modal .official-doc-modal-footer .official-doc-positive-btn')?.addEventListener('click', async () => {
let allData = [];
const projectId = docVars.project_id;
const currentCompanyList = collectAllCompanyNames();
// 삭제 반영 먼저
// 모달 열었을 때 리스트(origin)와 현재 리스트(curr) 비교
// origin에는 있었는데 curr에는 없는 친구들을 deleteList에 담기
const deletedCompanyList = [];
for (let i = 0; i < originCompanyList.length; i++) {
const originItem = originCompanyList[i];
// 현재 리스트에서 company_id가 일치하는 항목이 없으면 삭제 대상
const found = currentCompanyList.find((item) => item.company_id === originItem.company_id);
if (!found) {
deletedCompanyList.push({
project_id: projectId,
company_id: originItem.company_id,
company_name: originItem.company_name,
});
}
}
const deletedNames = deletedCompanyList.map((item) => item.company_id);
// 수정사항 반영
// 발주처-기준(order-base)
const orderBaseInputs = document.querySelector('.order-base .official-doc-inline input');
const companyId1 = orderBaseInputs.dataset.companyId || null;
const name1 = orderBaseInputs.value;
if (!deletedNames.includes(companyId1) && name1.trim() !== '') {
allData.push({
project_id: projectId,
company_id: companyId1,
company_name: name1,
company_type: '발주처',
company_role: '기준',
});
}
// 발주처-상대기관(order-target)
const orderTargetInputs = document.querySelector('.order-target .official-doc-inline input');
const companyId2 = orderTargetInputs.dataset.companyId || null;
const name2 = orderTargetInputs.value;
if (!deletedNames.includes(companyId2) && name2.trim() !== '') {
allData.push({
project_id: projectId,
company_id: companyId2,
company_name: name2,
company_type: '발주처',
company_role: '상대기관',
});
}
// 발주처외-기준(other-order-base)
const otherOrderBaseInputs = document.querySelector('.other-order-base .official-doc-inline input');
const companyId3 = otherOrderBaseInputs.dataset.companyId || null;
const name3 = otherOrderBaseInputs.value;
if (!deletedNames.includes(companyId3) && name3.trim() !== '') {
allData.push({
project_id: projectId,
company_id: companyId3,
company_name: name3,
company_type: '발주처외',
company_role: '기준',
});
}
// 발주처외-상대기관(other-order-target)
const otherOrderTargetInputs = document.querySelectorAll('.other-order-target input');
otherOrderTargetInputs.forEach((input) => {
const companyId = input.dataset.companyId || null;
const name = input.value;
if (!deletedNames.includes(companyId) && name.trim() !== '') {
allData.push({
project_id: projectId,
company_id: companyId,
company_name: name,
company_type: '발주처외',
company_role: '상대기관',
});
}
});
// 세팅에서 사용방법보기 라디오 버튼 켰는지 껐는지 확인하기
// 체크 X : 사용방법보기 사용 안함 (default) // 체크 X : 사용방법보기 사용
document.querySelector('#doc-instructions').addEventListener('click', (e) => {
if (e.target.checked) {
isInstructionsChecked = true;
} else {
isInstructionsChecked = false;
}
});
// 서버로 데이터 전송
await sendUserDataToServer(isInstructionsChecked, allData, deletedCompanyList);
});
// 세팅모달 저장버튼 - 서버
export async function sendUserDataToServer(isInstructionsChecked, allData, deletedCompanyList) {
try {
// 1. 사용자 설정 저장
const checkedRes = await axios.post(`${docVars.path_name}/saveUserSetting`, {
userInfo: vars.userInfoString,
isInstructionsChecked,
});
if (!checkedRes.data.success) {
alert('사용자 설정 저장 실패');
return;
}
// 2. 회사 리스트 저장
if(allData || deletedCompanyList) {
const saveCompanyRes = await axios.post(`${docVars.path_name}/saveCompanyList`, {
deletedCompanyList,
companyList: allData,
});
if (saveCompanyRes.data.success) {
alert('저장이 완료되었습니다.');
document.querySelector('.official-doc-modal-background').style.display = 'none';
document.querySelector('.official-doc-modal.official-doc-setting-modal').style.display = 'none';
await getGroupCompanyData();
await initCustomSelectBoxes();
} else {
alert('회사 목록 저장 실패');
}
}
} catch (error) {
console.error('데이터 저장 중 오류 발생: ', error);
}
}
// 세팅에서 사용방법보기 라디오 버튼 켰는지 껐는지 확인하기
let isInstructionsChecked = true;
document.querySelector('#doc-instructions')?.addEventListener('click', (e) => {
isInstructionsChecked = e.target.checked;
});
// 현재 세팅모달에 적혀있는 회사 리스트 (이름이랑 id) 가져오기
function collectAllCompanyNames() {
const selectors = ['.order-base', '.order-target', '.other-order-base', '.other-order-target'];
let result = [];
for (let i = 0; i < selectors.length; i++) {
const inputs = document.querySelectorAll(selectors[i] + ' input');
for (let j = 0; j < inputs.length; j++) {
const input = inputs[j];
let name = input.value.trim();
let id = input.dataset.companyId || null;
if (name !== '') {
result.push({
company_name: name,
company_id: id,
});
}
}
}
return result;
}
// 세팅모달 닫기 클릭
// document.querySelector('.official-doc-modal.official-doc-setting-modal .official-doc-setting-modal-header .doc-close')?.addEventListener('click', () => {
document.querySelector('.official-doc-modal.official-doc-setting-modal .official-doc-modal-footer .official-doc-negative-btn')?.addEventListener('click', () => {
const settingModalContainer = document.querySelector('.official-doc-modal.official-doc-setting-modal');
settingModalContainer.style.display = 'none';
const settingModalInput = document.querySelector('.official-doc-modal.official-doc-setting-modal input');
settingModalInput.innerHTML = '';
const modalBackground = document.querySelector('.official-doc-modal-background');
modalBackground.style.display = 'none';
});
// 회사명에서 괄호 뒤에 글자 빼기 > (발주처, 기준)
function getPureCompanyName(str) {
return str.split('(')[0].trim();
}