2026-05-26 02:39:46 +08:00

229 lines
8.9 KiB
JavaScript

import Add from "@mui/icons-material/Add";
import EditOutlined from "@mui/icons-material/EditOutlined";
import { AdminSwitch } from "@/shared/ui/AdminSwitch.jsx";
import TextField from "@mui/material/TextField";
import Tooltip from "@mui/material/Tooltip";
import { formatMillis } from "@/shared/utils/time.js";
import { DataState } from "@/shared/ui/DataState.jsx";
import { IconButton } from "@/shared/ui/IconButton.jsx";
import { MultiValueAutocomplete } from "@/shared/ui/MultiValueAutocomplete.jsx";
import { regionStatusFilters } from "@/features/host-org/constants.js";
import { HostOrgActionModal } from "@/features/host-org/components/HostOrgActionModal.jsx";
import { HostOrgTable } from "@/features/host-org/components/HostOrgTable.jsx";
import { useHostRegionsPage } from "@/features/host-org/hooks/useHostRegionsPage.js";
import { createOptionsColumnFilter, createTextColumnFilter } from "@/shared/ui/tableFilters.js";
import styles from "@/features/host-org/host-org.module.css";
const baseRegionColumns = [
{ key: "regionId", label: "区域 ID", width: "minmax(90px, 0.6fr)" },
{ key: "regionCode", label: "区域编码", width: "minmax(110px, 0.8fr)" },
{ key: "name", label: "区域名称", width: "minmax(150px, 1fr)" },
{
key: "countries",
label: "国家码",
render: (item) => <CountryCodes countries={item.countries} />,
width: "minmax(220px, 1.5fr)",
},
{ key: "sortOrder", label: "排序", width: "minmax(80px, 0.5fr)" },
{
key: "updatedAtMs",
label: "更新时间",
render: (item) => formatMillis(item.updatedAtMs),
width: "minmax(170px, 1fr)",
},
];
export function HostRegionsPage() {
const page = useHostRegionsPage();
const createDisabled = !page.abilities.canCreate;
const updateDisabled = !page.abilities.canUpdate;
const items = page.data.items || [];
const total = page.data.total || 0;
const regionColumns = [
...baseRegionColumns.slice(0, 3),
{
key: "status",
label: "状态",
render: (item) => <RegionStatusSwitch item={item} page={page} />,
filter: createOptionsColumnFilter({
options: regionStatusFilters,
placeholder: "搜索状态",
value: page.status,
onChange: page.changeStatus,
}),
width: "minmax(90px, 0.6fr)",
},
...baseRegionColumns.slice(3),
].map((column) =>
column.key === "regionCode"
? {
...column,
filter: createTextColumnFilter({
placeholder: "搜索区域、国家码",
value: page.query,
onChange: page.changeQuery,
}),
}
: column,
);
const columns = page.abilities.canUpdate
? [
...regionColumns,
{
key: "actions",
label: "操作",
render: (item) => <RegionActions item={item} page={page} />,
width: "minmax(80px, 0.5fr)",
},
]
: regionColumns;
return (
<section className={styles.root}>
<div className={styles.contentPanel}>
{page.abilities.canCreate ? (
<div className={styles.toolbar}>
<div className={styles.toolbarActions}>
<Tooltip arrow title="创建区域">
<span>
<IconButton label="创建区域" onClick={page.openCreateRegion} sx={primaryActionSx}>
<Add fontSize="small" />
</IconButton>
</span>
</Tooltip>
</div>
</div>
) : null}
<DataState error={page.error} loading={page.loading} onRetry={page.reload}>
<div className={styles.listBlock}>
<HostOrgTable
columns={columns}
items={items}
rowKey={(item) => item.regionId}
pagination={{ page: 1, pageSize: Math.max(items.length, 1), total }}
/>
</div>
</DataState>
</div>
<HostOrgActionModal
disabled={page.activeAction === "edit" ? updateDisabled : createDisabled}
loading={page.loadingAction === "region-create" || page.loadingAction === "region-edit"}
open={page.activeAction === "create" || page.activeAction === "edit"}
sectionTitle="区域信息"
onClose={page.closeAction}
onSubmit={page.submitRegion}
title={page.activeAction === "edit" ? "编辑区域" : "创建区域"}
>
<TextField
disabled={page.activeAction === "edit" ? updateDisabled : createDisabled}
label="区域编码"
required
value={page.regionForm.regionCode}
onChange={(event) => page.setRegionForm({ ...page.regionForm, regionCode: event.target.value })}
/>
<TextField
disabled={page.activeAction === "edit" ? updateDisabled : createDisabled}
label="区域名称"
required
value={page.regionForm.name}
onChange={(event) => page.setRegionForm({ ...page.regionForm, name: event.target.value })}
/>
<TextField
disabled={page.activeAction === "edit" ? updateDisabled : createDisabled}
label="排序"
type="number"
value={page.regionForm.sortOrder}
onChange={(event) => page.setRegionForm({ ...page.regionForm, sortOrder: event.target.value })}
/>
<CountryCodeSelect
disabled={
page.isGlobalRegionForm || (page.activeAction === "edit" ? updateDisabled : createDisabled)
}
label={page.isGlobalRegionForm ? "国家码" : "国家码 *"}
labels={page.countryLabels}
loading={page.loadingCountries}
options={page.countryOptions}
value={page.regionForm.countries}
onChange={(countries) => page.setRegionForm({ ...page.regionForm, countries })}
/>
</HostOrgActionModal>
</section>
);
}
function RegionActions({ item, page }) {
if (item.regionCode === "GLOBAL") {
return "-";
}
return (
<div className={styles.rowActions}>
{page.abilities.canUpdate ? (
<Tooltip arrow title="编辑区域">
<span>
<IconButton label="编辑区域" onClick={() => page.openEditRegion(item)}>
<EditOutlined fontSize="small" />
</IconButton>
</span>
</Tooltip>
) : null}
</div>
);
}
function RegionStatusSwitch({ item, page }) {
const checked = item.status === "active";
const isGlobal = item.regionCode === "GLOBAL";
return (
<AdminSwitch
checked={checked}
disabled={isGlobal || !page.abilities.canStatus || page.loadingAction === `region-status-${item.regionId}`}
inputProps={{ "aria-label": checked ? "停用区域" : "启用区域" }}
onChange={(event) => page.toggleRegion(item, event.target.checked)}
/>
);
}
function CountryCodeSelect({ disabled, label, labels, loading, onChange, options, value }) {
const selected = value || [];
const mergedOptions = [...new Set([...options, ...selected])].sort();
return (
<MultiValueAutocomplete
disabled={disabled}
label={label}
labels={labels}
loading={loading}
options={mergedOptions}
value={selected}
onChange={onChange}
/>
);
}
function CountryCodes({ countries = [] }) {
const items = Array.isArray(countries) ? countries : [];
if (!items.length) {
return "-";
}
return (
<div className="admin-tag-list">
{items.map((country) => (
<span className="admin-tag" key={country}>
{country}
</span>
))}
</div>
);
}
const primaryActionSx = {
borderColor: "var(--primary)",
backgroundColor: "var(--primary)",
color: "var(--active-contrast)",
"&:hover": {
borderColor: "var(--primary-strong)",
backgroundColor: "var(--primary-strong)",
color: "var(--active-contrast)",
},
};