Опубликовано через кнопку
This commit is contained in:
parent
be0a8e51f3
commit
790b8751bf
102
index.html
102
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;
|
||||
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 = `<span class="status-indicator ${statusClass}">${check.status || "Новая"}</span>`;
|
||||
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 = `<span class="status-indicator ${statusClass}">${check.status || "Новая"}</span>`;
|
||||
|
||||
renderDocuments(check.documents || []);
|
||||
renderCompleteness(check);
|
||||
renderRemarks(check.remarks || []);
|
||||
renderReport(check);
|
||||
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 => `
|
||||
<div class="remark-card ${r.category === "КРИТИЧНО" ? "crit" : r.category === "ТРЕБУЕТ ДОРАБОТКИ" ? "warn" : "tech"}">
|
||||
<div class="category">${r.category} • ${r.type}</div>
|
||||
<div class="text"><strong>${r.document || "Общее"}:</strong> ${r.text}</div>
|
||||
<div style="font-size: 13px; color: #666;">${r.recommendation || ""}</div>
|
||||
<div class="text"><strong>${r.document || "Общее"}:</strong> ${safeText(r.text)}</div>
|
||||
<div style="font-size: 13px; color: #666;">${safeText(r.recommendation || "")}</div>
|
||||
<div class="actions">
|
||||
<button class="btn btn-secondary" onclick="toggleRemark(${r.id})">${r.confirmed ? "✓ Подтверждено" : "Подтвердить"}</button>
|
||||
<button class="btn btn-secondary" onclick="deleteRemark(${r.id})">Отклонить</button>
|
||||
@ -524,7 +544,7 @@
|
||||
<h4>📝 Ответ исполнителю</h4>
|
||||
<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>
|
||||
${(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>
|
||||
</div>
|
||||
</div>
|
||||
@ -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);
|
||||
}
|
||||
|
||||
66
server.js
66
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 ? "На доработке" : "Готово к комиссии";
|
||||
|
||||
Loading…
Reference in New Issue
Block a user