mirror of https://github.com/interlegis/sapl.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
590 lines
26 KiB
590 lines
26 KiB
{% extends "crud/list.html" %}
|
|
{% load i18n %}
|
|
|
|
{% block extra_content %}
|
|
{% if pode_upload %}
|
|
<div class="card mb-3" id="upload-card">
|
|
<div class="card-header d-flex justify-content-between align-items-center"
|
|
style="cursor:pointer" onclick="document.getElementById('upload-body').classList.toggle('d-none')">
|
|
<strong><i class="fa fa-cloud-upload"></i> {% trans 'Upload Rápido de Anexos' %}</strong>
|
|
<small class="text-muted">{% trans 'clique para expandir' %}</small>
|
|
</div>
|
|
<div class="card-body d-none" id="upload-body">
|
|
<div class="form-group row mb-3">
|
|
<label class="col-sm-2 col-form-label">{% trans 'Tipo' %}</label>
|
|
<div class="col-sm-4">
|
|
<select id="upload-tipo" class="form-control">
|
|
{% for tipo in tipos_documento %}
|
|
<option value="{{ tipo.pk }}">{{ tipo.descricao }}</option>
|
|
{% empty %}
|
|
<option value="">{% trans 'Anexo (será criado automaticamente)' %}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="dropzone"
|
|
style="border: 2px dashed #007bff; border-radius: 8px; padding: 40px;
|
|
text-align: center; cursor: pointer; transition: all 0.2s;
|
|
background: #f8f9fa;">
|
|
<i class="fa fa-cloud-upload fa-3x text-primary mb-2" style="display:block"></i>
|
|
<p class="mb-1"><strong>{% trans 'Arraste arquivos aqui' %}</strong></p>
|
|
<p class="text-muted mb-0">{% trans 'ou clique para selecionar (fotos, PDFs, documentos)' %}</p>
|
|
<input type="file" id="upload-input" multiple
|
|
accept="image/*,.pdf,.doc,.docx,.odt,.txt,.xml"
|
|
style="display:none">
|
|
</div>
|
|
|
|
<div id="upload-progress" class="mt-3 d-none">
|
|
<div class="progress">
|
|
<div class="progress-bar progress-bar-striped progress-bar-animated"
|
|
role="progressbar" style="width: 0%" id="progress-bar"></div>
|
|
</div>
|
|
<small id="upload-status" class="text-muted mt-1 d-block"></small>
|
|
</div>
|
|
|
|
<div id="upload-results" class="mt-3"></div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
{% endblock %}
|
|
|
|
{% block base_content %}
|
|
{{ block.super }}
|
|
<div style="display:flex;padding-left: 600px;padding-top: 10px;">
|
|
<div class="actions btn-group float-right" role="group">
|
|
<a href="{% url 'sapl.materia:merge_docacessorios' root_pk %}" class="btn btn-outline-primary">{% trans 'Baixar documentos como PDF único' %}</a>
|
|
</div>
|
|
<div class="actions btn-group float-right" role="group">
|
|
<a href="{% url 'sapl.materia:compress_docacessorios' root_pk %}" class="btn btn-outline-primary">{% trans 'Baixar documentos compactados' %}</a>
|
|
</div>
|
|
{% if docs_pendentes_lote %}
|
|
<div class="actions btn-group float-right ml-2" role="group">
|
|
<button type="button" id="btn-assinar-doc-lote"
|
|
class="btn btn-outline-warning"
|
|
title="Assinar digitalmente os {{ docs_pendentes_lote|length }} documento(s) acessório(s) pendentes">
|
|
<i class="fa fa-certificate"></i>
|
|
Assinar em Lote
|
|
<span class="badge badge-warning text-dark ml-1">{{ docs_pendentes_lote|length }}</span>
|
|
</button>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
{% if pode_upload %}
|
|
<script>
|
|
(function() {
|
|
var dropzone = document.getElementById('dropzone');
|
|
var input = document.getElementById('upload-input');
|
|
var progressDiv = document.getElementById('upload-progress');
|
|
var progressBar = document.getElementById('progress-bar');
|
|
var statusEl = document.getElementById('upload-status');
|
|
var resultsDiv = document.getElementById('upload-results');
|
|
var uploadUrl = '{% url "sapl.materia:upload_anexos_materia" root_pk %}';
|
|
var csrfToken = '{{ csrf_token }}';
|
|
|
|
dropzone.addEventListener('click', function() { input.click(); });
|
|
|
|
dropzone.addEventListener('dragover', function(e) {
|
|
e.preventDefault();
|
|
dropzone.style.borderColor = '#28a745';
|
|
dropzone.style.background = '#e8f5e9';
|
|
});
|
|
|
|
dropzone.addEventListener('dragleave', function(e) {
|
|
e.preventDefault();
|
|
dropzone.style.borderColor = '#007bff';
|
|
dropzone.style.background = '#f8f9fa';
|
|
});
|
|
|
|
dropzone.addEventListener('drop', function(e) {
|
|
e.preventDefault();
|
|
dropzone.style.borderColor = '#007bff';
|
|
dropzone.style.background = '#f8f9fa';
|
|
if (e.dataTransfer.files.length > 0) {
|
|
uploadFiles(e.dataTransfer.files);
|
|
}
|
|
});
|
|
|
|
input.addEventListener('change', function() {
|
|
if (input.files.length > 0) {
|
|
uploadFiles(input.files);
|
|
}
|
|
});
|
|
|
|
function uploadFiles(files) {
|
|
var formData = new FormData();
|
|
var tipo = document.getElementById('upload-tipo').value;
|
|
if (tipo) formData.append('tipo', tipo);
|
|
for (var i = 0; i < files.length; i++) {
|
|
formData.append('arquivos', files[i]);
|
|
}
|
|
|
|
progressDiv.classList.remove('d-none');
|
|
progressBar.style.width = '0%';
|
|
statusEl.textContent = 'Enviando ' + files.length + ' arquivo(s)...';
|
|
resultsDiv.innerHTML = '';
|
|
|
|
var xhr = new XMLHttpRequest();
|
|
xhr.open('POST', uploadUrl, true);
|
|
xhr.setRequestHeader('X-CSRFToken', csrfToken);
|
|
|
|
xhr.upload.addEventListener('progress', function(e) {
|
|
if (e.lengthComputable) {
|
|
var pct = Math.round((e.loaded / e.total) * 100);
|
|
progressBar.style.width = pct + '%';
|
|
statusEl.textContent = 'Enviando... ' + pct + '%';
|
|
}
|
|
});
|
|
|
|
xhr.onload = function() {
|
|
progressBar.style.width = '100%';
|
|
if (xhr.status === 200) {
|
|
var data = JSON.parse(xhr.responseText);
|
|
progressBar.classList.remove('progress-bar-animated');
|
|
progressBar.classList.add('bg-success');
|
|
|
|
var html = '<div class="alert alert-success">' +
|
|
'<strong>' + data.total + ' arquivo(s) enviado(s) com sucesso!</strong></div>';
|
|
|
|
if (data.erros && data.erros.length > 0) {
|
|
html += '<div class="alert alert-warning"><strong>Erros:</strong><ul>';
|
|
for (var i = 0; i < data.erros.length; i++) {
|
|
html += '<li>' + data.erros[i] + '</li>';
|
|
}
|
|
html += '</ul></div>';
|
|
}
|
|
|
|
resultsDiv.innerHTML = html;
|
|
|
|
if (data.total > 0) {
|
|
setTimeout(function() { location.reload(); }, 1500);
|
|
}
|
|
} else {
|
|
progressBar.classList.add('bg-danger');
|
|
var errMsg = 'Erro ao enviar arquivos.';
|
|
try { errMsg = JSON.parse(xhr.responseText).error || errMsg; } catch(e) {}
|
|
resultsDiv.innerHTML = '<div class="alert alert-danger">' + errMsg + '</div>';
|
|
}
|
|
input.value = '';
|
|
};
|
|
|
|
xhr.onerror = function() {
|
|
progressBar.classList.add('bg-danger');
|
|
resultsDiv.innerHTML = '<div class="alert alert-danger">Erro de conexão.</div>';
|
|
input.value = '';
|
|
};
|
|
|
|
xhr.send(formData);
|
|
}
|
|
})();
|
|
</script>
|
|
{% endif %}
|
|
|
|
{% if docs_pendentes_lote %}
|
|
{# ── Modal de Assinatura em Lote – Documentos Acessórios ─────────────── #}
|
|
<div class="modal fade" id="assinaturaDocLoteModal" tabindex="-1" role="dialog" aria-labelledby="assinaturaDocLoteModalLabel" aria-hidden="true">
|
|
<div class="modal-dialog modal-lg" role="document">
|
|
<div class="modal-content">
|
|
<div class="modal-header bg-warning">
|
|
<h5 class="modal-title" id="assinaturaDocLoteModalLabel">
|
|
<i class="fa fa-certificate"></i>
|
|
Assinar Documentos Acessórios em Lote —
|
|
<strong id="doc-lote-titulo-total">{{ docs_pendentes_lote|length }}</strong> pendente(s)
|
|
</h5>
|
|
<button type="button" class="close" data-dismiss="modal" aria-label="Fechar">
|
|
<span aria-hidden="true">×</span>
|
|
</button>
|
|
</div>
|
|
|
|
<div class="modal-body">
|
|
|
|
{# Passo 0: seleção #}
|
|
<div id="doc-lote-selecao-step">
|
|
<div class="d-flex justify-content-between align-items-center mb-2">
|
|
<span class="font-weight-bold">
|
|
<i class="fa fa-list-ul"></i> Selecione os documentos a assinar
|
|
</span>
|
|
<div>
|
|
<button type="button" class="btn btn-sm btn-outline-secondary mr-1" id="doc-lote-btn-todas">
|
|
<i class="fa fa-check-square-o"></i> Todos
|
|
</button>
|
|
<button type="button" class="btn btn-sm btn-outline-secondary" id="doc-lote-btn-nenhuma">
|
|
<i class="fa fa-square-o"></i> Nenhum
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div id="doc-lote-lista-checkboxes" style="max-height:320px;overflow-y:auto;border:1px solid #dee2e6;border-radius:4px;padding:8px;">
|
|
{% for d in docs_pendentes_lote %}
|
|
<div class="form-check py-1" style="border-bottom:1px solid #f0f0f0;">
|
|
<input class="form-check-input doc-lote-chk" type="checkbox"
|
|
id="doc-lote-chk-{{ d.id }}" value="{{ d.id }}" checked>
|
|
<label class="form-check-label w-100" for="doc-lote-chk-{{ d.id }}" style="cursor:pointer;">
|
|
{{ d.descricao }}
|
|
</label>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
<p class="text-muted small mt-2 mb-0">
|
|
<span id="doc-lote-selecao-contagem">{{ docs_pendentes_lote|length }}</span> documento(s) selecionado(s)
|
|
</p>
|
|
</div>
|
|
|
|
{# Passo 1: certificado #}
|
|
<div id="doc-lote-form-step" style="display:none;">
|
|
<div class="alert alert-warning">
|
|
<i class="fa fa-exclamation-triangle"></i>
|
|
<strong>Atenção:</strong> Esta operação assinará digitalmente
|
|
<strong><span id="doc-lote-alerta-total">{{ docs_pendentes_lote|length }}</span> documento(s)</strong>
|
|
com validade jurídica nos termos da MP 2.200-2/2001.
|
|
</div>
|
|
<form id="form-assinatura-doc-lote" enctype="multipart/form-data">
|
|
{% csrf_token %}
|
|
<div class="form-group">
|
|
<label for="doc-lote-certificado">
|
|
<i class="fa fa-file"></i> Certificado Digital (.pfx / .p12)
|
|
</label>
|
|
<div class="custom-file">
|
|
<input type="file" class="custom-file-input" id="doc-lote-certificado"
|
|
name="certificado" accept=".pfx,.p12" required>
|
|
<label class="custom-file-label" for="doc-lote-certificado">
|
|
Selecione o arquivo do certificado...
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="doc-lote-senha">
|
|
<i class="fa fa-lock"></i> Senha do Certificado
|
|
</label>
|
|
<input type="password" class="form-control" id="doc-lote-senha"
|
|
name="senha" placeholder="Digite a senha do certificado" required>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
{# Passo 1.5: prévia #}
|
|
<div id="doc-lote-previa-step" style="display:none;">
|
|
<div class="alert alert-info mb-3">
|
|
<i class="fa fa-eye"></i>
|
|
<strong>Revise os documentos antes de assinar.</strong>
|
|
Clique em cada item para visualizar o PDF em nova aba.
|
|
</div>
|
|
<div id="doc-lote-previa-lista" style="max-height:300px;overflow-y:auto;border:1px solid #dee2e6;border-radius:4px;padding:8px;" class="mb-3"></div>
|
|
<div class="form-check mt-3">
|
|
<input class="form-check-input" type="checkbox" id="doc-lote-previa-confirmacao">
|
|
<label class="form-check-label font-weight-bold text-danger" for="doc-lote-previa-confirmacao">
|
|
<i class="fa fa-check-square-o"></i>
|
|
Confirmo que revisei os documentos e que estão corretos para assinatura.
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
{# Passo 2: progresso #}
|
|
<div id="doc-lote-progresso-step" style="display:none;">
|
|
<h6 class="mb-3">
|
|
<i class="fa fa-spinner fa-spin text-primary"></i>
|
|
Processando assinaturas…
|
|
</h6>
|
|
<div class="progress mb-3" style="height:22px;">
|
|
<div id="doc-lote-progress-bar"
|
|
class="progress-bar progress-bar-striped progress-bar-animated bg-warning"
|
|
role="progressbar" style="width:0%">0%</div>
|
|
</div>
|
|
<p class="text-muted small" id="doc-lote-status-texto">Iniciando…</p>
|
|
</div>
|
|
|
|
{# Passo 3: resumo #}
|
|
<div id="doc-lote-resumo-step" style="display:none;">
|
|
<div id="doc-lote-resumo-alerta"></div>
|
|
<div id="doc-lote-resumo-lista" style="max-height:300px;overflow-y:auto;"></div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-dismiss="modal" id="doc-lote-btn-fechar">
|
|
<i class="fa fa-times"></i> Fechar
|
|
</button>
|
|
<button type="button" class="btn btn-warning" id="doc-lote-btn-proximo">
|
|
<i class="fa fa-arrow-right"></i>
|
|
Continuar — <span id="doc-lote-btn-proximo-contagem">{{ docs_pendentes_lote|length }}</span> Doc(s)
|
|
</button>
|
|
<button type="button" class="btn btn-outline-secondary" id="doc-lote-btn-voltar" style="display:none;">
|
|
<i class="fa fa-arrow-left"></i> Voltar
|
|
</button>
|
|
<button type="button" class="btn btn-info" id="doc-lote-btn-previa" style="display:none;">
|
|
<i class="fa fa-eye"></i> Visualizar Documentos
|
|
</button>
|
|
<button type="button" class="btn btn-outline-secondary" id="doc-lote-btn-voltar-form" style="display:none;">
|
|
<i class="fa fa-arrow-left"></i> Voltar
|
|
</button>
|
|
<button type="button" class="btn btn-warning" id="doc-lote-btn-assinar" style="display:none;" disabled>
|
|
<i class="fa fa-certificate"></i>
|
|
Assinar <span id="doc-lote-btn-assinar-contagem">{{ docs_pendentes_lote|length }}</span> Doc(s)
|
|
</button>
|
|
<button type="button" class="btn btn-success" id="doc-lote-btn-recarregar" style="display:none;"
|
|
onclick="location.reload()">
|
|
<i class="fa fa-refresh"></i> Atualizar página
|
|
</button>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
(function () {
|
|
var DOCS_LOTE = {{ docs_pendentes_lote|safe }};
|
|
var URL_LOTE = "{% url 'sapl.materia:docacessorio_assinar_lote' %}";
|
|
|
|
function getCSRF() {
|
|
var el = document.querySelector('#form-assinatura-doc-lote [name=csrfmiddlewaretoken]');
|
|
if (el) return el.value;
|
|
var match = document.cookie.match(/csrftoken=([^;]+)/);
|
|
return match ? match[1] : '';
|
|
}
|
|
|
|
function getIdsSelecionados() {
|
|
return Array.from(document.querySelectorAll('.doc-lote-chk:checked'))
|
|
.map(function(c){ return parseInt(c.value, 10); });
|
|
}
|
|
|
|
function atualizarContagem() {
|
|
var n = getIdsSelecionados().length;
|
|
document.getElementById('doc-lote-selecao-contagem').textContent = n;
|
|
document.getElementById('doc-lote-btn-proximo-contagem').textContent = n;
|
|
document.getElementById('doc-lote-btn-assinar-contagem').textContent = n;
|
|
document.getElementById('doc-lote-alerta-total').textContent = n;
|
|
document.getElementById('doc-lote-titulo-total').textContent = n;
|
|
document.getElementById('doc-lote-btn-proximo').disabled = (n === 0);
|
|
}
|
|
|
|
document.getElementById('doc-lote-lista-checkboxes').addEventListener('change', function(e){
|
|
if (e.target && e.target.classList.contains('doc-lote-chk')) atualizarContagem();
|
|
});
|
|
|
|
document.getElementById('doc-lote-btn-todas').addEventListener('click', function(){
|
|
document.querySelectorAll('.doc-lote-chk').forEach(function(c){ c.checked = true; });
|
|
atualizarContagem();
|
|
});
|
|
document.getElementById('doc-lote-btn-nenhuma').addEventListener('click', function(){
|
|
document.querySelectorAll('.doc-lote-chk').forEach(function(c){ c.checked = false; });
|
|
atualizarContagem();
|
|
});
|
|
|
|
document.getElementById('doc-lote-certificado').addEventListener('change', function(){
|
|
var label = this.nextElementSibling;
|
|
label.textContent = this.files[0] ? this.files[0].name : 'Selecione o arquivo do certificado...';
|
|
});
|
|
|
|
function irParaSelecao() {
|
|
document.getElementById('doc-lote-selecao-step').style.display = 'block';
|
|
document.getElementById('doc-lote-form-step').style.display = 'none';
|
|
document.getElementById('doc-lote-previa-step').style.display = 'none';
|
|
document.getElementById('doc-lote-progresso-step').style.display = 'none';
|
|
document.getElementById('doc-lote-resumo-step').style.display = 'none';
|
|
document.getElementById('doc-lote-btn-proximo').style.display = 'inline-block';
|
|
document.getElementById('doc-lote-btn-voltar').style.display = 'none';
|
|
document.getElementById('doc-lote-btn-previa').style.display = 'none';
|
|
document.getElementById('doc-lote-btn-voltar-form').style.display= 'none';
|
|
document.getElementById('doc-lote-btn-assinar').style.display = 'none';
|
|
document.getElementById('doc-lote-btn-recarregar').style.display = 'none';
|
|
document.getElementById('doc-lote-btn-fechar').disabled = false;
|
|
}
|
|
|
|
function irParaForm() {
|
|
document.getElementById('doc-lote-selecao-step').style.display = 'none';
|
|
document.getElementById('doc-lote-form-step').style.display = 'block';
|
|
document.getElementById('doc-lote-previa-step').style.display = 'none';
|
|
document.getElementById('doc-lote-progresso-step').style.display = 'none';
|
|
document.getElementById('doc-lote-resumo-step').style.display = 'none';
|
|
document.getElementById('doc-lote-btn-proximo').style.display = 'none';
|
|
document.getElementById('doc-lote-btn-voltar').style.display = 'inline-block';
|
|
document.getElementById('doc-lote-btn-previa').style.display = 'inline-block';
|
|
document.getElementById('doc-lote-btn-voltar-form').style.display= 'none';
|
|
document.getElementById('doc-lote-btn-assinar').style.display = 'none';
|
|
document.getElementById('doc-lote-btn-recarregar').style.display = 'none';
|
|
}
|
|
|
|
function irParaPrevia() {
|
|
var certFile = document.getElementById('doc-lote-certificado').files[0];
|
|
var senha = document.getElementById('doc-lote-senha').value;
|
|
if (!certFile) { alert('Selecione o arquivo do certificado.'); return; }
|
|
if (!senha) { alert('Digite a senha do certificado.'); return; }
|
|
|
|
var idsSel = getIdsSelecionados();
|
|
var html = '';
|
|
DOCS_LOTE.filter(function(d){ return idsSel.indexOf(d.id) !== -1; })
|
|
.forEach(function(d) {
|
|
html += '<div class="d-flex align-items-center justify-content-between py-2" ' +
|
|
'style="border-bottom:1px solid #f0f0f0;">' +
|
|
'<span><i class="fa fa-file-pdf-o text-danger mr-1"></i>' +
|
|
'<strong>' + escHtml(d.descricao) + '</strong></span>' +
|
|
'<a href="/materia/documentoacessorio/' + d.id + '/pdf-previa" target="_blank" ' +
|
|
'class="btn btn-sm btn-outline-primary ml-2" title="Visualizar PDF">' +
|
|
'<i class="fa fa-eye"></i> Visualizar</a>' +
|
|
'</div>';
|
|
});
|
|
document.getElementById('doc-lote-previa-lista').innerHTML = html || '<p class="text-muted">Nenhum documento selecionado.</p>';
|
|
|
|
var chk = document.getElementById('doc-lote-previa-confirmacao');
|
|
chk.checked = false;
|
|
document.getElementById('doc-lote-btn-assinar').disabled = true;
|
|
|
|
document.getElementById('doc-lote-selecao-step').style.display = 'none';
|
|
document.getElementById('doc-lote-form-step').style.display = 'none';
|
|
document.getElementById('doc-lote-previa-step').style.display = 'block';
|
|
document.getElementById('doc-lote-progresso-step').style.display = 'none';
|
|
document.getElementById('doc-lote-resumo-step').style.display = 'none';
|
|
document.getElementById('doc-lote-btn-proximo').style.display = 'none';
|
|
document.getElementById('doc-lote-btn-voltar').style.display = 'none';
|
|
document.getElementById('doc-lote-btn-previa').style.display = 'none';
|
|
document.getElementById('doc-lote-btn-voltar-form').style.display= 'inline-block';
|
|
document.getElementById('doc-lote-btn-assinar').style.display = 'inline-block';
|
|
document.getElementById('doc-lote-btn-recarregar').style.display = 'none';
|
|
}
|
|
|
|
document.getElementById('btn-assinar-doc-lote').addEventListener('click', function(){
|
|
document.querySelectorAll('.doc-lote-chk').forEach(function(c){ c.checked = true; });
|
|
document.getElementById('form-assinatura-doc-lote').reset();
|
|
document.querySelector('#form-assinatura-doc-lote .custom-file-label').textContent =
|
|
'Selecione o arquivo do certificado...';
|
|
atualizarContagem();
|
|
irParaSelecao();
|
|
$('#assinaturaDocLoteModal').modal('show');
|
|
});
|
|
|
|
document.getElementById('doc-lote-btn-proximo').addEventListener('click', function(){
|
|
if (getIdsSelecionados().length === 0) { alert('Selecione ao menos um documento.'); return; }
|
|
irParaForm();
|
|
});
|
|
|
|
document.getElementById('doc-lote-btn-voltar').addEventListener('click', irParaSelecao);
|
|
document.getElementById('doc-lote-btn-previa').addEventListener('click', irParaPrevia);
|
|
document.getElementById('doc-lote-btn-voltar-form').addEventListener('click', irParaForm);
|
|
|
|
document.getElementById('doc-lote-previa-confirmacao').addEventListener('change', function(){
|
|
document.getElementById('doc-lote-btn-assinar').disabled = !this.checked;
|
|
});
|
|
|
|
document.getElementById('doc-lote-btn-assinar').addEventListener('click', function(){
|
|
var certFile = document.getElementById('doc-lote-certificado').files[0];
|
|
var senha = document.getElementById('doc-lote-senha').value;
|
|
var idsSel = getIdsSelecionados();
|
|
|
|
if (!certFile) { alert('Selecione o arquivo do certificado.'); return; }
|
|
if (!senha) { alert('Digite a senha do certificado.'); return; }
|
|
if (!idsSel.length) { alert('Nenhum documento selecionado.'); return; }
|
|
|
|
var fd = new FormData();
|
|
fd.append('certificado', certFile);
|
|
fd.append('senha', senha);
|
|
fd.append('ids', JSON.stringify(idsSel));
|
|
|
|
document.getElementById('doc-lote-previa-step').style.display = 'none';
|
|
document.getElementById('doc-lote-progresso-step').style.display = 'block';
|
|
document.getElementById('doc-lote-btn-voltar-form').style.display= 'none';
|
|
document.getElementById('doc-lote-btn-assinar').style.display = 'none';
|
|
document.getElementById('doc-lote-btn-fechar').disabled = true;
|
|
|
|
// Animação de progresso proporcional ao número de documentos
|
|
var nDocs = idsSel.length;
|
|
var fases = [
|
|
{ ate: 10, label: 'Enviando certificado e iniciando assinatura…', ms: 700 },
|
|
{ ate: 25, label: 'Validando certificado e gerando PDFs…', ms: Math.min(600, 200 + nDocs * 30) },
|
|
{ ate: 50, label: 'Preparando ' + nDocs + ' documento(s) para assinatura…', ms: Math.min(800, 200 + nDocs * 50) },
|
|
{ ate: 70, label: 'Enviando ao microserviço de assinatura…', ms: Math.min(1000, 300 + nDocs * 60) },
|
|
{ ate: 85, label: 'Aguardando resposta do servidor…', ms: Math.min(800, 300 + nDocs * 40) },
|
|
{ ate: 93, label: 'Salvando documentos assinados…', ms: 500 },
|
|
{ ate: 97, label: 'Finalizando…', ms: 300 },
|
|
];
|
|
var faseIdx = 0, progrAtual = 0, progrTimer = null;
|
|
function avancarProgresso() {
|
|
if (faseIdx >= fases.length) return;
|
|
var fase = fases[faseIdx];
|
|
if (progrAtual < fase.ate) {
|
|
progrAtual = Math.min(progrAtual + 1, fase.ate);
|
|
setProgresso(progrAtual, fase.label);
|
|
} else { faseIdx++; }
|
|
progrTimer = setTimeout(avancarProgresso, fases[Math.min(faseIdx, fases.length-1)].ms / (fase.ate - (faseIdx > 0 ? fases[faseIdx-1].ate : 0)));
|
|
}
|
|
progrTimer = setTimeout(avancarProgresso, 400);
|
|
|
|
fetch(URL_LOTE, {
|
|
method: 'POST',
|
|
body: fd,
|
|
headers: { 'X-CSRFToken': getCSRF() }
|
|
})
|
|
.then(function(r){
|
|
clearTimeout(progrTimer);
|
|
setProgresso(98, 'Processando resposta…');
|
|
if (!r.ok) return r.json().then(function(d){ throw new Error(d.error || ('HTTP ' + r.status)); });
|
|
return r.json();
|
|
})
|
|
.then(function(data){
|
|
clearTimeout(progrTimer);
|
|
setProgresso(100, 'Concluído.');
|
|
document.getElementById('doc-lote-btn-fechar').disabled = false;
|
|
mostrarResumo(data);
|
|
})
|
|
.catch(function(err){
|
|
clearTimeout(progrTimer);
|
|
document.getElementById('doc-lote-btn-fechar').disabled = false;
|
|
setProgresso(100, 'Erro.');
|
|
mostrarErroFatal(err.message || String(err));
|
|
});
|
|
});
|
|
|
|
function setProgresso(pct, texto) {
|
|
var bar = document.getElementById('doc-lote-progress-bar');
|
|
bar.style.width = pct + '%';
|
|
bar.textContent = pct + '%';
|
|
document.getElementById('doc-lote-status-texto').textContent = texto;
|
|
}
|
|
|
|
function mostrarResumo(data) {
|
|
document.getElementById('doc-lote-progresso-step').style.display = 'none';
|
|
document.getElementById('doc-lote-resumo-step').style.display = 'block';
|
|
document.getElementById('doc-lote-btn-recarregar').style.display = 'inline-block';
|
|
|
|
var temErro = data.erros > 0;
|
|
var alertaCls = data.sucesso > 0 ? (temErro ? 'alert-warning' : 'alert-success') : 'alert-danger';
|
|
var icone = data.sucesso > 0 ? (temErro ? 'exclamation-triangle' : 'check-circle') : 'times-circle';
|
|
|
|
document.getElementById('doc-lote-resumo-alerta').innerHTML =
|
|
'<div class="alert ' + alertaCls + '">' +
|
|
'<i class="fa fa-' + icone + '"></i> ' +
|
|
'<strong>' + data.sucesso + ' assinado(s)</strong> com sucesso' +
|
|
(temErro ? ', <strong>' + data.erros + '</strong> com erro(s).' : '.') +
|
|
' Total: ' + data.total + ' documento(s).</div>';
|
|
|
|
var html = '<ul class="list-group">';
|
|
(data.resultados || []).forEach(function(r){
|
|
var cls = r.success ? 'list-group-item-success' : 'list-group-item-danger';
|
|
var icon = r.success ? 'check text-success' : 'times text-danger';
|
|
html += '<li class="list-group-item list-group-item-sm ' + cls + '">' +
|
|
'<i class="fa fa-' + icon + ' mr-1"></i>' +
|
|
'<strong>' + escHtml(r.descricao) + '</strong>' +
|
|
(r.error ? ' — <small class="text-muted">' + escHtml(r.error) + '</small>' : '') +
|
|
'</li>';
|
|
});
|
|
html += '</ul>';
|
|
document.getElementById('doc-lote-resumo-lista').innerHTML = html;
|
|
}
|
|
|
|
function mostrarErroFatal(msg) {
|
|
document.getElementById('doc-lote-progresso-step').style.display = 'none';
|
|
document.getElementById('doc-lote-resumo-step').style.display = 'block';
|
|
document.getElementById('doc-lote-resumo-alerta').innerHTML =
|
|
'<div class="alert alert-danger"><i class="fa fa-times-circle"></i> ' +
|
|
'<strong>Erro:</strong> ' + escHtml(msg) + '</div>';
|
|
document.getElementById('doc-lote-resumo-lista').innerHTML = '';
|
|
}
|
|
|
|
function escHtml(s) {
|
|
return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');
|
|
}
|
|
})();
|
|
</script>
|
|
{% endif %}
|
|
{% endblock %}
|
|
|