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">
|
<Badge variant="outline">
|
||||||
ID: {member.user_id}
|
ID: {member.user_id}
|
||||||
</Badge>
|
</Badge>
|
||||||
|
{member.auth_type ? (
|
||||||
|
<Badge className="uppercase">
|
||||||
|
{member.auth_type}
|
||||||
|
</Badge>
|
||||||
|
) : null}
|
||||||
{member.device_no ? (
|
{member.device_no ? (
|
||||||
<Badge variant="outline">
|
<Badge variant="outline">
|
||||||
<span className="font-mono">
|
<span className="font-mono">
|
||||||
@ -268,7 +273,16 @@ export function FamilyDetailSheet({
|
|||||||
</span>
|
</span>
|
||||||
</Badge>
|
</Badge>
|
||||||
) : null}
|
) : 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>
|
<Badge>
|
||||||
{getFamilyRoleLabel(t, member.role_name)}
|
{getFamilyRoleLabel(t, member.role_name)}
|
||||||
</Badge>
|
</Badge>
|
||||||
|
|||||||
@ -54,8 +54,19 @@ export default function FamilyManagement({
|
|||||||
{
|
{
|
||||||
accessorKey: "owner_identifier",
|
accessorKey: "owner_identifier",
|
||||||
header: t("owner", "Owner"),
|
header: t("owner", "Owner"),
|
||||||
cell: ({ row }) =>
|
cell: ({ row }) => (
|
||||||
`${row.original.owner_identifier} (ID: ${row.original.owner_user_id})`,
|
<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",
|
accessorKey: "status",
|
||||||
|
|||||||
@ -163,42 +163,30 @@ export default function User() {
|
|||||||
<DropdownMenuContent align="end">
|
<DropdownMenuContent align="end">
|
||||||
<InviteStatsMenuItem userId={row.id} />
|
<InviteStatsMenuItem userId={row.id} />
|
||||||
<DropdownMenuItem asChild>
|
<DropdownMenuItem asChild>
|
||||||
<Link
|
<Link search={{ user_id: row.id }} to="/dashboard/order">
|
||||||
search={{ user_id: String(row.id) }}
|
|
||||||
to="/dashboard/order"
|
|
||||||
>
|
|
||||||
{t("orderList", "Order List")}
|
{t("orderList", "Order List")}
|
||||||
</Link>
|
</Link>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem asChild>
|
<DropdownMenuItem asChild>
|
||||||
<Link
|
<Link search={{ user_id: row.id }} to="/dashboard/log/login">
|
||||||
search={{ user_id: String(row.id) }}
|
|
||||||
to="/dashboard/log/login"
|
|
||||||
>
|
|
||||||
{t("loginLogs", "Login Logs")}
|
{t("loginLogs", "Login Logs")}
|
||||||
</Link>
|
</Link>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem asChild>
|
<DropdownMenuItem asChild>
|
||||||
<Link
|
<Link search={{ user_id: row.id }} to="/dashboard/log/balance">
|
||||||
search={{ user_id: String(row.id) }}
|
|
||||||
to="/dashboard/log/balance"
|
|
||||||
>
|
|
||||||
{t("balanceLogs", "Balance Logs")}
|
{t("balanceLogs", "Balance Logs")}
|
||||||
</Link>
|
</Link>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem asChild>
|
<DropdownMenuItem asChild>
|
||||||
<Link
|
<Link
|
||||||
search={{ user_id: String(row.id) }}
|
search={{ user_id: row.id }}
|
||||||
to="/dashboard/log/commission"
|
to="/dashboard/log/commission"
|
||||||
>
|
>
|
||||||
{t("commissionLogs", "Commission Logs")}
|
{t("commissionLogs", "Commission Logs")}
|
||||||
</Link>
|
</Link>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem asChild>
|
<DropdownMenuItem asChild>
|
||||||
<Link
|
<Link search={{ user_id: row.id }} to="/dashboard/log/gift">
|
||||||
search={{ user_id: String(row.id) }}
|
|
||||||
to="/dashboard/log/gift"
|
|
||||||
>
|
|
||||||
{t("giftLogs", "Gift Logs")}
|
{t("giftLogs", "Gift Logs")}
|
||||||
</Link>
|
</Link>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
|
|||||||
@ -12,7 +12,6 @@ import {
|
|||||||
getUserDetail,
|
getUserDetail,
|
||||||
getUserSubscribeById,
|
getUserSubscribeById,
|
||||||
} from "@workspace/ui/services/admin/user";
|
} from "@workspace/ui/services/admin/user";
|
||||||
import { shortenDeviceIdentifier } from "@workspace/ui/utils/device";
|
|
||||||
import { formatBytes } from "@workspace/ui/utils/formatting";
|
import { formatBytes } from "@workspace/ui/utils/formatting";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Display } from "@/components/display";
|
import { Display } from "@/components/display";
|
||||||
@ -230,21 +229,29 @@ export function UserDetail({ id }: { id: number }) {
|
|||||||
|
|
||||||
if (!id) return "--";
|
if (!id) return "--";
|
||||||
|
|
||||||
const emailMethod = data?.auth_methods.find((m) => m.auth_type === "email");
|
|
||||||
const firstMethod = data?.auth_methods[0];
|
const firstMethod = data?.auth_methods[0];
|
||||||
const rawIdentifier =
|
const isDevice = firstMethod?.auth_type === "device";
|
||||||
emailMethod?.auth_identifier || firstMethod?.auth_identifier || "";
|
const deviceNo = (data as any)?.user_devices?.[0]?.device_no;
|
||||||
const isDevice = !emailMethod && firstMethod?.auth_type === "device";
|
const rawIdentifier = firstMethod?.auth_identifier || "";
|
||||||
const identifier = isDevice
|
const identifier = isDevice ? deviceNo || rawIdentifier : rawIdentifier;
|
||||||
? shortenDeviceIdentifier(rawIdentifier)
|
|
||||||
: rawIdentifier;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HoverCard>
|
<HoverCard>
|
||||||
<HoverCardTrigger asChild>
|
<HoverCardTrigger asChild>
|
||||||
<Button asChild className="p-0" variant="link">
|
<Button asChild className="p-0" variant="link">
|
||||||
<Link search={{ user_id: id }} to="/dashboard/user">
|
<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>
|
</Link>
|
||||||
</Button>
|
</Button>
|
||||||
</HoverCardTrigger>
|
</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 { Alert, AlertDescription } from "@workspace/ui/components/alert";
|
||||||
import { Badge } from "@workspace/ui/components/badge";
|
import { Badge } from "@workspace/ui/components/badge";
|
||||||
import { Button } from "@workspace/ui/components/button";
|
import { Button } from "@workspace/ui/components/button";
|
||||||
@ -42,6 +42,7 @@ export default function UserSubscription({ userId }: { userId: number }) {
|
|||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const ref = useRef<ProTableActions>(null);
|
const ref = useRef<ProTableActions>(null);
|
||||||
const [sharedInfo, setSharedInfo] = useState<SharedInfo | null>(null);
|
const [sharedInfo, setSharedInfo] = useState<SharedInfo | null>(null);
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const request = useCallback(
|
const request = useCallback(
|
||||||
async (pagination: { page: number; size: number }) => {
|
async (pagination: { page: number; size: number }) => {
|
||||||
@ -116,19 +117,23 @@ export default function UserSubscription({ userId }: { userId: number }) {
|
|||||||
<span className="flex gap-2">
|
<span className="flex gap-2">
|
||||||
<Button asChild size="sm" variant="outline">
|
<Button asChild size="sm" variant="outline">
|
||||||
<Link
|
<Link
|
||||||
search={{ user_id: String(sharedInfo.ownerUserId) }}
|
search={{ user_id: sharedInfo.ownerUserId }}
|
||||||
to="/dashboard/family"
|
to="/dashboard/family"
|
||||||
>
|
>
|
||||||
{t("viewDeviceGroup", "View Device Group")}
|
{t("viewDeviceGroup", "View Device Group")}
|
||||||
</Link>
|
</Link>
|
||||||
</Button>
|
</Button>
|
||||||
<Button asChild size="sm" variant="outline">
|
<Button
|
||||||
<Link
|
onClick={() => {
|
||||||
search={{ user_id: String(sharedInfo.ownerUserId) }}
|
navigate({
|
||||||
to="/dashboard/user"
|
to: "/dashboard/user",
|
||||||
|
search: { user_id: sharedInfo.ownerUserId },
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
size="sm"
|
||||||
|
variant="outline"
|
||||||
>
|
>
|
||||||
{t("viewOwner", "View Owner")}
|
{t("viewOwner", "View Owner")}
|
||||||
</Link>
|
|
||||||
</Button>
|
</Button>
|
||||||
</span>
|
</span>
|
||||||
</AlertDescription>
|
</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 = {
|
type FamilyMemberItem = {
|
||||||
user_id: number;
|
user_id: number;
|
||||||
identifier: string;
|
identifier: string;
|
||||||
|
auth_type: string;
|
||||||
device_no: string;
|
device_no: string;
|
||||||
|
device_type: string;
|
||||||
role: number;
|
role: number;
|
||||||
role_name: string;
|
role_name: string;
|
||||||
status: number;
|
status: number;
|
||||||
@ -2621,6 +2623,7 @@ declare namespace API {
|
|||||||
family_id: number;
|
family_id: number;
|
||||||
owner_user_id: number;
|
owner_user_id: number;
|
||||||
owner_identifier: string;
|
owner_identifier: string;
|
||||||
|
owner_auth_type: string;
|
||||||
status: string;
|
status: string;
|
||||||
active_member_count: number;
|
active_member_count: number;
|
||||||
max_members: number;
|
max_members: number;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user