diff --git a/VERSION b/VERSION
index ee358a3..1ad6b81 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.0.130
+0.0.131
diff --git a/frontend/settings.html b/frontend/settings.html
index cfc7b34..8b510d9 100644
--- a/frontend/settings.html
+++ b/frontend/settings.html
@@ -56,7 +56,7 @@
- | ID | Камера | IP | Потоки | Вкл | Запись | Хранилище | Статус | |
+ | ID | Камера | IP | Потоки | Вкл | Запись | Хранилище | Статус | |
diff --git a/frontend/static/app.js b/frontend/static/app.js
index f81f416..ae55709 100644
--- a/frontend/static/app.js
+++ b/frontend/static/app.js
@@ -1168,12 +1168,14 @@ const CamSettings = {
if (!this.cams.length) { alert("Нет камер для редактирования"); return; }
this.bulk = true; this.editing = true;
this.setBulkButtons();
+ this.setBulkHeaders(true);
this.load();
},
async exitBulk() {
this.bulk = false; this.editing = false;
this.setBulkButtons();
+ this.setBulkHeaders(false);
await this.load();
},
@@ -1194,34 +1196,44 @@ const CamSettings = {
alert(`Удалено камер: ${d.removed != null ? d.removed : n}`);
},
- // ── строка камеры в режиме массового редактирования ──
+ // ── строка камеры в режиме массового редактирования: те же колонки, но ячейки —
+ // редактируемые инпуты (НЕ отдельная форма). Статус-колонка на время правки = логин/пароль.
bulkRowHtml(slotNo, cam) {
const v = (x) => this.esc(x);
return (
- `` +
- ` #${slotNo} · ID ${v(cam.id)} ` +
- `` +
- `` +
- `` +
- `` +
- `` +
- `` +
- `` +
- `` +
- `` +
- `` +
- ` ` +
- ` | `
+ `${slotNo} | ` +
+ ` | ` +
+ ` | ` +
+ `` +
+ `` +
+ `` +
+ ` | ` +
+ ` | ` +
+ ` | ` +
+ ` | ` +
+ `` +
+ `` +
+ `` +
+ ` | ` +
+ ` | `
);
},
+ // подмена заголовков колонок на время массового редактирования (Статус → Логин/Пароль)
+ setBulkHeaders(on) {
+ document.querySelectorAll("table.cams th[data-bulk-label]").forEach((th) => {
+ if (on) { if (th.dataset.orig == null) th.dataset.orig = th.textContent; th.textContent = th.dataset.bulkLabel; }
+ else if (th.dataset.orig != null) th.textContent = th.dataset.orig;
+ });
+ },
+
async saveBulk() {
- const grids = document.querySelectorAll(".cam-edit-grid[data-cam]");
+ const rows = document.querySelectorAll("#cam-rows tr[data-id]");
const errors = [];
- for (const g of grids) {
- const id = g.dataset.cam;
+ for (const tr of rows) {
+ const id = tr.dataset.id;
const orig = this.byId[id] || {};
- const get = (f) => { const el = g.querySelector(`[data-f="${f}"]`); return el ? el.value : ""; };
+ const get = (f) => { const el = tr.querySelector(`[data-f="${f}"]`); return el ? el.value : ""; };
const payload = {
name: get("name").trim(),
ip: get("ip").trim(),
@@ -1230,8 +1242,8 @@ const CamSettings = {
main_path: get("main_path").trim(),
sub_path: get("sub_path").trim(),
storage_id: get("storage_id"),
- enabled: g.querySelector('[data-f="enabled"]').checked,
- record: g.querySelector('[data-f="record"]').checked,
+ enabled: tr.querySelector('[data-f="enabled"]').checked,
+ record: tr.querySelector('[data-f="record"]').checked,
};
const changed =
payload.name !== orig.name || payload.ip !== orig.ip ||
@@ -1250,6 +1262,7 @@ const CamSettings = {
}
this.bulk = false; this.editing = false;
this.setBulkButtons();
+ this.setBulkHeaders(false);
await this.load();
if (errors.length) alert("Не сохранено:\n" + errors.join("\n"));
},
@@ -1352,10 +1365,10 @@ const CamSettings = {
);
tb.innerHTML = "";
if (this.bulk) {
- // режим массового редактирования: только существующие камеры, форма на каждую
+ // массовое редактирование: те же строки/колонки, но ячейки — редактируемые инпуты
cams.forEach((c, i) => {
const tr = document.createElement("tr");
- tr.className = "cam-edit-row";
+ tr.className = "bulk-row";
tr.dataset.id = c.id;
tr.innerHTML = this.bulkRowHtml(i + 1, c);
tb.appendChild(tr);
@@ -1433,6 +1446,7 @@ const CamSettings = {
};
tb.onchange = async (e) => { // в строке: «Вкл», «REC» (тумблеры) и «Хранилище» (select)
+ if (this.bulk) return; // в массовом режиме изменения копятся до «Сохранить»
const en = e.target.closest(".en-toggle");
const rec = e.target.closest(".rec-toggle");
const stor = e.target.closest(".stor-sel");
diff --git a/frontend/static/style.css b/frontend/static/style.css
index f197a7f..28730ec 100644
--- a/frontend/static/style.css
+++ b/frontend/static/style.css
@@ -443,6 +443,17 @@ table.cams td select.stor-sel {
border-radius: 5px; width: 100%; max-width: 150px; box-sizing: border-box;
vertical-align: middle;
}
+/* массовое редактирование: инпуты прямо в ячейках, компактные (высота строки не растёт) */
+table.cams td input.cell-in {
+ height: 22px; padding: 1px 6px; font-size: 12px; line-height: 1;
+ border-radius: 5px; width: 100%; box-sizing: border-box; vertical-align: middle;
+ background: var(--panel-2); border: 1px solid var(--border); color: var(--text);
+}
+table.cams td input.cell-in:focus { outline: none; border-color: #c0392b; }
+table.cams td.cell-dual { white-space: nowrap; }
+table.cams td.cell-dual input.cell-in { display: inline-block; width: calc(50% - 3px); }
+table.cams td.cell-dual input.cell-in + input.cell-in { margin-left: 4px; }
+table.cams tr.bulk-row td { background: rgba(192, 57, 43, .05); }
.badge {
display: inline-flex; align-items: center; gap: 6px; padding: 2px 9px;
border-radius: 11px; font-size: 12px; background: var(--panel-2);