1174 lines
53 KiB
JavaScript
1174 lines
53 KiB
JavaScript
// 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 = `
|
|
<i class="el-icon ${iconClass}" style="font-size: 16px; color: ${iconColor}; margin-right: 10px;"></i>
|
|
<span style="font-size: 14px;">${message}</span>
|
|
`;
|
|
|
|
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 = '<i class="m-icon fa-plus button-icon fad fa-fw" style="font-size: 17px;"></i> Добавить получателя';
|
|
|
|
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 = '<i class="m-icon fa-search button-icon fad fa-fw" style="font-size: 17px;"></i> Найти';
|
|
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 = '<div style="text-align: center; padding: 15px;"><i class="el-icon-loading" style="font-size: 18px;"></i><p style="margin: 5px 0 0; color: #909399;">Поиск...</p></div>';
|
|
|
|
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.id === person.id);
|
|
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 = '<div style="text-align: center; padding: 15px; color: #909399;">Ничего не найдено</div>';
|
|
}
|
|
} catch (error) {
|
|
console.error('Ошибка поиска получателей:', error);
|
|
searchResults.innerHTML = '<div class="el-alert el-alert--error" style="margin: 5px 0;"><div class="el-alert__content"><span class="el-alert__title">Ошибка поиска</span><p class="el-alert__description">' + error.message + '</p></div></div>';
|
|
}
|
|
}
|
|
|
|
// Обработчик кнопки добавления получателя
|
|
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 = `
|
|
<span>
|
|
<i class="m-icon fa-file-signature button-icon fad" style="font-size: 16px;"></i>
|
|
<span class="m-button__text use-indent">Подписать электронно</span>
|
|
</span>
|
|
`;
|
|
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);
|
|
})(); |