时区问题
This commit is contained in:
parent
2118a2ee4d
commit
e2c0565dbe
@ -2,7 +2,7 @@ import { useMemo } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useAuth } from "@/app/auth/AuthProvider.jsx";
|
||||
import { getFrequentSecondLevelMenus } from "@/app/navigation/menuUsage.js";
|
||||
import { formatMillis } from "@/shared/utils/time.js";
|
||||
import { TimeText } from "@/shared/ui/TimeText.jsx";
|
||||
|
||||
export function DashboardFrequentMenus({ menus = [] }) {
|
||||
const { user } = useAuth();
|
||||
@ -32,7 +32,7 @@ export function DashboardFrequentMenus({ menus = [] }) {
|
||||
<span className="frequent-menu-card__group">{item.parentLabel}</span>
|
||||
<strong>{item.label}</strong>
|
||||
<span className="frequent-menu-card__meta">
|
||||
进入 {item.count || 0} 次 · {formatMillis(item.lastVisitedAtMs)}
|
||||
进入 {item.count || 0} 次 · <TimeText value={item.lastVisitedAtMs} />
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
|
||||
@ -5,7 +5,7 @@ import MenuItem from "@mui/material/MenuItem";
|
||||
import TextField from "@mui/material/TextField";
|
||||
import { AdminActionIconButton } from "@/shared/ui/AdminListLayout.jsx";
|
||||
import { Button } from "@/shared/ui/Button.jsx";
|
||||
import { formatMillis } from "@/shared/utils/time.js";
|
||||
import { TimeText } from "@/shared/ui/TimeText.jsx";
|
||||
import { stageOptions } from "@/features/lucky-gift/constants.js";
|
||||
import { formFromConfig } from "@/features/lucky-gift/configModel.js";
|
||||
import styles from "@/features/lucky-gift/lucky-gift.module.css";
|
||||
@ -44,7 +44,7 @@ export function LuckyGiftConfigSummary({
|
||||
<SummaryItem label="房间小时">{formatNumber(config?.roomHourlyPayoutCap)}</SummaryItem>
|
||||
<SummaryItem label="阶段">{formatStages(form.stages)}</SummaryItem>
|
||||
<SummaryItem label="更新时间">
|
||||
{config?.createdAtMs ? formatMillis(config.createdAtMs) : configLoading ? "加载中" : "-"}
|
||||
{config?.createdAtMs ? <TimeText value={config.createdAtMs} /> : configLoading ? "加载中" : "-"}
|
||||
</SummaryItem>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -12,8 +12,8 @@ import { DataTable } from "@/shared/ui/DataTable.jsx";
|
||||
import { SideDrawer } from "@/shared/ui/SideDrawer.jsx";
|
||||
import { usePaginatedQuery } from "@/shared/hooks/usePaginatedQuery.js";
|
||||
import { TimeRangeFilter } from "@/shared/ui/TimeRangeFilter.jsx";
|
||||
import { TimeText } from "@/shared/ui/TimeText.jsx";
|
||||
import { createTextColumnFilter } from "@/shared/ui/tableFilters.js";
|
||||
import { formatMillis } from "@/shared/utils/time.js";
|
||||
import styles from "@/features/operations/operations.module.css";
|
||||
|
||||
const pageSize = 50;
|
||||
@ -47,7 +47,7 @@ const columns = [
|
||||
key: "createdAtMs",
|
||||
label: "时间",
|
||||
width: "minmax(170px, 0.85fr)",
|
||||
render: (entry) => formatMillis(entry.createdAtMs),
|
||||
render: (entry) => <TimeText value={entry.createdAtMs} />,
|
||||
},
|
||||
];
|
||||
|
||||
@ -252,7 +252,7 @@ function CoinLedgerDetailDrawer({ entry, onClose, open }) {
|
||||
detailRow("流水 ID", entry.entryId),
|
||||
detailRow("业务类型", bizTypeLabel(entry.bizType)),
|
||||
detailRow("方向", directionLabel(entry.direction)),
|
||||
detailRow("发生时间", formatMillis(entry.createdAtMs)),
|
||||
detailRow("发生时间", <TimeText value={entry.createdAtMs} />),
|
||||
]}
|
||||
title="交易信息"
|
||||
/>
|
||||
|
||||
@ -3,7 +3,7 @@ import Drawer from "@mui/material/Drawer";
|
||||
import IconButton from "@mui/material/IconButton";
|
||||
import { DataState } from "@/shared/ui/DataState.jsx";
|
||||
import { DataTable } from "@/shared/ui/DataTable.jsx";
|
||||
import { formatMillis } from "@/shared/utils/time.js";
|
||||
import { TimeText } from "@/shared/ui/TimeText.jsx";
|
||||
import styles from "@/features/red-packets/red-packets.module.css";
|
||||
|
||||
const claimColumns = [
|
||||
@ -29,7 +29,7 @@ const claimColumns = [
|
||||
key: "createdAt",
|
||||
label: "领取时间",
|
||||
width: "minmax(180px, 0.9fr)",
|
||||
render: (claim) => formatMillis(claim.createdAtMs),
|
||||
render: (claim) => <TimeText value={claim.createdAtMs} />,
|
||||
},
|
||||
];
|
||||
|
||||
@ -67,8 +67,8 @@ export function RedPacketDetailDrawer({ loading, onClose, open, packet }) {
|
||||
: "-"
|
||||
}
|
||||
/>
|
||||
<InfoRow label="开启时间" value={formatMillis(packet.openAtMs)} />
|
||||
<InfoRow label="过期时间" value={formatMillis(packet.expiresAtMs)} />
|
||||
<InfoRow label="开启时间" value={<TimeText value={packet.openAtMs} />} />
|
||||
<InfoRow label="过期时间" value={<TimeText value={packet.expiresAtMs} />} />
|
||||
<DataTable
|
||||
columns={claimColumns}
|
||||
emptyLabel="暂无领取记录"
|
||||
|
||||
@ -2,7 +2,7 @@ import EditOutlined from "@mui/icons-material/EditOutlined";
|
||||
import RefreshOutlined from "@mui/icons-material/RefreshOutlined";
|
||||
import { Button } from "@/shared/ui/Button.jsx";
|
||||
import { AdminActionIconButton } from "@/shared/ui/AdminListLayout.jsx";
|
||||
import { formatMillis } from "@/shared/utils/time.js";
|
||||
import { TimeText } from "@/shared/ui/TimeText.jsx";
|
||||
import styles from "@/features/room-rocket/room-rocket.module.css";
|
||||
|
||||
const broadcastScopeLabels = {
|
||||
@ -33,7 +33,7 @@ export function RoomRocketConfigSummary({ canUpdate, config, configLoading, onEd
|
||||
</SummaryItem>
|
||||
<SummaryItem label="角色奖励">{rewardStackPolicyLabels[config?.rewardStackPolicy] || "-"}</SummaryItem>
|
||||
<SummaryItem label="更新时间">
|
||||
{config?.updatedAtMs ? formatMillis(config.updatedAtMs) : configLoading ? "加载中" : "-"}
|
||||
{config?.updatedAtMs ? <TimeText value={config.updatedAtMs} /> : configLoading ? "加载中" : "-"}
|
||||
</SummaryItem>
|
||||
</div>
|
||||
<LevelPreview config={config} loading={configLoading} />
|
||||
|
||||
@ -2,6 +2,7 @@ import EditOutlined from "@mui/icons-material/EditOutlined";
|
||||
import RefreshOutlined from "@mui/icons-material/RefreshOutlined";
|
||||
import CircularProgress from "@mui/material/CircularProgress";
|
||||
import { Button } from "@/shared/ui/Button.jsx";
|
||||
import { TimeText } from "@/shared/ui/TimeText.jsx";
|
||||
import styles from "@/features/room-turnover-reward/room-turnover-reward.module.css";
|
||||
|
||||
export function RoomTurnoverRewardConfigSummary({ canUpdate, config, configLoading, onEdit, onRefresh }) {
|
||||
@ -31,7 +32,9 @@ export function RoomTurnoverRewardConfigSummary({ canUpdate, config, configLoadi
|
||||
</div>
|
||||
<div className={styles.summaryItem}>
|
||||
<span className={styles.summaryLabel}>更新时间</span>
|
||||
<span className={styles.summaryValue}>{config?.updatedAtMs ? config.updatedAtMs : "-"}</span>
|
||||
<span className={styles.summaryValue}>
|
||||
{config?.updatedAtMs ? <TimeText value={config.updatedAtMs} /> : configLoading ? "加载中" : "-"}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.summaryActions}>
|
||||
|
||||
@ -0,0 +1,52 @@
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { beforeEach, expect, test } from "vitest";
|
||||
import { TimeZoneProvider, useTimeZone } from "@/app/timezone/TimeZoneProvider.jsx";
|
||||
import { RoomTurnoverRewardConfigSummary } from "./RoomTurnoverRewardConfigSummary.jsx";
|
||||
|
||||
const updatedAtMs = Date.UTC(2026, 5, 7, 5, 4, 13);
|
||||
|
||||
beforeEach(() => {
|
||||
window.localStorage.clear();
|
||||
});
|
||||
|
||||
test("formats summary update time through the selected admin timezone", async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
render(
|
||||
<TimeZoneProvider>
|
||||
<SummaryFixture />
|
||||
</TimeZoneProvider>
|
||||
);
|
||||
|
||||
expect(screen.queryByText(String(updatedAtMs))).not.toBeInTheDocument();
|
||||
expect(screen.getByText("2026-06-07 13:04:13")).toBeInTheDocument();
|
||||
|
||||
await user.click(screen.getByRole("button", { name: "UTC" }));
|
||||
|
||||
expect(screen.getByText("2026-06-07 05:04:13")).toBeInTheDocument();
|
||||
expect(screen.queryByText(String(updatedAtMs))).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
function SummaryFixture() {
|
||||
const { setTimeZone } = useTimeZone();
|
||||
|
||||
return (
|
||||
<>
|
||||
<button type="button" onClick={() => setTimeZone("UTC")}>
|
||||
UTC
|
||||
</button>
|
||||
<RoomTurnoverRewardConfigSummary
|
||||
canUpdate
|
||||
config={{
|
||||
enabled: true,
|
||||
tiers: [{ rewardCoinAmount: 50, status: "active" }],
|
||||
updatedAtMs
|
||||
}}
|
||||
configLoading={false}
|
||||
onEdit={() => {}}
|
||||
onRefresh={() => {}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -7,7 +7,7 @@ import { AdminActionIconButton, AdminListBody, AdminListPage } from "@/shared/ui
|
||||
import { DataState } from "@/shared/ui/DataState.jsx";
|
||||
import { DataTable } from "@/shared/ui/DataTable.jsx";
|
||||
import { createOptionsColumnFilter, createTextColumnFilter } from "@/shared/ui/tableFilters.js";
|
||||
import { formatMillis } from "@/shared/utils/time.js";
|
||||
import { TimeText } from "@/shared/ui/TimeText.jsx";
|
||||
|
||||
const settlementStatusOptions = [
|
||||
["pending", "待发放"],
|
||||
@ -33,8 +33,10 @@ const columnsBase = [
|
||||
width: "minmax(260px, 1.1fr)",
|
||||
render: (item) => (
|
||||
<div className={styles.stack}>
|
||||
<span>{formatMillis(item.periodStartMs)}</span>
|
||||
<span className={styles.meta}>至 {formatMillis(item.periodEndMs)}</span>
|
||||
<TimeText value={item.periodStartMs} />
|
||||
<span className={styles.meta}>
|
||||
至 <TimeText value={item.periodEndMs} />
|
||||
</span>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
|
||||
@ -3,7 +3,7 @@ import MeetingRoomOutlined from "@mui/icons-material/MeetingRoomOutlined";
|
||||
import PersonOutlineOutlined from "@mui/icons-material/PersonOutlineOutlined";
|
||||
import { useEffect, useState } from "react";
|
||||
import { SideDrawer } from "@/shared/ui/SideDrawer.jsx";
|
||||
import { formatMillis } from "@/shared/utils/time.js";
|
||||
import { TimeText } from "@/shared/ui/TimeText.jsx";
|
||||
import { roomStatusLabels } from "@/features/rooms/constants.js";
|
||||
import styles from "@/features/rooms/rooms.module.css";
|
||||
|
||||
@ -37,8 +37,8 @@ export function RoomDetailDrawer({ onClose, open, room }) {
|
||||
["房间状态", roomStatusLabels[normalizedStatus]],
|
||||
["房间模式", room.mode],
|
||||
["区域", room.regionName],
|
||||
["创建时间", formatMillis(room.createdAtMs)],
|
||||
["更新时间", formatMillis(room.updatedAtMs)]
|
||||
["创建时间", <TimeText key="created-at" value={room.createdAtMs} />],
|
||||
["更新时间", <TimeText key="updated-at" value={room.updatedAtMs} />]
|
||||
]}
|
||||
title="基础信息"
|
||||
/>
|
||||
@ -47,7 +47,7 @@ export function RoomDetailDrawer({ onClose, open, room }) {
|
||||
<DetailSection
|
||||
items={[
|
||||
["操作人", closeOperator],
|
||||
["操作时间", formatMillis(room.closedAtMs)],
|
||||
["操作时间", <TimeText key="closed-at" value={room.closedAtMs} />],
|
||||
["关闭原因", room.closeReason]
|
||||
]}
|
||||
title="关闭信息"
|
||||
|
||||
@ -6,7 +6,7 @@ import MenuItem from "@mui/material/MenuItem";
|
||||
import TextField from "@mui/material/TextField";
|
||||
import { DataState } from "@/shared/ui/DataState.jsx";
|
||||
import { AdminActionIconButton, AdminListBody, AdminListPage, AdminListToolbar } from "@/shared/ui/AdminListLayout.jsx";
|
||||
import { formatMillis } from "@/shared/utils/time.js";
|
||||
import { TimeText } from "@/shared/ui/TimeText.jsx";
|
||||
import { useRoomConfigPage } from "@/features/rooms/hooks/useRoomConfigPage.js";
|
||||
import styles from "@/features/rooms/rooms.module.css";
|
||||
|
||||
@ -45,7 +45,7 @@ export function RoomConfigPage() {
|
||||
<div className={styles.configTitle}>
|
||||
<h2>座位数</h2>
|
||||
<span>
|
||||
{page.form.updatedAtMs ? formatMillis(page.form.updatedAtMs) : "默认配置"}
|
||||
{page.form.updatedAtMs ? <TimeText value={page.form.updatedAtMs} /> : "默认配置"}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -2,7 +2,7 @@ import EditOutlined from "@mui/icons-material/EditOutlined";
|
||||
import RefreshOutlined from "@mui/icons-material/RefreshOutlined";
|
||||
import { Button } from "@/shared/ui/Button.jsx";
|
||||
import { AdminActionIconButton } from "@/shared/ui/AdminListLayout.jsx";
|
||||
import { formatMillis } from "@/shared/utils/time.js";
|
||||
import { TimeText } from "@/shared/ui/TimeText.jsx";
|
||||
import styles from "@/features/seven-day-checkin/seven-day-checkin.module.css";
|
||||
|
||||
export function SevenDayCheckInConfigSummary({ canUpdate, config, configLoading, onEdit, onRefresh, resourceGroups }) {
|
||||
@ -16,7 +16,7 @@ export function SevenDayCheckInConfigSummary({ canUpdate, config, configLoading,
|
||||
<div className={styles.summaryItems}>
|
||||
<ConfigStatus config={config} loading={configLoading} />
|
||||
<SummaryItem label="更新时间">
|
||||
{config?.updatedAtMs ? formatMillis(config.updatedAtMs) : configLoading ? "加载中" : "-"}
|
||||
{config?.updatedAtMs ? <TimeText value={config.updatedAtMs} /> : configLoading ? "加载中" : "-"}
|
||||
</SummaryItem>
|
||||
</div>
|
||||
<RewardPreview config={config} loading={configLoading} resourceGroups={resourceGroups} />
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user