Добавлены фильтры: Способ закупки, Вид закупа, Тип бюджета
This commit is contained in:
parent
b83855e578
commit
5f4a08bb1e
67
index.html
67
index.html
@ -102,12 +102,17 @@
|
|||||||
|
|
||||||
<div class="filters">
|
<div class="filters">
|
||||||
<div class="filters-row">
|
<div class="filters-row">
|
||||||
<input type="text" id="searchInput" placeholder="Поиск по названию услуги, подразделению, статье...">
|
<input type="text" id="searchInput" placeholder="Поиск по названию услуги, подразделению...">
|
||||||
<select id="statusFilter">
|
<select id="procurementMethodFilter">
|
||||||
<option value="">Все статусы</option>
|
<option value="">Все способы закупки</option>
|
||||||
<option value="100">100% исполнение</option>
|
</select>
|
||||||
<option value="50-99">50-99% исполнение</option>
|
<select id="procurementTypeFilter">
|
||||||
<option value="0-49">0-49% исполнение</option>
|
<option value="">Все виды закупа</option>
|
||||||
|
</select>
|
||||||
|
<select id="budgetTypeFilter">
|
||||||
|
<option value="">Все типы бюджета</option>
|
||||||
|
<option value="БКВ">БКВ</option>
|
||||||
|
<option value="ОБ">ОБ</option>
|
||||||
</select>
|
</select>
|
||||||
<button onclick="applyFilters()">Применить</button>
|
<button onclick="applyFilters()">Применить</button>
|
||||||
<button class="secondary" onclick="resetFilters()">Сбросить</button>
|
<button class="secondary" onclick="resetFilters()">Сбросить</button>
|
||||||
@ -121,11 +126,12 @@
|
|||||||
<th onclick="sort('serviceName')">Наименование ↕</th>
|
<th onclick="sort('serviceName')">Наименование ↕</th>
|
||||||
<th onclick="sort('department')">Подразделение ↕</th>
|
<th onclick="sort('department')">Подразделение ↕</th>
|
||||||
<th onclick="sort('budgetType')">Тип бюджета ↕</th>
|
<th onclick="sort('budgetType')">Тип бюджета ↕</th>
|
||||||
|
<th onclick="sort('procurementMethod')">Способ закупки ↕</th>
|
||||||
|
<th onclick="sort('procurementType')">Вид закупа ↕</th>
|
||||||
<th class="amount" onclick="sort('budgetSum')">Бюджет закупок, тг ↕</th>
|
<th class="amount" onclick="sort('budgetSum')">Бюджет закупок, тг ↕</th>
|
||||||
<th class="amount" onclick="sort('executedSum')">Сумма поставки, тг ↕</th>
|
<th class="amount" onclick="sort('executedSum')">Сумма поставки, тг ↕</th>
|
||||||
<th class="amount" onclick="sort('economy')">Экономия, тг ↕</th>
|
<th class="amount" onclick="sort('economy')">Экономия, тг ↕</th>
|
||||||
<th class="percent" onclick="sort('execPercent')">Исполнение % ↕</th>
|
<th class="percent" onclick="sort('execPercent')">Исполнение % ↕</th>
|
||||||
<th onclick="sort('status')">Статус ↕</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="tableBody"></tbody>
|
<tbody id="tableBody"></tbody>
|
||||||
@ -150,16 +156,39 @@
|
|||||||
let currentSort = null;
|
let currentSort = null;
|
||||||
let sortAsc = true;
|
let sortAsc = true;
|
||||||
|
|
||||||
|
function populateFilters() {
|
||||||
|
const methods = [...new Set(allData.map(r => r.procurementMethod).filter(Boolean))].sort();
|
||||||
|
const types = [...new Set(allData.map(r => r.procurementType).filter(Boolean))].sort();
|
||||||
|
|
||||||
|
const methodSelect = document.getElementById('procurementMethodFilter');
|
||||||
|
const typeSelect = document.getElementById('procurementTypeFilter');
|
||||||
|
|
||||||
|
methods.forEach(m => {
|
||||||
|
const opt = document.createElement('option');
|
||||||
|
opt.value = m;
|
||||||
|
opt.textContent = m;
|
||||||
|
methodSelect.appendChild(opt);
|
||||||
|
});
|
||||||
|
|
||||||
|
types.forEach(t => {
|
||||||
|
const opt = document.createElement('option');
|
||||||
|
opt.value = t;
|
||||||
|
opt.textContent = t;
|
||||||
|
typeSelect.appendChild(opt);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async function loadData() {
|
async function loadData() {
|
||||||
try {
|
try {
|
||||||
const response = await fetch('data.json');
|
const response = await fetch('data.json');
|
||||||
allData = await response.json();
|
allData = await response.json();
|
||||||
filteredData = [...allData];
|
filteredData = [...allData];
|
||||||
|
populateFilters();
|
||||||
renderStats();
|
renderStats();
|
||||||
renderTable();
|
renderTable();
|
||||||
updatePagination();
|
updatePagination();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
document.getElementById('tableBody').innerHTML = '<tr><td colspan="7" class="loading">Ошибка загрузки данных: ' + error.message + '</td></tr>';
|
document.getElementById('tableBody').innerHTML = '<tr><td colspan="9" class="loading">Ошибка загрузки данных: ' + error.message + '</td></tr>';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,7 +213,6 @@
|
|||||||
const totalExecuted = allData.reduce((sum, r) => sum + (r.executedSum || 0), 0);
|
const totalExecuted = allData.reduce((sum, r) => sum + (r.executedSum || 0), 0);
|
||||||
const totalEconomy = allData.reduce((sum, r) => sum + (r.economy || 0), 0);
|
const totalEconomy = allData.reduce((sum, r) => sum + (r.economy || 0), 0);
|
||||||
const avgPercent = totalBudget > 0 ? (totalExecuted / totalBudget) * 100 : 0;
|
const avgPercent = totalBudget > 0 ? (totalExecuted / totalBudget) * 100 : 0;
|
||||||
const fullyExecuted = allData.filter(r => r.execPercent >= 95).length;
|
|
||||||
const bkvCount = allData.filter(r => r.budgetType === 'БКВ').length;
|
const bkvCount = allData.filter(r => r.budgetType === 'БКВ').length;
|
||||||
const obCount = allData.filter(r => r.budgetType === 'ОБ').length;
|
const obCount = allData.filter(r => r.budgetType === 'ОБ').length;
|
||||||
|
|
||||||
@ -209,17 +237,16 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
tbody.innerHTML = pageData.map(function(r) {
|
tbody.innerHTML = pageData.map(function(r) {
|
||||||
const statusClass = getStatusClass(r.execPercent || 0);
|
|
||||||
const statusText = getStatusText(r.execPercent || 0);
|
|
||||||
return '<tr>' +
|
return '<tr>' +
|
||||||
'<td>' + (r.serviceName || '-') + '</td>' +
|
'<td>' + (r.serviceName || '-') + '</td>' +
|
||||||
'<td>' + (r.department || '-') + '</td>' +
|
'<td>' + (r.department || '-') + '</td>' +
|
||||||
'<td>' + (r.budgetType || '-') + '</td>' +
|
'<td>' + (r.budgetType || '-') + '</td>' +
|
||||||
|
'<td>' + (r.procurementMethod || '-') + '</td>' +
|
||||||
|
'<td>' + (r.procurementType || '-') + '</td>' +
|
||||||
'<td class="amount">' + formatAmount(r.budgetSum || 0) + '</td>' +
|
'<td class="amount">' + formatAmount(r.budgetSum || 0) + '</td>' +
|
||||||
'<td class="amount">' + formatAmount(r.executedSum || 0) + '</td>' +
|
'<td class="amount">' + formatAmount(r.executedSum || 0) + '</td>' +
|
||||||
'<td class="amount" style="color: ' + (r.economy > 0 ? '#22c55e' : '#666') + '">' + formatAmount(r.economy || 0) + '</td>' +
|
'<td class="amount" style="color: ' + (r.economy > 0 ? '#22c55e' : '#666') + '">' + formatAmount(r.economy || 0) + '</td>' +
|
||||||
'<td class="percent">' + formatAmount(r.execPercent || 0) + '%</td>' +
|
'<td class="percent">' + formatAmount(r.execPercent || 0) + '%</td>' +
|
||||||
'<td><span class="status-badge ' + statusClass + '">' + statusText + '</span></td>' +
|
|
||||||
'</tr>';
|
'</tr>';
|
||||||
}).join('');
|
}).join('');
|
||||||
}
|
}
|
||||||
@ -236,20 +263,20 @@
|
|||||||
|
|
||||||
function applyFilters() {
|
function applyFilters() {
|
||||||
const search = document.getElementById('searchInput').value.toLowerCase();
|
const search = document.getElementById('searchInput').value.toLowerCase();
|
||||||
const status = document.getElementById('statusFilter').value;
|
const procurementMethod = document.getElementById('procurementMethodFilter').value;
|
||||||
|
const procurementType = document.getElementById('procurementTypeFilter').value;
|
||||||
|
const budgetType = document.getElementById('budgetTypeFilter').value;
|
||||||
|
|
||||||
filteredData = allData.filter(function(r) {
|
filteredData = allData.filter(function(r) {
|
||||||
const matchesSearch = !search ||
|
const matchesSearch = !search ||
|
||||||
(r.serviceName && r.serviceName.toLowerCase().includes(search)) ||
|
(r.serviceName && r.serviceName.toLowerCase().includes(search)) ||
|
||||||
(r.department && r.department.toLowerCase().includes(search)) ||
|
(r.department && r.department.toLowerCase().includes(search));
|
||||||
(r.articleCode && r.articleCode.toLowerCase().includes(search));
|
|
||||||
|
|
||||||
let matchesStatus = true;
|
const matchesMethod = !procurementMethod || r.procurementMethod === procurementMethod;
|
||||||
if (status === '100') matchesStatus = r.execPercent >= 95;
|
const matchesType = !procurementType || r.procurementType === procurementType;
|
||||||
else if (status === '50-99') matchesStatus = r.execPercent >= 50 && r.execPercent < 95;
|
const matchesBudget = !budgetType || r.budgetType === budgetType;
|
||||||
else if (status === '0-49') matchesStatus = r.execPercent < 50;
|
|
||||||
|
|
||||||
return matchesSearch && matchesStatus;
|
return matchesSearch && matchesMethod && matchesType && matchesBudget;
|
||||||
});
|
});
|
||||||
|
|
||||||
currentPage = 1;
|
currentPage = 1;
|
||||||
|
|||||||
@ -36,7 +36,8 @@ function parseExcelData() {
|
|||||||
const executedSumIdx = findColumn(['Сумма поставки,тг']);
|
const executedSumIdx = findColumn(['Сумма поставки,тг']);
|
||||||
const sppElementIdx = findColumn(['СПП-элемент']);
|
const sppElementIdx = findColumn(['СПП-элемент']);
|
||||||
const departmentIdx = findColumn(['Завод', 'МВЗ', 'Текст МВЗ']);
|
const departmentIdx = findColumn(['Завод', 'МВЗ', 'Текст МВЗ']);
|
||||||
const execPercentIdx = findColumn(['% исполнения']);
|
const procurementMethodIdx = findColumn(['Способ закупки']);
|
||||||
|
const procurementTypeIdx = findColumn(['Вид закупа']);
|
||||||
|
|
||||||
const data = [];
|
const data = [];
|
||||||
for (let i = 1; i < rows.length; i++) {
|
for (let i = 1; i < rows.length; i++) {
|
||||||
@ -46,6 +47,8 @@ function parseExcelData() {
|
|||||||
const budgetSum = parseFloat(String(row[budgetSumIdx] || 0).replace(/\s/g, '')) || 0;
|
const budgetSum = parseFloat(String(row[budgetSumIdx] || 0).replace(/\s/g, '')) || 0;
|
||||||
const executedSum = parseFloat(String(row[executedSumIdx] || 0).replace(/\s/g, '')) || 0;
|
const executedSum = parseFloat(String(row[executedSumIdx] || 0).replace(/\s/g, '')) || 0;
|
||||||
const sppElement = String(row[sppElementIdx] || '').trim();
|
const sppElement = String(row[sppElementIdx] || '').trim();
|
||||||
|
const procurementMethod = String(row[procurementMethodIdx] || '').trim();
|
||||||
|
const procurementType = String(row[procurementTypeIdx] || '').trim();
|
||||||
|
|
||||||
// Экономия = Бюджет - Сумма поставки
|
// Экономия = Бюджет - Сумма поставки
|
||||||
const economy = budgetSum - executedSum;
|
const economy = budgetSum - executedSum;
|
||||||
@ -68,6 +71,8 @@ function parseExcelData() {
|
|||||||
department: String(row[departmentIdx] || '').trim(),
|
department: String(row[departmentIdx] || '').trim(),
|
||||||
budgetType: budgetType,
|
budgetType: budgetType,
|
||||||
sppElement: sppElement,
|
sppElement: sppElement,
|
||||||
|
procurementMethod: procurementMethod,
|
||||||
|
procurementType: procurementType,
|
||||||
budgetSum: budgetSum,
|
budgetSum: budgetSum,
|
||||||
executedSum: executedSum,
|
executedSum: executedSum,
|
||||||
economy: economy,
|
economy: economy,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user