Опубликовано через кнопку
This commit is contained in:
parent
be0a8e51f3
commit
790b8751bf
42
index.html
42
index.html
@ -351,6 +351,7 @@
|
|||||||
let db = { checks: [], templates: [], typicalRemarks: [] };
|
let db = { checks: [], templates: [], typicalRemarks: [] };
|
||||||
|
|
||||||
async function load() {
|
async function load() {
|
||||||
|
try {
|
||||||
const res = await fetch("/api/data");
|
const res = await fetch("/api/data");
|
||||||
db = await res.json();
|
db = await res.json();
|
||||||
updateStats();
|
updateStats();
|
||||||
@ -358,6 +359,9 @@
|
|||||||
renderTemplates();
|
renderTemplates();
|
||||||
renderRemarksDb();
|
renderRemarksDb();
|
||||||
populateQuestionTypes();
|
populateQuestionTypes();
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Ошибка загрузки данных:", err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateStats() {
|
function updateStats() {
|
||||||
@ -407,6 +411,7 @@
|
|||||||
const formData = new FormData(e.target);
|
const formData = new FormData(e.target);
|
||||||
const data = Object.fromEntries(formData.entries());
|
const data = Object.fromEntries(formData.entries());
|
||||||
|
|
||||||
|
try {
|
||||||
const res = await fetch("/api/checks", {
|
const res = await fetch("/api/checks", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
@ -417,13 +422,23 @@
|
|||||||
currentCheckId = result.check.id;
|
currentCheckId = result.check.id;
|
||||||
showView("check-detail");
|
showView("check-detail");
|
||||||
loadCheckDetail(currentCheckId);
|
loadCheckDetail(currentCheckId);
|
||||||
|
} else {
|
||||||
|
alert("Ошибка создания проверки: " + (result.error || "Неизвестная ошибка"));
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
alert("Ошибка создания проверки: " + err.message);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
async function loadCheckDetail(id) {
|
async function loadCheckDetail(id) {
|
||||||
|
try {
|
||||||
const res = await fetch(`/api/checks/${id}`);
|
const res = await fetch(`/api/checks/${id}`);
|
||||||
const check = await res.json();
|
const check = await res.json();
|
||||||
if (!check.id) return;
|
if (!check || !check.id) {
|
||||||
|
alert("Проверка не найдена");
|
||||||
|
showView("checks");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
document.getElementById("detail-title").textContent = check.question || "Проверка";
|
document.getElementById("detail-title").textContent = check.question || "Проверка";
|
||||||
const statusEl = document.getElementById("detail-status");
|
const statusEl = document.getElementById("detail-status");
|
||||||
@ -434,6 +449,10 @@
|
|||||||
renderCompleteness(check);
|
renderCompleteness(check);
|
||||||
renderRemarks(check.remarks || []);
|
renderRemarks(check.remarks || []);
|
||||||
renderReport(check);
|
renderReport(check);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Ошибка загрузки проверки:", err);
|
||||||
|
alert("Ошибка загрузки данных проверки");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderDocuments(docs) {
|
function renderDocuments(docs) {
|
||||||
@ -474,11 +493,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function renderRemarks(remarks) {
|
function renderRemarks(remarks) {
|
||||||
|
const safeText = (text) => String(text || "").replace(/"/g, '"').replace(/'/g, ''');
|
||||||
document.getElementById("remarks-list").innerHTML = remarks.map(r => `
|
document.getElementById("remarks-list").innerHTML = remarks.map(r => `
|
||||||
<div class="remark-card ${r.category === "КРИТИЧНО" ? "crit" : r.category === "ТРЕБУЕТ ДОРАБОТКИ" ? "warn" : "tech"}">
|
<div class="remark-card ${r.category === "КРИТИЧНО" ? "crit" : r.category === "ТРЕБУЕТ ДОРАБОТКИ" ? "warn" : "tech"}">
|
||||||
<div class="category">${r.category} • ${r.type}</div>
|
<div class="category">${r.category} • ${r.type}</div>
|
||||||
<div class="text"><strong>${r.document || "Общее"}:</strong> ${r.text}</div>
|
<div class="text"><strong>${r.document || "Общее"}:</strong> ${safeText(r.text)}</div>
|
||||||
<div style="font-size: 13px; color: #666;">${r.recommendation || ""}</div>
|
<div style="font-size: 13px; color: #666;">${safeText(r.recommendation || "")}</div>
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
<button class="btn btn-secondary" onclick="toggleRemark(${r.id})">${r.confirmed ? "✓ Подтверждено" : "Подтвердить"}</button>
|
<button class="btn btn-secondary" onclick="toggleRemark(${r.id})">${r.confirmed ? "✓ Подтверждено" : "Подтвердить"}</button>
|
||||||
<button class="btn btn-secondary" onclick="deleteRemark(${r.id})">Отклонить</button>
|
<button class="btn btn-secondary" onclick="deleteRemark(${r.id})">Отклонить</button>
|
||||||
@ -524,7 +544,7 @@
|
|||||||
<h4>📝 Ответ исполнителю</h4>
|
<h4>📝 Ответ исполнителю</h4>
|
||||||
<div id="response-text" style="background: #f9f9f9; padding: 16px; border-radius: 6px; margin-top: 10px; font-size: 14px; line-height: 1.6;">
|
<div id="response-text" style="background: #f9f9f9; padding: 16px; border-radius: 6px; margin-top: 10px; font-size: 14px; line-height: 1.6;">
|
||||||
<strong>По результатам предварительной проверки материалов, представленных для вынесения вопроса на рассмотрение Заседания комиссии, необходимо дополнительно представить/скорректировать следующее:</strong><br><br>
|
<strong>По результатам предварительной проверки материалов, представленных для вынесения вопроса на рассмотрение Заседания комиссии, необходимо дополнительно представить/скорректировать следующее:</strong><br><br>
|
||||||
${(check.remarks || []).filter(r => r.confirmed).map((r, i) => `${i + 1}. ${r.text}`).join("<br>") || "Замечаний нет. Материалы могут быть рассмотрены на комиссии."}<br><br>
|
${(check.remarks || []).filter(r => r.confirmed).map((r, i) => `${i + 1}. ${String(r.text).replace(/</g, '<').replace(/>/g, '>')}`).join("<br>") || "Замечаний нет. Материалы могут быть рассмотрены на комиссии."}<br><br>
|
||||||
<strong>После устранения указанных замечаний просим повторно направить комплект документов для рассмотрения.</strong>
|
<strong>После устранения указанных замечаний просим повторно направить комплект документов для рассмотрения.</strong>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -545,14 +565,26 @@
|
|||||||
fileInput.addEventListener("change", (e) => handleFiles(e.target.files));
|
fileInput.addEventListener("change", (e) => handleFiles(e.target.files));
|
||||||
|
|
||||||
async function handleFiles(files) {
|
async function handleFiles(files) {
|
||||||
|
if (!currentCheckId) {
|
||||||
|
alert("Сначала создайте проверку!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append("file", file);
|
formData.append("file", file);
|
||||||
|
|
||||||
await fetch(`/api/upload?checkId=${currentCheckId}`, {
|
try {
|
||||||
|
const res = await fetch(`/api/upload?checkId=${currentCheckId}`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: formData
|
body: formData
|
||||||
});
|
});
|
||||||
|
const result = await res.json();
|
||||||
|
if (!result.ok && result.error) {
|
||||||
|
alert(`Ошибка загрузки файла ${file.name}: ${result.error}`);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
alert(`Ошибка загрузки файла ${file.name}: ${err.message}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
loadCheckDetail(currentCheckId);
|
loadCheckDetail(currentCheckId);
|
||||||
}
|
}
|
||||||
|
|||||||
30
server.js
30
server.js
@ -284,10 +284,14 @@ function compareRequisites(docs) {
|
|||||||
|
|
||||||
function generateRemarks(docs, discrepancies, arithmeticErrors) {
|
function generateRemarks(docs, discrepancies, arithmeticErrors) {
|
||||||
const remarks = [];
|
const remarks = [];
|
||||||
|
const existingIds = new Set();
|
||||||
|
|
||||||
for (const disc of discrepancies) {
|
for (const disc of discrepancies) {
|
||||||
|
const id = Date.now() + Math.random() * 1000;
|
||||||
|
if (!existingIds.has(id)) {
|
||||||
|
existingIds.add(id);
|
||||||
remarks.push({
|
remarks.push({
|
||||||
id: Date.now() + Math.random(),
|
id,
|
||||||
category: disc.severity,
|
category: disc.severity,
|
||||||
type: "Расхождение",
|
type: "Расхождение",
|
||||||
document: "Сравнение документов",
|
document: "Сравнение документов",
|
||||||
@ -297,10 +301,14 @@ function generateRemarks(docs, discrepancies, arithmeticErrors) {
|
|||||||
page: null
|
page: null
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (const err of arithmeticErrors) {
|
for (const err of arithmeticErrors) {
|
||||||
|
const id = Date.now() + Math.random() * 1000 + remarks.length;
|
||||||
|
if (!existingIds.has(id)) {
|
||||||
|
existingIds.add(id);
|
||||||
remarks.push({
|
remarks.push({
|
||||||
id: Date.now() + Math.random(),
|
id,
|
||||||
category: "КРИТИЧНО",
|
category: "КРИТИЧНО",
|
||||||
type: "Арифметика",
|
type: "Арифметика",
|
||||||
document: err.document || "Не определён",
|
document: err.document || "Не определён",
|
||||||
@ -310,6 +318,7 @@ function generateRemarks(docs, discrepancies, arithmeticErrors) {
|
|||||||
page: err.line
|
page: err.line
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return remarks;
|
return remarks;
|
||||||
}
|
}
|
||||||
@ -450,11 +459,17 @@ http.createServer(async (req, res) => {
|
|||||||
check.documents.push(docRecord);
|
check.documents.push(docRecord);
|
||||||
check.updatedAt = new Date().toISOString();
|
check.updatedAt = new Date().toISOString();
|
||||||
|
|
||||||
|
// Сохраняем ручные замечания (у которых нет типа "Расхождение" или "Арифметика")
|
||||||
|
const manualRemarks = (check.remarks || []).filter(r =>
|
||||||
|
r.type !== "Расхождение" && r.type !== "Арифметика"
|
||||||
|
);
|
||||||
|
|
||||||
const discrepancies = compareRequisites(check.documents);
|
const discrepancies = compareRequisites(check.documents);
|
||||||
const allArithmeticErrors = check.documents.flatMap(d =>
|
const allArithmeticErrors = check.documents.flatMap(d =>
|
||||||
(d.arithmeticErrors || []).map(e => ({ ...e, document: d.name }))
|
(d.arithmeticErrors || []).map(e => ({ ...e, document: d.name }))
|
||||||
);
|
);
|
||||||
check.remarks = generateRemarks(check.documents, discrepancies, allArithmeticErrors);
|
const autoRemarks = generateRemarks(check.documents, discrepancies, allArithmeticErrors);
|
||||||
|
check.remarks = [...manualRemarks, ...autoRemarks];
|
||||||
|
|
||||||
saveData(db);
|
saveData(db);
|
||||||
}
|
}
|
||||||
@ -482,6 +497,11 @@ http.createServer(async (req, res) => {
|
|||||||
(d.arithmeticErrors || []).map(e => ({ ...e, document: d.name }))
|
(d.arithmeticErrors || []).map(e => ({ ...e, document: d.name }))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Сохраняем только ручные замечания, автоматические перегенерируем
|
||||||
|
const manualRemarks = (check.remarks || []).filter(r =>
|
||||||
|
r.type !== "Расхождение" && r.type !== "Арифметика" && r.type !== "Комплектность"
|
||||||
|
);
|
||||||
|
|
||||||
const autoRemarks = generateRemarks(check.documents, discrepancies, allArithmeticErrors);
|
const autoRemarks = generateRemarks(check.documents, discrepancies, allArithmeticErrors);
|
||||||
|
|
||||||
const template = db.templates.find(t => t.name === check.questionType);
|
const template = db.templates.find(t => t.name === check.questionType);
|
||||||
@ -491,7 +511,7 @@ http.createServer(async (req, res) => {
|
|||||||
const isProvided = providedTypes.some(t => t.includes(required) || required.includes(t));
|
const isProvided = providedTypes.some(t => t.includes(required) || required.includes(t));
|
||||||
if (!isProvided) {
|
if (!isProvided) {
|
||||||
autoRemarks.push({
|
autoRemarks.push({
|
||||||
id: Date.now() + Math.random(),
|
id: Date.now() + Math.random() * 1000,
|
||||||
category: "КРИТИЧНО",
|
category: "КРИТИЧНО",
|
||||||
type: "Комплектность",
|
type: "Комплектность",
|
||||||
document: required,
|
document: required,
|
||||||
@ -504,7 +524,7 @@ http.createServer(async (req, res) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
check.remarks = [...check.remarks, ...autoRemarks];
|
check.remarks = [...manualRemarks, ...autoRemarks];
|
||||||
|
|
||||||
const criticalCount = check.remarks.filter(r => r.category === "КРИТИЧНО" && r.confirmed).length;
|
const criticalCount = check.remarks.filter(r => r.category === "КРИТИЧНО" && r.confirmed).length;
|
||||||
check.status = criticalCount > 0 ? "На доработке" : "Готово к комиссии";
|
check.status = criticalCount > 0 ? "На доработке" : "Готово к комиссии";
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user