// singing.js (function () { // Глобальная переменная для хранения текущего уведомления let currentMessageEl = null; let messageTimeout = null; let isSending = false; // Функция для отображения сообщений 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, }, '*'); }); } // Обработчик для финальных результатов от background function setupBackgroundResultHandler() { window.addEventListener('message', (event) => { if ( event.source !== window || !event.data || event.data.source !== 'medods-extension' || event.data.type !== 'backgroundProcessingResult' ) { return; } console.log('Получен финальный результат от background:', event.data); const result = event.data.payload; if (result.success) { showMessage(`Успешно отправлено документов: ${result.sentCount || 1}`, 'success'); } else { const errorMessage = result.message || 'Неизвестная ошибка'; showMessage(`Ошибка: ${errorMessage}`, 'error'); } }); } // Проверка совместимости документов function checkDocumentsCompatibility(documents) { if (documents.length === 0) { return { compatible: true, type: null }; } let hasIds = false; let hasNonIds = false; for (const doc of documents) { if (doc.title.includes('ИДС')) { hasIds = true; } else { hasNonIds = true; } if (hasIds && hasNonIds) { return { compatible: false, type: 'error', message: 'Нельзя одновременно отправлять документы требующие для подписания авторизацию ЕСИА (ГосУслуги) - "ИДС" и другие документы. Выберите документы одного типа.' }; } } const docType = hasIds ? 'через ЕСИА (ГосУслуги)' : 'ПЭП'; return { compatible: true, type: docType }; } // Функция получения выбранных документов function getSelectedDocuments() { const table = document.querySelector('.m-table.m-table-generator.m-si-generator__table'); if (!table) return []; const checkboxes = table.querySelectorAll('.el-checkbox__original'); const selectedDocs = []; checkboxes.forEach((checkbox) => { if (checkbox.checked) { const row = checkbox.closest('.m-table-row'); if (row) { const rowData = {}; const numberCell = row.querySelector('.col__number .m-table-row-cell__struct'); const titleCell = row.querySelector('.col__title .m-table-row-cell__struct'); if (numberCell) rowData.number = numberCell.textContent.trim(); if (titleCell) rowData.title = titleCell.textContent.trim(); if (rowData.number && rowData.title) { selectedDocs.push(rowData); } } } }); return selectedDocs; } // Функция проверки ограничения в 5 документов function validateDocumentCount(documents) { if (documents.length === 0) { return { valid: false, message: 'Выберите хотя бы один документ для отправки' }; } if (documents.length > 5) { return { valid: false, message: 'Нельзя отправить более 5 документов за раз' }; } return { valid: true, message: '' }; } // Функция создания модального окна предварительной проверки function showPreSingingDialog(documents, preSingingData) { // Проверяем данные const signature = preSingingData.signature || {}; const inProgress = preSingingData.inProgress || []; const complete = preSingingData.complete || []; const daysRemainingControl = preSingingData.daysRemainingControl || 30; const deliveryType = preSingingData.deliveryType || ["sms"]; const multipleSending = preSingingData.multipleSending || false; // Массив получателей (только если multipleSending = true) let recipients = []; // Маппинг типов доставки на отображаемые названия const deliveryTypeLabels = { 'sms': 'СМС-сообщение', 'max': 'МАХ-сообщение', 'mila': 'Mila-сообщение', 'goskey': 'Goskey-сообщение' }; // Определяем тип документов const hasIds = documents.some(doc => doc.title.includes('ИДС')); // Проверяем соответствие типов let typeMatch = false; if (signature.type === 'eSignature' && hasIds) { typeMatch = true; } else if (signature.type === 'eAttorney' && !hasIds) { typeMatch = true; } // Проверяем срок действия let expirationInfo = ''; let expirationFormatted = ''; let expirationClass = 'info'; let expirationTitle = ''; let isExpired = false; let daysRemaining = 0; if (signature.expiration) { const expirationDate = new Date(signature.expiration); const today = new Date(); const timeDiff = expirationDate - today; daysRemaining = Math.ceil(timeDiff / (1000 * 3600 * 24)); // Форматируем дату const dateFormatter = new Intl.DateTimeFormat('ru-RU', { day: 'numeric', month: 'long', year: 'numeric' }); expirationFormatted = dateFormatter.format(expirationDate); expirationInfo = expirationFormatted; // Определяем тип для заголовка if (signature.type === 'eSignature') { expirationTitle = 'Электронная подпись'; } else if (signature.type === 'eAttorney') { expirationTitle = 'Электронная доверенность'; } if (daysRemaining < 0) { expirationClass = 'danger'; isExpired = true; } else if (daysRemaining < daysRemainingControl) { expirationClass = 'warning'; } else { expirationClass = 'success'; } } // Определяем статусы документов const documentsWithStatus = documents.map(doc => { const docNumber = parseInt(doc.number, 10); let status = 'ready'; let statusClass = 'success'; let statusIcon = 'el-icon-success'; if (inProgress.includes(docNumber)) { status = 'in-progress'; statusClass = 'info'; statusIcon = 'el-icon-time'; } else if (complete.includes(docNumber)) { status = 'complete'; statusClass = 'primary'; statusIcon = 'el-icon-check'; } return { ...doc, status, statusClass, statusIcon, isEsia: doc.title.includes('ИДС') }; }); // Определяем, можно ли отправлять let canSend = typeMatch && !isExpired; // Проверяем статусы документов const hasInProgress = documentsWithStatus.some(doc => doc.status === 'in-progress'); const hasComplete = documentsWithStatus.some(doc => doc.status === 'complete'); if (hasInProgress || hasComplete) { canSend = false; } // Если нет signature - нельзя отправлять if (!signature.type) { canSend = false; } // Если типы не соответствуют if (!typeMatch && signature.type) { canSend = false; } // Создаем элементы модального окна напрямую 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 dialog = document.createElement('div'); dialog.className = 'el-dialog'; dialog.style.cssText = 'margin-top: 15vh; width: 750px; z-index: 2002; position: relative; margin: 15vh auto 50px; background: #fff; border-radius: 4px; box-shadow: 0 2px 12px 0 rgba(0,0,0,.1);'; // Создаем заголовок const dialogHeader = document.createElement('div'); dialogHeader.className = 'el-dialog__header'; const dialogTitle = document.createElement('span'); dialogTitle.className = 'el-dialog__title'; dialogTitle.style.cssText = 'line-height: 24px; font-size: 18px; color: #303133;'; dialogTitle.textContent = 'Подготовка документов к отправке для электронного подписания'; const closeButton = document.createElement('button'); closeButton.type = 'button'; closeButton.className = 'el-dialog__headerbtn'; closeButton.style.cssText = 'position: absolute; top: 20px; right: 20px; padding: 0; background: 0 0; border: none; outline: 0; cursor: pointer; font-size: 16px;'; const closeIcon = document.createElement('i'); closeIcon.className = 'el-dialog__close el-icon el-icon-close'; closeButton.appendChild(closeIcon); dialogHeader.appendChild(dialogTitle); dialogHeader.appendChild(closeButton); // Создаем тело диалога const dialogBody = document.createElement('div'); dialogBody.className = 'el-dialog__body'; dialogBody.style.cssText = 'padding: 15px; padding-bottom: 0; color: #606266; font-size: 14px;'; // Создаем контент const dialogContent = document.createElement('div'); dialogContent.className = 'dialog-content'; // Предупреждения (ближе к заголовку) if (!canSend) { const warningsSection = document.createElement('div'); warningsSection.className = 'warnings-section'; warningsSection.style.cssText = 'margin-bottom: 15px;'; // Собираем все предупреждения в один массив const warnings = []; if (!signature.type) { warnings.push({ type: 'error', title: 'Нет прав на отправку', message: 'У вас нет прав на электронное подписание документов' }); } if (daysRemaining < 0) { let typeName = 'документа'; if (signature.type === 'eSignature') { typeName = 'электронной подписи'; } else if (signature.type === 'eAttorney') { typeName = 'электронной доверенности'; } warnings.push({ type: 'error', title: 'Срок действия истек', message: `Срок действия ${typeName} истек ${Math.abs(daysRemaining)} дней назад` }); } if (!typeMatch && signature.type) { const availableType = signature.type === 'eSignature' ? 'электронная подпись' : 'электронная доверенность'; const neededType = hasIds ? 'электронная подпись' : 'электронная доверенность'; warnings.push({ type: 'warning', title: 'Несоответствие типов документов', message: `Для подписания подготовлены документы для которых требуется "${neededType}", но у вас доступна "${availableType}"` }); } if (hasInProgress || hasComplete) { warnings.push({ type: 'warning', title: 'Невозможно отправить документы', message: 'Среди выбранных документов есть уже подписанные (✔️) или направленные для подписания (🕒)' }); } // Отображаем все предупреждения warnings.forEach(warning => { const warningEl = document.createElement('div'); warningEl.className = `el-alert el-alert--${warning.type} is-light`; warningEl.style.cssText = 'margin-bottom: 8px; padding: 8px; border-radius: 4px;'; const warningContent = document.createElement('div'); warningContent.className = 'el-alert__content'; const warningTitle = document.createElement('span'); warningTitle.className = 'el-alert__title'; warningTitle.style.cssText = 'font-size: 13px; color: ' + (warning.type === 'error' ? '#f56c6c' : '#e6a23c') + ';'; warningTitle.textContent = warning.title; const warningDesc = document.createElement('p'); warningDesc.className = 'el-alert__description'; warningDesc.style.cssText = 'font-size: 12px; margin-top: 3px; color: ' + (warning.type === 'error' ? '#f56c6c' : '#e6a23c') + ';'; warningDesc.textContent = warning.message; warningContent.appendChild(warningTitle); warningContent.appendChild(warningDesc); warningEl.appendChild(warningContent); warningsSection.appendChild(warningEl); }); dialogContent.appendChild(warningsSection); } // Список документов const documentsSection = document.createElement('div'); documentsSection.className = 'documents-section'; const documentsTitle = document.createElement('h4'); documentsTitle.style.cssText = 'margin: 0 0 10px 0; color: #303133; font-size: 16px;'; documentsTitle.textContent = 'Список документов:'; const documentsList = document.createElement('div'); documentsList.style.cssText = 'border: 1px solid #EBEEF5; border-radius: 4px; padding: 0 10px; max-height: 200px; overflow-y: auto;'; // Добавляем документы в список documentsWithStatus.forEach((doc, index) => { const docItem = document.createElement('div'); // Для последнего элемента не добавляем нижнюю границу if (index === documentsWithStatus.length - 1) { docItem.style.cssText = 'display: flex; align-items: center; padding: 8px 0;'; } else { docItem.style.cssText = 'display: flex; align-items: center; padding: 8px 0; border-bottom: 1px solid #F0F0F0;'; } // Статус (иконка слева) const docStatusIcon = document.createElement('div'); docStatusIcon.style.cssText = 'flex: 0 0 24px; margin-right: 8px;'; const statusIcon = document.createElement('i'); statusIcon.className = doc.statusIcon; statusIcon.style.cssText = `font-size: 16px; color: ${doc.status === 'ready' ? '#67c23a' : doc.status === 'in-progress' ? '#909399' : '#409eff' };`; docStatusIcon.appendChild(statusIcon); // Номер документа (стилизован в зависимости от статуса) const docNumber = document.createElement('div'); docNumber.style.cssText = 'flex: 0 0 60px;'; const numberTag = document.createElement('span'); if (doc.status === 'ready') { numberTag.className = 'el-tag el-tag--success el-tag--small'; numberTag.style.cssText = 'display: inline-block; padding: 0 7px; height: 24px; line-height: 22px; font-size: 12px; border-width: 1px; border-style: solid; border-radius: 4px; box-sizing: border-box; white-space: nowrap;'; } else if (doc.status === 'in-progress') { numberTag.className = 'el-tag el-tag--info el-tag--small'; numberTag.style.cssText = 'display: inline-block; padding: 0 7px; height: 24px; line-height: 22px; font-size: 12px; border-width: 1px; border-style: solid; border-radius: 4px; box-sizing: border-box; white-space: nowrap;'; } else { numberTag.className = 'el-tag el-tag--primary el-tag--small'; numberTag.style.cssText = 'display: inline-block; padding: 0 7px; height: 24px; line-height: 22px; font-size: 12px; border-width: 1px; border-style: solid; border-radius: 4px; box-sizing: border-box; white-space: nowrap;'; } numberTag.textContent = `№${doc.number}`; docNumber.appendChild(numberTag); // Название документа (занимает все оставшееся пространство) const docTitle = document.createElement('div'); docTitle.style.cssText = 'flex: 1; color: #606266; font-size: 13px; padding: 0 10px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;'; docTitle.textContent = doc.title; docItem.appendChild(docStatusIcon); docItem.appendChild(docNumber); docItem.appendChild(docTitle); documentsList.appendChild(docItem); }); documentsSection.appendChild(documentsTitle); documentsSection.appendChild(documentsList); dialogContent.appendChild(documentsSection); // ========== НАЧАЛО: Блок дополнительных получателей (только при multipleSending=true) ========== if (multipleSending) { // Функция рендеринга блока получателей (будет вызываться при изменениях) function renderRecipientsBlock() { // Удаляем старый контейнер, если он есть const oldContainer = dialogContent.querySelector('.recipients-container'); if (oldContainer) { oldContainer.remove(); } // Если получателей нет - не отображаем блок if (recipients.length === 0) { return; } const recipientsContainer = document.createElement('div'); recipientsContainer.className = 'recipients-container'; recipientsContainer.style.cssText = 'margin-top: 15px; margin-bottom: 0px;'; // Заголовок с количеством получателей const recipientsHeader = document.createElement('div'); recipientsHeader.style.cssText = 'display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;'; const recipientsTitle = document.createElement('h4'); recipientsTitle.style.cssText = 'margin: 0; color: #303133; font-size: 14px; font-weight: 500;'; recipientsTitle.textContent = 'Дополнительные получатели:'; const recipientsCount = document.createElement('span'); recipientsCount.className = 'el-tag el-tag--info el-tag--mini'; recipientsCount.style.cssText = 'margin-left: 6px;'; recipientsCount.textContent = recipients.length; const titleWrapper = document.createElement('div'); titleWrapper.style.cssText = 'display: flex; align-items: center;'; titleWrapper.appendChild(recipientsTitle); titleWrapper.appendChild(recipientsCount); recipientsHeader.appendChild(titleWrapper); // Компактный список получателей const recipientsList = document.createElement('div'); recipientsList.className = 'recipients-list'; recipientsList.style.cssText = 'display: flex; flex-wrap: wrap; gap: 6px; margin-top: 5px;'; recipients.forEach((recipient, index) => { const recipientTag = document.createElement('span'); recipientTag.className = 'el-tag el-tag--info el-tag--small'; recipientTag.style.cssText = 'display: inline-flex; align-items: center; height: 24px; line-height: 22px; padding: 0 8px; margin-right: 4px; margin-bottom: 4px;'; const nameSpan = document.createElement('span'); nameSpan.textContent = `${recipient.surname} ${recipient.name.charAt(0)}. ${recipient.secondName ? recipient.secondName.charAt(0) + '.' : ''}`; const closeIcon = document.createElement('i'); closeIcon.className = 'el-icon-close'; closeIcon.style.cssText = 'margin-left: 4px; cursor: pointer; font-size: 12px;'; closeIcon.addEventListener('click', (e) => { e.stopPropagation(); if (window.confirm(`Удалить получателя ${recipient.surname} ${recipient.name}?`)) { recipients.splice(index, 1); renderRecipientsBlock(); // Перерисовываем блок } }); recipientTag.appendChild(nameSpan); recipientTag.appendChild(closeIcon); recipientsList.appendChild(recipientTag); }); recipientsContainer.appendChild(recipientsHeader); recipientsContainer.appendChild(recipientsList); // Вставляем после списка документов dialogContent.insertBefore(recipientsContainer, documentsSection.nextSibling); } // Кнопка добавления получателя const addButtonContainer = document.createElement('div'); addButtonContainer.style.cssText = 'margin: 10px 0;'; const addRecipientBtn = document.createElement('button'); addRecipientBtn.type = 'button'; addRecipientBtn.className = 'el-button m-button el-button--success el-button--small is-plain button-with-icon'; if (!canSend) { addRecipientBtn.classList.add('is-disabled'); addRecipientBtn.disabled = true; } addRecipientBtn.innerHTML = ' Добавить получателя'; addButtonContainer.appendChild(addRecipientBtn); dialogContent.appendChild(addButtonContainer); // Контейнер для аккордеона (поиск получателей) const searchAccordion = document.createElement('div'); searchAccordion.className = 'search-accordion'; searchAccordion.style.cssText = 'margin-top: 10px; display: none;'; const searchCard = document.createElement('div'); searchCard.className = 'el-card'; searchCard.style.cssText = 'border: 1px solid #EBEEF5; border-radius: 4px;'; const searchBody = document.createElement('div'); searchBody.className = 'el-card__body'; searchBody.style.cssText = 'padding: 12px;'; // Поле ввода номера телефона и кнопка поиска const searchRow = document.createElement('div'); searchRow.style.cssText = 'display: flex; gap: 8px; margin-bottom: 10px;'; const phoneInput = document.createElement('input'); phoneInput.type = 'text'; phoneInput.placeholder = 'Введите номер телефона'; phoneInput.className = 'el-input__inner'; phoneInput.style.cssText = 'flex: 1; height: 28px; line-height: 28px; border: 1px solid #DCDFE6; border-radius: 4px; padding: 0 8px; font-size: 12px;'; const searchBtn = document.createElement('button'); searchBtn.type = 'button'; searchBtn.className = 'el-button m-button el-button--primary el-button--small is-plain button-with-icon'; searchBtn.innerHTML = ' Найти'; searchBtn.style.cssText = 'height: 28px;'; searchRow.appendChild(phoneInput); searchRow.appendChild(searchBtn); // Контейнер для результатов поиска const searchResults = document.createElement('div'); searchResults.className = 'search-results'; searchResults.style.cssText = 'max-height: 150px; overflow-y: auto; font-size: 12px;'; // Кнопка отмены const cancelSearchBtn = document.createElement('button'); cancelSearchBtn.type = 'button'; cancelSearchBtn.className = 'el-button m-button el-button--small is-plain button-with-icon cancel-button'; const cancelSearchIcon = document.createElement('i'); cancelSearchIcon.className = 'm-icon fa-times button-icon fad'; cancelSearchIcon.style.cssText = 'font-size: 14px; margin-right: 6px;'; const cancelSearchText = document.createElement('span'); cancelSearchText.textContent = 'Отмена'; cancelSearchBtn.appendChild(cancelSearchIcon); cancelSearchBtn.appendChild(cancelSearchText); searchBody.appendChild(searchRow); searchBody.appendChild(searchResults); searchBody.appendChild(cancelSearchBtn); searchCard.appendChild(searchBody); searchAccordion.appendChild(searchCard); dialogContent.appendChild(searchAccordion); // Функция поиска получателей по номеру телефона async function searchRecipients(phone) { if (!phone || phone.trim() === '') { showMessage('Введите номер телефона', 'info'); return; } // Показываем загрузку searchResults.innerHTML = '

Поиск...

'; try { const result = await sendMessageToContent('searchRecipients', { phone: phone.trim() }); searchResults.innerHTML = ''; if (result && result.success && result.data && result.data.length > 0) { result.data.forEach(person => { const personItem = document.createElement('div'); personItem.style.cssText = 'display: flex; justify-content: space-between; align-items: center; padding: 6px 0; border-bottom: 1px solid #F0F0F0;'; const personInfo = document.createElement('div'); personInfo.style.cssText = 'flex: 1;'; const fullName = document.createElement('span'); fullName.style.cssText = 'font-size: 12px; color: #606266;'; fullName.textContent = `${person.surname} ${person.name} ${person.secondName || ''}`.trim(); const birthdate = document.createElement('span'); birthdate.style.cssText = 'font-size: 11px; color: #909399; margin-left: 6px;'; birthdate.textContent = person.birthDate ? `(${person.birthDate})` : ''; personInfo.appendChild(fullName); personInfo.appendChild(birthdate); const addBtn = document.createElement('button'); addBtn.type = 'button'; addBtn.className = 'el-button el-button--primary el-button--mini'; addBtn.innerHTML = 'Добавить'; addBtn.style.cssText = 'height: 24px; padding: 0 8px;'; // Проверяем, не добавлен ли уже этот получатель const alreadyAdded = recipients.some(r => r.idPatientMis === person.idPatientMis); if (alreadyAdded) { addBtn.disabled = true; addBtn.classList.add('is-disabled'); addBtn.innerHTML = 'Добавлен'; } addBtn.addEventListener('click', () => { if (!alreadyAdded) { recipients.push(person); renderRecipientsBlock(); // Обновляем отображение блока получателей // Закрываем аккордеон searchAccordion.style.display = 'none'; // Очищаем поле ввода и результаты phoneInput.value = ''; searchResults.innerHTML = ''; } }); personItem.appendChild(personInfo); personItem.appendChild(addBtn); searchResults.appendChild(personItem); }); } else { searchResults.innerHTML = '
Ничего не найдено
'; } } catch (error) { console.error('Ошибка поиска получателей:', error); searchResults.innerHTML = '
Ошибка поиска

