import { Button, Card, Drawer, Form, Input, Popconfirm, Select, Space, Table, Tag, Typography, message } from "antd"; import { DeleteOutlined, DesktopOutlined, EditOutlined, PlusOutlined, SearchOutlined, UserOutlined } from "@ant-design/icons"; import { useEffect, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { createDevice, deleteDevice, listDevices, listUsers, updateDevice } from "@/api"; import PageHeader from "@/components/shared/PageHeader"; import { useDict } from "@/hooks/useDict"; import { usePermission } from "@/hooks/usePermission"; import type { DeviceInfo, SysUser } from "@/types"; import { getStandardPagination } from "@/utils/pagination"; import "./index.less"; const { Text } = Typography; type DeviceFormValues = { userId: number; deviceCode: string; deviceName?: string; status: number; }; export default function Devices() { const { t } = useTranslation(); const { can } = usePermission(); const { items: statusDict } = useDict("sys_common_status"); const [loading, setLoading] = useState(false); const [saving, setSaving] = useState(false); const [searchText, setSearchText] = useState(""); const handleSearch = () => {}; const handleResetSearch = () => { setSearchText(""); }; const [devices, setDevices] = useState([]); const [users, setUsers] = useState([]); const [open, setOpen] = useState(false); const [editing, setEditing] = useState(null); const [form] = Form.useForm(); const loadData = async () => { setLoading(true); try { const [deviceList, userList] = await Promise.all([listDevices(), listUsers()]); setDevices(deviceList || []); setUsers(userList || []); } finally { setLoading(false); } }; useEffect(() => { loadData(); }, []); const userMap = useMemo(() => { const map: Record = {}; users.forEach((user) => { map[user.userId] = user; }); return map; }, [users]); const filteredData = useMemo(() => { if (!searchText) { return devices; } const lower = searchText.toLowerCase(); return devices.filter((device) => { const owner = userMap[device.userId]; return ( device.deviceCode.toLowerCase().includes(lower) || (device.deviceName || "").toLowerCase().includes(lower) || (owner?.displayName || "").toLowerCase().includes(lower) || String(device.userId).includes(lower) ); }); }, [devices, searchText, userMap]); const openCreate = () => { setEditing(null); form.resetFields(); form.setFieldsValue({ status: 1 }); setOpen(true); }; const openEdit = (record: DeviceInfo) => { setEditing(record); form.setFieldsValue({ userId: record.userId, deviceCode: record.deviceCode, deviceName: record.deviceName, status: record.status ?? 1 }); setOpen(true); }; const submit = async () => { const values = await form.validateFields(); setSaving(true); try { const payload: Partial = { userId: values.userId, deviceCode: values.deviceCode, deviceName: values.deviceName, status: values.status }; if (editing) { await updateDevice(editing.deviceId, payload); } else { await createDevice(payload); } message.success(t("devicesExt.operationSucceeded")); setOpen(false); await loadData(); } finally { setSaving(false); } }; const remove = async (id: number) => { await deleteDevice(id); message.success(t("devicesExt.operationSucceeded")); await loadData(); }; return (
} style={{ width: 360 }} value={searchText} onChange={(event) => setSearchText(event.target.value)} allowClear aria-label={t("devicesExt.searchLabel")} />
{can("device:create") ? ( ) : null}
rowKey="deviceId" dataSource={filteredData} loading={loading} size="middle" scroll={{ y: "calc(100vh - 350px)" }} pagination={getStandardPagination(filteredData.length, 1, 1000)} columns={[ { title: t("devicesExt.device"), key: "device", render: (_value: unknown, record) => (
{record.deviceName || t("devicesExt.unnamedDevice")}
{record.deviceCode}
) }, { title: t("devices.owner"), key: "user", render: (_value: unknown, record) => { const owner = userMap[record.userId]; return owner ? ( ) : ( {t("devicesExt.ownerId")}: {record.userId} ); } }, { title: t("common.status"), dataIndex: "status", width: 100, render: (status: number) => { const item = statusDict.find((dictItem) => dictItem.itemValue === String(status)); return {item?.itemLabel || (status === 1 ? t("devicesExt.enabled") : t("devicesExt.disabled"))}; } }, { title: t("devices.updateTime"), dataIndex: "updatedAt", width: 180, render: (text: string) => ( {text?.replace("T", " ").substring(0, 19)} ) }, { title: t("common.action"), key: "action", width: 120, fixed: "right", render: (_value: unknown, record) => ( {can("device:update") ? (
} open={open} onClose={() => setOpen(false)} width={420} destroyOnClose footer={
} >