From 790b8751bf3327f1ee026f8e974425d66daeed1f Mon Sep 17 00:00:00 2001 From: MeruertYes Date: Mon, 24 Aug 2026 08:44:57 +0000 Subject: [PATCH] =?UTF-8?q?=D0=9E=D0=BF=D1=83=D0=B1=D0=BB=D0=B8=D0=BA?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D0=BD=D0=BE=20=D1=87=D0=B5=D1=80=D0=B5=D0=B7?= =?UTF-8?q?=20=D0=BA=D0=BD=D0=BE=D0=BF=D0=BA=D1=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- index.html | 106 ++++++++++++++++++++++++++++++++++------------------- server.js | 66 +++++++++++++++++++++------------ 2 files changed, 112 insertions(+), 60 deletions(-) diff --git a/index.html b/index.html index 3288eba..3550f0e 100644 --- a/index.html +++ b/index.html @@ -351,13 +351,17 @@ let db = { checks: [], templates: [], typicalRemarks: [] }; async function load() { - const res = await fetch("/api/data"); - db = await res.json(); - updateStats(); - renderRecentChecks(); - renderTemplates(); - renderRemarksDb(); - populateQuestionTypes(); + try { + const res = await fetch("/api/data"); + db = await res.json(); + updateStats(); + renderRecentChecks(); + renderTemplates(); + renderRemarksDb(); + populateQuestionTypes(); + } catch (err) { + console.error("Ошибка загрузки данных:", err); + } } function updateStats() { @@ -407,33 +411,48 @@ const formData = new FormData(e.target); const data = Object.fromEntries(formData.entries()); - const res = await fetch("/api/checks", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(data) - }); - const result = await res.json(); - if (result.ok) { - currentCheckId = result.check.id; - showView("check-detail"); - loadCheckDetail(currentCheckId); + try { + const res = await fetch("/api/checks", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(data) + }); + const result = await res.json(); + if (result.ok) { + currentCheckId = result.check.id; + showView("check-detail"); + loadCheckDetail(currentCheckId); + } else { + alert("Ошибка создания проверки: " + (result.error || "Неизвестная ошибка")); + } + } catch (err) { + alert("Ошибка создания проверки: " + err.message); } }); async function loadCheckDetail(id) { - const res = await fetch(`/api/checks/${id}`); - const check = await res.json(); - if (!check.id) return; - - document.getElementById("detail-title").textContent = check.question || "Проверка"; - const statusEl = document.getElementById("detail-status"); - const statusClass = check.status === "Готово к комиссии" ? "status-green" : check.status === "На доработке" ? "status-red" : "status-yellow"; - statusEl.innerHTML = `${check.status || "Новая"}`; - - renderDocuments(check.documents || []); - renderCompleteness(check); - renderRemarks(check.remarks || []); - renderReport(check); + try { + const res = await fetch(`/api/checks/${id}`); + const check = await res.json(); + if (!check || !check.id) { + alert("Проверка не найдена"); + showView("checks"); + return; + } + + document.getElementById("detail-title").textContent = check.question || "Проверка"; + const statusEl = document.getElementById("detail-status"); + const statusClass = check.status === "Готово к комиссии" ? "status-green" : check.status === "На доработке" ? "status-red" : "status-yellow"; + statusEl.innerHTML = `${check.status || "Новая"}`; + + renderDocuments(check.documents || []); + renderCompleteness(check); + renderRemarks(check.remarks || []); + renderReport(check); + } catch (err) { + console.error("Ошибка загрузки проверки:", err); + alert("Ошибка загрузки данных проверки"); + } } function renderDocuments(docs) { @@ -474,11 +493,12 @@ } function renderRemarks(remarks) { + const safeText = (text) => String(text || "").replace(/"/g, '"').replace(/'/g, '''); document.getElementById("remarks-list").innerHTML = remarks.map(r => `
${r.category} • ${r.type}
-
${r.document || "Общее"}: ${r.text}
-
${r.recommendation || ""}
+
${r.document || "Общее"}: ${safeText(r.text)}
+
${safeText(r.recommendation || "")}
@@ -524,7 +544,7 @@

📝 Ответ исполнителю

По результатам предварительной проверки материалов, представленных для вынесения вопроса на рассмотрение Заседания комиссии, необходимо дополнительно представить/скорректировать следующее:

- ${(check.remarks || []).filter(r => r.confirmed).map((r, i) => `${i + 1}. ${r.text}`).join("
") || "Замечаний нет. Материалы могут быть рассмотрены на комиссии."}

+ ${(check.remarks || []).filter(r => r.confirmed).map((r, i) => `${i + 1}. ${String(r.text).replace(//g, '>')}`).join("
") || "Замечаний нет. Материалы могут быть рассмотрены на комиссии."}

После устранения указанных замечаний просим повторно направить комплект документов для рассмотрения.
@@ -545,14 +565,26 @@ fileInput.addEventListener("change", (e) => handleFiles(e.target.files)); async function handleFiles(files) { + if (!currentCheckId) { + alert("Сначала создайте проверку!"); + return; + } for (const file of files) { const formData = new FormData(); formData.append("file", file); - await fetch(`/api/upload?checkId=${currentCheckId}`, { - method: "POST", - body: formData - }); + try { + const res = await fetch(`/api/upload?checkId=${currentCheckId}`, { + method: "POST", + 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); } diff --git a/server.js b/server.js index 8fc73f0..2684cd8 100644 --- a/server.js +++ b/server.js @@ -284,31 +284,40 @@ function compareRequisites(docs) { function generateRemarks(docs, discrepancies, arithmeticErrors) { const remarks = []; + const existingIds = new Set(); for (const disc of discrepancies) { - remarks.push({ - id: Date.now() + Math.random(), - category: disc.severity, - type: "Расхождение", - document: "Сравнение документов", - text: `Выявлено расхождение в поле "${disc.field}": ${disc.values.map(v => `${v.documents.join(", ")} — ${v.value}`).join("; ")}`, - recommendation: "Уточнить корректное значение и привести документы к единому значению", - confirmed: true, - page: null - }); + const id = Date.now() + Math.random() * 1000; + if (!existingIds.has(id)) { + existingIds.add(id); + remarks.push({ + id, + category: disc.severity, + type: "Расхождение", + document: "Сравнение документов", + text: `Выявлено расхождение в поле "${disc.field}": ${disc.values.map(v => `${v.documents.join(", ")} — ${v.value}`).join("; ")}`, + recommendation: "Уточнить корректное значение и привести документы к единому значению", + confirmed: true, + page: null + }); + } } for (const err of arithmeticErrors) { - remarks.push({ - id: Date.now() + Math.random(), - category: "КРИТИЧНО", - type: "Арифметика", - document: err.document || "Не определён", - text: `Ошибка в расчёте: ${err.formula} = ${err.calculated}, указано ${err.indicated}`, - recommendation: "Проверить расчёт и исправить значение", - confirmed: true, - page: err.line - }); + const id = Date.now() + Math.random() * 1000 + remarks.length; + if (!existingIds.has(id)) { + existingIds.add(id); + remarks.push({ + id, + category: "КРИТИЧНО", + type: "Арифметика", + document: err.document || "Не определён", + text: `Ошибка в расчёте: ${err.formula} = ${err.calculated}, указано ${err.indicated}`, + recommendation: "Проверить расчёт и исправить значение", + confirmed: true, + page: err.line + }); + } } return remarks; @@ -450,11 +459,17 @@ http.createServer(async (req, res) => { check.documents.push(docRecord); check.updatedAt = new Date().toISOString(); + // Сохраняем ручные замечания (у которых нет типа "Расхождение" или "Арифметика") + const manualRemarks = (check.remarks || []).filter(r => + r.type !== "Расхождение" && r.type !== "Арифметика" + ); + const discrepancies = compareRequisites(check.documents); const allArithmeticErrors = check.documents.flatMap(d => (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); } @@ -482,6 +497,11 @@ http.createServer(async (req, res) => { (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 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)); if (!isProvided) { autoRemarks.push({ - id: Date.now() + Math.random(), + id: Date.now() + Math.random() * 1000, category: "КРИТИЧНО", type: "Комплектность", 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; check.status = criticalCount > 0 ? "На доработке" : "Готово к комиссии";