diff --git a/__pycache__/app.cpython-314.pyc b/__pycache__/app.cpython-314.pyc index 3e128f4..47e276b 100644 Binary files a/__pycache__/app.cpython-314.pyc and b/__pycache__/app.cpython-314.pyc differ diff --git a/app.py b/app.py index 6650140..dfd604e 100644 --- a/app.py +++ b/app.py @@ -1943,6 +1943,14 @@ def init_db(): ) """ ) + cur.execute( + """ + CREATE TABLE IF NOT EXISTS client_manage_icons ( + client_id INTEGER PRIMARY KEY REFERENCES client_test_devices(id) ON DELETE CASCADE, + icon_label TEXT NOT NULL DEFAULT '' + ) + """ + ) cur.execute( """ SELECT column_name @@ -3016,7 +3024,8 @@ def index(): NULLIF(BTRIM(cmi.icon_label), '') AS icon_label, c.inventory_number, c.brand, - c.model + c.model, + c.user_name AS full_name FROM computers c LEFT JOIN cabinets cab ON cab.id = c.cabinet_id LEFT JOIN computer_manage_icons cmi ON cmi.computer_id = c.id @@ -3033,12 +3042,14 @@ def index(): COALESCE(NULLIF(BTRIM(cab.organization), ''), NULLIF(BTRIM(t.building), ''), NULLIF(BTRIM(cab.building), ''), 'Без организации') AS organization_name, NULLIF(BTRIM(cab.floor), '') AS floor_name, COALESCE(NULLIF(BTRIM(cab.cabinet), ''), NULLIF(BTRIM(cab.name), ''), 'Без кабинета') AS cabinet_name, - NULLIF(BTRIM(COALESCE(cab.cabinet, cab.name, t.hostname, t.inventory_number)), '') AS icon_label, + NULLIF(BTRIM(ctmi.icon_label), '') AS icon_label, t.inventory_number, '' AS brand, - t.model + t.model, + t.full_name FROM client_test_devices t LEFT JOIN cabinets cab ON cab.id = t.cabinet_id + LEFT JOIN client_manage_icons ctmi ON ctmi.client_id = t.id WHERE COALESCE(BTRIM(t.rustdesk_id), '') <> '' AND COALESCE(BTRIM(t.rustdesk_password), '') <> '' """ @@ -3081,12 +3092,19 @@ def index(): inv, brand, model, + full_name, ) in computers_manage_rows: org_label = _group_label(organization_name, "Без организации") floor_label = _group_label(floor_name, "Без этажа") cabinet_label = _group_label(cabinet_name, "Без кабинета") - icon_label_value = _group_label(icon_label, cabinet_label) comp_name = " ".join(p for p in [brand or "", model or ""] if p).strip() + full_name_label = (full_name or "").strip() + default_icon_label = " — ".join( + part for part in [cabinet_label, full_name_label] if part + ) + if not default_icon_label: + default_icon_label = inv or comp_name or f"Компьютер #{comp_id}" + icon_label_value = _group_label(icon_label, default_icon_label) is_online_now = bool(is_online) if last_seen and now - last_seen > offline_after: is_online_now = False @@ -3103,6 +3121,7 @@ def index(): "rustdesk_password": rustdesk_password or "", "cabinet_name": cabinet_label, "icon_label": icon_label_value, + "default_icon_label": default_icon_label, "inventory_number": inv or "", "brand": brand or "", "model": model or "", diff --git a/handlers/__pycache__/computers.cpython-314.pyc b/handlers/__pycache__/computers.cpython-314.pyc index 0de651d..7e40641 100644 Binary files a/handlers/__pycache__/computers.cpython-314.pyc and b/handlers/__pycache__/computers.cpython-314.pyc differ diff --git a/handlers/computers.py b/handlers/computers.py index 77db6f1..5bcdd1a 100644 --- a/handlers/computers.py +++ b/handlers/computers.py @@ -1,38 +1,56 @@ from datetime import datetime, timedelta -from flask import flash, redirect, render_template, request, url_for +from flask import flash, jsonify, redirect, render_template, request, url_for def computers_manage_icon_label(*, get_conn, clean_text, safe_next_url): - computer_id_raw = clean_text(request.form.get("computer_id")) - icon_label = clean_text(request.form.get("icon_label")) - next_url = safe_next_url(request.form.get("next"), fallback="index") - if not computer_id_raw.isdigit(): + payload = request.get_json(silent=True) if request.is_json else request.form + payload = payload or {} + source_type = clean_text(payload.get("source_type")) or "computer" + source_id_raw = clean_text(payload.get("source_id") or payload.get("computer_id")) + icon_label = clean_text(payload.get("icon_label"))[:120] + next_url = safe_next_url(payload.get("next"), fallback="index") + + source_settings = { + "computer": ("computers", "computer_manage_icons", "computer_id"), + "client_test": ("client_test_devices", "client_manage_icons", "client_id"), + } + if source_type not in source_settings or not source_id_raw.isdigit(): + if request.is_json: + return jsonify(ok=False, error="invalid computer source"), 400 return redirect(next_url) - computer_id = int(computer_id_raw) + source_id = int(source_id_raw) + source_table, labels_table, source_column = source_settings[source_type] conn = get_conn() cur = conn.cursor() - cur.execute("SELECT 1 FROM computers WHERE id=%s", (computer_id,)) + cur.execute(f"SELECT 1 FROM {source_table} WHERE id=%s", (source_id,)) exists = cur.fetchone() is not None if not exists: conn.close() + if request.is_json: + return jsonify(ok=False, error="computer not found"), 404 return redirect(next_url) if icon_label: cur.execute( - """ - INSERT INTO computer_manage_icons (computer_id, icon_label) + f""" + INSERT INTO {labels_table} ({source_column}, icon_label) VALUES (%s, %s) - ON CONFLICT (computer_id) + ON CONFLICT ({source_column}) DO UPDATE SET icon_label = EXCLUDED.icon_label """, - (computer_id, icon_label), + (source_id, icon_label), ) else: - cur.execute("DELETE FROM computer_manage_icons WHERE computer_id=%s", (computer_id,)) + cur.execute( + f"DELETE FROM {labels_table} WHERE {source_column}=%s", + (source_id,), + ) conn.commit() conn.close() + if request.is_json: + return jsonify(ok=True, icon_label=icon_label) return redirect(next_url) @@ -301,4 +319,3 @@ def computers_delete(*, get_conn): conn.close() flash("Компьютер удалён") return redirect(url_for("computers")) - diff --git a/templates/home.html b/templates/home.html index 6379ae1..6d51fc4 100644 --- a/templates/home.html +++ b/templates/home.html @@ -665,6 +665,35 @@ .home-computers-manage-btn__status.is-offline { color: #d62828; } + .home-computer-context-menu { + position: fixed; + z-index: 1600; + min-width: 170px; + padding: 3px; + border: 1px solid #000; + background: #dcdcdc; + box-shadow: 2px 2px 0 #777; + } + .home-computer-context-menu.hidden { + display: none; + } + .home-computer-context-menu__item { + display: block; + width: 100%; + border: 0; + background: transparent; + color: #000; + padding: 5px 22px 5px 8px; + text-align: left; + font: inherit; + cursor: default; + } + .home-computer-context-menu__item:hover, + .home-computer-context-menu__item:focus { + outline: 0; + background: #000080; + color: #fff; + } .home-rustdesk-connecting { position: fixed; right: 16px; @@ -1031,6 +1060,7 @@ data-computer-id="{{ comp.id }}" data-source-type="{{ comp.source_type }}" data-icon-label="{{ comp.icon_label }}" + data-default-icon-label="{{ comp.default_icon_label }}" > - {% if session.get('role') in ('admin','storekeeper') and comp.source_type == 'computer' %} -
- - - -
- {% endif %} {% endfor %} @@ -1059,6 +1082,10 @@ +