' + error.message + '

'; } } // Обработчик кнопки добавления получателя addRecipientBtn.addEventListener('click', () => { if (searchAccordion.style.display === 'none' || searchAccordion.style.display === '') { searchAccordion.style.display = 'block'; phoneInput.value = ''; searchResults.innerHTML = ''; phoneInput.focus(); } else { searchAccordion.style.display = 'none'; } }); // Обработчик кнопки поиска searchBtn.addEventListener('click', () => { searchRecipients(phoneInput.value); }); // Поиск при нажатии Enter phoneInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') { searchRecipients(phoneInput.value); } }); // Обработчик кнопки отмены cancelSearchBtn.addEventListener('click', () => { searchAccordion.style.display = 'none'; phoneInput.value = ''; searchResults.innerHTML = ''; }); // Инициализация - рендерим блок (если есть получатели) renderRecipientsBlock(); } // ========== КОНЕЦ: Блок дополнительных получателей ========== dialogBody.appendChild(dialogContent); // Создаем футер const dialogFooter = document.createElement('div'); dialogFooter.className = 'el-dialog__footer'; dialogFooter.style.cssText = 'display: flex; justify-content: space-between; align-items: flex-start; padding: 15px; padding-top: 10px;'; // Левая часть футера (информация о сроке действия разрешения на подпись) const footerLeft = document.createElement('div'); footerLeft.style.cssText = 'flex: 1; display: flex; flex-direction: column; align-items: flex-start;'; if (signature.expiration && signature.type) { // Тип разрешения const typeContainer = document.createElement('div'); typeContainer.style.cssText = 'margin-bottom: 5px; margin-top: 10px;'; const typeText = document.createElement('span'); typeText.style.cssText = 'font-size: 12px; font-weight: 500; color: #606266;'; if (signature.type === 'eSignature') { typeText.textContent = 'Электронная подпись'; } else if (signature.type === 'eAttorney') { typeText.textContent = 'Электронная доверенность'; } typeContainer.appendChild(typeText); // Срок действия const expirationContainer = document.createElement('div'); expirationContainer.style.cssText = 'display: flex; align-items: center; flex-wrap: wrap; margin-bottom: 0px;'; const expirationLabel = document.createElement('span'); expirationLabel.style.cssText = 'font-size: 12px; color: #606266; margin-right: 5px;'; expirationLabel.textContent = 'Срок действия:'; const expirationDate = document.createElement('span'); expirationDate.style.cssText = 'font-size: 12px; color: #606266; margin-right: 8px;'; expirationDate.textContent = expirationFormatted; expirationContainer.appendChild(expirationLabel); expirationContainer.appendChild(expirationDate); // Остаточный срок (показываем справа от даты если недостаточно дней) if (daysRemaining >= 0 && daysRemaining < daysRemainingControl) { const warningIcon = document.createElement('i'); warningIcon.className = 'el-icon-warning'; warningIcon.style.cssText = 'font-size: 14px; color: #e6a23c; margin-left: 4px; margin-right: 4px;'; const remainingText = document.createElement('span'); remainingText.style.cssText = 'font-size: 12px; color: #e6a23c; font-weight: 500;'; remainingText.textContent = `осталось ${daysRemaining} ${getDaysText(daysRemaining)}`; typeContainer.appendChild(warningIcon); typeContainer.appendChild(remainingText); } footerLeft.appendChild(typeContainer); footerLeft.appendChild(expirationContainer); } // Вспомогательная функция для склонения дней function getDaysText(days) { const lastDigit = days % 10; const lastTwoDigits = days % 100; if (lastTwoDigits >= 11 && lastTwoDigits <= 19) { return 'дней'; } switch (lastDigit) { case 1: return 'день'; case 2: case 3: case 4: return 'дня'; default: return 'дней'; } } // Правая часть футера (выбор способа доставки и кнопки) const footerRight = document.createElement('div'); footerRight.style.cssText = 'display: flex; flex-direction: column; align-items: flex-end;'; const deliveryBlock = document.createElement('div'); deliveryBlock.style.cssText = 'margin-bottom: 10px; width: 210px;'; const selectWrapper = document.createElement('div'); selectWrapper.style.cssText = ` position: relative; border: 1px solid #dcdfe6; border-radius: 4px; background: ${canSend ? 'white' : '#f5f7fa'}; transition: all 0.2s; width: 100%; opacity: ${canSend ? '1' : '0.6'}; pointer-events: ${canSend ? 'auto' : 'none'}; `; // Создаем select элемент const deliverySelect = document.createElement('select'); deliverySelect.style.cssText = ` width: 100%; height: 25px; padding: 3px 24px 3px 8px; border: none; background: none; font-size: 12px; color: ${canSend ? '#606266' : '#c0c4cc'}; outline: none; cursor: ${canSend ? 'pointer' : 'not-allowed'}; appearance: none; -webkit-appearance: none; -moz-appearance: none; font-family: Verdana, Arial, Helvetica, sans-serif; `; const option = document.createElement('option'); option.value = null; option.textContent = "Способ отправки"; option.selected = true; option.disabled = true; deliverySelect.appendChild(option); // Добавляем опции выбора на основе доступных типов доставки deliveryType.forEach(type => { const option = document.createElement('option'); option.value = type; option.textContent = deliveryTypeLabels[type] || type; deliverySelect.appendChild(option); }); // Сохраняем выбранное значение let selectedDeliveryType = null; // Обработчик изменения выбора deliverySelect.addEventListener('change', (e) => { selectedDeliveryType = e.target.value; updateSendButtonState(); }); const selectArrow = document.createElement('span'); selectArrow.style.cssText = ` position: absolute; right: 8px; top: 50%; transform: translateY(-50%); pointer-events: none; color: ${canSend ? '#c0c4cc' : '#dcdfe6'}; font-size: 12px; `; selectArrow.textContent = '▼'; selectWrapper.appendChild(deliverySelect); selectWrapper.appendChild(selectArrow); deliveryBlock.appendChild(selectWrapper); // Блок кнопок const buttonsBlock = document.createElement('div'); buttonsBlock.className = 'dialog-footer'; buttonsBlock.style.cssText = 'display: flex; align-items: center; gap: 5px;'; // Кнопка "Отмена" - стиль как у кнопки на главной странице const cancelBtn = document.createElement('button'); cancelBtn.type = 'button'; cancelBtn.className = 'el-button m-button el-button--small is-plain button-with-icon cancel-button'; const cancelIcon = document.createElement('i'); cancelIcon.className = 'm-icon fa-times button-icon fad'; cancelIcon.style.cssText = 'font-size: 14px; margin-right: 6px;'; const cancelText = document.createElement('span'); cancelText.textContent = 'Отмена'; cancelBtn.appendChild(cancelIcon); cancelBtn.appendChild(cancelText); // Кнопка "Отправить документы" - стиль как у кнопки "подписать электронно" const sendBtn = document.createElement('button'); sendBtn.type = 'button'; // Функция обновления состояния кнопки отправки function updateSendButtonState() { const canSendWithDelivery = canSend && selectedDeliveryType !== null; if (canSendWithDelivery) { sendBtn.className = 'el-button m-button el-button--small is-plain send-button button-with-icon'; sendBtn.disabled = false; sendBtn.classList.remove('is-disabled'); } else { sendBtn.className = 'el-button m-button el-button--small is-plain send-button button-with-icon is-disabled'; sendBtn.disabled = true; sendBtn.classList.add('is-disabled'); } } const sendIcon = document.createElement('i'); sendIcon.className = 'm-icon fa-file-signature button-icon fad'; sendIcon.style.cssText = 'font-size: 14px; margin-right: 6px;'; const sendText = document.createElement('span'); sendText.textContent = 'Отправить документы'; sendBtn.appendChild(sendIcon); sendBtn.appendChild(sendText); // Устанавливаем начальное состояние кнопки updateSendButtonState(); buttonsBlock.appendChild(cancelBtn); buttonsBlock.appendChild(sendBtn); footerRight.appendChild(deliveryBlock); footerRight.appendChild(buttonsBlock); dialogFooter.appendChild(footerLeft); dialogFooter.appendChild(footerRight); // Собираем диалог dialog.appendChild(dialogHeader); dialog.appendChild(dialogBody); dialog.appendChild(dialogFooter); // Собираем модальное окно modalWrapper.appendChild(backdrop); modalWrapper.appendChild(dialog); document.body.appendChild(modalWrapper); // Обработчик отправки async function handleSend() { if (!canSend || selectedDeliveryType === null) return; closeModal(); // Отправляем документы на подписание с выбранным типом доставки try { const docNumbers = documents.map(doc => parseInt(doc.number, 10)); const result = await sendMessageToContent('sendDocuments', { docNumbers: docNumbers, deliveryType: selectedDeliveryType, recipients: recipients }); if (result && result.response) { if (result.response.status === 'processing') { showMessage('Документы приняты в обработку. Ожидайте результата...', 'info', 5000); } } } catch (error) { console.error('Ошибка при отправке:', error); showMessage(`Ошибка: ${error.message}`, 'error'); } } function closeModal() { if (modalWrapper.parentNode) { document.body.removeChild(modalWrapper); } } // Назначаем обработчики closeButton.addEventListener('click', closeModal); cancelBtn.addEventListener('click', closeModal); backdrop.addEventListener('click', closeModal); sendBtn.addEventListener('click', handleSend); // Закрытие по ESC const escHandler = (e) => { if (e.key === 'Escape') { closeModal(); document.removeEventListener('keydown', escHandler); } }; document.addEventListener('keydown', escHandler); // Удаляем обработчик ESC при закрытии const originalClose = closeModal; closeModal = () => { document.removeEventListener('keydown', escHandler); originalClose(); }; } // Функция предварительной проверки документов async function preSingingCheck(documents) { try { // Проверяем количество документов const countValidation = validateDocumentCount(documents); if (!countValidation.valid) { showMessage(countValidation.message, 'error'); return; } // Проверяем совместимость документов const compatibility = checkDocumentsCompatibility(documents); if (!compatibility.compatible) { showMessage(compatibility.message, 'error'); return; } // Отправляем запрос на предварительную проверку showMessage('Проверка документов...', 'info'); // Отправляем запрос в content.js, чтобы он собрал и отправил данные const result = await sendMessageToContent('preSingingCheck', { // Передаем только флаг о типе документов для совместимости docNumbers: documents.map(doc => parseInt(doc.number)) }); if (result && result.success && result.data) { // Показываем диалоговое окно с результатами проверки showPreSingingDialog(documents, result.data); } else { showMessage('Ошибка при проверке документов', 'error'); } } catch (error) { console.error('Ошибка предварительной проверки:', error); showMessage(`Ошибка: ${error.message}`, 'error'); } } // Функция добавления кнопки function addSendBtn() { const sendBtn = document.createElement('button'); sendBtn.className = 'el-button m-button el-button--small is-disabled is-plain button-with-icon'; sendBtn.innerHTML = ` Подписать электронно `; sendBtn.disabled = true; // Локальный обработчик для кнопки async function handleSendClick() { if (isSending) { console.log('Уже идет отправка, игнорируем клик'); return; } const selectedDocs = getSelectedDocuments(); // Запускаем предварительную проверку preSingingCheck(selectedDocs); } function checkSelectedDocuments() { return getSelectedDocuments().length > 0; } function updateButtonState() { const hasSelectedDocs = checkSelectedDocuments(); sendBtn.disabled = !hasSelectedDocs || isSending; if (sendBtn.disabled) { sendBtn.classList.add('is-disabled'); } else { sendBtn.classList.remove('is-disabled'); } } function setupTableObserver() { const table = document.querySelector('.m-table.m-table-generator.m-si-generator__table'); if (!table) { console.warn('Таблица документов не найдена'); return; } table.addEventListener('change', (e) => { if (e.target.classList.contains('el-checkbox__original')) { updateButtonState(); } }); table.addEventListener('click', (e) => { if (e.target.closest('.el-checkbox') || e.target.closest('.el-checkbox__inner')) { setTimeout(updateButtonState, 10); } }); setTimeout(updateButtonState, 100); } const m_panel_footer = document.querySelector('.m-panel__footer'); if (m_panel_footer) { m_panel_footer.prepend(sendBtn); sendBtn.addEventListener('click', handleSendClick); setupTableObserver(); updateButtonState(); setupBackgroundResultHandler(); } else { location.reload(); } } // Запуск setTimeout(() => { addSendBtn(); }, 500); })();