// 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 = `
История статусов
${statusesHtml}
`; 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); })();