98 lines
3.6 KiB
JavaScript
98 lines
3.6 KiB
JavaScript
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 (
|
||
<div className="ops-view">
|
||
<AdminListToolbar
|
||
filters={
|
||
<>
|
||
<AdminSearchBox label="应用" placeholder="应用编码 / 名称" value={keyword} onChange={setKeyword} />
|
||
<AdminFilterSelect
|
||
label="状态"
|
||
options={[
|
||
["", "全部状态"],
|
||
["active", "已启用"],
|
||
["disabled", "已停用"]
|
||
]}
|
||
value={status}
|
||
onChange={setStatus}
|
||
/>
|
||
<AdminFilterResetButton
|
||
disabled={!keyword && !status}
|
||
onClick={() => {
|
||
setKeyword("");
|
||
setStatus("");
|
||
}}
|
||
/>
|
||
</>
|
||
}
|
||
/>
|
||
<DataTable
|
||
columns={appColumns}
|
||
emptyLabel="暂无应用"
|
||
items={items}
|
||
minWidth="880px"
|
||
rowKey={(item) => item.app_code ?? item.appCode}
|
||
title="应用列表"
|
||
total={items.length}
|
||
/>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
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" ? (
|
||
<OpsStatusBadge label="已启用" tone="succeeded" />
|
||
) : (
|
||
<OpsStatusBadge label={item.status || "-"} />
|
||
),
|
||
width: "minmax(112px, 0.6fr)"
|
||
},
|
||
{
|
||
key: "source",
|
||
label: "来源",
|
||
// fixed 来源是 admin-server 为外部幸运礼物接入硬编码补齐的白名单 App(yumi/aslan),
|
||
// 不在 app registry 主数据里,运营需要知道它们没有包名/平台等完整信息。
|
||
render: (item) =>
|
||
item.source === "fixed" ? <OpsStatusBadge label="外部接入" tone="warning" /> : <OpsStatusBadge label="主数据" tone="succeeded" />,
|
||
width: "minmax(124px, 0.7fr)"
|
||
},
|
||
{
|
||
key: "updated_at_ms",
|
||
label: "更新时间",
|
||
render: (item) => (item.source === "fixed" ? "-" : <TimeText value={item.updated_at_ms ?? item.updatedAtMs} />),
|
||
width: "minmax(150px, 1fr)"
|
||
}
|
||
];
|