ывыфвфы

This commit is contained in:
dv 2026-04-08 23:31:07 +03:00
parent b618bdf0da
commit 0c6b949424
3 changed files with 52 additions and 14 deletions

View file

@ -1,12 +1,9 @@
"""Global devices endpoint — cross-object device listing with filters."""
import logging
from typing import Optional
from fastapi import APIRouter, Depends, Query
from sqlalchemy import select
logger = logging.getLogger(__name__)
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import selectinload
@ -37,8 +34,6 @@ async def list_all_devices(
- All bank terminals for a client: ?category=bank_terminal&client=Авангард
- All devices in a city: ?city=Санкт-Петербург
"""
logger.info("list_all_devices called: category=%r city=%r client=%r object_id=%r status=%r", category, city, client, object_id, status)
query = (
select(Device)
.join(Object, Device.object_id == Object.id)

View file

@ -49,6 +49,8 @@ async def _resolve_scope(scope: dict) -> list[tuple[Device, Object]]:
scope_type = scope.get("type")
if scope_type == "device":
query = query.where(Device.id == scope["id"])
elif scope_type == "device_list":
query = query.where(Device.id.in_(scope["ids"]))
elif scope_type == "object":
query = query.where(Device.object_id == scope["id"])
elif scope_type == "rack":
@ -63,17 +65,37 @@ async def _resolve_scope(scope: dict) -> list[tuple[Device, Object]]:
return []
query = query.where(Device.rack_id.in_(rack_ids))
elif scope_type == "category":
query = query.where(Device.category == scope["value"])
# support both "value" (legacy) and "category" key
cat = scope.get("category") or scope.get("value")
if cat:
query = query.where(Device.category == cat)
if "object_ids" in scope:
query = query.where(Device.object_id.in_(scope["object_ids"]))
elif "object_id" in scope:
query = query.where(Device.object_id == scope["object_id"])
elif scope_type == "role":
query = query.where(Device.role == scope["value"])
role = scope.get("role") or scope.get("value")
if role:
query = query.where(Device.role == role)
if "object_ids" in scope:
query = query.where(Device.object_id.in_(scope["object_ids"]))
elif "object_id" in scope:
query = query.where(Device.object_id == scope["object_id"])
elif scope_type == "filter":
# Cross-object filter scope (from WorkDevices global page)
from app.models.object import Object as Obj
if scope.get("category"):
cats = scope["category"]
if isinstance(cats, list):
query = query.where(Device.category.in_(cats))
else:
query = query.where(Device.category == cats)
if scope.get("object_id"):
query = query.where(Device.object_id == scope["object_id"])
if scope.get("city"):
query = query.where(Obj.city == scope["city"])
if scope.get("client"):
query = query.where(Obj.client == scope["client"])
else:
return []

View file

@ -52,6 +52,7 @@ export function WorkDevicesPage() {
const [filterClient, setFilterClient] = useState<string | undefined>()
const [filterObjectId, setFilterObjectId] = useState<number | undefined>()
const [filterStatus, setFilterStatus] = useState<string | undefined>()
const [searchText, setSearchText] = useState('')
const [selectedRowKeys, setSelectedRowKeys] = useState<number[]>([])
const [taskModal, setTaskModal] = useState(false)
@ -68,6 +69,14 @@ export function WorkDevicesPage() {
const currentAction = actions?.find((a) => a.name === selectedAction)
const displayedDevices = useMemo(() => {
if (!searchText) return devices ?? []
const q = searchText.toLowerCase()
return (devices ?? []).filter(
(d) => d.ip.toLowerCase().includes(q) || (d.hostname ?? '').toLowerCase().includes(q)
)
}, [devices, searchText])
// Object dropdown filtered by active city/client selectors
const objectOptions = useMemo(() => {
return (allObjects ?? [])
@ -275,15 +284,27 @@ export function WorkDevicesPage() {
</Col>
</Row>
<Row gutter={12} style={{ marginBottom: 12 }} align="middle">
<Col flex="auto">
<Input.Search
placeholder="Поиск по IP или hostname..."
allowClear
style={{ maxWidth: 360 }}
onChange={(e) => setSearchText(e.target.value)}
/>
</Col>
<Col>
{devices && (
<Typography.Text type="secondary" style={{ marginBottom: 8, display: 'block' }}>
Найдено: {devices.length} устройств
<Typography.Text type="secondary">
{searchText ? `${displayedDevices.length} из ${devices.length}` : `${devices.length} устройств`}
{selectedRowKeys.length > 0 && ` · Выбрано: ${selectedRowKeys.length}`}
</Typography.Text>
)}
</Col>
</Row>
<Table
dataSource={devices ?? []}
dataSource={displayedDevices}
columns={columns}
rowKey="id"
loading={isLoading}