工资相关
This commit is contained in:
parent
1447de8563
commit
64cfa472b8
@ -34,8 +34,23 @@ test("host org list APIs use generated admin paths and filters", async () => {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
await listBDs({ page: 1, page_size: 10, parent_leader_user_id: 21, region_id: 7, status: "active" });
|
await listBDs({
|
||||||
await listAgencies({ keyword: "agency", page: 2, page_size: 10, parent_bd_user_id: 31 });
|
page: 1,
|
||||||
|
page_size: 10,
|
||||||
|
parent_leader_user_id: 21,
|
||||||
|
region_id: 7,
|
||||||
|
sort_by: "salary_wallet",
|
||||||
|
sort_direction: "asc",
|
||||||
|
status: "active",
|
||||||
|
});
|
||||||
|
await listAgencies({
|
||||||
|
keyword: "agency",
|
||||||
|
page: 2,
|
||||||
|
page_size: 10,
|
||||||
|
parent_bd_user_id: 31,
|
||||||
|
sort_by: "salary_wallet",
|
||||||
|
sort_direction: "desc",
|
||||||
|
});
|
||||||
await listHosts({ agency_id: 41, page: 1, page_size: 10, sort_by: "diamond", sort_direction: "desc" });
|
await listHosts({ agency_id: 41, page: 1, page_size: 10, sort_by: "diamond", sort_direction: "desc" });
|
||||||
await listCoinSellers({
|
await listCoinSellers({
|
||||||
page: 1,
|
page: 1,
|
||||||
@ -84,10 +99,14 @@ test("host org list APIs use generated admin paths and filters", async () => {
|
|||||||
expect(String(bdUrl)).toContain("/api/v1/admin/bds?");
|
expect(String(bdUrl)).toContain("/api/v1/admin/bds?");
|
||||||
expect(String(bdUrl)).toContain("parent_leader_user_id=21");
|
expect(String(bdUrl)).toContain("parent_leader_user_id=21");
|
||||||
expect(String(bdUrl)).toContain("region_id=7");
|
expect(String(bdUrl)).toContain("region_id=7");
|
||||||
|
expect(String(bdUrl)).toContain("sort_by=salary_wallet");
|
||||||
|
expect(String(bdUrl)).toContain("sort_direction=asc");
|
||||||
expect(String(bdUrl)).toContain("status=active");
|
expect(String(bdUrl)).toContain("status=active");
|
||||||
expect(bdInit?.method).toBe("GET");
|
expect(bdInit?.method).toBe("GET");
|
||||||
expect(String(agencyUrl)).toContain("/api/v1/admin/agencies?");
|
expect(String(agencyUrl)).toContain("/api/v1/admin/agencies?");
|
||||||
expect(String(agencyUrl)).toContain("parent_bd_user_id=31");
|
expect(String(agencyUrl)).toContain("parent_bd_user_id=31");
|
||||||
|
expect(String(agencyUrl)).toContain("sort_by=salary_wallet");
|
||||||
|
expect(String(agencyUrl)).toContain("sort_direction=desc");
|
||||||
expect(String(hostUrl)).toContain("/api/v1/admin/hosts?");
|
expect(String(hostUrl)).toContain("/api/v1/admin/hosts?");
|
||||||
expect(String(hostUrl)).toContain("agency_id=41");
|
expect(String(hostUrl)).toContain("agency_id=41");
|
||||||
expect(String(hostUrl)).toContain("sort_by=diamond");
|
expect(String(hostUrl)).toContain("sort_by=diamond");
|
||||||
|
|||||||
32
src/features/host-org/components/HostOrgSortHeader.jsx
Normal file
32
src/features/host-org/components/HostOrgSortHeader.jsx
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import ArrowDownwardOutlined from "@mui/icons-material/ArrowDownwardOutlined";
|
||||||
|
import ArrowUpwardOutlined from "@mui/icons-material/ArrowUpwardOutlined";
|
||||||
|
import SwapVertOutlined from "@mui/icons-material/SwapVertOutlined";
|
||||||
|
|
||||||
|
export function HostOrgSortHeader({ field, label, onToggle, sortBy, sortDirection }) {
|
||||||
|
const active = sortBy === field;
|
||||||
|
const SortIcon =
|
||||||
|
active && sortDirection === "asc"
|
||||||
|
? ArrowUpwardOutlined
|
||||||
|
: active && sortDirection === "desc"
|
||||||
|
? ArrowDownwardOutlined
|
||||||
|
: SwapVertOutlined;
|
||||||
|
const directionLabel = sortDirection === "asc" ? "升序" : "降序";
|
||||||
|
const nextDirectionLabel = active && sortDirection === "desc" ? "升序" : "降序";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
aria-label={`按${label}${nextDirectionLabel}排序`}
|
||||||
|
className={["admin-cell__head-trigger", active ? "admin-cell__head-trigger--active" : ""]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(" ")}
|
||||||
|
type="button"
|
||||||
|
onClick={() => onToggle(field)}
|
||||||
|
>
|
||||||
|
<SortIcon className="admin-cell__head-icon" fontSize="inherit" />
|
||||||
|
<span className="admin-cell__head-label">
|
||||||
|
{label}
|
||||||
|
{active ? <span className="admin-cell__head-filter">({directionLabel})</span> : null}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -28,6 +28,8 @@ export function useHostAgenciesPage() {
|
|||||||
const [status, setStatus] = useState("");
|
const [status, setStatus] = useState("");
|
||||||
const [regionId, setRegionId] = useState("");
|
const [regionId, setRegionId] = useState("");
|
||||||
const [parentBdUserId, setParentBdUserId] = useState("");
|
const [parentBdUserId, setParentBdUserId] = useState("");
|
||||||
|
const [sortBy, setSortBy] = useState("");
|
||||||
|
const [sortDirection, setSortDirection] = useState("");
|
||||||
const [page, setPage] = useState(1);
|
const [page, setPage] = useState(1);
|
||||||
const [agencyForm, setAgencyForm] = useState(emptyAgencyForm);
|
const [agencyForm, setAgencyForm] = useState(emptyAgencyForm);
|
||||||
const [rowPatches, setRowPatches] = useState({});
|
const [rowPatches, setRowPatches] = useState({});
|
||||||
@ -39,9 +41,11 @@ export function useHostAgenciesPage() {
|
|||||||
keyword: query,
|
keyword: query,
|
||||||
parent_bd_user_id: parentBdUserId,
|
parent_bd_user_id: parentBdUserId,
|
||||||
region_id: regionId,
|
region_id: regionId,
|
||||||
|
sort_by: sortBy,
|
||||||
|
sort_direction: sortDirection,
|
||||||
status,
|
status,
|
||||||
}),
|
}),
|
||||||
[parentBdUserId, query, regionId, status],
|
[parentBdUserId, query, regionId, sortBy, sortDirection, status],
|
||||||
);
|
);
|
||||||
const {
|
const {
|
||||||
data = emptyData,
|
data = emptyData,
|
||||||
@ -97,11 +101,19 @@ export function useHostAgenciesPage() {
|
|||||||
setPage(1);
|
setPage(1);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const changeSort = (field) => {
|
||||||
|
setSortDirection((current) => (sortBy === field ? (current === "asc" ? "desc" : "asc") : "desc"));
|
||||||
|
setSortBy(field);
|
||||||
|
setPage(1);
|
||||||
|
};
|
||||||
|
|
||||||
const resetFilters = () => {
|
const resetFilters = () => {
|
||||||
setQuery("");
|
setQuery("");
|
||||||
setStatus("");
|
setStatus("");
|
||||||
setRegionId("");
|
setRegionId("");
|
||||||
setParentBdUserId("");
|
setParentBdUserId("");
|
||||||
|
setSortBy("");
|
||||||
|
setSortDirection("");
|
||||||
setPage(1);
|
setPage(1);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -212,8 +224,11 @@ export function useHostAgenciesPage() {
|
|||||||
removeAgencyFromList,
|
removeAgencyFromList,
|
||||||
setAgencyForm,
|
setAgencyForm,
|
||||||
setPage,
|
setPage,
|
||||||
|
sortBy,
|
||||||
|
sortDirection,
|
||||||
status,
|
status,
|
||||||
submitAgency,
|
submitAgency,
|
||||||
|
changeSort,
|
||||||
toggleAgencyJoinEnabledFromList,
|
toggleAgencyJoinEnabledFromList,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -38,6 +38,8 @@ export function useHostBdsPage({ profileType = "bd" } = {}) {
|
|||||||
const [status, setStatus] = useState("");
|
const [status, setStatus] = useState("");
|
||||||
const [regionId, setRegionId] = useState("");
|
const [regionId, setRegionId] = useState("");
|
||||||
const [parentLeaderUserId, setParentLeaderUserId] = useState("");
|
const [parentLeaderUserId, setParentLeaderUserId] = useState("");
|
||||||
|
const [sortBy, setSortBy] = useState("");
|
||||||
|
const [sortDirection, setSortDirection] = useState("");
|
||||||
const [page, setPage] = useState(1);
|
const [page, setPage] = useState(1);
|
||||||
const [bdLeaderForm, setBDLeaderForm] = useState(emptyBDLeaderForm);
|
const [bdLeaderForm, setBDLeaderForm] = useState(emptyBDLeaderForm);
|
||||||
const [bdForm, setBDForm] = useState(emptyBDForm);
|
const [bdForm, setBDForm] = useState(emptyBDForm);
|
||||||
@ -54,9 +56,11 @@ export function useHostBdsPage({ profileType = "bd" } = {}) {
|
|||||||
keyword: query,
|
keyword: query,
|
||||||
parent_leader_user_id: profileType === "bd" ? parentLeaderUserId : "",
|
parent_leader_user_id: profileType === "bd" ? parentLeaderUserId : "",
|
||||||
region_id: regionId,
|
region_id: regionId,
|
||||||
|
sort_by: sortBy,
|
||||||
|
sort_direction: sortDirection,
|
||||||
status,
|
status,
|
||||||
}),
|
}),
|
||||||
[parentLeaderUserId, profileType, query, regionId, status],
|
[parentLeaderUserId, profileType, query, regionId, sortBy, sortDirection, status],
|
||||||
);
|
);
|
||||||
const fetcher = profileType === "bd" ? listBDs : listBDLeaders;
|
const fetcher = profileType === "bd" ? listBDs : listBDLeaders;
|
||||||
const {
|
const {
|
||||||
@ -158,11 +162,19 @@ export function useHostBdsPage({ profileType = "bd" } = {}) {
|
|||||||
setPage(1);
|
setPage(1);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const changeSort = (field) => {
|
||||||
|
setSortDirection((current) => (sortBy === field ? (current === "asc" ? "desc" : "asc") : "desc"));
|
||||||
|
setSortBy(field);
|
||||||
|
setPage(1);
|
||||||
|
};
|
||||||
|
|
||||||
const resetFilters = () => {
|
const resetFilters = () => {
|
||||||
setQuery("");
|
setQuery("");
|
||||||
setStatus("");
|
setStatus("");
|
||||||
setRegionId("");
|
setRegionId("");
|
||||||
setParentLeaderUserId("");
|
setParentLeaderUserId("");
|
||||||
|
setSortBy("");
|
||||||
|
setSortDirection("");
|
||||||
setPage(1);
|
setPage(1);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -361,10 +373,13 @@ export function useHostBdsPage({ profileType = "bd" } = {}) {
|
|||||||
setBDForm,
|
setBDForm,
|
||||||
setBDLeaderForm,
|
setBDLeaderForm,
|
||||||
setPage,
|
setPage,
|
||||||
|
sortBy,
|
||||||
|
sortDirection,
|
||||||
status,
|
status,
|
||||||
submitBD,
|
submitBD,
|
||||||
submitBDLeader,
|
submitBDLeader,
|
||||||
submitLeaderBD,
|
submitLeaderBD,
|
||||||
|
changeSort,
|
||||||
toggleBD,
|
toggleBD,
|
||||||
toggleBDLeader,
|
toggleBDLeader,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -14,18 +14,19 @@ export function useHostHostsPage() {
|
|||||||
const [status, setStatus] = useState("");
|
const [status, setStatus] = useState("");
|
||||||
const [regionId, setRegionId] = useState("");
|
const [regionId, setRegionId] = useState("");
|
||||||
const [agencyId, setAgencyId] = useState("");
|
const [agencyId, setAgencyId] = useState("");
|
||||||
const [diamondSortDirection, setDiamondSortDirection] = useState("");
|
const [sortBy, setSortBy] = useState("");
|
||||||
|
const [sortDirection, setSortDirection] = useState("");
|
||||||
const [page, setPage] = useState(1);
|
const [page, setPage] = useState(1);
|
||||||
const filters = useMemo(
|
const filters = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
agency_id: agencyId,
|
agency_id: agencyId,
|
||||||
keyword: query,
|
keyword: query,
|
||||||
region_id: regionId,
|
region_id: regionId,
|
||||||
sort_by: diamondSortDirection ? "diamond" : "",
|
sort_by: sortBy,
|
||||||
sort_direction: diamondSortDirection,
|
sort_direction: sortDirection,
|
||||||
status,
|
status,
|
||||||
}),
|
}),
|
||||||
[agencyId, diamondSortDirection, query, regionId, status],
|
[agencyId, query, regionId, sortBy, sortDirection, status],
|
||||||
);
|
);
|
||||||
const {
|
const {
|
||||||
data = emptyData,
|
data = emptyData,
|
||||||
@ -61,8 +62,9 @@ export function useHostHostsPage() {
|
|||||||
setPage(1);
|
setPage(1);
|
||||||
};
|
};
|
||||||
|
|
||||||
const toggleDiamondSort = () => {
|
const changeSort = (field) => {
|
||||||
setDiamondSortDirection((current) => (current === "desc" ? "asc" : "desc"));
|
setSortDirection((current) => (sortBy === field ? (current === "asc" ? "desc" : "asc") : "desc"));
|
||||||
|
setSortBy(field);
|
||||||
setPage(1);
|
setPage(1);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -71,7 +73,8 @@ export function useHostHostsPage() {
|
|||||||
setStatus("");
|
setStatus("");
|
||||||
setRegionId("");
|
setRegionId("");
|
||||||
setAgencyId("");
|
setAgencyId("");
|
||||||
setDiamondSortDirection("");
|
setSortBy("");
|
||||||
|
setSortDirection("");
|
||||||
setPage(1);
|
setPage(1);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -83,7 +86,7 @@ export function useHostHostsPage() {
|
|||||||
changeRegionId,
|
changeRegionId,
|
||||||
changeStatus,
|
changeStatus,
|
||||||
data,
|
data,
|
||||||
diamondSortDirection,
|
diamondSortDirection: sortBy === "diamond" ? sortDirection : "",
|
||||||
error,
|
error,
|
||||||
loading,
|
loading,
|
||||||
loadingRegions,
|
loadingRegions,
|
||||||
@ -94,7 +97,10 @@ export function useHostHostsPage() {
|
|||||||
reload,
|
reload,
|
||||||
resetFilters,
|
resetFilters,
|
||||||
setPage,
|
setPage,
|
||||||
|
sortBy,
|
||||||
|
sortDirection,
|
||||||
status,
|
status,
|
||||||
toggleDiamondSort,
|
changeSort,
|
||||||
|
toggleDiamondSort: () => changeSort("diamond"),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import { DataState } from "@/shared/ui/DataState.jsx";
|
|||||||
import { agencyStatusFilters } from "@/features/host-org/constants.js";
|
import { agencyStatusFilters } from "@/features/host-org/constants.js";
|
||||||
import { HostOrgActionModal } from "@/features/host-org/components/HostOrgActionModal.jsx";
|
import { HostOrgActionModal } from "@/features/host-org/components/HostOrgActionModal.jsx";
|
||||||
import { HostOrgEntity, HostOrgPerson, hostOrgRegionName } from "@/features/host-org/components/HostOrgIdentity.jsx";
|
import { HostOrgEntity, HostOrgPerson, hostOrgRegionName } from "@/features/host-org/components/HostOrgIdentity.jsx";
|
||||||
|
import { HostOrgSortHeader } from "@/features/host-org/components/HostOrgSortHeader.jsx";
|
||||||
import { SalaryWalletAdjustButton, SalaryWalletCell } from "@/features/host-org/components/SalaryWalletControls.jsx";
|
import { SalaryWalletAdjustButton, SalaryWalletCell } from "@/features/host-org/components/SalaryWalletControls.jsx";
|
||||||
import { HostOrgTable } from "@/features/host-org/components/HostOrgTable.jsx";
|
import { HostOrgTable } from "@/features/host-org/components/HostOrgTable.jsx";
|
||||||
import { HostOrgToolbar } from "@/features/host-org/components/HostOrgToolbar.jsx";
|
import { HostOrgToolbar } from "@/features/host-org/components/HostOrgToolbar.jsx";
|
||||||
@ -31,6 +32,8 @@ const agencyColumns = [
|
|||||||
{
|
{
|
||||||
key: "salaryWallet",
|
key: "salaryWallet",
|
||||||
label: "工资钱包",
|
label: "工资钱包",
|
||||||
|
sortable: true,
|
||||||
|
sortField: "salary_wallet",
|
||||||
render: (item) => (
|
render: (item) => (
|
||||||
<SalaryWalletCell
|
<SalaryWalletCell
|
||||||
role="agency"
|
role="agency"
|
||||||
@ -121,15 +124,27 @@ export function HostAgenciesPage() {
|
|||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
if (column.sortable) {
|
||||||
|
return {
|
||||||
|
...column,
|
||||||
|
header: (
|
||||||
|
<HostOrgSortHeader
|
||||||
|
field={column.sortField}
|
||||||
|
label={column.label}
|
||||||
|
sortBy={page.sortBy}
|
||||||
|
sortDirection={page.sortDirection}
|
||||||
|
onToggle={page.changeSort}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
return column;
|
return column;
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className={styles.root}>
|
<section className={styles.root}>
|
||||||
<div className={styles.contentPanel}>
|
<div className={styles.contentPanel}>
|
||||||
<HostOrgToolbar
|
<HostOrgToolbar actions={toolbarActions} />
|
||||||
actions={toolbarActions}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<DataState error={page.error} loading={page.loading} onRetry={page.reload}>
|
<DataState error={page.error} loading={page.loading} onRetry={page.reload}>
|
||||||
<div className={styles.listBlock}>
|
<div className={styles.listBlock}>
|
||||||
@ -191,9 +206,7 @@ export function HostAgenciesPage() {
|
|||||||
disabled={createDisabled}
|
disabled={createDisabled}
|
||||||
label="入会状态"
|
label="入会状态"
|
||||||
uncheckedLabel="禁止"
|
uncheckedLabel="禁止"
|
||||||
onChange={(event) =>
|
onChange={(event) => page.setAgencyForm({ ...page.agencyForm, joinEnabled: event.target.checked })}
|
||||||
page.setAgencyForm({ ...page.agencyForm, joinEnabled: event.target.checked })
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
<TextField
|
<TextField
|
||||||
disabled={createDisabled}
|
disabled={createDisabled}
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import { AdminActionIconButton, AdminRowActions } from "@/shared/ui/AdminListLay
|
|||||||
import { AdminUserIdentity } from "@/shared/ui/AdminUserIdentity.jsx";
|
import { AdminUserIdentity } from "@/shared/ui/AdminUserIdentity.jsx";
|
||||||
import { SideDrawer } from "@/shared/ui/SideDrawer.jsx";
|
import { SideDrawer } from "@/shared/ui/SideDrawer.jsx";
|
||||||
import { HostOrgActionModal } from "@/features/host-org/components/HostOrgActionModal.jsx";
|
import { HostOrgActionModal } from "@/features/host-org/components/HostOrgActionModal.jsx";
|
||||||
|
import { HostOrgSortHeader } from "@/features/host-org/components/HostOrgSortHeader.jsx";
|
||||||
import { SalaryWalletAdjustButton, SalaryWalletCell } from "@/features/host-org/components/SalaryWalletControls.jsx";
|
import { SalaryWalletAdjustButton, SalaryWalletCell } from "@/features/host-org/components/SalaryWalletControls.jsx";
|
||||||
import { HostOrgStatus } from "@/features/host-org/components/HostOrgStatus.jsx";
|
import { HostOrgStatus } from "@/features/host-org/components/HostOrgStatus.jsx";
|
||||||
import { HostOrgTable } from "@/features/host-org/components/HostOrgTable.jsx";
|
import { HostOrgTable } from "@/features/host-org/components/HostOrgTable.jsx";
|
||||||
@ -35,6 +36,8 @@ const bdLeaderBaseColumns = [
|
|||||||
{
|
{
|
||||||
key: "salaryWallet",
|
key: "salaryWallet",
|
||||||
label: "工资钱包",
|
label: "工资钱包",
|
||||||
|
sortable: true,
|
||||||
|
sortField: "salary_wallet",
|
||||||
render: (item) => (
|
render: (item) => (
|
||||||
<SalaryWalletCell
|
<SalaryWalletCell
|
||||||
role="bd_leader"
|
role="bd_leader"
|
||||||
@ -111,6 +114,20 @@ export function HostBdLeadersPage() {
|
|||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
if (column.sortable) {
|
||||||
|
return {
|
||||||
|
...column,
|
||||||
|
header: (
|
||||||
|
<HostOrgSortHeader
|
||||||
|
field={column.sortField}
|
||||||
|
label={column.label}
|
||||||
|
sortBy={page.sortBy}
|
||||||
|
sortDirection={page.sortDirection}
|
||||||
|
onToggle={page.changeSort}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
return column;
|
return column;
|
||||||
});
|
});
|
||||||
const toolbarActions = page.abilities.canCreate
|
const toolbarActions = page.abilities.canCreate
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import { DataState } from "@/shared/ui/DataState.jsx";
|
|||||||
import { bdStatusFilters } from "@/features/host-org/constants.js";
|
import { bdStatusFilters } from "@/features/host-org/constants.js";
|
||||||
import { HostOrgActionModal } from "@/features/host-org/components/HostOrgActionModal.jsx";
|
import { HostOrgActionModal } from "@/features/host-org/components/HostOrgActionModal.jsx";
|
||||||
import { HostOrgPerson, hostOrgRegionName } from "@/features/host-org/components/HostOrgIdentity.jsx";
|
import { HostOrgPerson, hostOrgRegionName } from "@/features/host-org/components/HostOrgIdentity.jsx";
|
||||||
|
import { HostOrgSortHeader } from "@/features/host-org/components/HostOrgSortHeader.jsx";
|
||||||
import { SalaryWalletAdjustButton, SalaryWalletCell } from "@/features/host-org/components/SalaryWalletControls.jsx";
|
import { SalaryWalletAdjustButton, SalaryWalletCell } from "@/features/host-org/components/SalaryWalletControls.jsx";
|
||||||
import { HostOrgTable } from "@/features/host-org/components/HostOrgTable.jsx";
|
import { HostOrgTable } from "@/features/host-org/components/HostOrgTable.jsx";
|
||||||
import { HostOrgToolbar } from "@/features/host-org/components/HostOrgToolbar.jsx";
|
import { HostOrgToolbar } from "@/features/host-org/components/HostOrgToolbar.jsx";
|
||||||
@ -25,13 +26,10 @@ const bdColumns = [
|
|||||||
{
|
{
|
||||||
key: "salaryWallet",
|
key: "salaryWallet",
|
||||||
label: "工资钱包",
|
label: "工资钱包",
|
||||||
|
sortable: true,
|
||||||
|
sortField: "salary_wallet",
|
||||||
render: (item) => (
|
render: (item) => (
|
||||||
<SalaryWalletCell
|
<SalaryWalletCell role="bd" userId={item.userId} userLabel={bdUserLabel(item)} wallet={item.salaryWallet} />
|
||||||
role="bd"
|
|
||||||
userId={item.userId}
|
|
||||||
userLabel={bdUserLabel(item)}
|
|
||||||
wallet={item.salaryWallet}
|
|
||||||
/>
|
|
||||||
),
|
),
|
||||||
width: "minmax(140px, 0.85fr)",
|
width: "minmax(140px, 0.85fr)",
|
||||||
},
|
},
|
||||||
@ -93,47 +91,61 @@ export function HostBdsPage() {
|
|||||||
width: "minmax(80px, 0.45fr)",
|
width: "minmax(80px, 0.45fr)",
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
].filter(Boolean).map((column) => {
|
]
|
||||||
if (column.key === "user") {
|
.filter(Boolean)
|
||||||
return {
|
.map((column) => {
|
||||||
...column,
|
if (column.key === "user") {
|
||||||
filter: createTextColumnFilter({
|
return {
|
||||||
placeholder: "搜索用户 ID、展示 ID、用户名",
|
...column,
|
||||||
value: page.query,
|
filter: createTextColumnFilter({
|
||||||
onChange: page.changeQuery,
|
placeholder: "搜索用户 ID、展示 ID、用户名",
|
||||||
}),
|
value: page.query,
|
||||||
};
|
onChange: page.changeQuery,
|
||||||
}
|
}),
|
||||||
if (column.key === "parentLeaderUserId") {
|
};
|
||||||
return {
|
}
|
||||||
...column,
|
if (column.key === "parentLeaderUserId") {
|
||||||
filter: createTextColumnFilter({
|
return {
|
||||||
placeholder: "搜索 Leader 用户 ID",
|
...column,
|
||||||
value: page.parentLeaderUserId,
|
filter: createTextColumnFilter({
|
||||||
onChange: page.changeParentLeaderUserId,
|
placeholder: "搜索 Leader 用户 ID",
|
||||||
}),
|
value: page.parentLeaderUserId,
|
||||||
};
|
onChange: page.changeParentLeaderUserId,
|
||||||
}
|
}),
|
||||||
if (column.key === "status") {
|
};
|
||||||
return {
|
}
|
||||||
...column,
|
if (column.key === "status") {
|
||||||
filter: createOptionsColumnFilter({
|
return {
|
||||||
options: bdStatusFilters,
|
...column,
|
||||||
placeholder: "搜索状态",
|
filter: createOptionsColumnFilter({
|
||||||
value: page.status,
|
options: bdStatusFilters,
|
||||||
onChange: page.changeStatus,
|
placeholder: "搜索状态",
|
||||||
}),
|
value: page.status,
|
||||||
};
|
onChange: page.changeStatus,
|
||||||
}
|
}),
|
||||||
return column;
|
};
|
||||||
});
|
}
|
||||||
|
if (column.sortable) {
|
||||||
|
return {
|
||||||
|
...column,
|
||||||
|
header: (
|
||||||
|
<HostOrgSortHeader
|
||||||
|
field={column.sortField}
|
||||||
|
label={column.label}
|
||||||
|
sortBy={page.sortBy}
|
||||||
|
sortDirection={page.sortDirection}
|
||||||
|
onToggle={page.changeSort}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return column;
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className={styles.root}>
|
<section className={styles.root}>
|
||||||
<div className={styles.contentPanel}>
|
<div className={styles.contentPanel}>
|
||||||
<HostOrgToolbar
|
<HostOrgToolbar actions={toolbarActions} />
|
||||||
actions={toolbarActions}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<DataState error={page.error} loading={page.loading} onRetry={page.reload}>
|
<DataState error={page.error} loading={page.loading} onRetry={page.reload}>
|
||||||
<div className={styles.listBlock}>
|
<div className={styles.listBlock}>
|
||||||
|
|||||||
@ -1,11 +1,9 @@
|
|||||||
import ArrowDownwardOutlined from "@mui/icons-material/ArrowDownwardOutlined";
|
|
||||||
import ArrowUpwardOutlined from "@mui/icons-material/ArrowUpwardOutlined";
|
|
||||||
import SwapVertOutlined from "@mui/icons-material/SwapVertOutlined";
|
|
||||||
import { AdminSwitch } from "@/shared/ui/AdminSwitch.jsx";
|
import { AdminSwitch } from "@/shared/ui/AdminSwitch.jsx";
|
||||||
import { formatMillis } from "@/shared/utils/time.js";
|
import { formatMillis } from "@/shared/utils/time.js";
|
||||||
import { DataState } from "@/shared/ui/DataState.jsx";
|
import { DataState } from "@/shared/ui/DataState.jsx";
|
||||||
import { hostSourceLabels, hostStatusFilters } from "@/features/host-org/constants.js";
|
import { hostSourceLabels, hostStatusFilters } from "@/features/host-org/constants.js";
|
||||||
import { HostOrgPerson, hostOrgRegionName } from "@/features/host-org/components/HostOrgIdentity.jsx";
|
import { HostOrgPerson, hostOrgRegionName } from "@/features/host-org/components/HostOrgIdentity.jsx";
|
||||||
|
import { HostOrgSortHeader } from "@/features/host-org/components/HostOrgSortHeader.jsx";
|
||||||
import { SalaryWalletAdjustButton, SalaryWalletCell } from "@/features/host-org/components/SalaryWalletControls.jsx";
|
import { SalaryWalletAdjustButton, SalaryWalletCell } from "@/features/host-org/components/SalaryWalletControls.jsx";
|
||||||
import { HostOrgTable } from "@/features/host-org/components/HostOrgTable.jsx";
|
import { HostOrgTable } from "@/features/host-org/components/HostOrgTable.jsx";
|
||||||
import { useHostHostsPage } from "@/features/host-org/hooks/useHostHostsPage.js";
|
import { useHostHostsPage } from "@/features/host-org/hooks/useHostHostsPage.js";
|
||||||
@ -29,12 +27,16 @@ const hostColumns = [
|
|||||||
{
|
{
|
||||||
key: "diamond",
|
key: "diamond",
|
||||||
label: "钻石",
|
label: "钻石",
|
||||||
|
sortable: true,
|
||||||
|
sortField: "diamond",
|
||||||
render: (item) => formatNumber(item.diamond),
|
render: (item) => formatNumber(item.diamond),
|
||||||
width: "minmax(110px, 0.7fr)",
|
width: "minmax(110px, 0.7fr)",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "salaryWallet",
|
key: "salaryWallet",
|
||||||
label: "工资钱包",
|
label: "工资钱包",
|
||||||
|
sortable: true,
|
||||||
|
sortField: "salary_wallet",
|
||||||
render: (item) => (
|
render: (item) => (
|
||||||
<SalaryWalletCell
|
<SalaryWalletCell
|
||||||
role="host"
|
role="host"
|
||||||
@ -91,51 +93,70 @@ export function HostHostsPage() {
|
|||||||
width: "minmax(80px, 0.45fr)",
|
width: "minmax(80px, 0.45fr)",
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
].filter(Boolean).map((column) => {
|
]
|
||||||
if (column.key === "user") {
|
.filter(Boolean)
|
||||||
return {
|
.map((column) => {
|
||||||
...column,
|
if (column.key === "user") {
|
||||||
filter: createTextColumnFilter({
|
return {
|
||||||
placeholder: "搜索用户 ID、展示 ID、用户名、Agency",
|
...column,
|
||||||
value: page.query,
|
filter: createTextColumnFilter({
|
||||||
onChange: page.changeQuery,
|
placeholder: "搜索用户 ID、展示 ID、用户名、Agency",
|
||||||
}),
|
value: page.query,
|
||||||
};
|
onChange: page.changeQuery,
|
||||||
}
|
}),
|
||||||
if (column.key === "currentAgencyId") {
|
};
|
||||||
return {
|
}
|
||||||
...column,
|
if (column.key === "currentAgencyId") {
|
||||||
filter: createTextColumnFilter({
|
return {
|
||||||
placeholder: "搜索 Agency ID",
|
...column,
|
||||||
value: page.agencyId,
|
filter: createTextColumnFilter({
|
||||||
onChange: page.changeAgencyId,
|
placeholder: "搜索 Agency ID",
|
||||||
}),
|
value: page.agencyId,
|
||||||
};
|
onChange: page.changeAgencyId,
|
||||||
}
|
}),
|
||||||
if (column.key === "status") {
|
};
|
||||||
return {
|
}
|
||||||
...column,
|
if (column.key === "status") {
|
||||||
filter: createOptionsColumnFilter({
|
return {
|
||||||
options: hostStatusFilters,
|
...column,
|
||||||
placeholder: "搜索状态",
|
filter: createOptionsColumnFilter({
|
||||||
value: page.status,
|
options: hostStatusFilters,
|
||||||
onChange: page.changeStatus,
|
placeholder: "搜索状态",
|
||||||
}),
|
value: page.status,
|
||||||
};
|
onChange: page.changeStatus,
|
||||||
}
|
}),
|
||||||
if (column.key === "diamond") {
|
};
|
||||||
return {
|
}
|
||||||
...column,
|
if (column.key === "diamond") {
|
||||||
header: (
|
return {
|
||||||
<DiamondSortHeader
|
...column,
|
||||||
direction={page.diamondSortDirection}
|
header: (
|
||||||
onToggle={page.toggleDiamondSort}
|
<HostOrgSortHeader
|
||||||
/>
|
field={column.sortField}
|
||||||
),
|
label={column.label}
|
||||||
};
|
sortBy={page.sortBy}
|
||||||
}
|
sortDirection={page.sortDirection}
|
||||||
return column;
|
onToggle={page.changeSort}
|
||||||
});
|
/>
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (column.sortable) {
|
||||||
|
return {
|
||||||
|
...column,
|
||||||
|
header: (
|
||||||
|
<HostOrgSortHeader
|
||||||
|
field={column.sortField}
|
||||||
|
label={column.label}
|
||||||
|
sortBy={page.sortBy}
|
||||||
|
sortDirection={page.sortDirection}
|
||||||
|
onToggle={page.changeSort}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return column;
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className={styles.root}>
|
<section className={styles.root}>
|
||||||
@ -170,28 +191,6 @@ function formatNumber(value) {
|
|||||||
return Number(value || 0).toLocaleString("zh-CN");
|
return Number(value || 0).toLocaleString("zh-CN");
|
||||||
}
|
}
|
||||||
|
|
||||||
function DiamondSortHeader({ direction, onToggle }) {
|
|
||||||
const SortIcon =
|
|
||||||
direction === "asc" ? ArrowUpwardOutlined : direction === "desc" ? ArrowDownwardOutlined : SwapVertOutlined;
|
|
||||||
const directionLabel = direction === "asc" ? "升序" : "降序";
|
|
||||||
const nextDirectionLabel = direction === "desc" ? "升序" : "降序";
|
|
||||||
return (
|
|
||||||
<button
|
|
||||||
aria-label={`按钻石${nextDirectionLabel}排序`}
|
|
||||||
className={["admin-cell__head-trigger", direction ? "admin-cell__head-trigger--active" : ""]
|
|
||||||
.filter(Boolean)
|
|
||||||
.join(" ")}
|
|
||||||
type="button"
|
|
||||||
onClick={onToggle}
|
|
||||||
>
|
|
||||||
<SortIcon className="admin-cell__head-icon" fontSize="inherit" />
|
|
||||||
<span className="admin-cell__head-label">
|
|
||||||
钻石{direction ? <span className="admin-cell__head-filter">({directionLabel})</span> : null}
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function HostUser({ item }) {
|
function HostUser({ item }) {
|
||||||
return (
|
return (
|
||||||
<HostOrgPerson
|
<HostOrgPerson
|
||||||
|
|||||||
231
src/features/host-org/pages/HostSalaryWalletSortHeaders.test.jsx
Normal file
231
src/features/host-org/pages/HostSalaryWalletSortHeaders.test.jsx
Normal file
@ -0,0 +1,231 @@
|
|||||||
|
import { fireEvent, render, screen } from "@testing-library/react";
|
||||||
|
import { afterEach, expect, test, vi } from "vitest";
|
||||||
|
import { HostAgenciesPage } from "./HostAgenciesPage.jsx";
|
||||||
|
import { HostBdLeadersPage } from "./HostBdLeadersPage.jsx";
|
||||||
|
import { HostBdsPage } from "./HostBdsPage.jsx";
|
||||||
|
import { HostHostsPage } from "./HostHostsPage.jsx";
|
||||||
|
import { useHostAgenciesPage } from "@/features/host-org/hooks/useHostAgenciesPage.js";
|
||||||
|
import { useHostBdsPage } from "@/features/host-org/hooks/useHostBdsPage.js";
|
||||||
|
import { useHostHostsPage } from "@/features/host-org/hooks/useHostHostsPage.js";
|
||||||
|
|
||||||
|
vi.mock("@/features/host-org/hooks/useHostAgenciesPage.js", () => ({
|
||||||
|
useHostAgenciesPage: vi.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock("@/features/host-org/hooks/useHostBdsPage.js", () => ({
|
||||||
|
useHostBdsPage: vi.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock("@/features/host-org/hooks/useHostHostsPage.js", () => ({
|
||||||
|
useHostHostsPage: vi.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("agency list salary wallet header requests salary wallet sorting", () => {
|
||||||
|
const changeSort = vi.fn();
|
||||||
|
vi.mocked(useHostAgenciesPage).mockReturnValue(agencyPageFixture({ changeSort }));
|
||||||
|
|
||||||
|
render(<HostAgenciesPage />);
|
||||||
|
fireEvent.click(screen.getByLabelText("按工资钱包降序排序"));
|
||||||
|
|
||||||
|
expect(changeSort).toHaveBeenCalledWith("salary_wallet");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("bd leader list salary wallet header requests salary wallet sorting", () => {
|
||||||
|
const changeSort = vi.fn();
|
||||||
|
vi.mocked(useHostBdsPage).mockReturnValue(bdPageFixture({ changeSort, profileType: "leader" }));
|
||||||
|
|
||||||
|
render(<HostBdLeadersPage />);
|
||||||
|
fireEvent.click(screen.getByLabelText("按工资钱包降序排序"));
|
||||||
|
|
||||||
|
expect(changeSort).toHaveBeenCalledWith("salary_wallet");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("bd list salary wallet header requests salary wallet sorting", () => {
|
||||||
|
const changeSort = vi.fn();
|
||||||
|
vi.mocked(useHostBdsPage).mockReturnValue(bdPageFixture({ changeSort, profileType: "bd" }));
|
||||||
|
|
||||||
|
render(<HostBdsPage />);
|
||||||
|
fireEvent.click(screen.getByLabelText("按工资钱包降序排序"));
|
||||||
|
|
||||||
|
expect(changeSort).toHaveBeenCalledWith("salary_wallet");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("host list salary wallet header requests salary wallet sorting", () => {
|
||||||
|
const changeSort = vi.fn();
|
||||||
|
vi.mocked(useHostHostsPage).mockReturnValue(hostPageFixture({ changeSort }));
|
||||||
|
|
||||||
|
render(<HostHostsPage />);
|
||||||
|
fireEvent.click(screen.getByLabelText("按工资钱包降序排序"));
|
||||||
|
|
||||||
|
expect(changeSort).toHaveBeenCalledWith("salary_wallet");
|
||||||
|
});
|
||||||
|
|
||||||
|
function agencyPageFixture(patch = {}) {
|
||||||
|
return {
|
||||||
|
abilities: { canCreate: false, canDelete: false, canSalaryAdjust: false, canStatus: false },
|
||||||
|
activeAction: "",
|
||||||
|
agencyForm: { joinEnabled: true, name: "", ownerUserId: "", parentBdUserId: "", reason: "" },
|
||||||
|
changeParentBdUserId: vi.fn(),
|
||||||
|
changeQuery: vi.fn(),
|
||||||
|
changeRegionId: vi.fn(),
|
||||||
|
changeSort: vi.fn(),
|
||||||
|
changeStatus: vi.fn(),
|
||||||
|
closeAction: vi.fn(),
|
||||||
|
closeAgencyFromList: vi.fn(),
|
||||||
|
data: { items: [agencyFixture()], page: 1, pageSize: 50, total: 1 },
|
||||||
|
error: null,
|
||||||
|
loading: false,
|
||||||
|
loadingAction: "",
|
||||||
|
loadingRegions: false,
|
||||||
|
openAgencyForm: vi.fn(),
|
||||||
|
page: 1,
|
||||||
|
parentBdUserId: "",
|
||||||
|
query: "",
|
||||||
|
regionId: "",
|
||||||
|
regionOptions: [],
|
||||||
|
reload: vi.fn(),
|
||||||
|
removeAgencyFromList: vi.fn(),
|
||||||
|
resetFilters: vi.fn(),
|
||||||
|
setAgencyForm: vi.fn(),
|
||||||
|
setPage: vi.fn(),
|
||||||
|
sortBy: "",
|
||||||
|
sortDirection: "",
|
||||||
|
status: "",
|
||||||
|
submitAgency: vi.fn(),
|
||||||
|
toggleAgencyJoinEnabledFromList: vi.fn(),
|
||||||
|
...patch,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function bdPageFixture(patch = {}) {
|
||||||
|
return {
|
||||||
|
abilities: { canCreate: false, canSalaryAdjust: false, canUpdate: false, canView: true },
|
||||||
|
activeAction: "",
|
||||||
|
bdForm: { parentLeaderUserId: "", reason: "", targetUserId: "" },
|
||||||
|
bdLeaderForm: { positionAlias: "", reason: "", targetUserId: "" },
|
||||||
|
bdUserLookup: { error: "", loading: false, user: null },
|
||||||
|
changeParentLeaderUserId: vi.fn(),
|
||||||
|
changeQuery: vi.fn(),
|
||||||
|
changeRegionId: vi.fn(),
|
||||||
|
changeSort: vi.fn(),
|
||||||
|
changeStatus: vi.fn(),
|
||||||
|
closeAction: vi.fn(),
|
||||||
|
data: { items: [bdFixture()], page: 1, pageSize: 50, total: 1 },
|
||||||
|
error: null,
|
||||||
|
leaderBDs: { items: [], page: 1, pageSize: 50, total: 0 },
|
||||||
|
leaderBDsError: "",
|
||||||
|
leaderBDsLoading: false,
|
||||||
|
loadLeaderBDs: vi.fn(),
|
||||||
|
loading: false,
|
||||||
|
loadingAction: "",
|
||||||
|
loadingRegions: false,
|
||||||
|
openAddLeaderBD: vi.fn(),
|
||||||
|
openBDForm: vi.fn(),
|
||||||
|
openBDLeaderForm: vi.fn(),
|
||||||
|
openLeaderBDs: vi.fn(),
|
||||||
|
page: 1,
|
||||||
|
parentLeaderUserId: "",
|
||||||
|
profileType: "bd",
|
||||||
|
query: "",
|
||||||
|
regionId: "",
|
||||||
|
regionOptions: [],
|
||||||
|
reload: vi.fn(),
|
||||||
|
removeBDLeader: vi.fn(),
|
||||||
|
resetFilters: vi.fn(),
|
||||||
|
saveBDLeaderPositionAlias: vi.fn(),
|
||||||
|
selectedLeader: null,
|
||||||
|
setBDForm: vi.fn(),
|
||||||
|
setBDLeaderForm: vi.fn(),
|
||||||
|
setPage: vi.fn(),
|
||||||
|
sortBy: "",
|
||||||
|
sortDirection: "",
|
||||||
|
status: "",
|
||||||
|
submitBD: vi.fn(),
|
||||||
|
submitBDLeader: vi.fn(),
|
||||||
|
submitLeaderBD: vi.fn(),
|
||||||
|
toggleBD: vi.fn(),
|
||||||
|
toggleBDLeader: vi.fn(),
|
||||||
|
...patch,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function hostPageFixture(patch = {}) {
|
||||||
|
return {
|
||||||
|
abilities: { canSalaryAdjust: false, canView: true },
|
||||||
|
agencyId: "",
|
||||||
|
changeAgencyId: vi.fn(),
|
||||||
|
changeQuery: vi.fn(),
|
||||||
|
changeRegionId: vi.fn(),
|
||||||
|
changeSort: vi.fn(),
|
||||||
|
changeStatus: vi.fn(),
|
||||||
|
data: { items: [hostFixture()], page: 1, pageSize: 50, total: 1 },
|
||||||
|
diamondSortDirection: "",
|
||||||
|
error: null,
|
||||||
|
loading: false,
|
||||||
|
loadingRegions: false,
|
||||||
|
page: 1,
|
||||||
|
query: "",
|
||||||
|
regionId: "",
|
||||||
|
regionOptions: [],
|
||||||
|
reload: vi.fn(),
|
||||||
|
resetFilters: vi.fn(),
|
||||||
|
setPage: vi.fn(),
|
||||||
|
sortBy: "",
|
||||||
|
sortDirection: "",
|
||||||
|
status: "",
|
||||||
|
toggleDiamondSort: vi.fn(),
|
||||||
|
...patch,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function agencyFixture() {
|
||||||
|
return {
|
||||||
|
agencyId: "9001",
|
||||||
|
joinEnabled: true,
|
||||||
|
name: "Agency One",
|
||||||
|
ownerDisplayUserId: "160001",
|
||||||
|
ownerUserId: "1001",
|
||||||
|
ownerUsername: "agency_owner",
|
||||||
|
parentBdUserId: "2001",
|
||||||
|
regionName: "中东区",
|
||||||
|
salaryWallet: { availableAmount: 1200 },
|
||||||
|
status: "active",
|
||||||
|
updatedAtMs: 1760000000000,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function bdFixture() {
|
||||||
|
return {
|
||||||
|
createdAtMs: 1760000000000,
|
||||||
|
displayUserId: "160002",
|
||||||
|
parentLeaderUserId: "3001",
|
||||||
|
regionName: "中东区",
|
||||||
|
role: "bd",
|
||||||
|
salaryWallet: { availableAmount: 1200 },
|
||||||
|
status: "active",
|
||||||
|
subBdCount: 0,
|
||||||
|
updatedAtMs: 1760000000000,
|
||||||
|
userId: "2001",
|
||||||
|
username: "bd_user",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function hostFixture() {
|
||||||
|
return {
|
||||||
|
currentAgencyId: "9001",
|
||||||
|
diamond: 100,
|
||||||
|
displayUserId: "160003",
|
||||||
|
firstBecameHostAtMs: 1760000000000,
|
||||||
|
regionName: "中东区",
|
||||||
|
salaryWallet: { availableAmount: 1200 },
|
||||||
|
source: "agency_invite",
|
||||||
|
status: "active",
|
||||||
|
updatedAtMs: 1760000000000,
|
||||||
|
userId: "3001",
|
||||||
|
username: "host_user",
|
||||||
|
};
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user