// documents.js
(function () {
// Глобальная переменная для хранения текущего уведомления
let currentMessageEl = null;
let messageTimeout = null;
// Функция для отображения сообщений
function showMessage(message, type = 'success', duration = 3000) {
// Удаляем предыдущее уведомление, если оно есть
if (currentMessageEl && currentMessageEl.parentNode) {
clearTimeout(messageTimeout);
document.body.removeChild(currentMessageEl);
currentMessageEl = null;
}
const messageEl = document.createElement('div');
messageEl.className = `el-message el-message--${type}`;
messageEl.style.cssText = `
position: fixed;
top: 20px;
left: 50%;
transform: translateX(-50%);
z-index: 9999;
min-width: 380px;
padding: 15px 15px 15px 20px;
border-radius: 4px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
background-color: ${type === 'success' ? '#f0f9eb' :
type === 'info' ? '#f4f4f5' : '#fef0f0'};
border: 1px solid ${type === 'success' ? '#e1f3d8' :
type === 'info' ? '#e9e9eb' : '#fde2e2'};
color: ${type === 'success' ? '#67c23a' :
type === 'info' ? '#909399' : '#f56c6c'};
display: flex;
align-items: center;
`;
const iconClass = type === 'success' ? 'el-icon-success' :
type === 'info' ? 'el-icon-info' : 'el-icon-error';
const iconColor = type === 'success' ? '#67c23a' :
type === 'info' ? '#909399' : '#f56c6c';
messageEl.innerHTML = `
${message}
`;
document.body.appendChild(messageEl);
currentMessageEl = messageEl;
// Скрываем через указанное время
messageTimeout = setTimeout(() => {
if (currentMessageEl === messageEl && messageEl.parentNode) {
document.body.removeChild(messageEl);
currentMessageEl = null;
}
}, duration);
}
// Функция отправки сообщения
function sendMessageToContent(type, payload) {
return new Promise((resolve, reject) => {
const timeoutId = setTimeout(() => {
reject(new Error('Таймаут ожидания ответа'));
}, 60000);
const messageHandler = (event) => {
if (
event.source !== window ||
!event.data ||
event.data.source !== 'medods-extension' ||
event.data.type !== `${type}Response`
) {
return;
}
window.removeEventListener('message', messageHandler);
clearTimeout(timeoutId);
resolve(event.data.payload);
};
window.addEventListener('message', messageHandler);
window.postMessage({
source: 'medods-extension',
type: type,
payload: payload,
}, '*');
});
}
// Функция форматирования даты
function formatDate(dateString) {
const date = new Date(dateString);
return date.toLocaleString('ru-RU', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
hour: '2-digit',
minute: '2-digit'
});
}
// Функция получения класса для статуса
function getStatusClass(category) {
switch (category) {
case 'completed':
return 'status-completed';
case 'processing':
return 'status-processing';
case 'error':
return 'status-error';
default:
return '';
}
}
// Функция получения иконки для статуса
function getStatusIcon(category) {
switch (category) {
case 'completed':
return 'el-icon-success';
case 'processing':
return 'el-icon-loading';
case 'error':
return 'el-icon-error';
default:
return 'el-icon-question';
}
}
// Функция создания модального окна со статусами
function createStatusesModal(singing) {
const modalWrapper = document.createElement('div');
modalWrapper.className = 'el-dialog__wrapper';
modalWrapper.style.cssText = 'z-index: 2001; position: fixed; top: 0; right: 0; bottom: 0; left: 0; overflow: auto;';
const backdrop = document.createElement('div');
backdrop.className = 'v-modal';
backdrop.style.cssText = 'z-index: 2001;';
backdrop.tabIndex = 0;
const sortedStatuses = [...singing.statuses].sort((a, b) => a.id - b.id);
const statusesHtml = sortedStatuses.map(status => `
${status.description}
${formatDate(status.created_at)}
`).join('');
modalWrapper.innerHTML = `
`;
document.body.appendChild(modalWrapper);
// Обработчики закрытия
const closeModal = () => modalWrapper.remove();
modalWrapper.querySelector('.el-dialog__headerbtn').addEventListener('click', closeModal);
modalWrapper.querySelector('.close-btn').addEventListener('click', closeModal);
modalWrapper.addEventListener('click', (e) => {
if (e.target === modalWrapper) closeModal();
});
// Закрытие по ESC
const escHandler = (e) => {
if (e.key === 'Escape') {
closeModal();
document.removeEventListener('keydown', escHandler);
}
};
document.addEventListener('keydown', escHandler);
}
// Функция создания модального окна подтверждения отзыва
function createRevokeConfirmModal(singing) {
return new Promise((resolve) => {
const modalWrapper = document.createElement('div');
modalWrapper.className = 'el-dialog__wrapper';
modalWrapper.style.cssText = 'z-index: 2001; position: fixed; top: 0; right: 0; bottom: 0; left: 0; overflow: auto;';
const backdrop = document.createElement('div');
backdrop.className = 'v-modal';
backdrop.style.cssText = 'z-index: 2001;';
backdrop.tabIndex = 0;
modalWrapper.innerHTML = `
Вы действительно хотите отозвать документы из подписания?
Документы: ${singing.documents.map(d => `№${d.number} ${d.title}`).join(', ')}
`;
document.body.appendChild(modalWrapper);
const closeModal = () => {
modalWrapper.remove();
resolve(false);
};
modalWrapper.querySelector('.el-dialog__headerbtn').addEventListener('click', closeModal);
modalWrapper.querySelector('.cancel-btn').addEventListener('click', closeModal);
modalWrapper.querySelector('.confirm-btn').addEventListener('click', () => {
modalWrapper.remove();
resolve(true);
});
modalWrapper.addEventListener('click', (e) => {
if (e.target === modalWrapper) closeModal();
});
// Закрытие по ESC
const escHandler = (e) => {
if (e.key === 'Escape') {
closeModal();
document.removeEventListener('keydown', escHandler);
}
};
document.addEventListener('keydown', escHandler);
});
}
// Функция отображения PDF
async function viewDocument(documentPath, title) {
try {
showMessage('Загрузка документа...', 'info', 1000);
const response = await sendMessageToContent('getDocument', { documentPath });
if (response.success && response.data) {
// Декодируем base64
const byteCharacters = atob(response.data);
const byteNumbers = new Array(byteCharacters.length);
for (let i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);
// Создаем blob
const blob = new Blob([byteArray], { type: 'application/pdf' });
// Создаем URL для blob
const url = window.URL.createObjectURL(blob);
// Пытаемся открыть в новой вкладке
const newWindow = window.open(url, '_blank');
// Если браузер заблокировал всплывающее окно
if (!newWindow) {
// Создаем ссылку и имитируем клик для скачивания
const link = document.createElement('a');
link.href = url;
link.download = 'document.pdf'; // Простое имя по умолчанию
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
showMessage('Разрешите всплывающие окна для просмотра PDF', 'warning');
}
// Очищаем URL через некоторое время
setTimeout(() => {
window.URL.revokeObjectURL(url);
}, 1000);
} else {
showMessage('Ошибка загрузки документа: ' + (response.message || 'Неизвестная ошибка'), 'error');
}
} catch (error) {
console.error('Ошибка просмотра документа:', error);
showMessage('Ошибка просмотра документа', 'error');
}
}
// Функция отзыва документов
async function revokeDocuments(singing, modalToClose) {
try {
// Сразу закрываем окно со списком документов
if (modalToClose && modalToClose.parentNode) {
modalToClose.remove();
}
// Проверяем права на отзыв
const checkResult = await sendMessageToContent('checkRevokePermission', {
userIdLpu: singing.userIdLpu
});
if (!checkResult.hasPermission) {
showMessage('Отозвать документы может только автор запроса', 'error');
return;
}
// Показываем подтверждение
const confirmed = await createRevokeConfirmModal(singing);
if (!confirmed) return;
showMessage('Отзыв документов...', 'info');
// Отправляем запрос на отзыв
const response = await sendMessageToContent('revokeDocuments', {
trackingId: singing.trackingId
});
if (response.success) {
showMessage('Документы успешно отозваны', 'success');
console.log('Документы успешно отозваны');
// Обновляем список документов
await prepareDocuments();
} else {
showMessage('Ошибка отзыва документов: ' + (response.message || 'Неизвестная ошибка'), 'error');
}
} catch (error) {
console.error('Ошибка отзыва документов:', error);
showMessage('Ошибка отзыва документов', 'error');
}
}
// Функция создания модального окна подтверждения повторной отправки
function createResendConfirmModal(singing) {
return new Promise((resolve) => {
const modalWrapper = document.createElement('div');
modalWrapper.className = 'el-dialog__wrapper';
modalWrapper.style.cssText = 'z-index: 2001; position: fixed; top: 0; right: 0; bottom: 0; left: 0; overflow: auto;';
const backdrop = document.createElement('div');
backdrop.className = 'v-modal';
backdrop.style.cssText = 'z-index: 2001;';
backdrop.tabIndex = 0;
modalWrapper.innerHTML = `
Вы действительно хотите повторно отправить документы на подписание?
Документы: ${singing.documents.map(d => `№${d.number} ${d.title}`).join(', ')}
`;
document.body.appendChild(modalWrapper);
const closeModal = () => {
modalWrapper.remove();
resolve(false);
};
modalWrapper.querySelector('.el-dialog__headerbtn').addEventListener('click', closeModal);
modalWrapper.querySelector('.cancel-btn').addEventListener('click', closeModal);
modalWrapper.querySelector('.confirm-btn').addEventListener('click', () => {
modalWrapper.remove();
resolve(true);
});
modalWrapper.addEventListener('click', (e) => {
if (e.target === modalWrapper) closeModal();
});
// Закрытие по ESC
const escHandler = (e) => {
if (e.key === 'Escape') {
closeModal();
document.removeEventListener('keydown', escHandler);
}
};
document.addEventListener('keydown', escHandler);
});
}
// Функция повторной отправки документов
async function resendDocuments(singing, modalToClose) {
try {
// Сразу закрываем окно со списком документов
if (modalToClose && modalToClose.parentNode) {
modalToClose.remove();
}
// Показываем подтверждение
const confirmed = await createResendConfirmModal(singing);
if (!confirmed) return;
showMessage('Повторная отправка документов...', 'info');
// Отправляем запрос на повторную отправку
const response = await sendMessageToContent('resendDocuments', {
id: singing.id,
trackingId: singing.trackingId,
idPatientMis: singing.idPatientMis
});
if (response.success) {
showMessage('Документы успешно отправлены повторно', 'success');
console.log('Документы успешно отправлены повторно');
// Обновляем список документов
await prepareDocuments();
} else {
showMessage('Ошибка повторной отправки документов: ' + (response.message || 'Неизвестная ошибка'), 'error');
}
} catch (error) {
console.error('Ошибка повторной отправки документов:', error);
showMessage('Ошибка повторной отправки документов', 'error');
}
}
// Функция создания таблицы документов
function createDocumentsTable(data) {
// Создаем модальное окно
const modalWrapper = document.createElement('div');
modalWrapper.className = 'el-dialog__wrapper';
modalWrapper.style.cssText = 'z-index: 2001; position: fixed; top: 0; right: 0; bottom: 0; left: 0; overflow: auto;';
const backdrop = document.createElement('div');
backdrop.className = 'v-modal';
backdrop.style.cssText = 'z-index: 2001;';
backdrop.tabIndex = 0;
if (!data || data.length === 0) {
modalWrapper.innerHTML = `
Нет документов, подписанных электронным способом
`;
} else {
const tableRows = data.map((singing, index) => {
// Определяем последний статус
const lastStatus = singing.statuses.reduce((max, status) =>
status.id > max.id ? status : max
, singing.statuses[0]);
const statusClass = getStatusClass(lastStatus.category);
const statusIcon = getStatusIcon(lastStatus.category);
const statusColor = lastStatus.category === 'completed' ? '#67C23A' :
lastStatus.category === 'processing' ? '#409EFF' :
lastStatus.category === 'error' ? '#F56C6C' : '#909399';
// Формируем ячейку с документами
const documentsHtml = singing.documents.map(doc => `
№${doc.number}
${doc.title}
${doc.storagePath ? `
` : ''}
`).join('');
return `
|
${formatDate(singing.created_at)}
${singing.practitioner}
|
${documentsHtml}
|
${lastStatus.description}
${formatDate(lastStatus.created_at)}
${singing.storagePath ? `
` : ''}
${singing.statuses.length > 1 ? `
` : ''}
${lastStatus.category === 'processing' ? `
` : ''}
|
`;
}).join('');
modalWrapper.innerHTML = `
| Оформлено |
Документы |
Статус |
${tableRows}
`;
}
document.body.appendChild(modalWrapper);
// Обработчики закрытия
const closeModal = () => modalWrapper.remove();
modalWrapper.querySelector('.el-dialog__headerbtn').addEventListener('click', closeModal);
modalWrapper.querySelector('.close-btn').addEventListener('click', closeModal);
modalWrapper.addEventListener('click', (e) => {
if (e.target === modalWrapper) closeModal();
});
// Закрытие по ESC
const escHandler = (e) => {
if (e.key === 'Escape') {
closeModal();
document.removeEventListener('keydown', escHandler);
}
};
document.addEventListener('keydown', escHandler);
// Обработчики для кнопок просмотра документов
if (data && data.length > 0) {
modalWrapper.querySelectorAll('.view-doc-btn').forEach(btn => {
btn.addEventListener('click', (e) => {
e.stopPropagation();
const path = btn.dataset.path;
const title = btn.dataset.title;
viewDocument(path, title);
});
});
modalWrapper.querySelectorAll('.view-main-doc-btn').forEach(btn => {
btn.addEventListener('click', (e) => {
e.stopPropagation();
const path = btn.dataset.path;
const title = btn.dataset.title;
viewDocument(path, title);
});
});
modalWrapper.querySelectorAll('.view-statuses-btn').forEach((btn) => {
btn.addEventListener('click', (e) => {
e.stopPropagation();
const index = btn.dataset.index;
createStatusesModal(data[index]);
});
});
modalWrapper.querySelectorAll('.revoke-btn').forEach((btn) => {
btn.addEventListener('click', (e) => {
e.stopPropagation();
const index = btn.dataset.index;
revokeDocuments(data[index], modalWrapper);
});
});
// Добавляем обработчики для кнопок повторной отправки
modalWrapper.querySelectorAll('.resend-btn').forEach((btn) => {
btn.addEventListener('click', (e) => {
e.stopPropagation();
const index = btn.dataset.index;
resendDocuments(data[index], modalWrapper);
});
});
}
}
// Основная функция подготовки документов
async function prepareDocuments() {
try {
showMessage('Загрузка документов...', 'info');
const response = await sendMessageToContent('prepareDocuments', {});
if (response.success) {
createDocumentsTable(response.data);
} else {
showMessage('Ошибка загрузки документов: ' + (response.message || 'Неизвестная ошибка'), 'error');
}
} catch (error) {
console.error('Ошибка подготовки документов:', error);
showMessage('Ошибка загрузки документов', 'error');
}
}
// Функция добавления кнопки
function addDocsBtn() {
const docsBtn = document.createElement('button');
docsBtn.className = 'el-button m-button el-button--small is-plain button-with-icon';
docsBtn.innerHTML = `
ЭДО
`;
const m_panel_footer = document.querySelector('.m-panel__footer');
if (m_panel_footer) {
m_panel_footer.insertAdjacentElement('afterbegin', docsBtn);
docsBtn.addEventListener('click', prepareDocuments);
} else {
// Если панель не найдена, пробуем еще раз через небольшую задержку
setTimeout(addDocsBtn, 1000);
}
}
// Добавляем стили для статусов
const style = document.createElement('style');
style.textContent = `
.status-history-item:hover {
background-color: #F5F7FA;
}
.el-button--text {
padding: 0;
border: none;
background: none;
}
.el-button--text:hover {
background: none;
opacity: 0.8;
}
.el-button--text:focus {
outline: none;
}
`;
document.head.appendChild(style);
// Запуск
setTimeout(() => {
addDocsBtn();
}, 500);
})();