修复报错
This commit is contained in:
parent
ac5d3a53d9
commit
2ebacae7c2
@ -4892,10 +4892,10 @@
|
||||
"type": "string"
|
||||
},
|
||||
"ownerUserId": {
|
||||
"type": "integer"
|
||||
"type": "string"
|
||||
},
|
||||
"parentBdUserId": {
|
||||
"type": "integer"
|
||||
"type": "string"
|
||||
},
|
||||
"regionId": {
|
||||
"type": "integer"
|
||||
@ -4919,7 +4919,7 @@
|
||||
"type": "integer"
|
||||
},
|
||||
"parentLeaderUserId": {
|
||||
"type": "integer"
|
||||
"type": "string"
|
||||
},
|
||||
"regionId": {
|
||||
"type": "integer"
|
||||
@ -4934,7 +4934,7 @@
|
||||
"type": "integer"
|
||||
},
|
||||
"userId": {
|
||||
"type": "integer"
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -4991,7 +4991,7 @@
|
||||
"type": "integer"
|
||||
},
|
||||
"userId": {
|
||||
"type": "integer"
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -71,7 +71,7 @@ test("loads self game statistics from the big screen switch", async () => {
|
||||
|
||||
expect(fetchSelfGameStatisticsOverview).toHaveBeenCalledWith(expect.objectContaining({ appCode: "lalu", gameId: "all" }));
|
||||
expect(screen.getByText("Dice / Rock H5 聚合统计")).toBeTruthy();
|
||||
expect(screen.getByText("对局数")).toBeTruthy();
|
||||
expect(screen.getByText("创建局")).toBeTruthy();
|
||||
});
|
||||
|
||||
async function flushEffects() {
|
||||
|
||||
@ -158,11 +158,11 @@ function FilterMenu({ className = "", icon, isOpen, label, loading = false, onSe
|
||||
return (
|
||||
<div className={["filter-control", className].filter(Boolean).join(" ")}>
|
||||
<button aria-expanded={isOpen} className="filter-trigger" disabled={loading} onClick={onToggle} type="button">
|
||||
{icon ? <span className="filter-icon">{icon}</span> : null}
|
||||
<span className="filter-copy">
|
||||
<small>{label}</small>
|
||||
<b>{valueLabel}</b>
|
||||
<span className="filter-label">
|
||||
{icon ? <span className="filter-icon">{icon}</span> : null}
|
||||
<span>{label}</span>
|
||||
</span>
|
||||
<span className="filter-value">{valueLabel}</span>
|
||||
<KeyboardArrowDownOutlined className="filter-chevron" fontSize="small" />
|
||||
</button>
|
||||
{isOpen ? (
|
||||
@ -201,11 +201,11 @@ function SearchableFilterMenu({ icon, isOpen, label, loading = false, onSelect,
|
||||
return (
|
||||
<div className="filter-control filter-control--country">
|
||||
<button aria-expanded={isOpen} className="filter-trigger" disabled={loading} onClick={onToggle} type="button">
|
||||
{icon ? <span className="filter-icon">{icon}</span> : null}
|
||||
<span className="filter-copy">
|
||||
<small>{label}</small>
|
||||
<b>{valueLabel}</b>
|
||||
<span className="filter-label">
|
||||
{icon ? <span className="filter-icon">{icon}</span> : null}
|
||||
<span>{label}</span>
|
||||
</span>
|
||||
<span className="filter-value">{valueLabel}</span>
|
||||
<KeyboardArrowDownOutlined className="filter-chevron" fontSize="small" />
|
||||
</button>
|
||||
{isOpen ? (
|
||||
@ -301,14 +301,14 @@ function RangeMenu({ activeValue, isOpen, onSelect, onToggle, range, timeZone })
|
||||
return (
|
||||
<div className="filter-control filter-control--range">
|
||||
<button aria-expanded={isOpen} aria-haspopup="listbox" className="date-range-trigger" onClick={onToggle} type="button">
|
||||
<span className="date-range-icon"><CalendarMonthOutlined fontSize="small" /></span>
|
||||
<span className="date-range-copy">
|
||||
<span className="date-range-label">{activeOption?.label || "时间范围"}</span>
|
||||
<span className="date-range-values">
|
||||
<b>{range.start}</b>
|
||||
<i>~</i>
|
||||
<b>{range.end}</b>
|
||||
</span>
|
||||
<span className="date-range-label">
|
||||
<CalendarMonthOutlined fontSize="inherit" />
|
||||
<span>{activeOption?.label || "时间范围"}</span>
|
||||
</span>
|
||||
<span className="date-range-values">
|
||||
<b>{range.start}</b>
|
||||
<i>~</i>
|
||||
<b>{range.end}</b>
|
||||
</span>
|
||||
<KeyboardArrowDownOutlined className="date-range-chevron" fontSize="small" />
|
||||
</button>
|
||||
|
||||
@ -131,13 +131,14 @@
|
||||
}
|
||||
|
||||
.filter-trigger {
|
||||
position: relative;
|
||||
display: grid;
|
||||
min-width: 138px;
|
||||
height: 44px;
|
||||
grid-template-columns: auto minmax(0, 1fr) auto;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 0 12px;
|
||||
height: 34px;
|
||||
grid-template-columns: minmax(0, 1fr) 18px;
|
||||
align-items: end;
|
||||
gap: 0;
|
||||
padding: 7px 28px 3px 12px;
|
||||
border: 1px solid rgba(64, 148, 197, 0.48);
|
||||
border-radius: 8px;
|
||||
background: rgba(7, 29, 48, 0.9);
|
||||
@ -167,6 +168,13 @@
|
||||
box-shadow: 0 0 0 3px rgba(39, 228, 245, 0.08);
|
||||
}
|
||||
|
||||
.filter-trigger:hover .filter-label,
|
||||
.filter-trigger[aria-expanded="true"] .filter-label,
|
||||
.date-range-trigger:hover .date-range-label,
|
||||
.date-range-trigger[aria-expanded="true"] .date-range-label {
|
||||
color: #27e4f5;
|
||||
}
|
||||
|
||||
.filter-trigger:disabled {
|
||||
cursor: progress;
|
||||
opacity: 0.68;
|
||||
@ -177,10 +185,24 @@
|
||||
color: #cbd9e9;
|
||||
}
|
||||
|
||||
.filter-icon {
|
||||
display: grid;
|
||||
min-width: 20px;
|
||||
place-items: center;
|
||||
.filter-label {
|
||||
position: absolute;
|
||||
top: -8px;
|
||||
left: 10px;
|
||||
display: inline-flex;
|
||||
max-width: calc(100% - 20px);
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
overflow: hidden;
|
||||
padding: 0 5px;
|
||||
border-radius: 5px;
|
||||
background: rgba(2, 15, 31, 0.98);
|
||||
color: #7f9bb7;
|
||||
font-size: 10px;
|
||||
font-weight: 760;
|
||||
line-height: 14px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.control-glyph {
|
||||
@ -190,38 +212,46 @@
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
.filter-copy {
|
||||
display: grid;
|
||||
.filter-icon {
|
||||
display: inline-grid;
|
||||
flex: 0 0 auto;
|
||||
min-width: 16px;
|
||||
place-items: center;
|
||||
color: #cbd9e9;
|
||||
}
|
||||
|
||||
.filter-label .filter-icon {
|
||||
color: #27e4f5;
|
||||
}
|
||||
|
||||
.filter-value {
|
||||
display: block;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.filter-copy small {
|
||||
overflow: hidden;
|
||||
color: #7f9bb7;
|
||||
font-size: 10px;
|
||||
line-height: 1.1;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.filter-copy b {
|
||||
overflow: hidden;
|
||||
color: #e6f3ff;
|
||||
font-size: 13px;
|
||||
font-weight: 720;
|
||||
line-height: 1.2;
|
||||
line-height: 18px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.filter-chevron {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 8px;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
.date-range-trigger {
|
||||
position: relative;
|
||||
display: grid;
|
||||
width: 336px;
|
||||
height: 44px;
|
||||
grid-template-columns: 24px minmax(0, 1fr) 20px;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 0 12px;
|
||||
width: 318px;
|
||||
height: 34px;
|
||||
grid-template-columns: minmax(0, 1fr) 18px;
|
||||
align-items: end;
|
||||
gap: 0;
|
||||
padding: 7px 28px 3px 12px;
|
||||
border: 1px solid rgba(64, 148, 197, 0.48);
|
||||
border-radius: 8px;
|
||||
background: rgba(7, 29, 48, 0.9);
|
||||
@ -238,49 +268,46 @@
|
||||
box-shadow: 0 0 0 3px rgba(39, 228, 245, 0.08);
|
||||
}
|
||||
|
||||
.date-range-icon {
|
||||
display: grid;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
place-items: center;
|
||||
color: #cbd9e9;
|
||||
}
|
||||
|
||||
.date-range-icon svg {
|
||||
width: 19px;
|
||||
height: 19px;
|
||||
}
|
||||
|
||||
.date-range-copy {
|
||||
display: grid;
|
||||
min-width: 0;
|
||||
gap: 3px;
|
||||
}
|
||||
|
||||
.date-range-label {
|
||||
position: absolute;
|
||||
top: -8px;
|
||||
left: 10px;
|
||||
display: inline-flex;
|
||||
max-width: calc(100% - 20px);
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
overflow: hidden;
|
||||
padding: 0 5px;
|
||||
border-radius: 5px;
|
||||
background: rgba(2, 15, 31, 0.98);
|
||||
color: #7f9bb7;
|
||||
font-size: 10px;
|
||||
font-weight: 720;
|
||||
line-height: 1.05;
|
||||
font-weight: 760;
|
||||
line-height: 14px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.date-range-label svg {
|
||||
flex: 0 0 auto;
|
||||
color: #cbd9e9;
|
||||
}
|
||||
|
||||
.date-range-values {
|
||||
display: grid;
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
grid-template-columns: minmax(0, auto) 16px minmax(0, auto);
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
gap: 7px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.date-range-values b {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
color: #eaf6ff;
|
||||
font-size: 14px;
|
||||
font-size: 13px;
|
||||
font-weight: 760;
|
||||
line-height: 1.15;
|
||||
line-height: 18px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
@ -293,8 +320,11 @@
|
||||
}
|
||||
|
||||
.date-range-chevron {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 8px;
|
||||
color: #cbd9e9;
|
||||
justify-self: end;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
.filter-popover {
|
||||
|
||||
@ -155,6 +155,7 @@ export function Header({ activeLabel, isSidebarCollapsed, menuItems = [], onRefr
|
||||
<TextField
|
||||
className="app-switch"
|
||||
disabled={appScope.loading || !appScope.apps.length}
|
||||
label="应用"
|
||||
select
|
||||
size="small"
|
||||
value={appScope.appCode}
|
||||
@ -172,6 +173,7 @@ export function Header({ activeLabel, isSidebarCollapsed, menuItems = [], onRefr
|
||||
</TextField>
|
||||
<TextField
|
||||
className="timezone-switch"
|
||||
label="时区"
|
||||
select
|
||||
size="small"
|
||||
slotProps={{ htmlInput: { "aria-label": "显示时区" } }}
|
||||
@ -185,8 +187,11 @@ export function Header({ activeLabel, isSidebarCollapsed, menuItems = [], onRefr
|
||||
))}
|
||||
</TextField>
|
||||
<button className="search-trigger" type="button" onClick={openSearch}>
|
||||
<Search fontSize="small" />
|
||||
<span>搜索菜单、用户、角色...</span>
|
||||
<span className="search-trigger__label">
|
||||
<Search fontSize="inherit" />
|
||||
<span>搜索</span>
|
||||
</span>
|
||||
<span className="search-trigger__value">搜索菜单、用户、角色...</span>
|
||||
</button>
|
||||
<IconButton label="刷新" onClick={refresh}>
|
||||
<Refresh fontSize="small" />
|
||||
|
||||
@ -37,7 +37,7 @@ export interface CumulativeRechargeRewardConfigPayload {
|
||||
}
|
||||
|
||||
export interface CumulativeRechargeRewardGrantUserDto {
|
||||
userId?: number;
|
||||
userId?: string;
|
||||
displayUserId?: string;
|
||||
prettyDisplayUserId?: string;
|
||||
prettyId?: string;
|
||||
@ -52,7 +52,7 @@ export interface CumulativeRechargeRewardGrantDto {
|
||||
eventId?: string;
|
||||
transactionId?: string;
|
||||
commandId?: string;
|
||||
userId: number;
|
||||
userId: string;
|
||||
user?: CumulativeRechargeRewardGrantUserDto;
|
||||
tierId?: number;
|
||||
tierCode?: string;
|
||||
@ -143,9 +143,9 @@ function normalizeGrant(item: RawGrant): CumulativeRechargeRewardGrantDto {
|
||||
eventId: stringValue(item.eventId ?? item.event_id),
|
||||
transactionId: stringValue(item.transactionId ?? item.transaction_id),
|
||||
commandId: stringValue(item.commandId ?? item.command_id),
|
||||
userId: numberValue(item.userId ?? item.user_id),
|
||||
userId: stringValue(item.userId ?? item.user_id),
|
||||
user: {
|
||||
userId: numberValue(rawUser.userId ?? rawUser.user_id),
|
||||
userId: stringValue(rawUser.userId ?? rawUser.user_id),
|
||||
displayUserId: stringValue(rawUser.displayUserId ?? rawUser.display_user_id),
|
||||
prettyDisplayUserId: stringValue(rawUser.prettyDisplayUserId ?? rawUser.pretty_display_user_id),
|
||||
prettyId: stringValue(rawUser.prettyId ?? rawUser.pretty_id),
|
||||
|
||||
@ -45,7 +45,7 @@ export interface FirstRechargeRewardConfigPayload {
|
||||
}
|
||||
|
||||
export interface FirstRechargeRewardClaimUserDto {
|
||||
userId?: number;
|
||||
userId?: string;
|
||||
displayUserId?: string;
|
||||
prettyDisplayUserId?: string;
|
||||
prettyId?: string;
|
||||
@ -58,7 +58,7 @@ export interface FirstRechargeRewardClaimDto {
|
||||
eventId?: string;
|
||||
transactionId?: string;
|
||||
commandId?: string;
|
||||
userId: number;
|
||||
userId: string;
|
||||
user?: FirstRechargeRewardClaimUserDto;
|
||||
tierId?: number;
|
||||
tierCode?: string;
|
||||
@ -151,9 +151,9 @@ function normalizeClaim(item: RawClaim): FirstRechargeRewardClaimDto {
|
||||
eventId: stringValue(item.eventId ?? item.event_id),
|
||||
transactionId: stringValue(item.transactionId ?? item.transaction_id),
|
||||
commandId: stringValue(item.commandId ?? item.command_id),
|
||||
userId: numberValue(item.userId ?? item.user_id),
|
||||
userId: stringValue(item.userId ?? item.user_id),
|
||||
user: {
|
||||
userId: numberValue(rawUser.userId ?? rawUser.user_id),
|
||||
userId: stringValue(rawUser.userId ?? rawUser.user_id),
|
||||
displayUserId: stringValue(rawUser.displayUserId ?? rawUser.display_user_id),
|
||||
prettyDisplayUserId: stringValue(rawUser.prettyDisplayUserId ?? rawUser.pretty_display_user_id),
|
||||
prettyId: stringValue(rawUser.prettyId ?? rawUser.pretty_id),
|
||||
|
||||
@ -305,7 +305,7 @@ export interface TeamSalarySettlePayload {
|
||||
cycle_key: string;
|
||||
region_id?: number;
|
||||
country_code?: string;
|
||||
user_ids?: number[];
|
||||
user_ids?: string[];
|
||||
}
|
||||
|
||||
export interface TeamSalarySettleResult {
|
||||
|
||||
@ -298,8 +298,8 @@ function TeamPendingPanel() {
|
||||
// 只从当前页 items 中提取已勾选用户,服务端仍会再次按 user_ids 白名单过滤。
|
||||
const userIds = items
|
||||
.filter((item) => selected.has(teamPendingKey(item)))
|
||||
.map((item) => Number(item.userId))
|
||||
.filter((id) => Number.isFinite(id) && id > 0);
|
||||
.map((item) => String(item.userId || "").trim())
|
||||
.filter((id) => /^[1-9]\d*$/.test(id));
|
||||
if (!userIds.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -47,15 +47,15 @@ test("host org list APIs use generated admin paths and filters", async () => {
|
||||
await createBDLeader({
|
||||
commandId: "bd-leader-test",
|
||||
positionAlias: "Senior Admin",
|
||||
targetUserId: 1002,
|
||||
targetUserId: "1002",
|
||||
});
|
||||
await updateBDLeaderPositionAlias(1002, {
|
||||
await updateBDLeaderPositionAlias("1002", {
|
||||
commandId: "bd-leader-position-alias-test",
|
||||
positionAlias: "Regional Admin",
|
||||
});
|
||||
await createCoinSeller({ commandId: "coin-seller-test", reason: "contact", targetUserId: 1001 });
|
||||
await setCoinSellerStatus(1001, { commandId: "coin-seller-status-test", reason: "disable", status: "disabled" });
|
||||
await creditCoinSellerStock(1001, {
|
||||
await createCoinSeller({ commandId: "coin-seller-test", reason: "contact", targetUserId: "1001" });
|
||||
await setCoinSellerStatus("1001", { commandId: "coin-seller-status-test", reason: "disable", status: "disabled" });
|
||||
await creditCoinSellerStock("1001", {
|
||||
coinAmount: 8000000,
|
||||
commandId: "coin-seller-stock-test",
|
||||
rechargeAmount: "100.000000",
|
||||
|
||||
@ -11,7 +11,15 @@ const commandContactSchema = z.object({
|
||||
reason: z.string().trim().max(200, "原因不能超过 200 个字符").optional().default(""),
|
||||
});
|
||||
|
||||
const userIdSchema = z.coerce.number().int().positive("请输入有效用户短 ID");
|
||||
const userIdSchema = z.preprocess(
|
||||
(value) => {
|
||||
if (value === null || value === undefined) {
|
||||
return "";
|
||||
}
|
||||
return String(value).trim();
|
||||
},
|
||||
z.string().min(1, "请输入有效用户短 ID").regex(/^[1-9]\d*$/, "请输入有效用户短 ID"),
|
||||
);
|
||||
const entityIdSchema = z.preprocess(
|
||||
(value) => {
|
||||
if (value === null || value === undefined) {
|
||||
@ -24,14 +32,14 @@ const entityIdSchema = z.preprocess(
|
||||
const optionalUserIdSchema = z.preprocess(
|
||||
(value) => {
|
||||
if (value === null || value === undefined) {
|
||||
return 0;
|
||||
return "0";
|
||||
}
|
||||
if (typeof value === "string" && value.trim() === "") {
|
||||
return 0;
|
||||
return "0";
|
||||
}
|
||||
return value;
|
||||
return String(value).trim();
|
||||
},
|
||||
z.coerce.number().int().min(0, "请输入有效用户短 ID"),
|
||||
z.string().regex(/^(0|[1-9]\d*)$/, "请输入有效用户短 ID"),
|
||||
);
|
||||
const regionIdSchema = z.coerce.number().int().positive("请输入有效区域 ID");
|
||||
const sortOrderSchema = z.coerce.number().int().min(0, "排序不能小于 0").default(0);
|
||||
|
||||
@ -45,7 +45,7 @@ export interface InviteActivityRewardConfigPayload {
|
||||
}
|
||||
|
||||
export interface InviteActivityRewardClaimUserDto {
|
||||
userId?: number;
|
||||
userId?: string;
|
||||
displayUserId?: string;
|
||||
username?: string;
|
||||
avatar?: string;
|
||||
@ -55,7 +55,7 @@ export interface InviteActivityRewardClaimDto {
|
||||
claimId: string;
|
||||
appCode?: string;
|
||||
cycleKey?: string;
|
||||
userId: number;
|
||||
userId: string;
|
||||
user?: InviteActivityRewardClaimUserDto;
|
||||
rewardType?: string;
|
||||
commandId?: string;
|
||||
@ -150,9 +150,9 @@ function normalizeClaim(item: RawClaim): InviteActivityRewardClaimDto {
|
||||
claimId: stringValue(item.claimId ?? item.claim_id),
|
||||
appCode: stringValue(item.appCode ?? item.app_code),
|
||||
cycleKey: stringValue(item.cycleKey ?? item.cycle_key),
|
||||
userId: numberValue(item.userId ?? item.user_id),
|
||||
userId: stringValue(item.userId ?? item.user_id),
|
||||
user: {
|
||||
userId: numberValue(rawUser.userId ?? rawUser.user_id),
|
||||
userId: stringValue(rawUser.userId ?? rawUser.user_id),
|
||||
displayUserId: stringValue(rawUser.displayUserId ?? rawUser.display_user_id),
|
||||
username: stringValue(rawUser.username),
|
||||
avatar: stringValue(rawUser.avatar),
|
||||
|
||||
@ -10,7 +10,7 @@ import styles from "@/features/invite-activity-reward/invite-activity-reward.mod
|
||||
|
||||
const rewardTypeOptions = [
|
||||
["recharge", "累计充值"],
|
||||
["valid_invite", "有效人数"],
|
||||
["valid_invite", "Valid Users"],
|
||||
];
|
||||
|
||||
export function InviteActivityRewardConfigDrawer({
|
||||
|
||||
@ -25,8 +25,8 @@ export function InviteActivityRewardConfigSummary({ canUpdate, config, configLoa
|
||||
<SummaryItem label="累计充值档位">
|
||||
{thresholdSummary(rechargeTiers, "thresholdCoinAmount", "金币")}
|
||||
</SummaryItem>
|
||||
<SummaryItem label="有效人数档位">
|
||||
{thresholdSummary(validInviteTiers, "thresholdValidInviteCount", "人")}
|
||||
<SummaryItem label="Valid Users Tiers">
|
||||
{thresholdSummary(validInviteTiers, "thresholdValidInviteCount", "people")}
|
||||
</SummaryItem>
|
||||
<SummaryItem label="邀请人即时奖励">
|
||||
{coinSummary(config?.perInviteInviterRewardCoinAmount)}
|
||||
|
||||
@ -180,17 +180,16 @@ function payloadFromForm(form) {
|
||||
throw new Error("累计充值金币门槛必须大于 0");
|
||||
}
|
||||
if (rewardType === "valid_invite" && thresholdValidInviteCount <= 0) {
|
||||
throw new Error("有效人数门槛必须大于 0");
|
||||
throw new Error("Valid users threshold must be greater than 0");
|
||||
}
|
||||
if (rewardCoinAmount <= 0) {
|
||||
throw new Error("奖励金币必须大于 0");
|
||||
}
|
||||
const tierCode = String(tier.tierCode || "").trim() || `invite_${rewardType}_${Date.now()}_${index + 1}`;
|
||||
const tierCode = normalizeTierCode(tier.tierCode, rewardType, index);
|
||||
const tierName =
|
||||
String(tier.tierName || "").trim() ||
|
||||
(rewardType === "recharge"
|
||||
? `累计充值 ${formatNumber(thresholdCoinAmount)} 金币`
|
||||
: `有效邀请 ${formatNumber(thresholdValidInviteCount)} 人`);
|
||||
rewardType === "recharge"
|
||||
? `Cumulative Recharge ${formatNumber(thresholdCoinAmount)} coins`
|
||||
: `Valid Users ${formatNumber(thresholdValidInviteCount)} people`;
|
||||
return {
|
||||
tier_id: Number(tier.tierId || 0),
|
||||
reward_type: rewardType,
|
||||
@ -220,5 +219,13 @@ function payloadFromForm(form) {
|
||||
}
|
||||
|
||||
function formatNumber(value) {
|
||||
return new Intl.NumberFormat("zh-CN").format(Number(value || 0));
|
||||
return new Intl.NumberFormat("en-US").format(Number(value || 0));
|
||||
}
|
||||
|
||||
function normalizeTierCode(value, rewardType, index) {
|
||||
const code = String(value || "").trim();
|
||||
if (code && !/[\u3400-\u9fff]/.test(code)) {
|
||||
return code;
|
||||
}
|
||||
return `invite_${rewardType}_${Date.now()}_${index + 1}`;
|
||||
}
|
||||
|
||||
@ -17,7 +17,7 @@ const claimStatusOptions = [
|
||||
|
||||
const rewardTypeOptions = [
|
||||
["recharge", "累计充值"],
|
||||
["valid_invite", "有效人数"],
|
||||
["valid_invite", "Valid Users"],
|
||||
["invite_inviter", "邀请人即时"],
|
||||
["invite_invitee", "被邀请人即时"],
|
||||
];
|
||||
@ -202,7 +202,7 @@ function claimStatusLabel(status) {
|
||||
|
||||
function thresholdLabel(claim) {
|
||||
if (claim.rewardType === "valid_invite") {
|
||||
return `${formatNumber(claim.thresholdValidInviteCount)} 人`;
|
||||
return `${formatNumber(claim.thresholdValidInviteCount)} people`;
|
||||
}
|
||||
if (claim.rewardType === "invite_inviter" || claim.rewardType === "invite_invitee") {
|
||||
return claim.tierId ? `关联用户 ${claim.tierId}` : "-";
|
||||
@ -214,7 +214,7 @@ function reachedLabel(claim) {
|
||||
if (claim.rewardType === "invite_inviter" || claim.rewardType === "invite_invitee") {
|
||||
return "1 人";
|
||||
}
|
||||
const unit = claim.rewardType === "valid_invite" ? "人" : "金币";
|
||||
const unit = claim.rewardType === "valid_invite" ? "people" : "金币";
|
||||
return `${formatNumber(claim.reachedValue)} ${unit}`;
|
||||
}
|
||||
|
||||
|
||||
@ -30,7 +30,7 @@ export interface RedPacketClaimDto {
|
||||
claimId: string;
|
||||
commandId?: string;
|
||||
packetId: string;
|
||||
userId: number;
|
||||
userId: string;
|
||||
amount: number;
|
||||
walletTransactionId?: string;
|
||||
status?: string;
|
||||
@ -43,7 +43,7 @@ export interface RedPacketDto {
|
||||
appCode?: string;
|
||||
packetId: string;
|
||||
commandId?: string;
|
||||
senderUserId: number;
|
||||
senderUserId: string;
|
||||
roomId: string;
|
||||
regionId: number;
|
||||
packetType: string;
|
||||
@ -139,7 +139,7 @@ function normalizePacket(item: RawPacket): RedPacketDto {
|
||||
appCode: stringValue(item.appCode ?? item.app_code),
|
||||
packetId: stringValue(item.packetId ?? item.packet_id),
|
||||
commandId: stringValue(item.commandId ?? item.command_id),
|
||||
senderUserId: numberValue(item.senderUserId ?? item.sender_user_id),
|
||||
senderUserId: stringValue(item.senderUserId ?? item.sender_user_id),
|
||||
roomId: stringValue(item.roomId ?? item.room_id),
|
||||
regionId: numberValue(item.regionId ?? item.region_id),
|
||||
packetType: stringValue(item.packetType ?? item.packet_type),
|
||||
@ -164,7 +164,7 @@ function normalizeClaim(item: RawClaim): RedPacketClaimDto {
|
||||
claimId: stringValue(item.claimId ?? item.claim_id),
|
||||
commandId: stringValue(item.commandId ?? item.command_id),
|
||||
packetId: stringValue(item.packetId ?? item.packet_id),
|
||||
userId: numberValue(item.userId ?? item.user_id),
|
||||
userId: stringValue(item.userId ?? item.user_id),
|
||||
amount: numberValue(item.amount),
|
||||
walletTransactionId: stringValue(item.walletTransactionId ?? item.wallet_transaction_id),
|
||||
status: stringValue(item.status),
|
||||
|
||||
@ -23,7 +23,7 @@ export interface RegistrationRewardConfigPayload {
|
||||
}
|
||||
|
||||
export interface RegistrationRewardClaimUserDto {
|
||||
userId?: number;
|
||||
userId?: string;
|
||||
displayUserId?: string;
|
||||
prettyDisplayUserId?: string;
|
||||
prettyId?: string;
|
||||
@ -34,7 +34,7 @@ export interface RegistrationRewardClaimUserDto {
|
||||
export interface RegistrationRewardClaimDto {
|
||||
claimId: string;
|
||||
commandId?: string;
|
||||
userId: number;
|
||||
userId: string;
|
||||
user?: RegistrationRewardClaimUserDto;
|
||||
rewardDay?: string;
|
||||
rewardType?: string;
|
||||
@ -109,9 +109,9 @@ function normalizeClaim(item: RawClaim): RegistrationRewardClaimDto {
|
||||
return {
|
||||
claimId: stringValue(item.claimId ?? item.claim_id),
|
||||
commandId: stringValue(item.commandId ?? item.command_id),
|
||||
userId: numberValue(item.userId ?? item.user_id),
|
||||
userId: stringValue(item.userId ?? item.user_id),
|
||||
user: {
|
||||
userId: numberValue(rawUser.userId ?? rawUser.user_id),
|
||||
userId: stringValue(rawUser.userId ?? rawUser.user_id),
|
||||
displayUserId: stringValue(rawUser.displayUserId ?? rawUser.display_user_id),
|
||||
prettyDisplayUserId: stringValue(rawUser.prettyDisplayUserId ?? rawUser.pretty_display_user_id),
|
||||
prettyId: stringValue(rawUser.prettyId ?? rawUser.pretty_id),
|
||||
|
||||
@ -166,7 +166,7 @@ test("resource APIs use generated admin paths", async () => {
|
||||
commandId: "resource-group-grant-test",
|
||||
groupId: 22,
|
||||
reason: "manual",
|
||||
targetUserId: 1001,
|
||||
targetUserId: "1001",
|
||||
});
|
||||
|
||||
const [listUrl, listInit] = vi.mocked(fetch).mock.calls[0];
|
||||
@ -258,7 +258,7 @@ test("resource APIs use generated admin paths", async () => {
|
||||
});
|
||||
expect(String(grantGroupUrl)).toContain("/api/v1/admin/resource-grants/group");
|
||||
expect(grantGroupInit?.method).toBe("POST");
|
||||
expect(JSON.parse(String(grantGroupInit?.body))).toMatchObject({ groupId: 22, targetUserId: 1001 });
|
||||
expect(JSON.parse(String(grantGroupInit?.body))).toMatchObject({ groupId: 22, targetUserId: "1001" });
|
||||
});
|
||||
|
||||
test("resource mp4 layout batch API uses narrow update endpoint", async () => {
|
||||
|
||||
@ -209,7 +209,7 @@ export interface ResourceGrantOperatorDto {
|
||||
prettyId?: string;
|
||||
name?: string;
|
||||
source?: string;
|
||||
userId?: number | string;
|
||||
userId?: string;
|
||||
username?: string;
|
||||
}
|
||||
|
||||
@ -218,7 +218,7 @@ export interface ResourceGrantUserDto {
|
||||
displayUserId?: string;
|
||||
prettyDisplayUserId?: string;
|
||||
prettyId?: string;
|
||||
userId?: number | string;
|
||||
userId?: string;
|
||||
username?: string;
|
||||
}
|
||||
|
||||
@ -236,7 +236,7 @@ export interface ResourceGrantDto {
|
||||
reason?: string;
|
||||
status?: string;
|
||||
targetUser?: ResourceGrantUserDto;
|
||||
targetUserId?: number | string;
|
||||
targetUserId?: string;
|
||||
updatedAtMs?: number;
|
||||
}
|
||||
|
||||
@ -246,14 +246,14 @@ export interface GrantResourcePayload {
|
||||
quantity: number;
|
||||
reason: string;
|
||||
resourceId: number;
|
||||
targetUserId: number | string;
|
||||
targetUserId: string;
|
||||
}
|
||||
|
||||
export interface GrantResourceGroupPayload {
|
||||
commandId: string;
|
||||
groupId: number;
|
||||
reason: string;
|
||||
targetUserId: number | string;
|
||||
targetUserId: string;
|
||||
}
|
||||
|
||||
export interface ResourceGroupItemPayload {
|
||||
|
||||
@ -389,13 +389,13 @@ export const resourceGrantFormSchema = z
|
||||
targetUserId: z.union([z.string(), z.number()]),
|
||||
})
|
||||
.superRefine((value, context) => {
|
||||
const targetUserId = Number(value.targetUserId);
|
||||
const targetUserId = String(value.targetUserId || "").trim();
|
||||
const resourceIds = grantResourceIds(value);
|
||||
const groupId = Number(value.groupId || 0);
|
||||
const quantity = Number(value.quantity || 1);
|
||||
const durationDays = Number(value.durationDays || 0);
|
||||
|
||||
if (!Number.isInteger(targetUserId) || targetUserId <= 0) {
|
||||
if (!/^[1-9]\d*$/.test(targetUserId)) {
|
||||
context.addIssue({
|
||||
code: "custom",
|
||||
message: "请输入用户 ID",
|
||||
|
||||
@ -39,7 +39,7 @@ export interface RoomTurnoverRewardConfigPayload {
|
||||
export interface RoomTurnoverRewardSettlementDto {
|
||||
settlementId: string;
|
||||
roomId: string;
|
||||
ownerUserId: number;
|
||||
ownerUserId: string;
|
||||
periodStartMs: number;
|
||||
periodEndMs: number;
|
||||
coinSpent: number;
|
||||
@ -140,7 +140,7 @@ function normalizeSettlement(item: RawSettlement): RoomTurnoverRewardSettlementD
|
||||
return {
|
||||
settlementId: stringValue(item.settlementId ?? item.settlement_id),
|
||||
roomId: stringValue(item.roomId ?? item.room_id),
|
||||
ownerUserId: numberValue(item.ownerUserId ?? item.owner_user_id),
|
||||
ownerUserId: stringValue(item.ownerUserId ?? item.owner_user_id),
|
||||
periodStartMs: numberValue(item.periodStartMs ?? item.period_start_ms),
|
||||
periodEndMs: numberValue(item.periodEndMs ?? item.period_end_ms),
|
||||
coinSpent: numberValue(item.coinSpent ?? item.coin_spent),
|
||||
|
||||
@ -26,7 +26,7 @@ export interface SevenDayCheckInConfigPayload {
|
||||
}
|
||||
|
||||
export interface SevenDayCheckInClaimUserDto {
|
||||
userId?: number;
|
||||
userId?: string;
|
||||
displayUserId?: string;
|
||||
prettyDisplayUserId?: string;
|
||||
prettyId?: string;
|
||||
@ -37,7 +37,7 @@ export interface SevenDayCheckInClaimUserDto {
|
||||
export interface SevenDayCheckInClaimDto {
|
||||
claimId: string;
|
||||
commandId?: string;
|
||||
userId: number;
|
||||
userId: string;
|
||||
user?: SevenDayCheckInClaimUserDto;
|
||||
cycleNo: number;
|
||||
checkinDay?: string;
|
||||
@ -121,9 +121,9 @@ function normalizeClaim(item: RawClaim): SevenDayCheckInClaimDto {
|
||||
return {
|
||||
claimId: stringValue(item.claimId ?? item.claim_id),
|
||||
commandId: stringValue(item.commandId ?? item.command_id),
|
||||
userId: numberValue(item.userId ?? item.user_id),
|
||||
userId: stringValue(item.userId ?? item.user_id),
|
||||
user: {
|
||||
userId: numberValue(rawUser.userId ?? rawUser.user_id),
|
||||
userId: stringValue(rawUser.userId ?? rawUser.user_id),
|
||||
displayUserId: stringValue(rawUser.displayUserId ?? rawUser.display_user_id),
|
||||
prettyDisplayUserId: stringValue(rawUser.prettyDisplayUserId ?? rawUser.pretty_display_user_id),
|
||||
prettyId: stringValue(rawUser.prettyId ?? rawUser.pretty_id),
|
||||
|
||||
@ -41,7 +41,7 @@ export interface WeeklyStarCyclePayload {
|
||||
|
||||
export interface WeeklyStarEntryDto {
|
||||
rankNo: number;
|
||||
userId: number;
|
||||
userId: string;
|
||||
score: number;
|
||||
firstScoredAtMs: number;
|
||||
lastScoredAtMs: number;
|
||||
@ -51,7 +51,7 @@ export interface WeeklyStarSettlementDto {
|
||||
settlementId: string;
|
||||
cycleId: string;
|
||||
rankNo: number;
|
||||
userId: number;
|
||||
userId: string;
|
||||
score: number;
|
||||
resourceGroupId: number;
|
||||
walletCommandId: string;
|
||||
@ -161,7 +161,7 @@ function normalizeReward(item: Raw): WeeklyStarRewardDto {
|
||||
function normalizeEntry(item: Raw): WeeklyStarEntryDto {
|
||||
return {
|
||||
rankNo: numberValue(item.rankNo ?? item.rank_no),
|
||||
userId: numberValue(item.userId ?? item.user_id),
|
||||
userId: stringValue(item.userId ?? item.user_id),
|
||||
score: numberValue(item.score),
|
||||
firstScoredAtMs: numberValue(item.firstScoredAtMs ?? item.first_scored_at_ms),
|
||||
lastScoredAtMs: numberValue(item.lastScoredAtMs ?? item.last_scored_at_ms),
|
||||
@ -173,7 +173,7 @@ function normalizeSettlement(item: Raw): WeeklyStarSettlementDto {
|
||||
settlementId: stringValue(item.settlementId ?? item.settlement_id),
|
||||
cycleId: stringValue(item.cycleId ?? item.cycle_id),
|
||||
rankNo: numberValue(item.rankNo ?? item.rank_no),
|
||||
userId: numberValue(item.userId ?? item.user_id),
|
||||
userId: stringValue(item.userId ?? item.user_id),
|
||||
score: numberValue(item.score),
|
||||
resourceGroupId: numberValue(item.resourceGroupId ?? item.resource_group_id),
|
||||
walletCommandId: stringValue(item.walletCommandId ?? item.wallet_command_id),
|
||||
|
||||
10
src/shared/api/generated/schema.d.ts
vendored
10
src/shared/api/generated/schema.d.ts
vendored
@ -3021,8 +3021,8 @@ export interface components {
|
||||
joinEnabled?: boolean;
|
||||
maxHosts?: number;
|
||||
name?: string;
|
||||
ownerUserId?: number;
|
||||
parentBdUserId?: number;
|
||||
ownerUserId?: string;
|
||||
parentBdUserId?: string;
|
||||
regionId?: number;
|
||||
status?: string;
|
||||
updatedAtMs?: number;
|
||||
@ -3030,12 +3030,12 @@ export interface components {
|
||||
BDProfile: {
|
||||
createdAtMs?: number;
|
||||
createdByUserId?: number;
|
||||
parentLeaderUserId?: number;
|
||||
parentLeaderUserId?: string;
|
||||
regionId?: number;
|
||||
role?: string;
|
||||
status?: string;
|
||||
updatedAtMs?: number;
|
||||
userId: number;
|
||||
userId: string;
|
||||
};
|
||||
CreateUserResult: {
|
||||
initialPassword?: string;
|
||||
@ -3057,7 +3057,7 @@ export interface components {
|
||||
source?: string;
|
||||
status?: string;
|
||||
updatedAtMs?: number;
|
||||
userId: number;
|
||||
userId: string;
|
||||
};
|
||||
Log: {
|
||||
[key: string]: unknown;
|
||||
|
||||
@ -324,13 +324,13 @@ export interface RechargeBillDto {
|
||||
policyVersion?: string;
|
||||
rechargeType?: string;
|
||||
sellerRegionId?: number;
|
||||
sellerUserId?: number;
|
||||
sellerUserId?: string;
|
||||
status?: string;
|
||||
targetRegionId?: number;
|
||||
transactionId: string;
|
||||
usdMinorAmount: number;
|
||||
user?: RechargeBillUserDto;
|
||||
userId: number;
|
||||
userId: string;
|
||||
seller?: RechargeBillUserDto;
|
||||
}
|
||||
|
||||
@ -441,7 +441,7 @@ export interface BDProfileDto {
|
||||
displayUserId?: string;
|
||||
prettyDisplayUserId?: string;
|
||||
prettyId?: string;
|
||||
parentLeaderUserId?: number;
|
||||
parentLeaderUserId?: string;
|
||||
parentLeaderDisplayUserId?: string;
|
||||
parentLeaderUsername?: string;
|
||||
parentLeaderAvatar?: string;
|
||||
@ -452,7 +452,7 @@ export interface BDProfileDto {
|
||||
status?: string;
|
||||
subBdCount?: number;
|
||||
updatedAtMs?: number;
|
||||
userId: number;
|
||||
userId: string;
|
||||
username?: string;
|
||||
}
|
||||
|
||||
@ -466,11 +466,11 @@ export interface AgencyDto {
|
||||
ownerAvatar?: string;
|
||||
ownerDisplayUserId?: string;
|
||||
ownerUsername?: string;
|
||||
ownerUserId?: number;
|
||||
ownerUserId?: string;
|
||||
parentBdAvatar?: string;
|
||||
parentBdDisplayUserId?: string;
|
||||
parentBdUsername?: string;
|
||||
parentBdUserId?: number;
|
||||
parentBdUserId?: string;
|
||||
regionId?: number;
|
||||
regionName?: string;
|
||||
status?: string;
|
||||
@ -484,7 +484,7 @@ export interface HostProfileDto {
|
||||
currentAgencyName?: string;
|
||||
currentAgencyOwnerAvatar?: string;
|
||||
currentAgencyOwnerDisplayUserId?: string;
|
||||
currentAgencyOwnerUserId?: number;
|
||||
currentAgencyOwnerUserId?: string;
|
||||
currentAgencyOwnerUsername?: string;
|
||||
currentMembershipId?: number;
|
||||
diamond?: number;
|
||||
@ -497,7 +497,7 @@ export interface HostProfileDto {
|
||||
source?: string;
|
||||
status?: string;
|
||||
updatedAtMs?: number;
|
||||
userId: number;
|
||||
userId: string;
|
||||
username?: string;
|
||||
}
|
||||
|
||||
@ -516,7 +516,7 @@ export interface CoinSellerDto {
|
||||
status?: string;
|
||||
totalRechargeUsdtMicro?: number;
|
||||
updatedAtMs?: number;
|
||||
userId: number;
|
||||
userId: string;
|
||||
username?: string;
|
||||
}
|
||||
|
||||
@ -1038,7 +1038,7 @@ export interface HostCommandPayload {
|
||||
|
||||
export interface CreateBDLeaderPayload extends HostCommandPayload {
|
||||
positionAlias?: string;
|
||||
targetUserId: number;
|
||||
targetUserId: string;
|
||||
}
|
||||
|
||||
export interface BDLeaderPositionAliasPayload extends HostCommandPayload {
|
||||
@ -1046,8 +1046,8 @@ export interface BDLeaderPositionAliasPayload extends HostCommandPayload {
|
||||
}
|
||||
|
||||
export interface CreateBDPayload extends HostCommandPayload {
|
||||
parentLeaderUserId: number;
|
||||
targetUserId: number;
|
||||
parentLeaderUserId: string;
|
||||
targetUserId: string;
|
||||
}
|
||||
|
||||
export interface BDStatusPayload extends HostCommandPayload {
|
||||
@ -1056,7 +1056,7 @@ export interface BDStatusPayload extends HostCommandPayload {
|
||||
|
||||
export interface CreateCoinSellerPayload extends HostCommandPayload {
|
||||
contact?: string;
|
||||
targetUserId: number;
|
||||
targetUserId: string;
|
||||
}
|
||||
|
||||
export interface CoinSellerStatusPayload extends BDStatusPayload {
|
||||
@ -1066,8 +1066,8 @@ export interface CoinSellerStatusPayload extends BDStatusPayload {
|
||||
export interface CreateAgencyPayload extends HostCommandPayload {
|
||||
joinEnabled: boolean;
|
||||
name: string;
|
||||
ownerUserId: number;
|
||||
parentBdUserId: number;
|
||||
ownerUserId: string;
|
||||
parentBdUserId: string;
|
||||
}
|
||||
|
||||
export interface AgencyJoinEnabledPayload extends HostCommandPayload {
|
||||
|
||||
@ -1,10 +1,15 @@
|
||||
import InputBase from "@mui/material/InputBase";
|
||||
import Search from "@mui/icons-material/Search";
|
||||
|
||||
export function SearchBox({ autoFocus = false, className = "", onChange, placeholder, value }) {
|
||||
export function SearchBox({ autoFocus = false, className = "", label = "搜索", onChange, placeholder, value }) {
|
||||
const hasValue = value !== undefined && value !== null && String(value).length > 0;
|
||||
|
||||
return (
|
||||
<label className={["search-box", className].filter(Boolean).join(" ")}>
|
||||
<Search fontSize="small" />
|
||||
<label className={["search-box", hasValue ? "search-box--active" : "", className].filter(Boolean).join(" ")}>
|
||||
<span className="search-box__label">
|
||||
<Search fontSize="inherit" />
|
||||
<span>{label}</span>
|
||||
</span>
|
||||
<InputBase
|
||||
autoFocus={autoFocus}
|
||||
value={value}
|
||||
|
||||
@ -28,6 +28,7 @@ export function TimeRangeFilter({ className = "", disabled = false, label = "时
|
||||
const open = Boolean(anchorEl);
|
||||
const days = useMemo(() => calendarDays(viewDate), [viewDate]);
|
||||
const selectedDate = dateForField(draft, activeField, viewDate);
|
||||
const hasRange = Boolean(range.startMs || range.endMs);
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) {
|
||||
@ -110,14 +111,17 @@ export function TimeRangeFilter({ className = "", disabled = false, label = "时
|
||||
<>
|
||||
<Button
|
||||
className={[styles.trigger, className].filter(Boolean).join(" ")}
|
||||
endIcon={<KeyboardArrowDownOutlined fontSize="small" />}
|
||||
startIcon={<AccessTimeOutlined fontSize="small" />}
|
||||
aria-expanded={open}
|
||||
aria-haspopup="dialog"
|
||||
disabled={disabled}
|
||||
onClick={openPopover}
|
||||
>
|
||||
<span className={styles.triggerText}>{displayLabel}</span>
|
||||
<span className={styles.triggerMeta}>
|
||||
<AccessTimeOutlined fontSize="inherit" />
|
||||
<span>{label}</span>
|
||||
</span>
|
||||
<span className={styles.triggerText}>{hasRange ? displayLabel : "不限"}</span>
|
||||
<KeyboardArrowDownOutlined className={styles.triggerChevron} fontSize="small" />
|
||||
</Button>
|
||||
<Popover
|
||||
anchorEl={anchorEl}
|
||||
|
||||
@ -1,9 +1,15 @@
|
||||
.trigger {
|
||||
position: relative;
|
||||
display: inline-grid !important;
|
||||
grid-template-columns: minmax(0, 1fr) 18px;
|
||||
flex: 0 0 auto;
|
||||
justify-content: flex-start !important;
|
||||
align-items: end !important;
|
||||
justify-content: stretch !important;
|
||||
gap: 0 !important;
|
||||
width: auto;
|
||||
min-width: 132px !important;
|
||||
max-width: min(420px, 100%);
|
||||
padding: 7px 28px 3px var(--control-padding-x) !important;
|
||||
border-color: var(--border) !important;
|
||||
background: var(--bg-input-strong) !important;
|
||||
box-shadow: none !important;
|
||||
@ -21,24 +27,50 @@
|
||||
transform: none !important;
|
||||
}
|
||||
|
||||
.trigger :global(.MuiButton-startIcon) {
|
||||
margin-right: var(--space-2);
|
||||
margin-left: 0;
|
||||
.triggerMeta {
|
||||
position: absolute;
|
||||
top: -7px;
|
||||
left: var(--space-2);
|
||||
display: inline-flex;
|
||||
max-width: calc(100% - var(--space-4));
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
overflow: hidden;
|
||||
padding: 0 var(--space-1);
|
||||
border-radius: 4px;
|
||||
background: var(--bg-card);
|
||||
color: var(--text-tertiary);
|
||||
font-size: 11px;
|
||||
font-weight: 650;
|
||||
line-height: 14px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.trigger :global(.MuiButton-endIcon) {
|
||||
margin-right: 0;
|
||||
margin-left: var(--space-4);
|
||||
.trigger:hover .triggerMeta,
|
||||
.trigger:focus-visible .triggerMeta {
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.triggerText {
|
||||
flex: 0 1 auto;
|
||||
overflow: hidden;
|
||||
color: var(--text-primary);
|
||||
font-size: var(--control-font-size);
|
||||
line-height: var(--control-line-height);
|
||||
text-align: left;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.triggerChevron {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: var(--space-2);
|
||||
color: var(--text-tertiary);
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
.paper {
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--border);
|
||||
|
||||
@ -1,16 +1,17 @@
|
||||
import MenuItem from "@mui/material/MenuItem";
|
||||
import Select from "@mui/material/Select";
|
||||
import TextField from "@mui/material/TextField";
|
||||
|
||||
const ranges = ["近 1 小时", "近 24 小时", "近 7 天"];
|
||||
|
||||
export function TimeRangeSelect({ className = "", onChange, value }) {
|
||||
export function TimeRangeSelect({ className = "", label = "时间", onChange, value }) {
|
||||
return (
|
||||
<Select
|
||||
<TextField
|
||||
className={["time-select", className].filter(Boolean).join(" ")}
|
||||
label={label}
|
||||
select
|
||||
value={value}
|
||||
onChange={(event) => onChange(event.target.value)}
|
||||
size="small"
|
||||
sx={{ width: 116, height: 36, fontSize: "var(--admin-font-size)" }}
|
||||
variant="outlined"
|
||||
>
|
||||
{ranges.map((range) => (
|
||||
@ -18,6 +19,6 @@ export function TimeRangeSelect({ className = "", onChange, value }) {
|
||||
{range}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</TextField>
|
||||
);
|
||||
}
|
||||
|
||||
@ -165,11 +165,11 @@
|
||||
}
|
||||
|
||||
.app-switch {
|
||||
flex: 0 0 156px;
|
||||
flex: 0 0 164px;
|
||||
}
|
||||
|
||||
.timezone-switch {
|
||||
flex: 0 0 124px;
|
||||
flex: 0 0 140px;
|
||||
}
|
||||
|
||||
.app-switch .MuiOutlinedInput-root,
|
||||
@ -202,14 +202,15 @@
|
||||
}
|
||||
|
||||
.search-box {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex: 1 1 420px;
|
||||
min-width: 180px;
|
||||
max-width: 520px;
|
||||
height: var(--control-height);
|
||||
align-items: center;
|
||||
gap: var(--space-3);
|
||||
padding: 0 var(--space-3);
|
||||
align-items: flex-end;
|
||||
gap: 0;
|
||||
padding: 7px var(--control-padding-x) 3px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-control);
|
||||
background: var(--bg-input);
|
||||
@ -222,31 +223,58 @@
|
||||
transform var(--motion-base) var(--ease-emphasized);
|
||||
}
|
||||
|
||||
.search-box__label {
|
||||
position: absolute;
|
||||
top: -7px;
|
||||
left: var(--space-2);
|
||||
display: inline-flex;
|
||||
max-width: calc(100% - var(--space-4));
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
overflow: hidden;
|
||||
padding: 0 var(--space-1);
|
||||
border-radius: 4px;
|
||||
background: var(--bg-card);
|
||||
color: var(--text-tertiary);
|
||||
font-size: 11px;
|
||||
font-weight: 650;
|
||||
line-height: 14px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.search-box:focus-within {
|
||||
border-color: var(--primary-border-active);
|
||||
background: var(--bg-input-strong);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.search-box svg {
|
||||
.search-box__label svg {
|
||||
flex: 0 0 auto;
|
||||
transition:
|
||||
color var(--motion-base) var(--ease-standard),
|
||||
transform var(--motion-base) var(--ease-emphasized);
|
||||
}
|
||||
|
||||
.search-box:focus-within svg {
|
||||
.search-box:focus-within .search-box__label,
|
||||
.search-box--active .search-box__label {
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.search-box:focus-within .search-box__label svg {
|
||||
color: var(--primary);
|
||||
transform: scale(1.08);
|
||||
}
|
||||
|
||||
.search-trigger {
|
||||
display: flex;
|
||||
position: relative;
|
||||
display: grid;
|
||||
width: min(360px, 34vw);
|
||||
min-width: 260px;
|
||||
height: var(--control-height);
|
||||
align-items: center;
|
||||
gap: var(--space-3);
|
||||
padding: 0 var(--space-3);
|
||||
align-items: end;
|
||||
gap: 0;
|
||||
padding: 7px var(--control-padding-x) 3px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-control);
|
||||
background: var(--bg-input);
|
||||
@ -262,8 +290,36 @@
|
||||
transform var(--motion-base) var(--ease-emphasized);
|
||||
}
|
||||
|
||||
.search-trigger span {
|
||||
.search-trigger__label {
|
||||
position: absolute;
|
||||
top: -7px;
|
||||
left: var(--space-2);
|
||||
display: inline-flex;
|
||||
max-width: calc(100% - var(--space-4));
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
overflow: hidden;
|
||||
padding: 0 var(--space-1);
|
||||
border-radius: 4px;
|
||||
background: var(--bg-card);
|
||||
color: var(--text-tertiary);
|
||||
font-size: 11px;
|
||||
font-weight: 650;
|
||||
line-height: 14px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.search-trigger__label svg {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.search-trigger__value {
|
||||
overflow: hidden;
|
||||
color: var(--text-primary);
|
||||
font-size: var(--control-font-size);
|
||||
font-weight: 650;
|
||||
line-height: var(--control-line-height);
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
@ -276,16 +332,22 @@
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.search-trigger:hover .search-trigger__label,
|
||||
.search-trigger:focus-visible .search-trigger__label {
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.search-box input {
|
||||
width: 100%;
|
||||
height: 20px;
|
||||
height: var(--control-line-height);
|
||||
min-width: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
color: var(--text-primary);
|
||||
font-size: var(--admin-font-size);
|
||||
line-height: 20px;
|
||||
font-size: var(--control-font-size);
|
||||
font-weight: 650;
|
||||
line-height: var(--control-line-height);
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
@ -293,15 +355,10 @@
|
||||
color: var(--text-tertiary);
|
||||
}
|
||||
|
||||
.time-select {
|
||||
width: 116px;
|
||||
height: var(--control-height);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-control);
|
||||
background: var(--bg-input);
|
||||
backdrop-filter: var(--glass-blur);
|
||||
color: var(--text-secondary);
|
||||
-webkit-backdrop-filter: var(--glass-blur);
|
||||
.time-select.MuiTextField-root,
|
||||
.time-select.MuiFormControl-root {
|
||||
flex: 0 0 124px;
|
||||
width: 124px;
|
||||
}
|
||||
|
||||
.icon-button {
|
||||
|
||||
@ -108,6 +108,10 @@
|
||||
--admin-control-height: var(--control-height);
|
||||
--admin-control-font-size: var(--control-font-size);
|
||||
--admin-control-line-height: var(--control-line-height);
|
||||
--admin-floating-label-font-size: 11px;
|
||||
--admin-floating-label-line-height: 14px;
|
||||
--admin-floating-label-offset-x: 9px;
|
||||
--admin-floating-label-offset-y: -7px;
|
||||
}
|
||||
|
||||
.region-select.MuiTextField-root,
|
||||
@ -219,12 +223,19 @@
|
||||
color: var(--text-tertiary);
|
||||
font-size: var(--admin-control-font-size);
|
||||
line-height: var(--admin-control-line-height);
|
||||
transform: translate(12px, 6px) scale(1);
|
||||
letter-spacing: 0;
|
||||
transform: translate(var(--control-padding-x), calc((var(--admin-control-height) - var(--admin-control-line-height)) / 2)) scale(1);
|
||||
}
|
||||
|
||||
.MuiTextField-root .MuiInputLabel-outlined.MuiInputLabel-shrink,
|
||||
.MuiFormControl-root .MuiInputLabel-outlined.MuiInputLabel-shrink {
|
||||
transform: translate(12px, -7px) scale(0.75);
|
||||
max-width: calc((100% - 18px) / 0.78);
|
||||
padding: 0 var(--space-1);
|
||||
border-radius: 4px;
|
||||
background: var(--bg-card);
|
||||
font-size: var(--admin-floating-label-font-size);
|
||||
line-height: var(--admin-floating-label-line-height);
|
||||
transform: translate(var(--admin-floating-label-offset-x), var(--admin-floating-label-offset-y)) scale(0.78);
|
||||
}
|
||||
|
||||
.MuiTextField-root .MuiFormLabel-root.Mui-focused,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user