feat: [approval] 공문서 양식 추가
- 공문서 전용 폼/조회 파셜 추가 - create/edit/show 페이지에 공문서 통합 - 문서번호, 수신, 참조, 제목, 본문, 붙임 입력 - 발신자 정보 테넌트에서 자동 로드 - 미리보기/인쇄 기능 (공문서 형식)
This commit is contained in:
@@ -145,6 +145,11 @@ class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-
|
||||
'tenantInfo' => $tenantInfo ?? [],
|
||||
])
|
||||
|
||||
{{-- 공문서 전용 폼 --}}
|
||||
@include('approvals.partials._official-letter-form', [
|
||||
'tenantInfo' => $tenantInfo ?? [],
|
||||
])
|
||||
|
||||
{{-- 견적서 전용 폼 --}}
|
||||
@include('approvals.partials._quotation-form', [
|
||||
'tenantInfo' => $tenantInfo ?? [],
|
||||
@@ -284,6 +289,11 @@ class="p-1 text-gray-400 hover:text-gray-600 transition">
|
||||
color: 'border-slate-200 bg-slate-50', titleColor: 'text-slate-800', textColor: 'text-slate-600',
|
||||
text: '업무 추진에 필요한 사항을 기안하여 결재를 받는 기본 문서입니다. 프로젝트 계획, 업무 협조 요청, 내부 제안 등 정형화된 양식이 없는 일반적인 업무 보고·요청에 사용합니다.',
|
||||
},
|
||||
official_letter: {
|
||||
title: '공문서', icon: '📨',
|
||||
color: 'border-blue-200 bg-blue-50', titleColor: 'text-blue-800', textColor: 'text-blue-600',
|
||||
text: '외부 기관이나 거래처에 발송하는 공식 문서입니다. 문서번호, 수신처, 제목, 본문, 붙임 서류를 기재하며, 승인 후 회사 직인이 날인된 공문서로 사용합니다.',
|
||||
},
|
||||
leave: {
|
||||
title: '휴가신청', icon: '🏖️',
|
||||
color: 'border-sky-200 bg-sky-50', titleColor: 'text-sky-800', textColor: 'text-sky-600',
|
||||
@@ -373,7 +383,7 @@ class="p-1 text-gray-400 hover:text-gray-600 transition">
|
||||
|
||||
// 2단계 분류 정의 (코드 → 카테고리)
|
||||
const formCategoryMap = {
|
||||
BUSINESS_DRAFT: '일반',
|
||||
BUSINESS_DRAFT: '일반', official_letter: '일반',
|
||||
leave: '인사/근태', attendance_request: '인사/근태', resignation: '인사/근태', reason_report: '인사/근태', delegation: '인사/근태', board_minutes: '인사/근태',
|
||||
employment_cert: '증명서', career_cert: '증명서', appointment_cert: '증명서', seal_usage: '증명서',
|
||||
pr_expense: '품의', pr_contract: '품의', pr_purchase: '품의', pr_trip: '품의', pr_settlement: '품의',
|
||||
@@ -465,6 +475,7 @@ function updateFormDescription(formId) {
|
||||
let isDelegationForm = false;
|
||||
let isBoardMinutesForm = false;
|
||||
let isQuotationForm = false;
|
||||
let isOfficialLetterForm = false;
|
||||
|
||||
// 양식코드별 표시할 유형 목록
|
||||
const leaveTypesByFormCode = {
|
||||
@@ -717,6 +728,7 @@ function switchFormMode(formId) {
|
||||
const delegationContainer = document.getElementById('delegation-form-container');
|
||||
const boardMinutesContainer = document.getElementById('board-minutes-form-container');
|
||||
const quotationContainer = document.getElementById('quotation-form-container');
|
||||
const officialLetterContainer = document.getElementById('official-letter-form-container');
|
||||
const bodyArea = document.getElementById('body-area');
|
||||
const expenseLoadBtn = document.getElementById('expense-load-btn');
|
||||
|
||||
@@ -734,6 +746,7 @@ function switchFormMode(formId) {
|
||||
delegationContainer.style.display = 'none';
|
||||
boardMinutesContainer.style.display = 'none';
|
||||
quotationContainer.style.display = 'none';
|
||||
officialLetterContainer.style.display = 'none';
|
||||
expenseLoadBtn.style.display = 'none';
|
||||
bodyArea.style.display = 'none';
|
||||
isExpenseForm = false;
|
||||
@@ -747,6 +760,7 @@ function switchFormMode(formId) {
|
||||
isDelegationForm = false;
|
||||
isBoardMinutesForm = false;
|
||||
isQuotationForm = false;
|
||||
isOfficialLetterForm = false;
|
||||
|
||||
if (code === 'expense') {
|
||||
isExpenseForm = true;
|
||||
@@ -818,6 +832,9 @@ function switchFormMode(formId) {
|
||||
} else if (code === 'quotation') {
|
||||
isQuotationForm = true;
|
||||
quotationContainer.style.display = '';
|
||||
} else if (code === 'official_letter') {
|
||||
isOfficialLetterForm = true;
|
||||
officialLetterContainer.style.display = '';
|
||||
} else {
|
||||
bodyArea.style.display = '';
|
||||
}
|
||||
@@ -828,7 +845,7 @@ function applyBodyTemplate(formId) {
|
||||
switchFormMode(formId);
|
||||
|
||||
// 전용 폼이면 제목을 양식명으로 설정하고 body template 적용 건너뜀
|
||||
if (isExpenseForm || isPurchaseRequestForm || isLeaveForm || isCertForm || isCareerCertForm || isAppointmentCertForm || isResignationForm || isSealUsageForm || isDelegationForm || isBoardMinutesForm || isQuotationForm) {
|
||||
if (isExpenseForm || isPurchaseRequestForm || isLeaveForm || isCertForm || isCareerCertForm || isAppointmentCertForm || isResignationForm || isSealUsageForm || isDelegationForm || isBoardMinutesForm || isQuotationForm || isOfficialLetterForm) {
|
||||
const titleEl = document.getElementById('title');
|
||||
const formSelect = document.getElementById('form_id');
|
||||
titleEl.value = formSelect.options[formSelect.selectedIndex].text;
|
||||
@@ -1140,6 +1157,30 @@ function applyBodyTemplate(formId) {
|
||||
remarks: document.getElementById('qt-remarks').value.trim(),
|
||||
};
|
||||
formBody = null;
|
||||
} else if (isOfficialLetterForm) {
|
||||
const olRecipient = document.getElementById('ol-recipient').value.trim();
|
||||
if (!olRecipient) { showToast('수신처를 입력해주세요.', 'warning'); return; }
|
||||
const olSubject = document.getElementById('ol-subject').value.trim();
|
||||
if (!olSubject) { showToast('제목을 입력해주세요.', 'warning'); return; }
|
||||
const olBody = document.getElementById('ol-body').value.trim();
|
||||
if (!olBody) { showToast('본문을 입력해주세요.', 'warning'); return; }
|
||||
|
||||
formContent = {
|
||||
doc_number: document.getElementById('ol-doc-number').value.trim(),
|
||||
doc_date: document.getElementById('ol-doc-date').value,
|
||||
recipient: olRecipient,
|
||||
reference: document.getElementById('ol-reference').value.trim(),
|
||||
subject: olSubject,
|
||||
body: olBody,
|
||||
attachments_desc: document.getElementById('ol-attachments-desc').value.trim(),
|
||||
company_name: document.getElementById('ol-company-name').value,
|
||||
ceo_name: document.getElementById('ol-ceo-name').value,
|
||||
company_address: document.getElementById('ol-company-address').value,
|
||||
phone: document.getElementById('ol-phone-input')?.value || document.getElementById('ol-phone').value,
|
||||
fax: document.getElementById('ol-fax-input')?.value || document.getElementById('ol-fax').value,
|
||||
email: document.getElementById('ol-email-input')?.value || document.getElementById('ol-email').value,
|
||||
};
|
||||
formBody = null;
|
||||
} else if (isDelegationForm) {
|
||||
const dlAgentName = document.getElementById('dl-agent-name').value.trim();
|
||||
if (!dlAgentName) {
|
||||
@@ -2431,6 +2472,72 @@ function printQuotationPreview() {
|
||||
win.print();
|
||||
}
|
||||
|
||||
// ─── 공문서 미리보기 ───
|
||||
function buildOfficialLetterPreviewHtml(data) {
|
||||
const bodyHtml = (data.body || '').replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/\n/g, '<br>');
|
||||
const attachHtml = (data.attachments_desc || '').replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/\n/g, '<br>');
|
||||
return `
|
||||
<div style="text-align:center;margin-bottom:32px;">
|
||||
<h1 style="font-size:22px;font-weight:800;margin:0;">${data.company_name || ''}</h1>
|
||||
</div>
|
||||
<div style="border-bottom:2px solid #333;padding-bottom:16px;margin-bottom:24px;font-size:13px;line-height:2;">
|
||||
<div><span style="display:inline-block;width:80px;letter-spacing:12px;font-weight:600;">문서번호</span> : ${data.doc_number || ''}</div>
|
||||
<div><span style="display:inline-block;width:80px;letter-spacing:18px;font-weight:600;">일자</span> : ${data.doc_date || ''}</div>
|
||||
<div><span style="display:inline-block;width:80px;letter-spacing:18px;font-weight:600;">수신</span> : ${data.recipient || ''}</div>
|
||||
${data.reference ? `<div><span style="display:inline-block;width:80px;letter-spacing:18px;font-weight:600;">참조</span> : ${data.reference}</div>` : ''}
|
||||
<div><span style="display:inline-block;width:80px;letter-spacing:18px;font-weight:600;">제목</span> : <strong>${data.subject || ''}</strong></div>
|
||||
</div>
|
||||
<div style="font-size:13px;line-height:1.8;margin-bottom:24px;min-height:200px;">${bodyHtml}</div>
|
||||
${data.attachments_desc ? `<div style="font-size:13px;line-height:1.6;margin-bottom:24px;padding-top:12px;border-top:1px solid #ddd;"><strong>붙임 :</strong><br>${attachHtml}</div>` : ''}
|
||||
<div style="text-align:center;margin:40px 0 16px;font-size:14px;">
|
||||
<span>${data.company_name || ''}</span>
|
||||
<span>대표이사 ${data.ceo_name || ''}</span>
|
||||
<span style="color:#999;">[직인날인]</span>
|
||||
</div>
|
||||
<div style="border-top:1px solid #ccc;padding-top:8px;font-size:10px;color:#888;line-height:1.6;">
|
||||
${data.company_address ? `<div>${data.company_address}</div>` : ''}
|
||||
<div>${data.phone ? '전화 ' + data.phone : ''}${data.fax ? ' / 팩스 ' + data.fax : ''}${data.email ? ' / 이메일 ' + data.email : ''}</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
function openOfficialLetterPreview() {
|
||||
const data = {
|
||||
doc_number: document.getElementById('ol-doc-number').value.trim(),
|
||||
doc_date: document.getElementById('ol-doc-date').value,
|
||||
recipient: document.getElementById('ol-recipient').value.trim(),
|
||||
reference: document.getElementById('ol-reference').value.trim(),
|
||||
subject: document.getElementById('ol-subject').value.trim(),
|
||||
body: document.getElementById('ol-body').value.trim(),
|
||||
attachments_desc: document.getElementById('ol-attachments-desc').value.trim(),
|
||||
company_name: document.getElementById('ol-company-name').value,
|
||||
ceo_name: document.getElementById('ol-ceo-name').value,
|
||||
company_address: document.getElementById('ol-company-address').value,
|
||||
phone: document.getElementById('ol-phone-input')?.value || '',
|
||||
fax: document.getElementById('ol-fax-input')?.value || '',
|
||||
email: document.getElementById('ol-email-input')?.value || '',
|
||||
};
|
||||
document.getElementById('official-letter-preview-content').innerHTML = buildOfficialLetterPreviewHtml(data);
|
||||
document.getElementById('official-letter-preview-modal').style.display = '';
|
||||
document.body.style.overflow = 'hidden';
|
||||
}
|
||||
|
||||
function closeOfficialLetterPreview() {
|
||||
document.getElementById('official-letter-preview-modal').style.display = 'none';
|
||||
document.body.style.overflow = '';
|
||||
}
|
||||
|
||||
function printOfficialLetterPreview() {
|
||||
const content = document.getElementById('official-letter-preview-content').innerHTML;
|
||||
const win = window.open('', '_blank');
|
||||
win.document.write('<html><head><title>공문서</title>');
|
||||
win.document.write('<style>body{font-family:"Pretendard","Malgun Gothic",sans-serif;padding:48px 56px;margin:0;}@media print{body{padding:40px 48px;}}</style>');
|
||||
win.document.write('</head><body>');
|
||||
win.document.write(content);
|
||||
win.document.write('</body></html>');
|
||||
win.document.close();
|
||||
win.print();
|
||||
}
|
||||
|
||||
function printDelegationPreview() {
|
||||
const content = document.getElementById('delegation-preview-content').innerHTML;
|
||||
const win = window.open('', '_blank');
|
||||
|
||||
@@ -163,6 +163,11 @@ class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-
|
||||
'tenantInfo' => $tenantInfo ?? [],
|
||||
])
|
||||
|
||||
{{-- 공문서 전용 폼 --}}
|
||||
@include('approvals.partials._official-letter-form', [
|
||||
'tenantInfo' => $tenantInfo ?? [],
|
||||
])
|
||||
|
||||
{{-- 견적서 전용 폼 --}}
|
||||
@include('approvals.partials._quotation-form', [
|
||||
'tenantInfo' => $tenantInfo ?? [],
|
||||
@@ -308,6 +313,7 @@ class="px-6 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-lg text-sm fon
|
||||
let isDelegationForm = false;
|
||||
let isBoardMinutesForm = false;
|
||||
let isQuotationForm = false;
|
||||
let isOfficialLetterForm = false;
|
||||
|
||||
const formDescriptions = {
|
||||
BUSINESS_DRAFT: {
|
||||
@@ -315,6 +321,11 @@ class="px-6 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-lg text-sm fon
|
||||
color: 'border-slate-200 bg-slate-50', titleColor: 'text-slate-800', textColor: 'text-slate-600',
|
||||
text: '업무 추진에 필요한 사항을 기안하여 결재를 받는 기본 문서입니다. 프로젝트 계획, 업무 협조 요청, 내부 제안 등 정형화된 양식이 없는 일반적인 업무 보고·요청에 사용합니다.',
|
||||
},
|
||||
official_letter: {
|
||||
title: '공문서', icon: '📨',
|
||||
color: 'border-blue-200 bg-blue-50', titleColor: 'text-blue-800', textColor: 'text-blue-600',
|
||||
text: '외부 기관이나 거래처에 발송하는 공식 문서입니다. 문서번호, 수신처, 제목, 본문, 붙임 서류를 기재하며, 승인 후 회사 직인이 날인된 공문서로 사용합니다.',
|
||||
},
|
||||
leave: {
|
||||
title: '휴가신청', icon: '🏖️',
|
||||
color: 'border-sky-200 bg-sky-50', titleColor: 'text-sky-800', textColor: 'text-sky-600',
|
||||
@@ -404,7 +415,7 @@ class="px-6 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-lg text-sm fon
|
||||
|
||||
// 2단계 분류 정의
|
||||
const formCategoryMap = {
|
||||
BUSINESS_DRAFT: '일반',
|
||||
BUSINESS_DRAFT: '일반', official_letter: '일반',
|
||||
leave: '인사/근태', attendance_request: '인사/근태', resignation: '인사/근태', reason_report: '인사/근태', delegation: '인사/근태', board_minutes: '인사/근태',
|
||||
employment_cert: '증명서', career_cert: '증명서', appointment_cert: '증명서', seal_usage: '증명서',
|
||||
pr_expense: '품의', pr_contract: '품의', pr_purchase: '품의', pr_trip: '품의', pr_settlement: '품의',
|
||||
@@ -649,6 +660,7 @@ function switchFormMode(formId) {
|
||||
const delegationContainer = document.getElementById('delegation-form-container');
|
||||
const boardMinutesContainer = document.getElementById('board-minutes-form-container');
|
||||
const quotationContainer = document.getElementById('quotation-form-container');
|
||||
const officialLetterContainer = document.getElementById('official-letter-form-container');
|
||||
const bodyArea = document.getElementById('body-area');
|
||||
|
||||
expenseContainer.style.display = 'none';
|
||||
@@ -658,6 +670,7 @@ function switchFormMode(formId) {
|
||||
delegationContainer.style.display = 'none';
|
||||
boardMinutesContainer.style.display = 'none';
|
||||
quotationContainer.style.display = 'none';
|
||||
officialLetterContainer.style.display = 'none';
|
||||
bodyArea.style.display = 'none';
|
||||
isExpenseForm = false;
|
||||
isPurchaseRequestForm = false;
|
||||
@@ -666,6 +679,7 @@ function switchFormMode(formId) {
|
||||
isDelegationForm = false;
|
||||
isBoardMinutesForm = false;
|
||||
isQuotationForm = false;
|
||||
isOfficialLetterForm = false;
|
||||
|
||||
if (code === 'expense') {
|
||||
isExpenseForm = true;
|
||||
@@ -694,6 +708,9 @@ function switchFormMode(formId) {
|
||||
} else if (code === 'quotation') {
|
||||
isQuotationForm = true;
|
||||
quotationContainer.style.display = '';
|
||||
} else if (code === 'official_letter') {
|
||||
isOfficialLetterForm = true;
|
||||
officialLetterContainer.style.display = '';
|
||||
} else {
|
||||
bodyArea.style.display = '';
|
||||
}
|
||||
@@ -704,7 +721,7 @@ function applyBodyTemplate(formId) {
|
||||
switchFormMode(formId);
|
||||
|
||||
// 전용 폼이면 제목만 자동 설정하고 body template 적용 건너뜀
|
||||
if (isExpenseForm || isPurchaseRequestForm || isCertForm || isSealUsageForm || isDelegationForm || isBoardMinutesForm || isQuotationForm) {
|
||||
if (isExpenseForm || isPurchaseRequestForm || isCertForm || isSealUsageForm || isDelegationForm || isBoardMinutesForm || isQuotationForm || isOfficialLetterForm) {
|
||||
const titleEl = document.getElementById('title');
|
||||
if (!titleEl.value.trim()) {
|
||||
const formSelect = document.getElementById('form_id');
|
||||
@@ -882,8 +899,25 @@ function applyBodyTemplate(formId) {
|
||||
}
|
||||
}
|
||||
|
||||
// 공문서 기존 데이터 복원
|
||||
if (isOfficialLetterForm) {
|
||||
const olContent = @json($approval->content ?? []);
|
||||
if (olContent.recipient) {
|
||||
document.getElementById('ol-doc-number').value = olContent.doc_number || '';
|
||||
document.getElementById('ol-doc-date').value = olContent.doc_date || '';
|
||||
document.getElementById('ol-recipient').value = olContent.recipient || '';
|
||||
document.getElementById('ol-reference').value = olContent.reference || '';
|
||||
document.getElementById('ol-subject').value = olContent.subject || '';
|
||||
document.getElementById('ol-body').value = olContent.body || '';
|
||||
document.getElementById('ol-attachments-desc').value = olContent.attachments_desc || '';
|
||||
if (olContent.phone) { const el = document.getElementById('ol-phone-input'); if (el) el.value = olContent.phone; }
|
||||
if (olContent.fax) { const el = document.getElementById('ol-fax-input'); if (el) el.value = olContent.fax; }
|
||||
if (olContent.email) { const el = document.getElementById('ol-email-input'); if (el) el.value = olContent.email; }
|
||||
}
|
||||
}
|
||||
|
||||
// 전용 폼이 아닌 경우에만 Quill 편집기 자동 활성화
|
||||
if (!isExpenseForm && !isPurchaseRequestForm && !isCertForm && !isSealUsageForm && !isDelegationForm && !isBoardMinutesForm && !isQuotationForm) {
|
||||
if (!isExpenseForm && !isPurchaseRequestForm && !isCertForm && !isSealUsageForm && !isDelegationForm && !isBoardMinutesForm && !isQuotationForm && !isOfficialLetterForm) {
|
||||
const existingBody = document.getElementById('body').value;
|
||||
if (/<[a-z][\s\S]*>/i.test(existingBody)) {
|
||||
document.getElementById('useEditor').checked = true;
|
||||
@@ -1044,6 +1078,28 @@ function applyBodyTemplate(formId) {
|
||||
remarks: document.getElementById('qt-remarks').value.trim(),
|
||||
};
|
||||
formBody = null;
|
||||
} else if (isOfficialLetterForm) {
|
||||
const olRecipient = document.getElementById('ol-recipient').value.trim();
|
||||
if (!olRecipient) { showToast('수신처를 입력해주세요.', 'warning'); return; }
|
||||
const olSubject = document.getElementById('ol-subject').value.trim();
|
||||
if (!olSubject) { showToast('제목을 입력해주세요.', 'warning'); return; }
|
||||
const olBody = document.getElementById('ol-body').value.trim();
|
||||
if (!olBody) { showToast('본문을 입력해주세요.', 'warning'); return; }
|
||||
|
||||
formContent = {
|
||||
doc_number: document.getElementById('ol-doc-number').value.trim(),
|
||||
doc_date: document.getElementById('ol-doc-date').value,
|
||||
recipient: olRecipient, reference: document.getElementById('ol-reference').value.trim(),
|
||||
subject: olSubject, body: olBody,
|
||||
attachments_desc: document.getElementById('ol-attachments-desc').value.trim(),
|
||||
company_name: document.getElementById('ol-company-name').value,
|
||||
ceo_name: document.getElementById('ol-ceo-name').value,
|
||||
company_address: document.getElementById('ol-company-address').value,
|
||||
phone: document.getElementById('ol-phone-input')?.value || document.getElementById('ol-phone').value,
|
||||
fax: document.getElementById('ol-fax-input')?.value || document.getElementById('ol-fax').value,
|
||||
email: document.getElementById('ol-email-input')?.value || document.getElementById('ol-email').value,
|
||||
};
|
||||
formBody = null;
|
||||
} else if (isDelegationForm) {
|
||||
const dlAgentName = document.getElementById('dl-agent-name').value.trim();
|
||||
if (!dlAgentName) {
|
||||
@@ -1710,5 +1766,71 @@ function printQuotationPreview() {
|
||||
win.document.close();
|
||||
win.print();
|
||||
}
|
||||
|
||||
// ─── 공문서 미리보기 ───
|
||||
function buildOfficialLetterPreviewHtml(data) {
|
||||
const bodyHtml = (data.body || '').replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/\n/g, '<br>');
|
||||
const attachHtml = (data.attachments_desc || '').replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/\n/g, '<br>');
|
||||
return `
|
||||
<div style="text-align:center;margin-bottom:32px;">
|
||||
<h1 style="font-size:22px;font-weight:800;margin:0;">${data.company_name || ''}</h1>
|
||||
</div>
|
||||
<div style="border-bottom:2px solid #333;padding-bottom:16px;margin-bottom:24px;font-size:13px;line-height:2;">
|
||||
<div><span style="display:inline-block;width:80px;letter-spacing:12px;font-weight:600;">문서번호</span> : ${data.doc_number || ''}</div>
|
||||
<div><span style="display:inline-block;width:80px;letter-spacing:18px;font-weight:600;">일자</span> : ${data.doc_date || ''}</div>
|
||||
<div><span style="display:inline-block;width:80px;letter-spacing:18px;font-weight:600;">수신</span> : ${data.recipient || ''}</div>
|
||||
${data.reference ? `<div><span style="display:inline-block;width:80px;letter-spacing:18px;font-weight:600;">참조</span> : ${data.reference}</div>` : ''}
|
||||
<div><span style="display:inline-block;width:80px;letter-spacing:18px;font-weight:600;">제목</span> : <strong>${data.subject || ''}</strong></div>
|
||||
</div>
|
||||
<div style="font-size:13px;line-height:1.8;margin-bottom:24px;min-height:200px;">${bodyHtml}</div>
|
||||
${data.attachments_desc ? `<div style="font-size:13px;line-height:1.6;margin-bottom:24px;padding-top:12px;border-top:1px solid #ddd;"><strong>붙임 :</strong><br>${attachHtml}</div>` : ''}
|
||||
<div style="text-align:center;margin:40px 0 16px;font-size:14px;">
|
||||
<span>${data.company_name || ''}</span>
|
||||
<span>대표이사 ${data.ceo_name || ''}</span>
|
||||
<span style="color:#999;">[직인날인]</span>
|
||||
</div>
|
||||
<div style="border-top:1px solid #ccc;padding-top:8px;font-size:10px;color:#888;line-height:1.6;">
|
||||
${data.company_address ? `<div>${data.company_address}</div>` : ''}
|
||||
<div>${data.phone ? '전화 ' + data.phone : ''}${data.fax ? ' / 팩스 ' + data.fax : ''}${data.email ? ' / 이메일 ' + data.email : ''}</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
function openOfficialLetterPreview() {
|
||||
const data = {
|
||||
doc_number: document.getElementById('ol-doc-number').value.trim(),
|
||||
doc_date: document.getElementById('ol-doc-date').value,
|
||||
recipient: document.getElementById('ol-recipient').value.trim(),
|
||||
reference: document.getElementById('ol-reference').value.trim(),
|
||||
subject: document.getElementById('ol-subject').value.trim(),
|
||||
body: document.getElementById('ol-body').value.trim(),
|
||||
attachments_desc: document.getElementById('ol-attachments-desc').value.trim(),
|
||||
company_name: document.getElementById('ol-company-name').value,
|
||||
ceo_name: document.getElementById('ol-ceo-name').value,
|
||||
company_address: document.getElementById('ol-company-address').value,
|
||||
phone: document.getElementById('ol-phone-input')?.value || '',
|
||||
fax: document.getElementById('ol-fax-input')?.value || '',
|
||||
email: document.getElementById('ol-email-input')?.value || '',
|
||||
};
|
||||
document.getElementById('official-letter-preview-content').innerHTML = buildOfficialLetterPreviewHtml(data);
|
||||
document.getElementById('official-letter-preview-modal').style.display = '';
|
||||
document.body.style.overflow = 'hidden';
|
||||
}
|
||||
|
||||
function closeOfficialLetterPreview() {
|
||||
document.getElementById('official-letter-preview-modal').style.display = 'none';
|
||||
document.body.style.overflow = '';
|
||||
}
|
||||
|
||||
function printOfficialLetterPreview() {
|
||||
const content = document.getElementById('official-letter-preview-content').innerHTML;
|
||||
const win = window.open('', '_blank');
|
||||
win.document.write('<html><head><title>공문서</title>');
|
||||
win.document.write('<style>body{font-family:"Pretendard","Malgun Gothic",sans-serif;padding:48px 56px;margin:0;}@media print{body{padding:40px 48px;}}</style>');
|
||||
win.document.write('</head><body>');
|
||||
win.document.write(content);
|
||||
win.document.write('</body></html>');
|
||||
win.document.close();
|
||||
win.print();
|
||||
}
|
||||
</script>
|
||||
@endpush
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
{{--
|
||||
공문서 전용 폼
|
||||
Props:
|
||||
$tenantInfo (array) - 테넌트(회사) 정보
|
||||
--}}
|
||||
@php
|
||||
$tenantInfo = $tenantInfo ?? [];
|
||||
@endphp
|
||||
|
||||
<div id="official-letter-form-container" style="display: none;" class="mb-4">
|
||||
<input type="hidden" id="ol-company-name" value="{{ $tenantInfo['company_name'] ?? '' }}">
|
||||
<input type="hidden" id="ol-business-num" value="{{ $tenantInfo['business_num'] ?? '' }}">
|
||||
<input type="hidden" id="ol-ceo-name" value="{{ $tenantInfo['ceo_name'] ?? '' }}">
|
||||
<input type="hidden" id="ol-company-address" value="{{ $tenantInfo['address'] ?? '' }}">
|
||||
<input type="hidden" id="ol-phone" value="{{ $tenantInfo['phone'] ?? '' }}">
|
||||
<input type="hidden" id="ol-fax" value="{{ $tenantInfo['fax'] ?? '' }}">
|
||||
<input type="hidden" id="ol-email" value="{{ $tenantInfo['email'] ?? '' }}">
|
||||
|
||||
<div class="space-y-4">
|
||||
{{-- 1. 문서 정보 --}}
|
||||
<div class="border border-gray-200 rounded-lg overflow-hidden">
|
||||
<div class="bg-gray-50 px-4 py-2 border-b border-gray-200">
|
||||
<h3 class="text-sm font-semibold text-gray-700">1. 문서 정보</h3>
|
||||
</div>
|
||||
<div class="p-4 space-y-3">
|
||||
<div class="flex gap-4 flex-wrap">
|
||||
<div style="flex: 1 1 250px; max-width: 300px;">
|
||||
<label class="block text-xs font-medium text-gray-500 mb-1">문서번호</label>
|
||||
<input type="text" id="ol-doc-number" placeholder="예: 2026030601"
|
||||
class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500">
|
||||
</div>
|
||||
<div style="flex: 0 0 180px;">
|
||||
<label class="block text-xs font-medium text-gray-500 mb-1">일자 <span class="text-red-500">*</span></label>
|
||||
<input type="date" id="ol-doc-date" value="{{ now()->format('Y-m-d') }}"
|
||||
class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- 2. 수신/참조/제목 --}}
|
||||
<div class="border border-gray-200 rounded-lg overflow-hidden">
|
||||
<div class="bg-gray-50 px-4 py-2 border-b border-gray-200">
|
||||
<h3 class="text-sm font-semibold text-gray-700">2. 수신 정보</h3>
|
||||
</div>
|
||||
<div class="p-4 space-y-3">
|
||||
<div class="flex gap-4 flex-wrap">
|
||||
<div style="flex: 1 1 300px;">
|
||||
<label class="block text-xs font-medium text-gray-500 mb-1">수신 <span class="text-red-500">*</span></label>
|
||||
<input type="text" id="ol-recipient" placeholder="수신처 (예: ㈜OOO)"
|
||||
class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500">
|
||||
</div>
|
||||
<div style="flex: 1 1 250px;">
|
||||
<label class="block text-xs font-medium text-gray-500 mb-1">참조</label>
|
||||
<input type="text" id="ol-reference" placeholder="참조 (선택사항)"
|
||||
class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500">
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-gray-500 mb-1">제목 <span class="text-red-500">*</span></label>
|
||||
<input type="text" id="ol-subject" placeholder="공문서 제목"
|
||||
class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- 3. 본문 --}}
|
||||
<div class="border border-gray-200 rounded-lg overflow-hidden">
|
||||
<div class="bg-gray-50 px-4 py-2 border-b border-gray-200">
|
||||
<h3 class="text-sm font-semibold text-gray-700">3. 본문</h3>
|
||||
</div>
|
||||
<div class="p-4">
|
||||
<textarea id="ol-body" rows="10" placeholder="공문서 본문을 작성하세요. 1. 귀사의 무궁한 발전을 기원합니다. 2. ..."
|
||||
class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 leading-relaxed"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- 4. 붙임 --}}
|
||||
<div class="border border-gray-200 rounded-lg overflow-hidden">
|
||||
<div class="bg-gray-50 px-4 py-2 border-b border-gray-200">
|
||||
<h3 class="text-sm font-semibold text-gray-700">4. 붙임 (첨부서류)</h3>
|
||||
</div>
|
||||
<div class="p-4">
|
||||
<textarea id="ol-attachments-desc" rows="3" placeholder="붙임 서류 목록 (선택사항) 예: 1. 기성실적증명서 3부 2. 세금계산서 사본 1부"
|
||||
class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- 5. 발신자 정보 (테넌트 자동) --}}
|
||||
<div class="border border-gray-200 rounded-lg overflow-hidden">
|
||||
<div class="bg-gray-50 px-4 py-2 border-b border-gray-200">
|
||||
<h3 class="text-sm font-semibold text-gray-700">5. 발신자</h3>
|
||||
</div>
|
||||
<div class="p-4 space-y-3">
|
||||
<div class="flex gap-4 flex-wrap">
|
||||
<div style="flex: 1 1 200px;">
|
||||
<label class="block text-xs font-medium text-gray-500 mb-1">상호</label>
|
||||
<input type="text" readonly value="{{ $tenantInfo['company_name'] ?? '' }}"
|
||||
class="w-full px-3 py-2 border border-gray-200 rounded-lg text-sm bg-gray-50 text-gray-700">
|
||||
</div>
|
||||
<div style="flex: 0 0 150px;">
|
||||
<label class="block text-xs font-medium text-gray-500 mb-1">대표이사</label>
|
||||
<input type="text" readonly value="{{ $tenantInfo['ceo_name'] ?? '' }}"
|
||||
class="w-full px-3 py-2 border border-gray-200 rounded-lg text-sm bg-gray-50 text-gray-700">
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-gray-500 mb-1">주소</label>
|
||||
<input type="text" readonly value="{{ $tenantInfo['address'] ?? '' }}"
|
||||
class="w-full px-3 py-2 border border-gray-200 rounded-lg text-sm bg-gray-50 text-gray-700">
|
||||
</div>
|
||||
<div class="flex gap-4 flex-wrap">
|
||||
<div style="flex: 1 1 180px; max-width: 220px;">
|
||||
<label class="block text-xs font-medium text-gray-500 mb-1">전화</label>
|
||||
<input type="text" id="ol-phone-input" value="{{ $tenantInfo['phone'] ?? '' }}" placeholder="전화번호"
|
||||
class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500">
|
||||
</div>
|
||||
<div style="flex: 1 1 180px; max-width: 220px;">
|
||||
<label class="block text-xs font-medium text-gray-500 mb-1">팩스</label>
|
||||
<input type="text" id="ol-fax-input" value="{{ $tenantInfo['fax'] ?? '' }}" placeholder="팩스번호"
|
||||
class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500">
|
||||
</div>
|
||||
<div style="flex: 1 1 220px;">
|
||||
<label class="block text-xs font-medium text-gray-500 mb-1">이메일</label>
|
||||
<input type="text" id="ol-email-input" value="{{ $tenantInfo['email'] ?? '' }}" placeholder="이메일"
|
||||
class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- 미리보기 버튼 --}}
|
||||
<div class="flex justify-end">
|
||||
<button type="button" onclick="openOfficialLetterPreview()"
|
||||
class="px-3 py-2 bg-indigo-50 text-indigo-600 hover:bg-indigo-100 border border-indigo-200 rounded-lg text-sm font-medium transition inline-flex items-center gap-1">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/>
|
||||
</svg>
|
||||
미리보기
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- 공문서 미리보기 모달 --}}
|
||||
<div id="official-letter-preview-modal" style="display: none;" class="fixed inset-0 z-50">
|
||||
<div class="absolute inset-0 bg-black/50" onclick="closeOfficialLetterPreview()"></div>
|
||||
<div class="relative flex items-center justify-center min-h-full p-4">
|
||||
<div class="bg-white rounded-xl shadow-2xl w-full overflow-hidden relative" style="max-width: 780px;">
|
||||
<div class="flex items-center justify-between px-5 py-3 border-b border-gray-200 bg-gray-50">
|
||||
<h3 class="text-base font-semibold text-gray-800">공문서 미리보기</h3>
|
||||
<div class="flex items-center gap-2">
|
||||
<button type="button" onclick="printOfficialLetterPreview()"
|
||||
class="px-3 py-1.5 bg-white border border-gray-300 hover:bg-gray-50 rounded-lg text-xs font-medium transition inline-flex items-center gap-1">
|
||||
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 17h2a2 2 0 002-2v-4a2 2 0 00-2-2H5a2 2 0 00-2 2v4a2 2 0 002 2h2m2 4h6a2 2 0 002-2v-4a2 2 0 00-2-2H9a2 2 0 00-2 2v4a2 2 0 002 2zm8-12V5a2 2 0 00-2-2H9a2 2 0 00-2 2v4h10z"/>
|
||||
</svg>
|
||||
인쇄
|
||||
</button>
|
||||
<button type="button" onclick="closeOfficialLetterPreview()"
|
||||
class="p-1 text-gray-400 hover:text-gray-600 transition">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="overflow-y-auto" style="max-height: 80vh;">
|
||||
<div id="official-letter-preview-content" style="padding: 40px 48px; font-family: 'Pretendard', 'Malgun Gothic', sans-serif;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,179 @@
|
||||
{{--
|
||||
공문서 읽기전용 렌더링
|
||||
Props:
|
||||
$content (array) - approvals.content JSON
|
||||
--}}
|
||||
<div class="space-y-4">
|
||||
{{-- 미리보기 버튼 --}}
|
||||
<div class="flex justify-end gap-2">
|
||||
<button type="button" onclick="openOfficialLetterShowPreview()"
|
||||
class="px-3 py-1.5 bg-indigo-50 text-indigo-600 hover:bg-indigo-100 border border-indigo-200 rounded-lg text-sm font-medium transition inline-flex items-center gap-1">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/>
|
||||
</svg>
|
||||
공문서 미리보기
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{{-- 문서 정보 --}}
|
||||
<div class="border border-gray-200 rounded-lg overflow-hidden">
|
||||
<div class="bg-gray-50 px-4 py-2 border-b border-gray-200">
|
||||
<h3 class="text-sm font-semibold text-gray-700">문서 정보</h3>
|
||||
</div>
|
||||
<div class="p-4">
|
||||
<div class="grid gap-3" style="grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));">
|
||||
<div>
|
||||
<span class="text-xs text-gray-500">문서번호</span>
|
||||
<div class="text-sm font-medium mt-0.5">{{ $content['doc_number'] ?? '-' }}</div>
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-xs text-gray-500">일자</span>
|
||||
<div class="text-sm font-medium mt-0.5">{{ $content['doc_date'] ?? '-' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- 수신/참조/제목 --}}
|
||||
<div class="border border-gray-200 rounded-lg overflow-hidden">
|
||||
<div class="bg-gray-50 px-4 py-2 border-b border-gray-200">
|
||||
<h3 class="text-sm font-semibold text-gray-700">수신 정보</h3>
|
||||
</div>
|
||||
<div class="p-4 space-y-2">
|
||||
<div class="grid gap-3" style="grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));">
|
||||
<div>
|
||||
<span class="text-xs text-gray-500">수신</span>
|
||||
<div class="text-sm font-medium mt-0.5">{{ $content['recipient'] ?? '-' }}</div>
|
||||
</div>
|
||||
@if(!empty($content['reference']))
|
||||
<div>
|
||||
<span class="text-xs text-gray-500">참조</span>
|
||||
<div class="text-sm font-medium mt-0.5">{{ $content['reference'] }}</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-xs text-gray-500">제목</span>
|
||||
<div class="text-sm font-semibold mt-0.5">{{ $content['subject'] ?? '-' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- 본문 --}}
|
||||
<div class="border border-gray-200 rounded-lg overflow-hidden">
|
||||
<div class="bg-gray-50 px-4 py-2 border-b border-gray-200">
|
||||
<h3 class="text-sm font-semibold text-gray-700">본문</h3>
|
||||
</div>
|
||||
<div class="p-4">
|
||||
<div class="text-sm text-gray-700 whitespace-pre-wrap leading-relaxed">{{ $content['body'] ?? '' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- 붙임 --}}
|
||||
@if(!empty($content['attachments_desc']))
|
||||
<div class="border border-gray-200 rounded-lg overflow-hidden">
|
||||
<div class="bg-gray-50 px-4 py-2 border-b border-gray-200">
|
||||
<h3 class="text-sm font-semibold text-gray-700">붙임</h3>
|
||||
</div>
|
||||
<div class="p-4">
|
||||
<div class="text-sm text-gray-700 whitespace-pre-wrap">{{ $content['attachments_desc'] }}</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- 발신자 --}}
|
||||
<div class="border border-gray-200 rounded-lg overflow-hidden">
|
||||
<div class="bg-gray-50 px-4 py-2 border-b border-gray-200">
|
||||
<h3 class="text-sm font-semibold text-gray-700">발신자</h3>
|
||||
</div>
|
||||
<div class="p-4">
|
||||
<div class="grid gap-3" style="grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));">
|
||||
<div>
|
||||
<span class="text-xs text-gray-500">상호</span>
|
||||
<div class="text-sm font-medium mt-0.5">{{ $content['company_name'] ?? '-' }}</div>
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-xs text-gray-500">대표이사</span>
|
||||
<div class="text-sm font-medium mt-0.5">{{ $content['ceo_name'] ?? '-' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- 미리보기 모달 --}}
|
||||
<div id="official-letter-show-preview-modal" style="display: none;" class="fixed inset-0 z-50">
|
||||
<div class="absolute inset-0 bg-black/50" onclick="closeOfficialLetterShowPreview()"></div>
|
||||
<div class="relative flex items-center justify-center min-h-full p-4">
|
||||
<div class="bg-white rounded-xl shadow-2xl w-full overflow-hidden relative" style="max-width: 780px;">
|
||||
<div class="flex items-center justify-between px-5 py-3 border-b border-gray-200 bg-gray-50">
|
||||
<h3 class="text-base font-semibold text-gray-800">공문서 미리보기</h3>
|
||||
<div class="flex items-center gap-2">
|
||||
<button type="button" onclick="printOfficialLetterShowPreview()"
|
||||
class="px-3 py-1.5 bg-white border border-gray-300 hover:bg-gray-50 rounded-lg text-xs font-medium transition inline-flex items-center gap-1">
|
||||
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 17h2a2 2 0 002-2v-4a2 2 0 00-2-2H5a2 2 0 00-2 2v4a2 2 0 002 2h2m2 4h6a2 2 0 002-2v-4a2 2 0 00-2-2H9a2 2 0 00-2 2v4a2 2 0 002 2zm8-12V5a2 2 0 00-2-2H9a2 2 0 00-2 2v4h10z"/>
|
||||
</svg>
|
||||
인쇄
|
||||
</button>
|
||||
<button type="button" onclick="closeOfficialLetterShowPreview()"
|
||||
class="p-1 text-gray-400 hover:text-gray-600 transition">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="overflow-y-auto" style="max-height: 80vh;">
|
||||
<div id="official-letter-show-preview-content" style="padding: 40px 48px; font-family: 'Pretendard', 'Malgun Gothic', sans-serif;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
function buildOfficialLetterShowPreviewHtml(data) {
|
||||
const bodyHtml = (data.body || '').replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/\n/g, '<br>');
|
||||
const attachHtml = (data.attachments_desc || '').replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/\n/g, '<br>');
|
||||
return '<div style="text-align:center;margin-bottom:32px;">' +
|
||||
'<h1 style="font-size:22px;font-weight:800;margin:0;">' + (data.company_name || '') + '</h1>' +
|
||||
'</div>' +
|
||||
'<div style="border-bottom:2px solid #333;padding-bottom:16px;margin-bottom:24px;font-size:13px;line-height:2;">' +
|
||||
'<div><span style="display:inline-block;width:80px;letter-spacing:12px;font-weight:600;">문서번호</span> : ' + (data.doc_number || '') + '</div>' +
|
||||
'<div><span style="display:inline-block;width:80px;letter-spacing:18px;font-weight:600;">일자</span> : ' + (data.doc_date || '') + '</div>' +
|
||||
'<div><span style="display:inline-block;width:80px;letter-spacing:18px;font-weight:600;">수신</span> : ' + (data.recipient || '') + '</div>' +
|
||||
(data.reference ? '<div><span style="display:inline-block;width:80px;letter-spacing:18px;font-weight:600;">참조</span> : ' + data.reference + '</div>' : '') +
|
||||
'<div><span style="display:inline-block;width:80px;letter-spacing:18px;font-weight:600;">제목</span> : <strong>' + (data.subject || '') + '</strong></div>' +
|
||||
'</div>' +
|
||||
'<div style="font-size:13px;line-height:1.8;margin-bottom:24px;min-height:200px;">' + bodyHtml + '</div>' +
|
||||
(data.attachments_desc ? '<div style="font-size:13px;line-height:1.6;margin-bottom:24px;padding-top:12px;border-top:1px solid #ddd;"><strong>붙임 :</strong><br>' + attachHtml + '</div>' : '') +
|
||||
'<div style="text-align:center;margin:40px 0 16px;font-size:14px;">' +
|
||||
'<span>' + (data.company_name || '') + '</span> ' +
|
||||
'<span>대표이사 ' + (data.ceo_name || '') + '</span> ' +
|
||||
'<span style="color:#999;">[직인날인]</span>' +
|
||||
'</div>' +
|
||||
'<div style="border-top:1px solid #ccc;padding-top:8px;font-size:10px;color:#888;line-height:1.6;">' +
|
||||
(data.company_address ? '<div>' + data.company_address + '</div>' : '') +
|
||||
'<div>' +
|
||||
(data.phone ? '전화 ' + data.phone : '') +
|
||||
(data.fax ? ' / 팩스 ' + data.fax : '') +
|
||||
(data.email ? ' / 이메일 ' + data.email : '') +
|
||||
'</div></div>';
|
||||
}
|
||||
function openOfficialLetterShowPreview() {
|
||||
const data = @json($content);
|
||||
document.getElementById('official-letter-show-preview-content').innerHTML = buildOfficialLetterShowPreviewHtml(data);
|
||||
document.getElementById('official-letter-show-preview-modal').style.display = '';
|
||||
}
|
||||
function closeOfficialLetterShowPreview() { document.getElementById('official-letter-show-preview-modal').style.display = 'none'; }
|
||||
function printOfficialLetterShowPreview() {
|
||||
const content = document.getElementById('official-letter-show-preview-content').innerHTML;
|
||||
const win = window.open('', '_blank');
|
||||
win.document.write('<html><head><title>공문서</title><style>body{font-family:"Pretendard","Malgun Gothic",sans-serif;padding:40px 48px;}@media print{body{padding:20px 30px;}}</style></head><body>' + content + '</body></html>');
|
||||
win.document.close();
|
||||
win.print();
|
||||
}
|
||||
</script>
|
||||
@endpush
|
||||
@@ -121,6 +121,8 @@ class="bg-gray-600 hover:bg-gray-700 text-white px-4 py-2 rounded-lg transition
|
||||
@include('approvals.partials._board-minutes-show', ['content' => $approval->content])
|
||||
@elseif(!empty($approval->content) && $approval->form?->code === 'quotation')
|
||||
@include('approvals.partials._quotation-show', ['content' => $approval->content])
|
||||
@elseif(!empty($approval->content) && $approval->form?->code === 'official_letter')
|
||||
@include('approvals.partials._official-letter-show', ['content' => $approval->content])
|
||||
@elseif($approval->body && preg_match('/<[a-z][\s\S]*>/i', $approval->body))
|
||||
<div class="prose prose-sm max-w-none text-gray-700">
|
||||
{!! strip_tags($approval->body, '<p><br><strong><b><em><i><u><s><del><h1><h2><h3><h4><h5><h6><ul><ol><li><blockquote><pre><code><a><span><div><table><thead><tbody><tr><th><td>') !!}
|
||||
|
||||
Reference in New Issue
Block a user