ывыфвфы
This commit is contained in:
parent
b618bdf0da
commit
0c6b949424
3 changed files with 52 additions and 14 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 []
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
{devices && (
|
||||
<Typography.Text type="secondary" style={{ marginBottom: 8, display: 'block' }}>
|
||||
Найдено: {devices.length} устройств
|
||||
{selectedRowKeys.length > 0 && ` · Выбрано: ${selectedRowKeys.length}`}
|
||||
</Typography.Text>
|
||||
)}
|
||||
<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">
|
||||
{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}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue