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' %}
-