fix: 修复订阅跳转引号问题 + 设备组显示设备类型 + 统一用户名显示
Some checks failed
Build and Release / Build (push) Has been cancelled
Some checks failed
Build and Release / Build (push) Has been cancelled
- 去掉所有 Link search 中的 String() 包装(7处),修复 URL 引号问题 - View Owner 改用 useNavigate 强制导航,解决同页面跳转不生效 - 设备组详情成员列表添加 auth_type Badge 和 device_type Badge - 设备组列表 owner 列统一为 [AUTH_TYPE Badge] + identifier 格式 - UserDetail 组件统一设备标识逻辑,去掉 shortenDeviceIdentifier Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
parent
5019f76a73
commit
5a493fce67
Binary file not shown.
@ -261,6 +261,11 @@ export function FamilyDetailSheet({
|
||||
<Badge variant="outline">
|
||||
ID: {member.user_id}
|
||||
</Badge>
|
||||
{member.auth_type ? (
|
||||
<Badge className="uppercase">
|
||||
{member.auth_type}
|
||||
</Badge>
|
||||
) : null}
|
||||
{member.device_no ? (
|
||||
<Badge variant="outline">
|
||||
<span className="font-mono">
|
||||
@ -268,7 +273,16 @@ export function FamilyDetailSheet({
|
||||
</span>
|
||||
</Badge>
|
||||
) : null}
|
||||
<span>{member.identifier}</span>
|
||||
{member.device_type ? (
|
||||
<Badge variant="secondary">
|
||||
{member.device_type}
|
||||
</Badge>
|
||||
) : null}
|
||||
<span>
|
||||
{member.auth_type === "device"
|
||||
? member.device_no || member.identifier
|
||||
: member.identifier}
|
||||
</span>
|
||||
<Badge>
|
||||
{getFamilyRoleLabel(t, member.role_name)}
|
||||
</Badge>
|
||||
|
||||
@ -54,8 +54,19 @@ export default function FamilyManagement({
|
||||
{
|
||||
accessorKey: "owner_identifier",
|
||||
header: t("owner", "Owner"),
|
||||
cell: ({ row }) =>
|
||||
`${row.original.owner_identifier} (ID: ${row.original.owner_user_id})`,
|
||||
cell: ({ row }) => (
|
||||
<div className="flex items-center gap-1">
|
||||
{row.original.owner_auth_type ? (
|
||||
<Badge className="uppercase">
|
||||
{row.original.owner_auth_type}
|
||||
</Badge>
|
||||
) : null}
|
||||
<span>{row.original.owner_identifier}</span>
|
||||
<span className="text-muted-foreground text-xs">
|
||||
(ID: {row.original.owner_user_id})
|
||||
</span>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: "status",
|
||||
|
||||
@ -163,42 +163,30 @@ export default function User() {
|
||||
<DropdownMenuContent align="end">
|
||||
<InviteStatsMenuItem userId={row.id} />
|
||||
<DropdownMenuItem asChild>
|
||||
<Link
|
||||
search={{ user_id: String(row.id) }}
|
||||
to="/dashboard/order"
|
||||
>
|
||||
<Link search={{ user_id: row.id }} to="/dashboard/order">
|
||||
{t("orderList", "Order List")}
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem asChild>
|
||||
<Link
|
||||
search={{ user_id: String(row.id) }}
|
||||
to="/dashboard/log/login"
|
||||
>
|
||||
<Link search={{ user_id: row.id }} to="/dashboard/log/login">
|
||||
{t("loginLogs", "Login Logs")}
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem asChild>
|
||||
<Link
|
||||
search={{ user_id: String(row.id) }}
|
||||
to="/dashboard/log/balance"
|
||||
>
|
||||
<Link search={{ user_id: row.id }} to="/dashboard/log/balance">
|
||||
{t("balanceLogs", "Balance Logs")}
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem asChild>
|
||||
<Link
|
||||
search={{ user_id: String(row.id) }}
|
||||
search={{ user_id: row.id }}
|
||||
to="/dashboard/log/commission"
|
||||
>
|
||||
{t("commissionLogs", "Commission Logs")}
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem asChild>
|
||||
<Link
|
||||
search={{ user_id: String(row.id) }}
|
||||
to="/dashboard/log/gift"
|
||||
>
|
||||
<Link search={{ user_id: row.id }} to="/dashboard/log/gift">
|
||||
{t("giftLogs", "Gift Logs")}
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
|
||||
@ -12,7 +12,6 @@ import {
|
||||
getUserDetail,
|
||||
getUserSubscribeById,
|
||||
} from "@workspace/ui/services/admin/user";
|
||||
import { shortenDeviceIdentifier } from "@workspace/ui/utils/device";
|
||||
import { formatBytes } from "@workspace/ui/utils/formatting";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Display } from "@/components/display";
|
||||
@ -230,21 +229,29 @@ export function UserDetail({ id }: { id: number }) {
|
||||
|
||||
if (!id) return "--";
|
||||
|
||||
const emailMethod = data?.auth_methods.find((m) => m.auth_type === "email");
|
||||
const firstMethod = data?.auth_methods[0];
|
||||
const rawIdentifier =
|
||||
emailMethod?.auth_identifier || firstMethod?.auth_identifier || "";
|
||||
const isDevice = !emailMethod && firstMethod?.auth_type === "device";
|
||||
const identifier = isDevice
|
||||
? shortenDeviceIdentifier(rawIdentifier)
|
||||
: rawIdentifier;
|
||||
const isDevice = firstMethod?.auth_type === "device";
|
||||
const deviceNo = (data as any)?.user_devices?.[0]?.device_no;
|
||||
const rawIdentifier = firstMethod?.auth_identifier || "";
|
||||
const identifier = isDevice ? deviceNo || rawIdentifier : rawIdentifier;
|
||||
|
||||
return (
|
||||
<HoverCard>
|
||||
<HoverCardTrigger asChild>
|
||||
<Button asChild className="p-0" variant="link">
|
||||
<Link search={{ user_id: id }} to="/dashboard/user">
|
||||
{identifier || t("loading", "Loading...")}
|
||||
{data ? (
|
||||
<>
|
||||
{firstMethod && (
|
||||
<span className="mr-1 inline-flex items-center rounded border px-1 py-0.5 font-mono text-xs uppercase">
|
||||
{firstMethod.auth_type}
|
||||
</span>
|
||||
)}
|
||||
{identifier}
|
||||
</>
|
||||
) : (
|
||||
t("loading", "Loading...")
|
||||
)}
|
||||
</Link>
|
||||
</Button>
|
||||
</HoverCardTrigger>
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { Link } from "@tanstack/react-router";
|
||||
import { Link, useNavigate } from "@tanstack/react-router";
|
||||
import { Alert, AlertDescription } from "@workspace/ui/components/alert";
|
||||
import { Badge } from "@workspace/ui/components/badge";
|
||||
import { Button } from "@workspace/ui/components/button";
|
||||
@ -42,6 +42,7 @@ export default function UserSubscription({ userId }: { userId: number }) {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const ref = useRef<ProTableActions>(null);
|
||||
const [sharedInfo, setSharedInfo] = useState<SharedInfo | null>(null);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const request = useCallback(
|
||||
async (pagination: { page: number; size: number }) => {
|
||||
@ -116,19 +117,23 @@ export default function UserSubscription({ userId }: { userId: number }) {
|
||||
<span className="flex gap-2">
|
||||
<Button asChild size="sm" variant="outline">
|
||||
<Link
|
||||
search={{ user_id: String(sharedInfo.ownerUserId) }}
|
||||
search={{ user_id: sharedInfo.ownerUserId }}
|
||||
to="/dashboard/family"
|
||||
>
|
||||
{t("viewDeviceGroup", "View Device Group")}
|
||||
</Link>
|
||||
</Button>
|
||||
<Button asChild size="sm" variant="outline">
|
||||
<Link
|
||||
search={{ user_id: String(sharedInfo.ownerUserId) }}
|
||||
to="/dashboard/user"
|
||||
<Button
|
||||
onClick={() => {
|
||||
navigate({
|
||||
to: "/dashboard/user",
|
||||
search: { user_id: sharedInfo.ownerUserId },
|
||||
});
|
||||
}}
|
||||
size="sm"
|
||||
variant="outline"
|
||||
>
|
||||
{t("viewOwner", "View Owner")}
|
||||
</Link>
|
||||
</Button>
|
||||
</span>
|
||||
</AlertDescription>
|
||||
|
||||
3
packages/ui/src/services/admin/typings.d.ts
vendored
3
packages/ui/src/services/admin/typings.d.ts
vendored
@ -2607,7 +2607,9 @@ declare namespace API {
|
||||
type FamilyMemberItem = {
|
||||
user_id: number;
|
||||
identifier: string;
|
||||
auth_type: string;
|
||||
device_no: string;
|
||||
device_type: string;
|
||||
role: number;
|
||||
role_name: string;
|
||||
status: number;
|
||||
@ -2621,6 +2623,7 @@ declare namespace API {
|
||||
family_id: number;
|
||||
owner_user_id: number;
|
||||
owner_identifier: string;
|
||||
owner_auth_type: string;
|
||||
status: string;
|
||||
active_member_count: number;
|
||||
max_members: number;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user