ывыфвфы

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.""" """Global devices endpoint — cross-object device listing with filters."""
import logging
from typing import Optional from typing import Optional
from fastapi import APIRouter, Depends, Query from fastapi import APIRouter, Depends, Query
from sqlalchemy import select from sqlalchemy import select
logger = logging.getLogger(__name__)
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import selectinload 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 bank terminals for a client: ?category=bank_terminal&client=Авангард
- All devices in a city: ?city=Санкт-Петербург - 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 = ( query = (
select(Device) select(Device)
.join(Object, Device.object_id == Object.id) .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") scope_type = scope.get("type")
if scope_type == "device": if scope_type == "device":
query = query.where(Device.id == scope["id"]) query = query.where(Device.id == scope["id"])
elif scope_type == "device_list":
query = query.where(Device.id.in_(scope["ids"]))
elif scope_type == "object": elif scope_type == "object":
query = query.where(Device.object_id == scope["id"]) query = query.where(Device.object_id == scope["id"])
elif scope_type == "rack": elif scope_type == "rack":
@ -63,17 +65,37 @@ async def _resolve_scope(scope: dict) -> list[tuple[Device, Object]]:
return [] return []
query = query.where(Device.rack_id.in_(rack_ids)) query = query.where(Device.rack_id.in_(rack_ids))
elif scope_type == "category": 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: if "object_ids" in scope:
query = query.where(Device.object_id.in_(scope["object_ids"])) query = query.where(Device.object_id.in_(scope["object_ids"]))
elif "object_id" in scope: elif "object_id" in scope:
query = query.where(Device.object_id == scope["object_id"]) query = query.where(Device.object_id == scope["object_id"])
elif scope_type == "role": 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: if "object_ids" in scope:
query = query.where(Device.object_id.in_(scope["object_ids"])) query = query.where(Device.object_id.in_(scope["object_ids"]))
elif "object_id" in scope: elif "object_id" in scope:
query = query.where(Device.object_id == scope["object_id"]) 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: else:
return [] return []

View file

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