import { useMemo, useState } from "react"; import { AdminFilterResetButton, AdminFilterSelect, AdminListToolbar, AdminSearchBox } from "@/shared/ui/AdminListLayout.jsx"; import { DataTable } from "@/shared/ui/DataTable.jsx"; import { TimeText } from "@/shared/ui/TimeText.jsx"; import { OpsStatusBadge } from "./OpsStatusBadge.jsx"; export function AppsView({ apps }) { const [keyword, setKeyword] = useState(""); const [status, setStatus] = useState(""); // 应用主数据总量很小(个位数),筛选全部在前端完成,不回源。 const items = useMemo(() => { const normalizedKeyword = keyword.trim().toLowerCase(); return apps.filter((item) => { const code = String(item.app_code ?? item.appCode ?? "").toLowerCase(); const name = String(item.app_name ?? item.appName ?? "").toLowerCase(); if (normalizedKeyword && !code.includes(normalizedKeyword) && !name.includes(normalizedKeyword)) { return false; } if (status && String(item.status || "").toLowerCase() !== status) { return false; } return true; }); }, [apps, keyword, status]); return (
{ setKeyword(""); setStatus(""); }} /> } /> item.app_code ?? item.appCode} title="应用列表" total={items.length} />
); } const appColumns = [ { key: "app_code", label: "应用编码", render: (item) => item.app_code ?? item.appCode, width: "minmax(100px, 0.8fr)" }, { key: "app_name", label: "应用名称", render: (item) => item.app_name ?? item.appName }, { key: "platform", label: "平台", render: (item) => item.platform || "-", width: "minmax(80px, 0.6fr)" }, { key: "package_name", label: "包名", render: (item) => item.package_name ?? item.packageName ?? "-", width: "minmax(180px, 1.4fr)" }, { key: "status", label: "状态", render: (item) => String(item.status || "").toLowerCase() === "active" ? ( ) : ( ), width: "minmax(112px, 0.6fr)" }, { key: "source", label: "来源", // fixed 来源是 admin-server 为外部幸运礼物接入硬编码补齐的白名单 App(yumi/aslan), // 不在 app registry 主数据里,运营需要知道它们没有包名/平台等完整信息。 render: (item) => item.source === "fixed" ? : , width: "minmax(124px, 0.7fr)" }, { key: "updated_at_ms", label: "更新时间", render: (item) => (item.source === "fixed" ? "-" : ), width: "minmax(150px, 1fr)" } ];