From 2ebacae7c23c18ea66958024e3c835a35c6918a1 Mon Sep 17 00:00:00 2001 From: zhx Date: Fri, 12 Jun 2026 11:20:02 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- contracts/admin-openapi.json | 10 +- databi/src/DatabiApp.test.jsx | 2 +- databi/src/components/DatabiHeader.jsx | 32 ++-- databi/src/styles/layout.css | 144 +++++++++++------- src/app/layout/Header.jsx | 9 +- .../cumulative-recharge-reward/api.ts | 8 +- src/features/first-recharge-reward/api.ts | 8 +- src/features/host-agency-policy/api.ts | 2 +- .../pages/HostSalarySettlementPage.jsx | 4 +- src/features/host-org/api.test.ts | 10 +- src/features/host-org/schema.ts | 18 ++- src/features/invite-activity-reward/api.ts | 8 +- .../InviteActivityRewardConfigDrawer.jsx | 2 +- .../InviteActivityRewardConfigSummary.jsx | 4 +- .../hooks/useInviteActivityRewardPage.js | 21 ++- .../pages/InviteActivityRewardPage.jsx | 6 +- src/features/red-packets/api.ts | 8 +- src/features/registration-reward/api.ts | 8 +- src/features/resources/api.test.ts | 4 +- src/features/resources/api.ts | 10 +- src/features/resources/schema.js | 4 +- src/features/room-turnover-reward/api.ts | 4 +- src/features/seven-day-checkin/api.ts | 8 +- src/features/weekly-star/api.ts | 8 +- src/shared/api/generated/schema.d.ts | 10 +- src/shared/api/types.ts | 30 ++-- src/shared/ui/SearchBox.jsx | 11 +- src/shared/ui/TimeRangeFilter.jsx | 10 +- src/shared/ui/TimeRangeFilter.module.css | 46 +++++- src/shared/ui/TimeRangeSelect.jsx | 11 +- src/styles/layout.css | 105 ++++++++++--- src/styles/shared-ui.css | 15 +- 32 files changed, 370 insertions(+), 210 deletions(-) diff --git a/contracts/admin-openapi.json b/contracts/admin-openapi.json index b82f182..b04bf4c 100644 --- a/contracts/admin-openapi.json +++ b/contracts/admin-openapi.json @@ -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" } } }, diff --git a/databi/src/DatabiApp.test.jsx b/databi/src/DatabiApp.test.jsx index 40e4fe0..4ac6ba4 100644 --- a/databi/src/DatabiApp.test.jsx +++ b/databi/src/DatabiApp.test.jsx @@ -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() { diff --git a/databi/src/components/DatabiHeader.jsx b/databi/src/components/DatabiHeader.jsx index 6e7b26b..4286d35 100644 --- a/databi/src/components/DatabiHeader.jsx +++ b/databi/src/components/DatabiHeader.jsx @@ -158,11 +158,11 @@ function FilterMenu({ className = "", icon, isOpen, label, loading = false, onSe return (
{isOpen ? ( @@ -201,11 +201,11 @@ function SearchableFilterMenu({ icon, isOpen, label, loading = false, onSelect, return (
{isOpen ? ( @@ -301,14 +301,14 @@ function RangeMenu({ activeValue, isOpen, onSelect, onToggle, range, timeZone }) return (
diff --git a/databi/src/styles/layout.css b/databi/src/styles/layout.css index 9a95f7f..d1e7a90 100644 --- a/databi/src/styles/layout.css +++ b/databi/src/styles/layout.css @@ -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 { diff --git a/src/app/layout/Header.jsx b/src/app/layout/Header.jsx index 58b43e3..3e02f5f 100644 --- a/src/app/layout/Header.jsx +++ b/src/app/layout/Header.jsx @@ -155,6 +155,7 @@ export function Header({ activeLabel, isSidebarCollapsed, menuItems = [], onRefr diff --git a/src/features/cumulative-recharge-reward/api.ts b/src/features/cumulative-recharge-reward/api.ts index 2f3c89f..47c5881 100644 --- a/src/features/cumulative-recharge-reward/api.ts +++ b/src/features/cumulative-recharge-reward/api.ts @@ -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), diff --git a/src/features/first-recharge-reward/api.ts b/src/features/first-recharge-reward/api.ts index 2504bbc..f9ec2d3 100644 --- a/src/features/first-recharge-reward/api.ts +++ b/src/features/first-recharge-reward/api.ts @@ -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), diff --git a/src/features/host-agency-policy/api.ts b/src/features/host-agency-policy/api.ts index 371c728..08692fc 100644 --- a/src/features/host-agency-policy/api.ts +++ b/src/features/host-agency-policy/api.ts @@ -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 { diff --git a/src/features/host-agency-policy/pages/HostSalarySettlementPage.jsx b/src/features/host-agency-policy/pages/HostSalarySettlementPage.jsx index 26aeb99..0a17e8a 100644 --- a/src/features/host-agency-policy/pages/HostSalarySettlementPage.jsx +++ b/src/features/host-agency-policy/pages/HostSalarySettlementPage.jsx @@ -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; } diff --git a/src/features/host-org/api.test.ts b/src/features/host-org/api.test.ts index 547585d..bdabdfa 100644 --- a/src/features/host-org/api.test.ts +++ b/src/features/host-org/api.test.ts @@ -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", diff --git a/src/features/host-org/schema.ts b/src/features/host-org/schema.ts index 8f5e488..366643a 100644 --- a/src/features/host-org/schema.ts +++ b/src/features/host-org/schema.ts @@ -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); diff --git a/src/features/invite-activity-reward/api.ts b/src/features/invite-activity-reward/api.ts index 45f428f..0e8ac8c 100644 --- a/src/features/invite-activity-reward/api.ts +++ b/src/features/invite-activity-reward/api.ts @@ -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), diff --git a/src/features/invite-activity-reward/components/InviteActivityRewardConfigDrawer.jsx b/src/features/invite-activity-reward/components/InviteActivityRewardConfigDrawer.jsx index 2096087..16fe03e 100644 --- a/src/features/invite-activity-reward/components/InviteActivityRewardConfigDrawer.jsx +++ b/src/features/invite-activity-reward/components/InviteActivityRewardConfigDrawer.jsx @@ -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({ diff --git a/src/features/invite-activity-reward/components/InviteActivityRewardConfigSummary.jsx b/src/features/invite-activity-reward/components/InviteActivityRewardConfigSummary.jsx index 871f009..58d66af 100644 --- a/src/features/invite-activity-reward/components/InviteActivityRewardConfigSummary.jsx +++ b/src/features/invite-activity-reward/components/InviteActivityRewardConfigSummary.jsx @@ -25,8 +25,8 @@ export function InviteActivityRewardConfigSummary({ canUpdate, config, configLoa {thresholdSummary(rechargeTiers, "thresholdCoinAmount", "金币")} - - {thresholdSummary(validInviteTiers, "thresholdValidInviteCount", "人")} + + {thresholdSummary(validInviteTiers, "thresholdValidInviteCount", "people")} {coinSummary(config?.perInviteInviterRewardCoinAmount)} diff --git a/src/features/invite-activity-reward/hooks/useInviteActivityRewardPage.js b/src/features/invite-activity-reward/hooks/useInviteActivityRewardPage.js index f9bf69d..19338ee 100644 --- a/src/features/invite-activity-reward/hooks/useInviteActivityRewardPage.js +++ b/src/features/invite-activity-reward/hooks/useInviteActivityRewardPage.js @@ -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}`; } diff --git a/src/features/invite-activity-reward/pages/InviteActivityRewardPage.jsx b/src/features/invite-activity-reward/pages/InviteActivityRewardPage.jsx index 85d4c48..088d37b 100644 --- a/src/features/invite-activity-reward/pages/InviteActivityRewardPage.jsx +++ b/src/features/invite-activity-reward/pages/InviteActivityRewardPage.jsx @@ -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}`; } diff --git a/src/features/red-packets/api.ts b/src/features/red-packets/api.ts index 6062942..2cb7684 100644 --- a/src/features/red-packets/api.ts +++ b/src/features/red-packets/api.ts @@ -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), diff --git a/src/features/registration-reward/api.ts b/src/features/registration-reward/api.ts index 829246b..31b2805 100644 --- a/src/features/registration-reward/api.ts +++ b/src/features/registration-reward/api.ts @@ -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), diff --git a/src/features/resources/api.test.ts b/src/features/resources/api.test.ts index 34960b6..83afc34 100644 --- a/src/features/resources/api.test.ts +++ b/src/features/resources/api.test.ts @@ -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 () => { diff --git a/src/features/resources/api.ts b/src/features/resources/api.ts index 40b4c0b..4999b29 100644 --- a/src/features/resources/api.ts +++ b/src/features/resources/api.ts @@ -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 { diff --git a/src/features/resources/schema.js b/src/features/resources/schema.js index a91142c..48b1ee8 100644 --- a/src/features/resources/schema.js +++ b/src/features/resources/schema.js @@ -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", diff --git a/src/features/room-turnover-reward/api.ts b/src/features/room-turnover-reward/api.ts index 8c64cdf..5d9fce4 100644 --- a/src/features/room-turnover-reward/api.ts +++ b/src/features/room-turnover-reward/api.ts @@ -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), diff --git a/src/features/seven-day-checkin/api.ts b/src/features/seven-day-checkin/api.ts index e3a369b..1a01bde 100644 --- a/src/features/seven-day-checkin/api.ts +++ b/src/features/seven-day-checkin/api.ts @@ -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), diff --git a/src/features/weekly-star/api.ts b/src/features/weekly-star/api.ts index e1db4f9..f0502e2 100644 --- a/src/features/weekly-star/api.ts +++ b/src/features/weekly-star/api.ts @@ -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), diff --git a/src/shared/api/generated/schema.d.ts b/src/shared/api/generated/schema.d.ts index fd07075..9f0c122 100644 --- a/src/shared/api/generated/schema.d.ts +++ b/src/shared/api/generated/schema.d.ts @@ -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; diff --git a/src/shared/api/types.ts b/src/shared/api/types.ts index ddcceb0..1fce49e 100644 --- a/src/shared/api/types.ts +++ b/src/shared/api/types.ts @@ -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 { diff --git a/src/shared/ui/SearchBox.jsx b/src/shared/ui/SearchBox.jsx index 5667c76..c961df2 100644 --- a/src/shared/ui/SearchBox.jsx +++ b/src/shared/ui/SearchBox.jsx @@ -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 ( - ); } diff --git a/src/styles/layout.css b/src/styles/layout.css index 3e61373..7d2eb1d 100644 --- a/src/styles/layout.css +++ b/src/styles/layout.css @@ -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 { diff --git a/src/styles/shared-ui.css b/src/styles/shared-ui.css index b651727..b47ce13 100644 --- a/src/styles/shared-ui.css +++ b/src/styles/shared-ui.css @@ -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,