表格工资相关
This commit is contained in:
parent
79da033dc9
commit
aa7ce50e4f
@ -71,7 +71,6 @@ const historyColumns = [
|
||||
export function SalaryWalletCell({ role, userId, userLabel, wallet }) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const balance = Number(wallet?.availableAmount || 0);
|
||||
const assetType = wallet?.assetType || salaryWalletAssetType(role);
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -81,7 +80,6 @@ export function SalaryWalletCell({ role, userId, userLabel, wallet }) {
|
||||
onClick={() => setOpen(true)}
|
||||
>
|
||||
<span className={styles.salaryWalletAmount}>{formatSalaryWalletAmount(balance)}</span>
|
||||
<span className={styles.salaryWalletAsset}>{assetType || "-"}</span>
|
||||
</button>
|
||||
<SalaryWalletHistoryDrawer
|
||||
open={open}
|
||||
@ -163,10 +161,9 @@ export function SalaryWalletAdjustButton({ disabled = false, onAdjusted, role, u
|
||||
onSubmit={submit}
|
||||
>
|
||||
<AdminFormSection title="目标身份">
|
||||
<AdminFormFieldGrid columns="repeat(3, minmax(0, 1fr))">
|
||||
<AdminFormFieldGrid columns="repeat(2, minmax(0, 1fr))">
|
||||
<AdminFormReadOnlyField label="身份" value={salaryWalletRoleLabel(role)} />
|
||||
<AdminFormReadOnlyField label="用户" value={userLabel || userId || "-"} />
|
||||
<AdminFormReadOnlyField label="钱包" value={salaryWalletAssetType(role) || "-"} />
|
||||
</AdminFormFieldGrid>
|
||||
</AdminFormSection>
|
||||
<AdminFormSection title="操作信息">
|
||||
@ -260,7 +257,6 @@ function SalaryWalletHistoryDrawer({ onClose, open, role, userId, userLabel }) {
|
||||
<div className={styles.salaryWalletSummary}>
|
||||
<AccountBalanceWalletOutlined fontSize="small" />
|
||||
<span>{salaryWalletRoleLabel(role)}</span>
|
||||
<span>{salaryWalletAssetType(role) || "-"}</span>
|
||||
</div>
|
||||
<DataState error={error} loading={loading} onRetry={loadHistory}>
|
||||
<DataTable
|
||||
@ -317,21 +313,6 @@ function parseSalaryUSDMinor(value) {
|
||||
return amountMinor;
|
||||
}
|
||||
|
||||
function salaryWalletAssetType(role) {
|
||||
switch (role) {
|
||||
case "host":
|
||||
return "HOST_SALARY_USD";
|
||||
case "agency":
|
||||
return "AGENCY_SALARY_USD";
|
||||
case "bd":
|
||||
return "BD_SALARY_USD";
|
||||
case "bd_leader":
|
||||
return "ADMIN_SALARY_USD";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
function salaryWalletRoleLabel(role) {
|
||||
switch (role) {
|
||||
case "host":
|
||||
|
||||
@ -307,15 +307,6 @@
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.salaryWalletAsset {
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
color: var(--text-tertiary);
|
||||
font-size: 12px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.salaryWalletDrawer.salaryWalletDrawer {
|
||||
width: min(980px, calc(100vw - 32px));
|
||||
}
|
||||
|
||||
@ -80,6 +80,9 @@ export function DataTable({
|
||||
);
|
||||
|
||||
const gridTemplate = preparedColumns.map((column) => column.width).join(" ");
|
||||
const tableContentWidth = `${Math.ceil(
|
||||
preparedColumns.reduce((total, column) => total + readMinWidth(column.width), 0)
|
||||
)}px`;
|
||||
const activeFilterColumn = preparedColumns.find((column) => column.key === filterMenu.columnKey && column.filter) || null;
|
||||
const activeFilter = activeFilterColumn?.filter || null;
|
||||
const activeFilterIsText = activeFilter?.type === "text";
|
||||
@ -267,7 +270,11 @@ export function DataTable({
|
||||
<div className="table-scroll" ref={scrollRef}>
|
||||
<div
|
||||
className={["admin-table", resizeSession ? "admin-table--resizing" : "", tableClassName].filter(Boolean).join(" ")}
|
||||
style={{ "--admin-table-columns": gridTemplate, "--admin-table-min-width": minWidth }}
|
||||
style={{
|
||||
"--admin-table-columns": gridTemplate,
|
||||
"--admin-table-content-width": tableContentWidth,
|
||||
"--admin-table-min-width": minWidth
|
||||
}}
|
||||
>
|
||||
<div className="admin-row admin-row--head">
|
||||
{preparedColumns.map((column, index) => {
|
||||
|
||||
@ -61,6 +61,22 @@ test("only shows the table filter reset action when filters are active", () => {
|
||||
expect(screen.getByRole("button", { name: "重置筛选" })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test("expands table background to the minimum width required by columns", () => {
|
||||
const columns = [
|
||||
{ key: "user", label: "用户", width: "minmax(220px, 1fr)" },
|
||||
{ key: "createdAt", label: "创建时间", width: "minmax(180px, 1fr)" },
|
||||
{ key: "actions", label: "操作" }
|
||||
];
|
||||
|
||||
render(
|
||||
<TimeZoneProvider>
|
||||
<DataTable columns={columns} items={[{ createdAt: "2026-06-15", id: 1, user: "admin" }]} minWidth="300px" rowKey={(item) => item.id} />
|
||||
</TimeZoneProvider>
|
||||
);
|
||||
|
||||
expect(document.querySelector(".admin-table")?.style.getPropertyValue("--admin-table-content-width")).toBe("512px");
|
||||
});
|
||||
|
||||
function TimezoneTableFixture() {
|
||||
const { setTimeZone } = useTimeZone();
|
||||
const columns = [
|
||||
|
||||
@ -8,7 +8,7 @@ export function ToastProvider({ children }) {
|
||||
const [toast, setToast] = useState(null);
|
||||
|
||||
const showToast = useCallback((message, severity = "info") => {
|
||||
setToast({ message, severity });
|
||||
setToast(normalizeToast(message, severity));
|
||||
}, []);
|
||||
|
||||
const value = useMemo(() => ({ showToast }), [showToast]);
|
||||
@ -39,3 +39,16 @@ export function useToast() {
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
function normalizeToast(message, severity) {
|
||||
if (message && typeof message === "object") {
|
||||
return {
|
||||
message: String(message.message || "操作失败"),
|
||||
severity: message.severity || severity || "info"
|
||||
};
|
||||
}
|
||||
return {
|
||||
message: String(message || "操作失败"),
|
||||
severity: severity || "info"
|
||||
};
|
||||
}
|
||||
|
||||
36
src/shared/ui/ToastProvider.test.jsx
Normal file
36
src/shared/ui/ToastProvider.test.jsx
Normal file
@ -0,0 +1,36 @@
|
||||
import { fireEvent, render, screen } from "@testing-library/react";
|
||||
import { expect, test } from "vitest";
|
||||
import { ToastProvider, useToast } from "./ToastProvider.jsx";
|
||||
|
||||
function ToastTrigger({ payload, severity }) {
|
||||
const { showToast } = useToast();
|
||||
return (
|
||||
<button type="button" onClick={() => showToast(payload, severity)}>
|
||||
show
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
test("renders object-style toast payloads as text", () => {
|
||||
render(
|
||||
<ToastProvider>
|
||||
<ToastTrigger payload={{ message: "工资结算失败", severity: "error" }} />
|
||||
</ToastProvider>,
|
||||
);
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: "show" }));
|
||||
|
||||
expect(screen.getByText("工资结算失败")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test("keeps tuple-style toast payloads working", () => {
|
||||
render(
|
||||
<ToastProvider>
|
||||
<ToastTrigger payload="区域已更新" severity="success" />
|
||||
</ToastProvider>,
|
||||
);
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: "show" }));
|
||||
|
||||
expect(screen.getByText("区域已更新")).toBeInTheDocument();
|
||||
});
|
||||
@ -1546,9 +1546,9 @@
|
||||
--admin-table-cell-x: var(--space-3);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: max(100%, var(--admin-table-min-width, 900px));
|
||||
width: max(100%, var(--admin-table-min-width, 900px), var(--admin-table-content-width, 0px));
|
||||
min-height: 100%;
|
||||
min-width: max(100%, var(--admin-table-min-width, 900px));
|
||||
min-width: max(100%, var(--admin-table-min-width, 900px), var(--admin-table-content-width, 0px));
|
||||
}
|
||||
|
||||
.admin-row {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user