增加备注功能
This commit is contained in:
@@ -31,6 +31,13 @@ import {
|
|||||||
SheetTitle,
|
SheetTitle,
|
||||||
SheetTrigger,
|
SheetTrigger,
|
||||||
} from "@workspace/ui/components/sheet";
|
} from "@workspace/ui/components/sheet";
|
||||||
|
import { FilePenLine } from 'lucide-react';
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
PopoverClose,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverTrigger,
|
||||||
|
} from '@workspace/ui/components/popover';
|
||||||
import { Switch } from "@workspace/ui/components/switch";
|
import { Switch } from "@workspace/ui/components/switch";
|
||||||
import {
|
import {
|
||||||
Tabs,
|
Tabs,
|
||||||
@@ -39,7 +46,7 @@ import {
|
|||||||
TabsTrigger,
|
TabsTrigger,
|
||||||
} from "@workspace/ui/components/tabs";
|
} from "@workspace/ui/components/tabs";
|
||||||
import { Combobox } from "@workspace/ui/composed/combobox";
|
import { Combobox } from "@workspace/ui/composed/combobox";
|
||||||
import { ConfirmButton } from "@workspace/ui/composed/confirm-button";
|
// import { ConfirmButton } from "@workspace/ui/composed/confirm-button";
|
||||||
import {
|
import {
|
||||||
ProTable,
|
ProTable,
|
||||||
type ProTableActions,
|
type ProTableActions,
|
||||||
@@ -50,13 +57,13 @@ import {
|
|||||||
} from "@workspace/ui/services/admin/group";
|
} from "@workspace/ui/services/admin/group";
|
||||||
import {
|
import {
|
||||||
createUser,
|
createUser,
|
||||||
deleteUser,
|
// deleteUser,
|
||||||
getUserDetail,
|
getUserDetail,
|
||||||
getUserList,
|
getUserList,
|
||||||
updateUserBasicInfo,
|
updateUserBasicInfo,
|
||||||
} from "@workspace/ui/services/admin/user";
|
} from "@workspace/ui/services/admin/user";
|
||||||
import { parseDeviceType } from "@workspace/ui/utils/device";
|
import { parseDeviceType } from "@workspace/ui/utils/device";
|
||||||
import { useCallback, useRef, useState } from "react";
|
import React, { useRef, useState, useCallback } from 'react';
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { Display } from "@/components/display";
|
import { Display } from "@/components/display";
|
||||||
@@ -72,6 +79,44 @@ import { NotifySettingsForm } from "./user-profile/notify-settings-form";
|
|||||||
import UserSubscription from "./user-subscription";
|
import UserSubscription from "./user-subscription";
|
||||||
// import EditUserGroupDialog from "./edit-user-group-dialog";
|
// import EditUserGroupDialog from "./edit-user-group-dialog";
|
||||||
|
|
||||||
|
|
||||||
|
// 为 RemarkForm 组件定义 props 类型
|
||||||
|
interface RemarkFormProps {
|
||||||
|
initialRemark?: string | null;
|
||||||
|
onSave: (remark: string) => void;
|
||||||
|
CloseComponent: React.ComponentType<{ asChild?: boolean; children: React.ReactNode }>;
|
||||||
|
}
|
||||||
|
// 新的子组件,在管理它自己的备注状态
|
||||||
|
const RemarkForm: React.FC<RemarkFormProps> = ({ onSave, initialRemark, CloseComponent }) => {
|
||||||
|
const [remark, setRemark] = useState<string>(initialRemark ?? '');
|
||||||
|
|
||||||
|
const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
setRemark(event.target.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSaveClick = () => {
|
||||||
|
onSave(remark);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className='mb-2 text-sm font-semibold'>备注</div>
|
||||||
|
<Input
|
||||||
|
type='text'
|
||||||
|
value={remark}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
placeholder='在此输入备注...'
|
||||||
|
className='w-full'
|
||||||
|
/>
|
||||||
|
<CloseComponent asChild>
|
||||||
|
<Button onClick={handleSaveClick} variant='default' size={'sm'} className={'mt-2'}>
|
||||||
|
保存
|
||||||
|
</Button>
|
||||||
|
</CloseComponent>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default function User() {
|
export default function User() {
|
||||||
const { t } = useTranslation("user");
|
const { t } = useTranslation("user");
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
@@ -139,7 +184,7 @@ export default function User() {
|
|||||||
userId={row.id}
|
userId={row.id}
|
||||||
/>,
|
/>,
|
||||||
<PreviewNodesDialog key="preview-nodes" userId={row.id} />,
|
<PreviewNodesDialog key="preview-nodes" userId={row.id} />,
|
||||||
<ConfirmButton
|
/* <ConfirmButton
|
||||||
cancelText={t("cancel", "Cancel")}
|
cancelText={t("cancel", "Cancel")}
|
||||||
confirmText={t("confirm", "Confirm")}
|
confirmText={t("confirm", "Confirm")}
|
||||||
description={t(
|
description={t(
|
||||||
@@ -156,7 +201,7 @@ export default function User() {
|
|||||||
trigger={
|
trigger={
|
||||||
<Button variant="destructive">{t("delete", "Delete")}</Button>
|
<Button variant="destructive">{t("delete", "Delete")}</Button>
|
||||||
}
|
}
|
||||||
/>,
|
/>,*/
|
||||||
<DropdownMenu key="more" modal={false}>
|
<DropdownMenu key="more" modal={false}>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
<Button variant="outline">{t("more", "More")}</Button>
|
<Button variant="outline">{t("more", "More")}</Button>
|
||||||
@@ -232,7 +277,7 @@ export default function User() {
|
|||||||
accessorKey: "id",
|
accessorKey: "id",
|
||||||
header: "ID",
|
header: "ID",
|
||||||
},
|
},
|
||||||
{
|
/*{
|
||||||
id: "deleted_at",
|
id: "deleted_at",
|
||||||
accessorKey: "deleted_at",
|
accessorKey: "deleted_at",
|
||||||
header: t("isDeleted", "Deleted"),
|
header: t("isDeleted", "Deleted"),
|
||||||
@@ -244,11 +289,11 @@ export default function User() {
|
|||||||
<Badge variant="outline">{t("normal", "Normal")}</Badge>
|
<Badge variant="outline">{t("normal", "Normal")}</Badge>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},*/
|
||||||
{
|
{
|
||||||
id: "auth_methods",
|
id: "auth_methods",
|
||||||
accessorKey: "auth_methods",
|
accessorKey: "auth_methods",
|
||||||
header: t("userName", "Username"),
|
header: '设备码/邮箱',
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const method = row.original.auth_methods?.[0];
|
const method = row.original.auth_methods?.[0];
|
||||||
const identifier = method?.auth_identifier || "";
|
const identifier = method?.auth_identifier || "";
|
||||||
@@ -259,23 +304,48 @@ export default function User() {
|
|||||||
const display = isDevice ? deviceNo || identifier : identifier;
|
const display = isDevice ? deviceNo || identifier : identifier;
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<Badge
|
{/* <Badge
|
||||||
className="mr-1 uppercase"
|
className="mr-1 uppercase"
|
||||||
title={method?.verified ? t("verified", "Verified") : ""}
|
title={method?.verified ? t("verified", "Verified") : ""}
|
||||||
>
|
>
|
||||||
{method?.auth_type}
|
{method?.auth_type}
|
||||||
</Badge>
|
</Badge>*/}
|
||||||
{deviceType && (
|
{deviceType && (
|
||||||
<Badge className="mr-1" variant="secondary">
|
<Badge className="mr-1" variant="secondary">
|
||||||
{deviceType}
|
{deviceType}
|
||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
<span title={isDevice ? display : undefined}>{display}</span>
|
<span title={isDevice ? display : undefined}>{display}</span>
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger>
|
||||||
|
<div className={'flex items-center'}>
|
||||||
|
{row.original?.remark ? `(${row.original.remark})` : ''}
|
||||||
|
<FilePenLine size={14} className={'text-primary ml-2'} />
|
||||||
|
</div>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent className={'w-64'}>
|
||||||
|
<RemarkForm
|
||||||
|
initialRemark={row.original.remark}
|
||||||
|
CloseComponent={PopoverClose}
|
||||||
|
onSave={async (remark) => {
|
||||||
|
const {
|
||||||
|
id,
|
||||||
|
} = row.original;
|
||||||
|
await updateUserBasicInfo({
|
||||||
|
user_id: id,
|
||||||
|
remark,
|
||||||
|
} as unknown as API.UpdateUserBasiceInfoRequest);
|
||||||
|
toast.success(t('updateSuccess'));
|
||||||
|
ref.current?.refresh();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
/* {
|
||||||
id: "balance",
|
id: "balance",
|
||||||
accessorKey: "balance",
|
accessorKey: "balance",
|
||||||
header: t("balance", "Balance"),
|
header: t("balance", "Balance"),
|
||||||
@@ -290,7 +360,7 @@ export default function User() {
|
|||||||
cell: ({ row }) => (
|
cell: ({ row }) => (
|
||||||
<Display type="currency" value={row.getValue("gift_amount")} />
|
<Display type="currency" value={row.getValue("gift_amount")} />
|
||||||
),
|
),
|
||||||
},
|
},*/
|
||||||
{
|
{
|
||||||
id: "commission",
|
id: "commission",
|
||||||
accessorKey: "commission",
|
accessorKey: "commission",
|
||||||
@@ -531,12 +601,11 @@ function UserSearchBar({
|
|||||||
}}
|
}}
|
||||||
value={searchType}
|
value={searchType}
|
||||||
>
|
>
|
||||||
<SelectTrigger className="w-24">
|
<SelectTrigger className="w-48">
|
||||||
<SelectValue />
|
<SelectValue />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectItem value="email">{t("email", "Email")}</SelectItem>
|
<SelectItem value="email">设备码/邮箱</SelectItem>
|
||||||
<SelectItem value="device">{t("deviceSearch", "Device")}</SelectItem>
|
|
||||||
<SelectItem value="user_id">{t("userId", "User ID")}</SelectItem>
|
<SelectItem value="user_id">{t("userId", "User ID")}</SelectItem>
|
||||||
<SelectItem value="subscribe_id">
|
<SelectItem value="subscribe_id">
|
||||||
{t("subscription", "Subscription")}
|
{t("subscription", "Subscription")}
|
||||||
|
|||||||
@@ -43,5 +43,6 @@ function PopoverAnchor({
|
|||||||
}: React.ComponentProps<typeof PopoverPrimitive.Anchor>) {
|
}: React.ComponentProps<typeof PopoverPrimitive.Anchor>) {
|
||||||
return <PopoverPrimitive.Anchor data-slot="popover-anchor" {...props} />;
|
return <PopoverPrimitive.Anchor data-slot="popover-anchor" {...props} />;
|
||||||
}
|
}
|
||||||
|
const PopoverClose = PopoverPrimitive.Close;
|
||||||
|
|
||||||
export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor };
|
export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor, PopoverClose };
|
||||||
|
|||||||
+1
@@ -2438,6 +2438,7 @@ declare namespace API {
|
|||||||
group_locked: boolean;
|
group_locked: boolean;
|
||||||
auth_methods: UserAuthMethod[];
|
auth_methods: UserAuthMethod[];
|
||||||
user_devices: UserDevice[];
|
user_devices: UserDevice[];
|
||||||
|
remark: string;
|
||||||
rules: string[];
|
rules: string[];
|
||||||
created_at: number;
|
created_at: number;
|
||||||
updated_at: number;
|
updated_at: number;
|
||||||
|
|||||||
Reference in New Issue
Block a user