release
This commit is contained in:
+766
@@ -0,0 +1,766 @@
|
||||
// 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 = `
|
||||
<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,
|
||||
}, '*');
|
||||
});
|
||||
}
|
||||
|
||||
// Функция форматирования даты
|
||||
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 => `
|
||||
<div class="status-history-item" style="padding: 12px; border-bottom: 1px solid #ebeef5; display: flex; align-items: center; gap: 12px;">
|
||||
<i class="el-icon ${getStatusIcon(status.category)}" style="font-size: 16px; color: ${status.category === 'completed' ? '#67C23A' :
|
||||
status.category === 'processing' ? '#409EFF' :
|
||||
status.category === 'error' ? '#F56C6C' : '#909399'
|
||||
}; width: 20px;"></i>
|
||||
<div style="flex: 1;">
|
||||
<div style="font-weight: 500; color: #303133;">${status.description}</div>
|
||||
<div style="font-size: 12px; color: #909399; margin-top: 4px;">${formatDate(status.created_at)}</div>
|
||||
</div>
|
||||
</div>
|
||||
`).join('');
|
||||
|
||||
modalWrapper.innerHTML = `
|
||||
<div class="el-dialog" style="width: 500px; margin-top: 15vh;">
|
||||
<div class="el-dialog__header">
|
||||
<span class="el-dialog__title">История статусов</span>
|
||||
<button type="button" class="el-dialog__headerbtn" aria-label="Close">
|
||||
<i class="el-dialog__close el-icon el-icon-close"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="el-dialog__body" style="max-height: 400px; overflow-y: auto; padding: 0;">
|
||||
${statusesHtml}
|
||||
</div>
|
||||
<div class="el-dialog__footer" style="margin-top: 12px; width: 100%; text-align: right;">
|
||||
<button type="button" class="el-button m-button el-button--small is-plain button-with-icon close-btn">
|
||||
<i class="m-icon fa-times button-icon fad" style="font-size: 14px; margin-right: 6px;"></i>
|
||||
<span>Закрыть</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
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 = `
|
||||
<div class="el-dialog" style="width: 450px; margin-top: 25vh;">
|
||||
<div class="el-dialog__header">
|
||||
<span class="el-dialog__title">Подтверждение отзыва</span>
|
||||
<button type="button" class="el-dialog__headerbtn" aria-label="Close">
|
||||
<i class="el-dialog__close el-icon el-icon-close"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="el-dialog__body" style="padding: 20px;">
|
||||
<div style="display: flex; align-items: flex-start; gap: 12px;">
|
||||
<i class="el-icon el-icon-warning" style="font-size: 24px; color: #E6A23C;"></i>
|
||||
<div>
|
||||
<p style="margin: 0 0 10px 0; color: #606266;">Вы действительно хотите отозвать документы из подписания?</p>
|
||||
<div style="background: #f8f8f8; padding: 10px; border-radius: 4px; font-size: 13px; color: #E6A23C; border-left: 3px solid #E6A23C;">
|
||||
<i class="el-icon el-icon-document" style="margin-right: 6px;"></i>
|
||||
<strong>Документы:</strong> ${singing.documents.map(d => `№${d.number} ${d.title}`).join(', ')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="el-dialog__footer" style="display: flex; justify-content: space-between; align-items: center;">
|
||||
<button type="button" class="el-button m-button el-button--small is-plain button-with-icon cancel-btn"
|
||||
style="display: flex; align-items: center;">
|
||||
<i class="m-icon fa-times button-icon fad" style="font-size: 14px; margin-right: 6px;"></i>
|
||||
<span>Отмена</span>
|
||||
</button>
|
||||
<button type="button" class="el-button m-button el-button--small is-plain button-with-icon confirm-btn" style="color: #F56C6C; border-color: #F56C6C; display: flex; align-items: center;">
|
||||
<i class="m-icon fa-ban button-icon fad" style="font-size: 14px; margin-right: 6px;"></i>
|
||||
<span>Отозвать</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
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 = `
|
||||
<div class="el-dialog" style="width: 450px; margin-top: 25vh;">
|
||||
<div class="el-dialog__header">
|
||||
<span class="el-dialog__title">Подтверждение повторной отправки</span>
|
||||
<button type="button" class="el-dialog__headerbtn" aria-label="Close">
|
||||
<i class="el-dialog__close el-icon el-icon-close"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="el-dialog__body" style="padding: 20px;">
|
||||
<div style="display: flex; align-items: flex-start; gap: 12px;">
|
||||
<i class="el-icon el-icon-warning" style="font-size: 24px; color: #E6A23C;"></i>
|
||||
<div>
|
||||
<p style="margin: 0 0 10px 0; color: #606266;">Вы действительно хотите повторно отправить документы на подписание?</p>
|
||||
<div style="background: #f8f8f8; padding: 10px; border-radius: 4px; font-size: 13px; color: #E6A23C; border-left: 3px solid #E6A23C;">
|
||||
<i class="el-icon el-icon-document" style="margin-right: 6px;"></i>
|
||||
<strong>Документы:</strong> ${singing.documents.map(d => `№${d.number} ${d.title}`).join(', ')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="el-dialog__footer" style="display: flex; justify-content: space-between; align-items: center;">
|
||||
<button type="button" class="el-button m-button el-button--small is-plain button-with-icon cancel-btn"
|
||||
style="display: flex; align-items: center;">
|
||||
<i class="m-icon fa-times button-icon fad" style="font-size: 14px; margin-right: 6px;"></i>
|
||||
<span>Отмена</span>
|
||||
</button>
|
||||
<button type="button" class="el-button m-button el-button--small is-plain button-with-icon confirm-btn" style="color: #67C23A; border-color: #67C23A; display: flex; align-items: center;">
|
||||
<i class="m-icon fa-redo-alt button-icon fad" style="font-size: 14px; margin-right: 6px;"></i>
|
||||
<span>Отправить повторно</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
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 = `
|
||||
<div class="el-dialog" style="width: 500px; margin-top: 25vh;">
|
||||
<div class="el-dialog__header">
|
||||
<span class="el-dialog__title">Электронные документы</span>
|
||||
<button type="button" class="el-dialog__headerbtn" aria-label="Close">
|
||||
<i class="el-dialog__close el-icon el-icon-close"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="el-dialog__body" style="text-align: center; padding: 40px;">
|
||||
<i class="el-icon el-icon-document" style="font-size: 48px; color: #DCDFE6; margin-bottom: 16px;"></i>
|
||||
<p style="color: #909399; font-size: 16px; margin: 0;">Нет документов, подписанных электронным способом</p>
|
||||
</div>
|
||||
<div class="el-dialog__footer" style="text-align: right;">
|
||||
<button type="button" class="el-button m-button el-button--small is-plain button-with-icon close-btn">
|
||||
<i class="m-icon fa-times button-icon fad" style="font-size: 14px; margin-right: 6px;"></i>
|
||||
<span>Закрыть</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
} 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 => `
|
||||
<div style="display: flex; align-items: flex-start; gap: 8px; margin-bottom: ${singing.documents.length > 1 ? '8px' : '0'}; padding: 4px 0;">
|
||||
<div style="flex: 1; word-break: break-word; color: #606266;">
|
||||
<span style="font-weight: 500; color: #303133;">№${doc.number}</span>
|
||||
<span style="margin-left: 4px;">${doc.title}</span>
|
||||
</div>
|
||||
${doc.storagePath ? `
|
||||
<button class="el-button el-button--text view-doc-btn"
|
||||
data-title="${doc.title}"
|
||||
data-path="${doc.storagePath}"
|
||||
style="padding: 0 4px; min-height: auto; border: none;">
|
||||
<i class="el-icon el-icon-view" style="color: #409EFF; font-size: 16px;"></i>
|
||||
</button>
|
||||
` : ''}
|
||||
</div>
|
||||
`).join('');
|
||||
|
||||
return `
|
||||
<tr style="border-bottom: 1px solid #EBEEF5;">
|
||||
<td style="padding: 12px; vertical-align: top;">
|
||||
<div style="font-weight: 500; color: #303133;">${formatDate(singing.created_at)}</div>
|
||||
<div style="font-size: 12px; color: #909399; margin-top: 4px;">
|
||||
<i class="el-icon el-icon-user" style="margin-right: 4px;"></i>
|
||||
${singing.practitioner}
|
||||
</div>
|
||||
</td>
|
||||
<td style="padding: 12px; vertical-align: top; max-width: 550px;">
|
||||
${documentsHtml}
|
||||
</td>
|
||||
<td style="padding: 12px; vertical-align: top;">
|
||||
<div style="display: flex; align-items: center; gap: 12px;">
|
||||
<div style="display: flex; align-items: center; gap: 8px; flex: 1;">
|
||||
<i class="el-icon ${statusIcon}" style="color: ${statusColor}; font-size: 16px;"></i>
|
||||
<div>
|
||||
<div style="color: ${statusColor};">${lastStatus.description}</div>
|
||||
<div style="font-size: 12px; color: #909399; margin-top: 2px;">${formatDate(lastStatus.created_at)}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: flex; gap: 8px;">
|
||||
${singing.storagePath ? `
|
||||
<button class="el-button el-button--text view-main-doc-btn"
|
||||
data-title="${singing.title}"
|
||||
data-path="${singing.storagePath}"
|
||||
title="Просмотреть основной документ"
|
||||
style="padding: 0; border: none;">
|
||||
<i class="el-icon el-icon-document" style="color: #409EFF; font-size: 18px;"></i>
|
||||
</button>
|
||||
` : ''}
|
||||
${singing.statuses.length > 1 ? `
|
||||
<button class="el-button el-button--text view-statuses-btn"
|
||||
data-index="${index}"
|
||||
title="История статусов"
|
||||
style="padding: 0; border: none;">
|
||||
<i class="el-icon el-icon-time" style="color: #909399; font-size: 18px;"></i>
|
||||
</button>
|
||||
` : ''}
|
||||
${lastStatus.category === 'processing' ? `
|
||||
<div style="display: flex; gap: 4px;">
|
||||
<button class="el-button el-button--text resend-btn"
|
||||
data-index="${index}"
|
||||
title="Повторно отправить документы"
|
||||
style="padding: 0; border: none;">
|
||||
<i class="el-icon el-icon-refresh" style="color: #67C23A; font-size: 18px;"></i>
|
||||
</button>
|
||||
<button class="el-button el-button--text revoke-btn"
|
||||
data-index="${index}"
|
||||
title="Отозвать документы"
|
||||
style="padding: 0; border: none;">
|
||||
<i class="el-icon el-icon-circle-close" style="color: #F56C6C; font-size: 18px;"></i>
|
||||
</button>
|
||||
</div>
|
||||
` : ''}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
`;
|
||||
}).join('');
|
||||
|
||||
modalWrapper.innerHTML = `
|
||||
<div class="el-dialog" style="width: 90%; max-width: 1200px; margin-top: 5vh;">
|
||||
<div class="el-dialog__header">
|
||||
<span class="el-dialog__title">Электронные документы</span>
|
||||
<button type="button" class="el-dialog__headerbtn" aria-label="Close">
|
||||
<i class="el-dialog__close el-icon el-icon-close"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="el-dialog__body" style="padding: 20px;">
|
||||
<div style="border: 1px solid #EBEEF5; border-radius: 4px; overflow: hidden;">
|
||||
<table style="width: 100%; border-collapse: collapse; font-size: 14px;">
|
||||
<thead>
|
||||
<tr style="background: #F5F7FA;">
|
||||
<th style="padding: 12px; text-align: left; font-weight: 500; color: #909399;">Оформлено</th>
|
||||
<th style="padding: 12px; text-align: left; font-weight: 500; color: #909399;">Документы</th>
|
||||
<th style="padding: 12px; text-align: left; font-weight: 500; color: #909399;">Статус</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${tableRows}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="el-dialog__footer" style="text-align: right;">
|
||||
<button type="button" class="el-button m-button el-button--small is-plain button-with-icon close-btn">
|
||||
<i class="m-icon fa-times button-icon fad" style="font-size: 14px; margin-right: 6px;"></i>
|
||||
<span>Закрыть</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
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 = `
|
||||
<span>
|
||||
<i class="m-icon fa-copy button-icon fad" style="font-size: 16px;"></i>
|
||||
<span class="m-button__text use-indent">ЭДО</span>
|
||||
</span>
|
||||
`;
|
||||
|
||||
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);
|
||||
})();
|
||||
Reference in New Issue
Block a user